1 /*
2 * Copyright 2015-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15 package com.amazonaws.http.timers.client;
16
17 import java.util.concurrent.ScheduledFuture;
18 import java.util.concurrent.ScheduledThreadPoolExecutor;
19 import java.util.concurrent.TimeUnit;
20
21 import com.amazonaws.annotation.SdkInternalApi;
22 import com.amazonaws.annotation.SdkTestInternalApi;
23 import com.amazonaws.annotation.ThreadSafe;
24 import com.amazonaws.http.AmazonHttpClient;
25 import com.amazonaws.http.timers.TimeoutThreadPoolBuilder;
26
27 /**
28 * Represents a timer to enforce a timeout on the total client execution time. That is the time
29 * spent executing request handlers, any HTTP request including retries, unmarshalling, etc.
30 * Essentially all the time spent in {@link AmazonHttpClient}
31 */
32 // DO NOT override finalize(). The shutdown() method is called from AmazonHttpClient#shutdown()
33 // which is called from it's finalize() method. Since finalize methods can be be called in any
34 // order and even concurrently, we need to rely on AmazonHttpClient to call our shutdown() method.
35 @SdkInternalApi
36 @ThreadSafe
37 public class ClientExecutionTimer {
38
39 private static final String threadNamePrefix = "AwsSdkClientExecutionTimerThread";
40
41 private volatile ScheduledThreadPoolExecutor executor;
42
43 /**
44 * Start the timer with the specified timeout and return a object that can be used to track the
45 * state of the timer and cancel it if need be.
46 *
47 * @param clientExecutionTimeoutMillis
48 * A positive value here enables the timer, a non-positive value disables it and
49 * returns a dummy tracker task
50 * @return Implementation of {@link ClientExecutionAbortTrackerTaskImpl} to query the state of
51 * the task, provide it with up to date context, and cancel it if appropriate
52 */
53 public ClientExecutionAbortTrackerTask startTimer(int clientExecutionTimeoutMillis) {
54 if (isTimeoutDisabled(clientExecutionTimeoutMillis)) {
55 return NoOpClientExecutionAbortTrackerTask.INSTANCE;
56 } else if (executor == null) {
57 initializeExecutor();
58 }
59 return scheduleTimerTask(clientExecutionTimeoutMillis);
60 }
61
62 /**
63 * Executor is lazily initialized as it's not compatible with Java 6
64 */
65 private synchronized void initializeExecutor() {
66 if (executor == null) {
67 executor = TimeoutThreadPoolBuilder.buildDefaultTimeoutThreadPool(threadNamePrefix);
68 }
69 }
70
71 /**
72 * This method is current exposed for testing purposes
73 *
74 * @return The underlying {@link ScheduledThreadPoolExecutor}
75 */
76 @SdkTestInternalApi
77 public ScheduledThreadPoolExecutor getExecutor() {
78 return this.executor;
79 }
80
81 /**
82 * Shutdown the underlying {@link ScheduledThreadPoolExecutor}. Should be invoked when
83 * {@link AmazonHttpClient} is shutdown
84 */
85 public synchronized void shutdown() {
86 if (executor != null) {
87 executor.shutdown();
88 }
89 }
90
91 private ClientExecutionAbortTrackerTask scheduleTimerTask(int clientExecutionTimeoutMillis) {
92 ClientExecutionAbortTask timerTask = new ClientExecutionAbortTaskImpl(Thread.currentThread());
93 ScheduledFuture<?> timerTaskFuture = executor.schedule(timerTask, clientExecutionTimeoutMillis,
94 TimeUnit.MILLISECONDS);
95 return new ClientExecutionAbortTrackerTaskImpl(timerTask, timerTaskFuture);
96 }
97
98 private boolean isTimeoutDisabled(int clientExecutionTimeoutMillis) {
99 return clientExecutionTimeoutMillis <= 0;
100 }
101
102 }
103