1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.connector.basic;
20
21 import java.io.IOException;
22 import java.io.UncheckedIOException;
23 import java.net.URI;
24 import java.nio.file.Path;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.atomic.AtomicBoolean;
32
33 import org.eclipse.aether.RepositorySystemSession;
34 import org.eclipse.aether.metadata.Metadata;
35 import org.eclipse.aether.repository.RemoteRepository;
36 import org.eclipse.aether.spi.checksums.ProvidedChecksumsSource;
37 import org.eclipse.aether.spi.connector.ArtifactDownload;
38 import org.eclipse.aether.spi.connector.ArtifactTransfer;
39 import org.eclipse.aether.spi.connector.ArtifactUpload;
40 import org.eclipse.aether.spi.connector.MetadataDownload;
41 import org.eclipse.aether.spi.connector.MetadataTransfer;
42 import org.eclipse.aether.spi.connector.MetadataUpload;
43 import org.eclipse.aether.spi.connector.RepositoryConnector;
44 import org.eclipse.aether.spi.connector.Transfer;
45 import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
46 import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmHelper;
47 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy;
48 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider;
49 import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
50 import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider;
51 import org.eclipse.aether.spi.connector.transport.GetTask;
52 import org.eclipse.aether.spi.connector.transport.PeekTask;
53 import org.eclipse.aether.spi.connector.transport.PutTask;
54 import org.eclipse.aether.spi.connector.transport.Transporter;
55 import org.eclipse.aether.spi.connector.transport.TransporterProvider;
56 import org.eclipse.aether.spi.io.ChecksumProcessor;
57 import org.eclipse.aether.spi.io.PathProcessor;
58 import org.eclipse.aether.transfer.ChecksumFailureException;
59 import org.eclipse.aether.transfer.NoRepositoryConnectorException;
60 import org.eclipse.aether.transfer.NoRepositoryLayoutException;
61 import org.eclipse.aether.transfer.TransferEvent;
62 import org.eclipse.aether.transfer.TransferResource;
63 import org.eclipse.aether.util.ConfigUtils;
64 import org.eclipse.aether.util.concurrency.RunnableErrorForwarder;
65 import org.eclipse.aether.util.concurrency.SmartExecutor;
66 import org.eclipse.aether.util.concurrency.SmartExecutorUtils;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70 import static java.util.Objects.requireNonNull;
71 import static org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.CONFIG_PROP_DOWNSTREAM_THREADS;
72 import static org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.CONFIG_PROP_FAIL_ON_CHECKSUM_UPLOAD_FAILURE;
73 import static org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.CONFIG_PROP_INCLUDED_CHECKSUMS;
74 import static org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.CONFIG_PROP_PARALLEL_PUT;
75 import static org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.CONFIG_PROP_PERSISTED_CHECKSUMS;
76 import static org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.CONFIG_PROP_THREADS;
77 import static org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.CONFIG_PROP_UPSTREAM_THREADS;
78 import static org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.DEFAULT_FAIL_ON_CHECKSUM_UPLOAD_FAILURE;
79 import static org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.DEFAULT_INCLUDED_CHECKSUMS;
80 import static org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.DEFAULT_PARALLEL_PUT;
81 import static org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.DEFAULT_PERSISTED_CHECKSUMS;
82 import static org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.DEFAULT_THREADS;
83
84
85
86
87 final class BasicRepositoryConnector implements RepositoryConnector {
88 private static final Logger LOGGER = LoggerFactory.getLogger(BasicRepositoryConnector.class);
89
90 private final Map<String, ProvidedChecksumsSource> providedChecksumsSources;
91
92 private final PathProcessor pathProcessor;
93
94 private final ChecksumProcessor checksumProcessor;
95
96 private final RemoteRepository repository;
97
98 private final RepositorySystemSession session;
99
100 private final Transporter transporter;
101
102 private final RepositoryLayout layout;
103
104 private final ChecksumPolicyProvider checksumPolicyProvider;
105
106 private final int maxDownstreamThreads;
107
108 private final int maxUpstreamThreads;
109
110 private final boolean includedChecksums;
111
112 private final boolean parallelPut;
113
114 private final boolean persistedChecksums;
115
116 private final boolean failOnChecksumUploadFailure;
117
118 private final ConcurrentHashMap<Boolean, SmartExecutor> executors;
119
120 private final AtomicBoolean closed;
121
122 @SuppressWarnings("checkstyle:parameternumber")
123 BasicRepositoryConnector(
124 RepositorySystemSession session,
125 RemoteRepository repository,
126 TransporterProvider transporterProvider,
127 RepositoryLayoutProvider layoutProvider,
128 ChecksumPolicyProvider checksumPolicyProvider,
129 PathProcessor pathProcessor,
130 ChecksumProcessor checksumProcessor,
131 Map<String, ProvidedChecksumsSource> providedChecksumsSources)
132 throws NoRepositoryConnectorException {
133 try {
134 layout = layoutProvider.newRepositoryLayout(session, repository);
135 } catch (NoRepositoryLayoutException e) {
136 throw new NoRepositoryConnectorException(repository, e.getMessage(), e);
137 }
138 try {
139 transporter = transporterProvider.newTransporter(session, repository);
140 } catch (RuntimeException e) {
141 throw new NoRepositoryConnectorException(
142 repository, "Transporter configuration issue: " + e.getMessage(), e);
143 } catch (Exception e) {
144 throw new NoRepositoryConnectorException(repository, e.getMessage(), e);
145 }
146 this.checksumPolicyProvider = checksumPolicyProvider;
147
148 this.session = session;
149 this.repository = repository;
150 this.checksumProcessor = checksumProcessor;
151 this.pathProcessor = pathProcessor;
152 this.providedChecksumsSources = providedChecksumsSources;
153 this.executors = new ConcurrentHashMap<>();
154 this.closed = new AtomicBoolean(false);
155
156 maxUpstreamThreads = ConfigUtils.getInteger(
157 session,
158 DEFAULT_THREADS,
159 CONFIG_PROP_UPSTREAM_THREADS + "." + repository.getId(),
160 CONFIG_PROP_UPSTREAM_THREADS,
161 CONFIG_PROP_THREADS);
162 maxDownstreamThreads = ConfigUtils.getInteger(
163 session,
164 DEFAULT_THREADS,
165 CONFIG_PROP_DOWNSTREAM_THREADS + "." + repository.getId(),
166 CONFIG_PROP_DOWNSTREAM_THREADS,
167 CONFIG_PROP_THREADS);
168 includedChecksums = ConfigUtils.getBoolean(
169 session, DEFAULT_INCLUDED_CHECKSUMS, CONFIG_PROP_INCLUDED_CHECKSUMS, "aether.connector.smartChecksums");
170 parallelPut = ConfigUtils.getBoolean(
171 session,
172 DEFAULT_PARALLEL_PUT,
173 CONFIG_PROP_PARALLEL_PUT + "." + repository.getId(),
174 CONFIG_PROP_PARALLEL_PUT);
175 persistedChecksums =
176 ConfigUtils.getBoolean(session, DEFAULT_PERSISTED_CHECKSUMS, CONFIG_PROP_PERSISTED_CHECKSUMS);
177 failOnChecksumUploadFailure = ConfigUtils.getBoolean(
178 session,
179 DEFAULT_FAIL_ON_CHECKSUM_UPLOAD_FAILURE,
180 CONFIG_PROP_FAIL_ON_CHECKSUM_UPLOAD_FAILURE + "." + repository.getId(),
181 CONFIG_PROP_FAIL_ON_CHECKSUM_UPLOAD_FAILURE);
182 }
183
184
185
186
187 private SmartExecutor getExecutor(boolean downstream, int tasks) {
188 int maxThreads = downstream ? maxDownstreamThreads : maxUpstreamThreads;
189 if (maxThreads <= 1 || tasks <= 1) {
190
191 return null;
192 }
193
194
195 return executors.computeIfAbsent(
196 downstream,
197 k -> SmartExecutorUtils.smartExecutor(
198 session, null, maxThreads, getClass().getSimpleName() + '-' + repository.getHost() + '-'));
199 }
200
201 @Override
202 public void close() {
203 if (closed.compareAndSet(false, true)) {
204 for (SmartExecutor executor : executors.values()) {
205 executor.close();
206 }
207 transporter.close();
208 }
209 }
210
211 private void failIfClosed() {
212 if (closed.get()) {
213 throw new IllegalStateException("connector already closed");
214 }
215 }
216
217 @Override
218 public void get(
219 Collection<? extends ArtifactDownload> artifactDownloads,
220 Collection<? extends MetadataDownload> metadataDownloads) {
221 failIfClosed();
222
223 Collection<? extends ArtifactDownload> safeArtifactDownloads = safe(artifactDownloads);
224 Collection<? extends MetadataDownload> safeMetadataDownloads = safe(metadataDownloads);
225
226 SmartExecutor executor = getExecutor(true, safeArtifactDownloads.size() + safeMetadataDownloads.size());
227 RunnableErrorForwarder errorForwarder = new RunnableErrorForwarder();
228 List<ChecksumAlgorithmFactory> checksumAlgorithmFactories = layout.getChecksumAlgorithmFactories(false);
229
230 boolean first = true;
231
232 for (MetadataDownload transfer : safeMetadataDownloads) {
233 Map<String, String> providedChecksums = Collections.emptyMap();
234 for (ProvidedChecksumsSource providedChecksumsSource : providedChecksumsSources.values()) {
235 Map<String, String> provided = providedChecksumsSource.getProvidedMetadataChecksums(
236 session, transfer, repository, checksumAlgorithmFactories);
237
238 if (provided != null) {
239 providedChecksums = provided;
240 break;
241 }
242 }
243
244 URI location = layout.getLocation(transfer.getMetadata(), false);
245
246 TransferResource resource = newTransferResource(location, transfer);
247 TransferEvent.Builder builder = newEventBuilder(resource, false, false);
248 MetadataTransportListener listener = new MetadataTransportListener(transfer, repository, builder);
249
250 ChecksumPolicy checksumPolicy = newChecksumPolicy(transfer.getChecksumPolicy(), resource);
251 List<RepositoryLayout.ChecksumLocation> checksumLocations = null;
252 if (checksumPolicy != null) {
253 checksumLocations = layout.getChecksumLocations(transfer.getMetadata(), false, location);
254 }
255
256 Runnable task = new GetTaskRunner(
257 location,
258 transfer.getPath(),
259 checksumPolicy,
260 checksumAlgorithmFactories,
261 checksumLocations,
262 providedChecksums,
263 listener);
264 if (executor == null || first) {
265 task.run();
266 first = false;
267 } else {
268 executor.submit(errorForwarder.wrap(task));
269 }
270 }
271
272 for (ArtifactDownload transfer : safeArtifactDownloads) {
273 Map<String, String> providedChecksums = Collections.emptyMap();
274 for (ProvidedChecksumsSource providedChecksumsSource : providedChecksumsSources.values()) {
275 Map<String, String> provided = providedChecksumsSource.getProvidedArtifactChecksums(
276 session, transfer, repository, checksumAlgorithmFactories);
277
278 if (provided != null) {
279 providedChecksums = provided;
280 break;
281 }
282 }
283
284 URI location = layout.getLocation(transfer.getArtifact(), false);
285
286 TransferResource resource = newTransferResource(location, transfer);
287 TransferEvent.Builder builder = newEventBuilder(resource, false, transfer.isExistenceCheck());
288 ArtifactTransportListener listener = new ArtifactTransportListener(transfer, repository, builder);
289
290 Runnable task;
291 if (transfer.isExistenceCheck()) {
292 task = new PeekTaskRunner(location, listener);
293 } else {
294 ChecksumPolicy checksumPolicy = newChecksumPolicy(transfer.getChecksumPolicy(), resource);
295 List<RepositoryLayout.ChecksumLocation> checksumLocations = null;
296 if (checksumPolicy != null) {
297 checksumLocations = layout.getChecksumLocations(transfer.getArtifact(), false, location);
298 }
299
300 task = new GetTaskRunner(
301 location,
302 transfer.getPath(),
303 checksumPolicy,
304 checksumAlgorithmFactories,
305 checksumLocations,
306 providedChecksums,
307 listener);
308 }
309 if (executor == null || first) {
310 task.run();
311 first = false;
312 } else {
313 executor.submit(errorForwarder.wrap(task));
314 }
315 }
316
317 errorForwarder.await();
318 }
319
320 @Override
321 public void put(
322 Collection<? extends ArtifactUpload> artifactUploads,
323 Collection<? extends MetadataUpload> metadataUploads) {
324 failIfClosed();
325
326 Collection<? extends ArtifactUpload> safeArtifactUploads = safe(artifactUploads);
327 Collection<? extends MetadataUpload> safeMetadataUploads = safe(metadataUploads);
328
329 SmartExecutor executor =
330 getExecutor(false, parallelPut ? safeArtifactUploads.size() + safeMetadataUploads.size() : 1);
331 RunnableErrorForwarder errorForwarder = new RunnableErrorForwarder();
332
333 boolean first = true;
334
335 for (ArtifactUpload transfer : safeArtifactUploads) {
336 URI location = layout.getLocation(transfer.getArtifact(), true);
337
338 TransferResource resource = newTransferResource(location, transfer);
339 TransferEvent.Builder builder = newEventBuilder(resource, true, false);
340 ArtifactTransportListener listener = new ArtifactTransportListener(transfer, repository, builder);
341
342 List<RepositoryLayout.ChecksumLocation> checksumLocations =
343 layout.getChecksumLocations(transfer.getArtifact(), true, location);
344
345 Runnable task = new PutTaskRunner(location, transfer.getPath(), checksumLocations, listener);
346 if (executor == null || first) {
347 task.run();
348 first = false;
349 } else {
350 executor.submit(errorForwarder.wrap(task));
351 }
352 }
353
354 errorForwarder.await();
355
356 for (List<? extends MetadataUpload> transferGroup : groupUploads(safeMetadataUploads)) {
357 for (MetadataUpload transfer : transferGroup) {
358 URI location = layout.getLocation(transfer.getMetadata(), true);
359
360 TransferResource resource = newTransferResource(location, transfer);
361 TransferEvent.Builder builder = newEventBuilder(resource, true, false);
362 MetadataTransportListener listener = new MetadataTransportListener(transfer, repository, builder);
363
364 List<RepositoryLayout.ChecksumLocation> checksumLocations =
365 layout.getChecksumLocations(transfer.getMetadata(), true, location);
366
367 Runnable task = new PutTaskRunner(location, transfer.getPath(), checksumLocations, listener);
368 if (executor == null || first) {
369 task.run();
370 first = false;
371 } else {
372 executor.submit(errorForwarder.wrap(task));
373 }
374 }
375
376 errorForwarder.await();
377 }
378 }
379
380
381
382
383
384
385 private static List<List<MetadataUpload>> groupUploads(Collection<? extends MetadataUpload> metadataUploads) {
386 ArrayList<MetadataUpload> v = new ArrayList<>();
387 ArrayList<MetadataUpload> a = new ArrayList<>();
388 ArrayList<MetadataUpload> g = new ArrayList<>();
389 ArrayList<MetadataUpload> r = new ArrayList<>();
390
391 for (MetadataUpload transfer : metadataUploads) {
392 Metadata metadata = transfer.getMetadata();
393 if (!"".equals(metadata.getVersion())) {
394 v.add(transfer);
395 } else if (!"".equals(metadata.getArtifactId())) {
396 a.add(transfer);
397 } else if (!"".equals(metadata.getGroupId())) {
398 g.add(transfer);
399 } else {
400 r.add(transfer);
401 }
402 }
403
404 List<List<MetadataUpload>> result = new ArrayList<>(4);
405 if (!v.isEmpty()) {
406 result.add(v);
407 }
408 if (!a.isEmpty()) {
409 result.add(a);
410 }
411 if (!g.isEmpty()) {
412 result.add(g);
413 }
414 if (!r.isEmpty()) {
415 result.add(r);
416 }
417 return result;
418 }
419
420 private static <T> Collection<T> safe(Collection<T> items) {
421 return (items != null) ? items : Collections.emptyList();
422 }
423
424 private TransferResource newTransferResource(URI path, Transfer transfer) {
425 if (transfer instanceof ArtifactTransfer) {
426 ArtifactTransfer artifactTransfer = (ArtifactTransfer) transfer;
427 return new TransferResource(
428 repository.getId(),
429 repository.getUrl(),
430 path.toString(),
431 artifactTransfer.getPath(),
432 artifactTransfer.getArtifact(),
433 artifactTransfer.getTrace());
434 } else if (transfer instanceof MetadataTransfer) {
435 MetadataTransfer metadataTransfer = (MetadataTransfer) transfer;
436 return new TransferResource(
437 repository.getId(),
438 repository.getUrl(),
439 path.toString(),
440 metadataTransfer.getPath(),
441 metadataTransfer.getMetadata(),
442 metadataTransfer.getTrace());
443 } else {
444 throw new IllegalArgumentException("Accepting only artifact or metadata transfers");
445 }
446 }
447
448 private TransferEvent.Builder newEventBuilder(TransferResource resource, boolean upload, boolean peek) {
449 TransferEvent.Builder builder = new TransferEvent.Builder(session, resource);
450 if (upload) {
451 builder.setRequestType(TransferEvent.RequestType.PUT);
452 } else if (!peek) {
453 builder.setRequestType(TransferEvent.RequestType.GET);
454 } else {
455 builder.setRequestType(TransferEvent.RequestType.GET_EXISTENCE);
456 }
457 return builder;
458 }
459
460 private ChecksumPolicy newChecksumPolicy(String policy, TransferResource resource) {
461 return checksumPolicyProvider.newChecksumPolicy(session, repository, resource, policy);
462 }
463
464 @Override
465 public String toString() {
466 return BasicRepositoryConnectorFactory.NAME + "( " + repository + " )";
467 }
468
469 abstract class TaskRunner implements Runnable {
470
471 protected final URI path;
472
473 protected final TransferTransportListener<?> listener;
474
475 TaskRunner(URI path, TransferTransportListener<?> listener) {
476 this.path = path;
477 this.listener = listener;
478 }
479
480 @Override
481 public void run() {
482 try {
483 listener.transferInitiated();
484 runTask();
485 listener.transferSucceeded();
486 } catch (Exception e) {
487 listener.transferFailed(e, transporter.classify(e));
488 }
489 }
490
491 protected abstract void runTask() throws Exception;
492 }
493
494 class PeekTaskRunner extends TaskRunner {
495
496 PeekTaskRunner(URI path, TransferTransportListener<?> listener) {
497 super(path, listener);
498 }
499
500 @Override
501 protected void runTask() throws Exception {
502 transporter.peek(new PeekTask(path));
503 }
504 }
505
506 class GetTaskRunner extends TaskRunner implements ChecksumValidator.ChecksumFetcher {
507
508 private final Path file;
509
510 private final ChecksumValidator checksumValidator;
511
512 GetTaskRunner(
513 URI path,
514 Path file,
515 ChecksumPolicy checksumPolicy,
516 List<ChecksumAlgorithmFactory> checksumAlgorithmFactories,
517 List<RepositoryLayout.ChecksumLocation> checksumLocations,
518 Map<String, String> providedChecksums,
519 TransferTransportListener<?> listener) {
520 super(path, listener);
521 this.file = requireNonNull(file, "destination file cannot be null");
522 checksumValidator = new ChecksumValidator(
523 file,
524 checksumAlgorithmFactories,
525 pathProcessor,
526 checksumProcessor,
527 this,
528 checksumPolicy,
529 providedChecksums,
530 safe(checksumLocations));
531 }
532
533 @Override
534 public boolean fetchChecksum(URI remote, Path local) throws Exception {
535 try {
536 transporter.get(new GetTask(remote).setDataPath(local));
537 } catch (Exception e) {
538 if (transporter.classify(e) == Transporter.ERROR_NOT_FOUND) {
539 return false;
540 }
541 throw e;
542 }
543 return true;
544 }
545
546 @Override
547 protected void runTask() throws Exception {
548 try (PathProcessor.CollocatedTempFile tempFile = pathProcessor.newTempFile(file)) {
549 final Path tmp = tempFile.getPath();
550 listener.setChecksumCalculator(checksumValidator.newChecksumCalculator(tmp));
551 for (int firstTrial = 0, lastTrial = 1, trial = firstTrial; ; trial++) {
552 GetTask task = new GetTask(path).setDataPath(tmp, false).setListener(listener);
553 transporter.get(task);
554 try {
555 checksumValidator.validate(
556 listener.getChecksums(), includedChecksums ? task.getChecksums() : null);
557 break;
558 } catch (ChecksumFailureException e) {
559 boolean retry = trial < lastTrial && e.isRetryWorthy();
560 if (!retry && !checksumValidator.handle(e)) {
561 throw e;
562 }
563 listener.transferCorrupted(e);
564 if (retry) {
565 checksumValidator.retry();
566 } else {
567 break;
568 }
569 }
570 }
571 tempFile.move();
572 if (persistedChecksums) {
573 checksumValidator.commit();
574 }
575 }
576 }
577 }
578
579 class PutTaskRunner extends TaskRunner {
580
581 private final Path file;
582
583 private final Collection<RepositoryLayout.ChecksumLocation> checksumLocations;
584
585 PutTaskRunner(
586 URI path,
587 Path file,
588 List<RepositoryLayout.ChecksumLocation> checksumLocations,
589 TransferTransportListener<?> listener) {
590 super(path, listener);
591 this.file = requireNonNull(file, "source file cannot be null");
592 this.checksumLocations = safe(checksumLocations);
593 }
594
595 @SuppressWarnings("checkstyle:innerassignment")
596 @Override
597 protected void runTask() throws Exception {
598 transporter.put(new PutTask(path).setDataPath(file).setListener(listener));
599 uploadChecksums(file, null);
600 }
601
602
603
604
605
606 private void uploadChecksums(Path path, byte[] bytes) throws Exception {
607 if (checksumLocations.isEmpty()) {
608 return;
609 }
610 Map<String, String> sumsByAlgo;
611 try {
612 ArrayList<ChecksumAlgorithmFactory> algorithms = new ArrayList<>();
613 for (RepositoryLayout.ChecksumLocation checksumLocation : checksumLocations) {
614 algorithms.add(checksumLocation.getChecksumAlgorithmFactory());
615 }
616
617 if (bytes != null) {
618 sumsByAlgo = ChecksumAlgorithmHelper.calculate(bytes, algorithms);
619 } else {
620 sumsByAlgo = ChecksumAlgorithmHelper.calculate(path, algorithms);
621 }
622 } catch (IOException e) {
623 LOGGER.warn("Failed to upload checksums for {}", file, e);
624 throw new UncheckedIOException(e);
625 }
626
627 for (RepositoryLayout.ChecksumLocation checksumLocation : checksumLocations) {
628 uploadChecksum(
629 checksumLocation.getLocation(),
630 sumsByAlgo.get(
631 checksumLocation.getChecksumAlgorithmFactory().getName()));
632 }
633 }
634
635 private void uploadChecksum(URI location, Object checksum) throws Exception {
636 try {
637 if (checksum instanceof Exception) {
638 throw (Exception) checksum;
639 }
640 transporter.put(new PutTask(location).setDataString((String) checksum));
641 } catch (Exception e) {
642 if (failOnChecksumUploadFailure) {
643
644
645
646 LOGGER.error("Failed to upload checksum to {}", location, e);
647 throw e;
648 }
649 LOGGER.warn("Failed to upload checksum to {}", location, e);
650 }
651 }
652 }
653 }