@@ -80,8 +80,7 @@ final class ResumableUploadFutureImpl<ResponseT> implements ResumableUploadFutur
8080 private final ApiCallContext callContext ;
8181 private final ScheduledExecutorService executor ;
8282 private final ExponentialRetryAlgorithm recoveryAlgorithm ;
83- private final ResumableUploadProgressTracker progressTracker =
84- new ResumableUploadProgressTracker ();
83+ private final ResumableUploadProgressTracker progressTracker ;
8584 private final SettableApiFuture <ResponseT > resultFuture = SettableApiFuture .create ();
8685
8786 private volatile @ Nullable InputStream payload ;
@@ -120,7 +119,8 @@ static <ResponseT> ResumableUploadFutureImpl<ResponseT> create(
120119 queryStatusCallable ,
121120 options ,
122121 clientContext ,
123- recoveryAlgorithm );
122+ recoveryAlgorithm ,
123+ ResumableUploadProgressTracker .create ());
124124 try {
125125 future .start (payloadSupplier );
126126 } catch (Throwable t ) {
@@ -135,7 +135,8 @@ private ResumableUploadFutureImpl(
135135 UnaryCallable <QueryStatusRequest , QueryStatusResponse <ResponseT >> queryStatusCallable ,
136136 ResumableUploadOptions options ,
137137 ClientContext clientContext ,
138- ExponentialRetryAlgorithm recoveryAlgorithm ) {
138+ ExponentialRetryAlgorithm recoveryAlgorithm ,
139+ ResumableUploadProgressTracker progressTracker ) {
139140 this .startFuture = checkNotNull (startFuture , "startFuture must not be null" );
140141 this .uploadChunkCallable =
141142 checkNotNull (uploadChunkCallable , "uploadChunkCallable must not be null" );
@@ -147,6 +148,7 @@ private ResumableUploadFutureImpl(
147148 this .callContext = clientContext .getDefaultCallContext ();
148149 this .executor = checkNotNull (clientContext .getExecutor (), "executor must not be null" );
149150 this .recoveryAlgorithm = checkNotNull (recoveryAlgorithm , "recoveryAlgorithm must not be null" );
151+ this .progressTracker = checkNotNull (progressTracker , "progressTracker must not be null" );
150152 this .inFlightFuture = startFuture ;
151153 ResumableUploadProgressListener progressListener = options .getProgressListener ();
152154 if (progressListener != null ) {
@@ -155,10 +157,7 @@ private ResumableUploadFutureImpl(
155157 }
156158
157159 private void start (InputStreamSupplier payloadSupplier ) throws IOException {
158- Duration timeout = firstNonNull (options .getGlobalTimeout (), DEFAULT_GLOBAL_TIMEOUT );
159- ScheduledFuture <?> timeoutFuture =
160- executor .schedule (this ::onTimeout , timeout .toMillis (), TimeUnit .MILLISECONDS );
161- resultFuture .addListener (() -> timeoutFuture .cancel (false ), MoreExecutors .directExecutor ());
160+ setUpGlobalTimeout ();
162161 this .payload = checkNotNull (payloadSupplier .get (), "payload supplier returned null" );
163162 ApiFutures .addCallback (
164163 startFuture ,
@@ -174,43 +173,10 @@ public void onSuccess(ResumableUploadSession session) {
174173 return ;
175174 }
176175 ResumableUploadChunkCoordinator <ResponseT > coordinator =
177- new ResumableUploadChunkCoordinator <>(
178- uploadChunkCallable ,
179- queryStatusCallable ,
180- sessionUrl ,
181- payload ,
182- chunkSize ,
183- callContext ,
184- recoveryAlgorithm ,
185- executor ,
186- progressTracker );
187- ApiFuture <ResponseT > uploadFuture = coordinator .getFuture ();
188- synchronized (lock ) {
189- if (inFlightFuture == null ) {
190- return ;
191- }
192- effectiveChunkSize = chunkSize ;
193- uploadSessionUrl = sessionUrl ;
194- inFlightFuture = uploadFuture ;
176+ createAndRegisterCoordinator (sessionUrl , chunkSize );
177+ if (coordinator != null ) {
178+ coordinator .start ();
195179 }
196- ApiFutures .addCallback (
197- uploadFuture ,
198- new ApiFutureCallback <ResponseT >() {
199- @ Override
200- public void onSuccess (ResponseT response ) {
201- succeed (response );
202- }
203-
204- @ Override
205- public void onFailure (Throwable t ) {
206- if (t instanceof CancellationException ) {
207- return ;
208- }
209- fail (t );
210- }
211- },
212- MoreExecutors .directExecutor ());
213- coordinator .start ();
214180 }
215181
216182 @ Override
@@ -224,6 +190,55 @@ public void onFailure(Throwable t) {
224190 MoreExecutors .directExecutor ());
225191 }
226192
193+ private void setUpGlobalTimeout () {
194+ Duration timeout = firstNonNull (options .getGlobalTimeout (), DEFAULT_GLOBAL_TIMEOUT );
195+ ScheduledFuture <?> timeoutFuture =
196+ executor .schedule (this ::onTimeout , timeout .toMillis (), TimeUnit .MILLISECONDS );
197+ resultFuture .addListener (() -> timeoutFuture .cancel (false ), MoreExecutors .directExecutor ());
198+ }
199+
200+ private @ Nullable ResumableUploadChunkCoordinator <ResponseT > createAndRegisterCoordinator (
201+ String sessionUrl , int chunkSize ) {
202+ ResumableUploadChunkCoordinator <ResponseT > coordinator =
203+ new ResumableUploadChunkCoordinator <>(
204+ uploadChunkCallable ,
205+ queryStatusCallable ,
206+ sessionUrl ,
207+ checkNotNull (payload ),
208+ chunkSize ,
209+ callContext ,
210+ recoveryAlgorithm ,
211+ executor ,
212+ progressTracker );
213+ ApiFuture <ResponseT > uploadFuture = coordinator .getFuture ();
214+ synchronized (lock ) {
215+ if (inFlightFuture == null ) {
216+ return null ;
217+ }
218+ effectiveChunkSize = chunkSize ;
219+ uploadSessionUrl = sessionUrl ;
220+ inFlightFuture = uploadFuture ;
221+ }
222+ ApiFutures .addCallback (
223+ uploadFuture ,
224+ new ApiFutureCallback <ResponseT >() {
225+ @ Override
226+ public void onSuccess (ResponseT response ) {
227+ succeed (response );
228+ }
229+
230+ @ Override
231+ public void onFailure (Throwable t ) {
232+ if (t instanceof CancellationException ) {
233+ return ;
234+ }
235+ fail (t );
236+ }
237+ },
238+ MoreExecutors .directExecutor ());
239+ return coordinator ;
240+ }
241+
227242 private void onTimeout () {
228243 String message ;
229244 if (getUploadSessionUrl () != null ) {
0 commit comments