View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.repository.legacy;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.io.File;
26  import java.io.IOException;
27  import java.lang.reflect.Method;
28  import java.nio.charset.StandardCharsets;
29  import java.nio.file.Files;
30  import java.nio.file.Paths;
31  import java.nio.file.StandardCopyOption;
32  import java.nio.file.StandardOpenOption;
33  import java.security.NoSuchAlgorithmException;
34  import java.util.ArrayList;
35  import java.util.HashMap;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Properties;
39  
40  import org.apache.maven.artifact.Artifact;
41  import org.apache.maven.artifact.metadata.ArtifactMetadata;
42  import org.apache.maven.artifact.repository.ArtifactRepository;
43  import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
44  import org.apache.maven.plugin.LegacySupport;
45  import org.apache.maven.wagon.ConnectionException;
46  import org.apache.maven.wagon.ResourceDoesNotExistException;
47  import org.apache.maven.wagon.TransferFailedException;
48  import org.apache.maven.wagon.UnsupportedProtocolException;
49  import org.apache.maven.wagon.Wagon;
50  import org.apache.maven.wagon.authentication.AuthenticationException;
51  import org.apache.maven.wagon.authentication.AuthenticationInfo;
52  import org.apache.maven.wagon.authorization.AuthorizationException;
53  import org.apache.maven.wagon.events.TransferListener;
54  import org.apache.maven.wagon.observers.ChecksumObserver;
55  import org.apache.maven.wagon.proxy.ProxyInfo;
56  import org.apache.maven.wagon.repository.Repository;
57  import org.codehaus.plexus.PlexusContainer;
58  import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
59  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
60  import org.codehaus.plexus.logging.Logger;
61  import org.eclipse.aether.ConfigurationProperties;
62  import org.eclipse.aether.util.ConfigUtils;
63  
64  // TODO remove the update check manager
65  // TODO separate into retriever and publisher
66  // TODO remove hardcoding of checksum logic
67  
68  /**
69   * Manages <a href="https://maven.apache.org/wagon">Wagon</a> related operations in Maven.
70   */
71  @Named
72  @Singleton
73  @Deprecated
74  public class DefaultWagonManager implements WagonManager {
75  
76      private static final String[] CHECKSUM_IDS = {"md5", "sha1"};
77  
78      /**
79       * have to match the CHECKSUM_IDS
80       */
81      private static final String[] CHECKSUM_ALGORITHMS = {"MD5", "SHA-1"};
82  
83      @Inject
84      private Logger logger;
85  
86      @Inject
87      private PlexusContainer container;
88  
89      @Inject
90      private UpdateCheckManager updateCheckManager;
91  
92      @Inject
93      private LegacySupport legacySupport;
94  
95      //
96      // Retriever
97      //
98      @Override
99      public void getArtifact(
100             Artifact artifact, ArtifactRepository repository, TransferListener downloadMonitor, boolean force)
101             throws TransferFailedException, ResourceDoesNotExistException {
102         String remotePath = repository.pathOf(artifact);
103 
104         ArtifactRepositoryPolicy policy = artifact.isSnapshot() ? repository.getSnapshots() : repository.getReleases();
105 
106         if (!policy.isEnabled()) {
107             logger.debug(
108                     "Skipping disabled repository " + repository.getId() + " for resolution of " + artifact.getId());
109 
110         } else if (artifact.isSnapshot() || !artifact.getFile().exists()) {
111             if (force || updateCheckManager.isUpdateRequired(artifact, repository)) {
112                 logger.debug("Trying repository " + repository.getId() + " for resolution of " + artifact.getId()
113                         + " from " + remotePath);
114 
115                 try {
116                     getRemoteFile(
117                             repository,
118                             artifact.getFile(),
119                             remotePath,
120                             downloadMonitor,
121                             policy.getChecksumPolicy(),
122                             false);
123 
124                     updateCheckManager.touch(artifact, repository, null);
125                 } catch (ResourceDoesNotExistException e) {
126                     updateCheckManager.touch(artifact, repository, null);
127                     throw e;
128                 } catch (TransferFailedException e) {
129                     String error = (e.getMessage() != null)
130                             ? e.getMessage()
131                             : e.getClass().getSimpleName();
132                     updateCheckManager.touch(artifact, repository, error);
133                     throw e;
134                 }
135 
136                 logger.debug("  Artifact " + artifact.getId() + " resolved to " + artifact.getFile());
137 
138                 artifact.setResolved(true);
139             } else if (!artifact.getFile().exists()) {
140                 String error = updateCheckManager.getError(artifact, repository);
141                 if (error != null) {
142                     throw new TransferFailedException("Failure to resolve " + remotePath + " from "
143                             + repository.getUrl()
144                             + " was cached in the local repository. "
145                             + "Resolution will not be reattempted until the update interval of "
146                             + repository.getId() + " has elapsed or updates are forced. Original error: " + error);
147 
148                 } else {
149                     throw new ResourceDoesNotExistException(
150                             "Failure to resolve " + remotePath + " from " + repository.getUrl()
151                                     + " was cached in the local repository. "
152                                     + "Resolution will not be reattempted until the update interval of "
153                                     + repository.getId() + " has elapsed or updates are forced.");
154                 }
155             }
156         }
157     }
158 
159     @Override
160     public void getArtifact(
161             Artifact artifact,
162             List<ArtifactRepository> remoteRepositories,
163             TransferListener downloadMonitor,
164             boolean force)
165             throws TransferFailedException, ResourceDoesNotExistException {
166         TransferFailedException tfe = null;
167 
168         for (ArtifactRepository repository : remoteRepositories) {
169             try {
170                 getArtifact(artifact, repository, downloadMonitor, force);
171 
172                 if (artifact.isResolved()) {
173                     artifact.setRepository(repository);
174                     break;
175                 }
176             } catch (ResourceDoesNotExistException e) {
177                 // This one we will eat when looking through remote repositories
178                 // because we want to cycle through them all before squawking.
179 
180                 logger.debug(
181                         "Unable to find artifact " + artifact.getId() + " in repository " + repository.getId() + " ("
182                                 + repository.getUrl() + ")",
183                         e);
184 
185             } catch (TransferFailedException e) {
186                 tfe = e;
187 
188                 String msg = "Unable to get artifact " + artifact.getId() + " from repository " + repository.getId()
189                         + " (" + repository.getUrl() + "): " + e.getMessage();
190 
191                 if (logger.isDebugEnabled()) {
192                     logger.warn(msg, e);
193                 } else {
194                     logger.warn(msg);
195                 }
196             }
197         }
198 
199         // if it already exists locally we were just trying to force it - ignore the update
200         if (!artifact.getFile().exists()) {
201             if (tfe != null) {
202                 throw tfe;
203             } else {
204                 throw new ResourceDoesNotExistException("Unable to download the artifact from any repository");
205             }
206         }
207     }
208 
209     @Override
210     public void getArtifactMetadata(
211             ArtifactMetadata metadata, ArtifactRepository repository, File destination, String checksumPolicy)
212             throws TransferFailedException, ResourceDoesNotExistException {
213         String remotePath = repository.pathOfRemoteRepositoryMetadata(metadata);
214 
215         getRemoteFile(repository, destination, remotePath, null, checksumPolicy, true);
216     }
217 
218     @Override
219     public void getArtifactMetadataFromDeploymentRepository(
220             ArtifactMetadata metadata, ArtifactRepository repository, File destination, String checksumPolicy)
221             throws TransferFailedException, ResourceDoesNotExistException {
222         String remotePath = repository.pathOfRemoteRepositoryMetadata(metadata);
223 
224         getRemoteFile(repository, destination, remotePath, null, checksumPolicy, true);
225     }
226 
227     /**
228      * Deal with connecting to a wagon repository taking into account authentication and proxies.
229      *
230      * @param wagon
231      * @param repository
232      *
233      * @throws ConnectionException
234      * @throws AuthenticationException
235      */
236     private void connectWagon(Wagon wagon, ArtifactRepository repository)
237             throws ConnectionException, AuthenticationException {
238         // MNG-5509
239         // See org.eclipse.aether.connector.wagon.WagonRepositoryConnector.connectWagon(Wagon)
240         if (legacySupport.getRepositorySession() != null) {
241             String userAgent = ConfigUtils.getString(
242                     legacySupport.getRepositorySession(), null, ConfigurationProperties.USER_AGENT);
243 
244             if (userAgent == null) {
245                 Properties headers = new Properties();
246 
247                 headers.put(
248                         "User-Agent",
249                         ConfigUtils.getString(
250                                 legacySupport.getRepositorySession(), "Maven", ConfigurationProperties.USER_AGENT));
251                 try {
252                     Method setHttpHeaders = wagon.getClass().getMethod("setHttpHeaders", Properties.class);
253                     setHttpHeaders.invoke(wagon, headers);
254                 } catch (NoSuchMethodException e) {
255                     // normal for non-http wagons
256                 } catch (Exception e) {
257                     logger.debug("Could not set user agent for wagon "
258                             + wagon.getClass().getName() + ": " + e);
259                 }
260             }
261         }
262 
263         if (repository.getProxy() != null && logger.isDebugEnabled()) {
264             logger.debug("Using proxy " + repository.getProxy().getHost() + ":"
265                     + repository.getProxy().getPort() + " for " + repository.getUrl());
266         }
267 
268         if (repository.getAuthentication() != null && repository.getProxy() != null) {
269             wagon.connect(
270                     new Repository(repository.getId(), repository.getUrl()),
271                     authenticationInfo(repository),
272                     proxyInfo(repository));
273 
274         } else if (repository.getAuthentication() != null) {
275             wagon.connect(new Repository(repository.getId(), repository.getUrl()), authenticationInfo(repository));
276 
277         } else if (repository.getProxy() != null) {
278             wagon.connect(new Repository(repository.getId(), repository.getUrl()), proxyInfo(repository));
279         } else {
280             wagon.connect(new Repository(repository.getId(), repository.getUrl()));
281         }
282     }
283 
284     private AuthenticationInfo authenticationInfo(ArtifactRepository repository) {
285         AuthenticationInfo ai = new AuthenticationInfo();
286         ai.setUserName(repository.getAuthentication().getUsername());
287         ai.setPassword(repository.getAuthentication().getPassword());
288         return ai;
289     }
290 
291     private ProxyInfo proxyInfo(ArtifactRepository repository) {
292         ProxyInfo proxyInfo = new ProxyInfo();
293         proxyInfo.setHost(repository.getProxy().getHost());
294         proxyInfo.setType(repository.getProxy().getProtocol());
295         proxyInfo.setPort(repository.getProxy().getPort());
296         proxyInfo.setNonProxyHosts(repository.getProxy().getNonProxyHosts());
297         proxyInfo.setUserName(repository.getProxy().getUserName());
298         proxyInfo.setPassword(repository.getProxy().getPassword());
299         return proxyInfo;
300     }
301 
302     @SuppressWarnings("checkstyle:methodlength")
303     @Override
304     public void getRemoteFile(
305             ArtifactRepository repository,
306             File destination,
307             String remotePath,
308             TransferListener downloadMonitor,
309             String checksumPolicy,
310             boolean force)
311             throws TransferFailedException, ResourceDoesNotExistException {
312         String protocol = repository.getProtocol();
313 
314         Wagon wagon;
315 
316         try {
317             wagon = getWagon(protocol);
318         } catch (UnsupportedProtocolException e) {
319             throw new TransferFailedException("Unsupported Protocol: '" + protocol + "': " + e.getMessage(), e);
320         }
321 
322         if (downloadMonitor != null) {
323             wagon.addTransferListener(downloadMonitor);
324         }
325 
326         File temp = new File(destination + ".tmp");
327 
328         temp.deleteOnExit();
329 
330         boolean downloaded = false;
331 
332         try {
333             connectWagon(wagon, repository);
334 
335             boolean firstRun = true;
336             boolean retry = true;
337 
338             // this will run at most twice. The first time, the firstRun flag is turned off, and if the retry flag
339             // is set on the first run, it will be turned off and not re-set on the second try. This is because the
340             // only way the retry flag can be set is if ( firstRun == true ).
341             while (firstRun || retry) {
342                 ChecksumObserver md5ChecksumObserver = null;
343                 ChecksumObserver sha1ChecksumObserver = null;
344                 try {
345                     // TODO configure on repository
346                     int i = 0;
347 
348                     md5ChecksumObserver = addChecksumObserver(wagon, CHECKSUM_ALGORITHMS[i++]);
349                     sha1ChecksumObserver = addChecksumObserver(wagon, CHECKSUM_ALGORITHMS[i++]);
350 
351                     // reset the retry flag.
352                     retry = false;
353 
354                     // This should take care of creating destination directory now on
355                     if (destination.exists() && !force) {
356                         try {
357                             downloaded = wagon.getIfNewer(remotePath, temp, destination.lastModified());
358 
359                             if (!downloaded) {
360                                 // prevent additional checks of this artifact until it expires again
361                                 destination.setLastModified(System.currentTimeMillis());
362                             }
363                         } catch (UnsupportedOperationException e) {
364                             // older wagons throw this. Just get() instead
365                             wagon.get(remotePath, temp);
366 
367                             downloaded = true;
368                         }
369                     } else {
370                         wagon.get(remotePath, temp);
371                         downloaded = true;
372                     }
373                 } finally {
374                     wagon.removeTransferListener(md5ChecksumObserver);
375                     wagon.removeTransferListener(sha1ChecksumObserver);
376                 }
377 
378                 if (downloaded) {
379                     // keep the checksum files from showing up on the download monitor...
380                     if (downloadMonitor != null) {
381                         wagon.removeTransferListener(downloadMonitor);
382                     }
383 
384                     // try to verify the SHA-1 checksum for this file.
385                     try {
386                         verifyChecksum(sha1ChecksumObserver, destination, temp, remotePath, ".sha1", wagon);
387                     } catch (ChecksumFailedException e) {
388                         // if we catch a ChecksumFailedException, it means the transfer/read succeeded, but the
389                         // checksum doesn't match. This could be a problem with the server (ibiblio HTTP-200 error
390                         // page), so we'll try this up to two times. On the second try, we'll handle it as a bona-fide
391                         // error, based on the repository's checksum checking policy.
392                         if (firstRun) {
393                             logger.warn("*** CHECKSUM FAILED - " + e.getMessage() + " - RETRYING");
394                             retry = true;
395                         } else {
396                             handleChecksumFailure(checksumPolicy, e.getMessage(), e.getCause());
397                         }
398                     } catch (ResourceDoesNotExistException sha1TryException) {
399                         logger.debug("SHA1 not found, trying MD5: " + sha1TryException.getMessage());
400 
401                         // if this IS NOT a ChecksumFailedException, it was a problem with transfer/read of the checksum
402                         // file...we'll try again with the MD5 checksum.
403                         try {
404                             verifyChecksum(md5ChecksumObserver, destination, temp, remotePath, ".md5", wagon);
405                         } catch (ChecksumFailedException e) {
406                             // if we also fail to verify based on the MD5 checksum, and the checksum transfer/read
407                             // succeeded, then we need to determine whether to retry or handle it as a failure.
408                             if (firstRun) {
409                                 retry = true;
410                             } else {
411                                 handleChecksumFailure(checksumPolicy, e.getMessage(), e.getCause());
412                             }
413                         } catch (ResourceDoesNotExistException md5TryException) {
414                             // this was a failed transfer, and we don't want to retry.
415                             handleChecksumFailure(
416                                     checksumPolicy,
417                                     "Error retrieving checksum file for " + remotePath,
418                                     md5TryException);
419                         }
420                     }
421 
422                     // reinstate the download monitor...
423                     if (downloadMonitor != null) {
424                         wagon.addTransferListener(downloadMonitor);
425                     }
426                 }
427 
428                 // unset the firstRun flag, so we don't get caught in an infinite loop...
429                 firstRun = false;
430             }
431         } catch (ConnectionException e) {
432             throw new TransferFailedException("Connection failed: " + e.getMessage(), e);
433         } catch (AuthenticationException e) {
434             throw new TransferFailedException("Authentication failed: " + e.getMessage(), e);
435         } catch (AuthorizationException e) {
436             throw new TransferFailedException("Authorization failed: " + e.getMessage(), e);
437         } finally {
438             // Remove remaining TransferListener instances (checksum handlers removed in above finally clause)
439             if (downloadMonitor != null) {
440                 wagon.removeTransferListener(downloadMonitor);
441             }
442 
443             disconnectWagon(wagon);
444 
445             releaseWagon(protocol, wagon);
446         }
447 
448         if (downloaded) {
449             if (!temp.exists()) {
450                 throw new ResourceDoesNotExistException("Downloaded file does not exist: " + temp);
451             }
452 
453             // The temporary file is named destination + ".tmp" and is done this way to ensure
454             // that the temporary file is in the same file system as the destination because the
455             // File.renameTo operation doesn't really work across file systems.
456             // So we will attempt to do a File.renameTo for efficiency and atomicity, if this fails
457             // then we will use a brute force copy and delete the temporary file.
458             if (!temp.renameTo(destination)) {
459                 try {
460                     Files.copy(
461                             temp.toPath(),
462                             destination.toPath(),
463                             StandardCopyOption.REPLACE_EXISTING,
464                             StandardCopyOption.COPY_ATTRIBUTES);
465 
466                     if (!temp.delete()) {
467                         temp.deleteOnExit();
468                     }
469                 } catch (IOException e) {
470                     throw new TransferFailedException(
471                             "Error copying temporary file to the final destination: " + e.getMessage(), e);
472                 }
473             }
474         }
475     }
476 
477     //
478     // Publisher
479     //
480     @Override
481     public void putArtifact(
482             File source, Artifact artifact, ArtifactRepository deploymentRepository, TransferListener downloadMonitor)
483             throws TransferFailedException {
484         putRemoteFile(deploymentRepository, source, deploymentRepository.pathOf(artifact), downloadMonitor);
485     }
486 
487     @Override
488     public void putArtifactMetadata(File source, ArtifactMetadata artifactMetadata, ArtifactRepository repository)
489             throws TransferFailedException {
490         logger.info("Uploading " + artifactMetadata);
491         putRemoteFile(repository, source, repository.pathOfRemoteRepositoryMetadata(artifactMetadata), null);
492     }
493 
494     @Override
495     public void putRemoteFile(
496             ArtifactRepository repository, File source, String remotePath, TransferListener downloadMonitor)
497             throws TransferFailedException {
498         String protocol = repository.getProtocol();
499 
500         Wagon wagon;
501         try {
502             wagon = getWagon(protocol);
503         } catch (UnsupportedProtocolException e) {
504             throw new TransferFailedException("Unsupported Protocol: '" + protocol + "': " + e.getMessage(), e);
505         }
506 
507         if (downloadMonitor != null) {
508             wagon.addTransferListener(downloadMonitor);
509         }
510 
511         Map<String, ChecksumObserver> checksums = new HashMap<>(2);
512 
513         Map<String, String> sums = new HashMap<>(2);
514 
515         // TODO configure these on the repository
516         for (int i = 0; i < CHECKSUM_IDS.length; i++) {
517             checksums.put(CHECKSUM_IDS[i], addChecksumObserver(wagon, CHECKSUM_ALGORITHMS[i]));
518         }
519 
520         List<File> temporaryFiles = new ArrayList<>();
521 
522         try {
523             try {
524                 connectWagon(wagon, repository);
525 
526                 wagon.put(source, remotePath);
527             } finally {
528                 if (downloadMonitor != null) {
529                     wagon.removeTransferListener(downloadMonitor);
530                 }
531             }
532 
533             // Pre-store the checksums as any future puts will overwrite them
534             for (String extension : checksums.keySet()) {
535                 ChecksumObserver observer = checksums.get(extension);
536                 sums.put(extension, observer.getActualChecksum());
537             }
538 
539             // We do this in here so we can checksum the artifact metadata too, otherwise it could be metadata itself
540             for (String extension : checksums.keySet()) {
541                 // TODO shouldn't need a file intermediary - improve wagon to take a stream
542                 File temp = File.createTempFile("maven-artifact", null);
543                 temp.deleteOnExit();
544                 byte[] bytes = sums.get(extension).getBytes(StandardCharsets.UTF_8);
545                 Files.write(
546                         Paths.get(temp.getAbsolutePath()), bytes, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
547 
548                 temporaryFiles.add(temp);
549                 wagon.put(temp, remotePath + "." + extension);
550             }
551         } catch (ConnectionException e) {
552             throw new TransferFailedException("Connection failed: " + e.getMessage(), e);
553         } catch (AuthenticationException e) {
554             throw new TransferFailedException("Authentication failed: " + e.getMessage(), e);
555         } catch (AuthorizationException e) {
556             throw new TransferFailedException("Authorization failed: " + e.getMessage(), e);
557         } catch (ResourceDoesNotExistException e) {
558             throw new TransferFailedException("Resource to deploy not found: " + e.getMessage(), e);
559         } catch (IOException e) {
560             throw new TransferFailedException("Error creating temporary file for deployment: " + e.getMessage(), e);
561         } finally {
562             // MNG-4543
563             cleanupTemporaryFiles(temporaryFiles);
564 
565             // Remove every checksum listener
566             for (String id : CHECKSUM_IDS) {
567                 TransferListener checksumListener = checksums.get(id);
568                 if (checksumListener != null) {
569                     wagon.removeTransferListener(checksumListener);
570                 }
571             }
572 
573             disconnectWagon(wagon);
574 
575             releaseWagon(protocol, wagon);
576         }
577     }
578 
579     private void cleanupTemporaryFiles(List<File> files) {
580         for (File file : files) {
581             // really don't care if it failed here only log warning
582             if (!file.delete()) {
583                 logger.warn("skip failed to delete temporary file : " + file.getAbsolutePath());
584                 file.deleteOnExit();
585             }
586         }
587     }
588 
589     private ChecksumObserver addChecksumObserver(Wagon wagon, String algorithm) throws TransferFailedException {
590         try {
591             ChecksumObserver checksumObserver = new ChecksumObserver(algorithm);
592             wagon.addTransferListener(checksumObserver);
593             return checksumObserver;
594         } catch (NoSuchAlgorithmException e) {
595             throw new TransferFailedException("Unable to add checksum for unsupported algorithm " + algorithm, e);
596         }
597     }
598 
599     private void handleChecksumFailure(String checksumPolicy, String message, Throwable cause)
600             throws ChecksumFailedException {
601         if (ArtifactRepositoryPolicy.CHECKSUM_POLICY_FAIL.equals(checksumPolicy)) {
602             throw new ChecksumFailedException(message, cause);
603         } else if (!ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE.equals(checksumPolicy)) {
604             // warn if it is set to anything other than ignore
605             logger.warn("*** CHECKSUM FAILED - " + message + " - IGNORING");
606         }
607         // otherwise it is ignore
608     }
609 
610     private void verifyChecksum(
611             ChecksumObserver checksumObserver,
612             File destination,
613             File tempDestination,
614             String remotePath,
615             String checksumFileExtension,
616             Wagon wagon)
617             throws ResourceDoesNotExistException, TransferFailedException, AuthorizationException {
618         try {
619             // grab it first, because it's about to change...
620             String actualChecksum = checksumObserver.getActualChecksum();
621 
622             File tempChecksumFile = new File(tempDestination + checksumFileExtension + ".tmp");
623             tempChecksumFile.deleteOnExit();
624             wagon.get(remotePath + checksumFileExtension, tempChecksumFile);
625             byte[] bytes = Files.readAllBytes(tempChecksumFile.toPath());
626             String expectedChecksum = new String(bytes, StandardCharsets.UTF_8);
627 
628             // remove whitespaces at the end
629             expectedChecksum = expectedChecksum.trim();
630 
631             // check for 'ALGO (name) = CHECKSUM' like used by openssl
632             if (expectedChecksum.regionMatches(true, 0, "MD", 0, 2)
633                     || expectedChecksum.regionMatches(true, 0, "SHA", 0, 3)) {
634                 int lastSpacePos = expectedChecksum.lastIndexOf(' ');
635                 expectedChecksum = expectedChecksum.substring(lastSpacePos + 1);
636             } else {
637                 // remove everything after the first space (if available)
638                 int spacePos = expectedChecksum.indexOf(' ');
639 
640                 if (spacePos != -1) {
641                     expectedChecksum = expectedChecksum.substring(0, spacePos);
642                 }
643             }
644             if (expectedChecksum.equalsIgnoreCase(actualChecksum)) {
645                 File checksumFile = new File(destination + checksumFileExtension);
646                 if (checksumFile.exists()) {
647                     checksumFile.delete(); // ignore if failed as we will overwrite
648                 }
649                 Files.copy(
650                         tempChecksumFile.toPath(),
651                         checksumFile.toPath(),
652                         StandardCopyOption.REPLACE_EXISTING,
653                         StandardCopyOption.COPY_ATTRIBUTES);
654 
655                 if (!tempChecksumFile.delete()) {
656                     tempChecksumFile.deleteOnExit();
657                 }
658             } else {
659                 throw new ChecksumFailedException("Checksum failed on download: local = '" + actualChecksum
660                         + "'; remote = '" + expectedChecksum + "'");
661             }
662         } catch (IOException e) {
663             throw new ChecksumFailedException("Invalid checksum file", e);
664         }
665     }
666 
667     private void disconnectWagon(Wagon wagon) {
668         try {
669             wagon.disconnect();
670         } catch (ConnectionException e) {
671             logger.error("Problem disconnecting from wagon - ignoring: " + e.getMessage());
672         }
673     }
674 
675     private void releaseWagon(String protocol, Wagon wagon) {
676         try {
677             container.release(wagon);
678         } catch (ComponentLifecycleException e) {
679             logger.error("Problem releasing wagon - ignoring: " + e.getMessage());
680             logger.debug("", e);
681         }
682     }
683 
684     @Override
685     @Deprecated
686     public Wagon getWagon(Repository repository) throws UnsupportedProtocolException {
687         return getWagon(repository.getProtocol());
688     }
689 
690     @Override
691     @Deprecated
692     public Wagon getWagon(String protocol) throws UnsupportedProtocolException {
693         if (protocol == null) {
694             throw new UnsupportedProtocolException("Unspecified protocol");
695         }
696 
697         String hint = protocol.toLowerCase(java.util.Locale.ENGLISH);
698 
699         Wagon wagon;
700         try {
701             wagon = container.lookup(Wagon.class, hint);
702         } catch (ComponentLookupException e) {
703             throw new UnsupportedProtocolException(
704                     "Cannot find wagon which supports the requested protocol: " + protocol, e);
705         }
706 
707         return wagon;
708     }
709 }