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