1 package org.apache.archiva.metadata.repository.storage.maven2;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import org.apache.archiva.common.utils.VersionUtil;
23 import org.apache.archiva.configuration.ManagedRepositoryConfiguration;
24 import org.apache.archiva.configuration.RemoteRepositoryConfiguration;
25 import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
26 import org.apache.archiva.proxy.common.WagonFactory;
27 import org.apache.archiva.proxy.common.WagonFactoryException;
28 import org.apache.archiva.xml.XMLException;
29 import org.apache.commons.io.FileUtils;
30 import org.apache.commons.lang.StringUtils;
31 import org.apache.maven.model.Repository;
32 import org.apache.maven.model.building.FileModelSource;
33 import org.apache.maven.model.building.ModelSource;
34 import org.apache.maven.model.resolution.InvalidRepositoryException;
35 import org.apache.maven.model.resolution.ModelResolver;
36 import org.apache.maven.model.resolution.UnresolvableModelException;
37 import org.apache.maven.wagon.ConnectionException;
38 import org.apache.maven.wagon.ResourceDoesNotExistException;
39 import org.apache.maven.wagon.TransferFailedException;
40 import org.apache.maven.wagon.Wagon;
41 import org.apache.maven.wagon.authentication.AuthenticationException;
42 import org.apache.maven.wagon.authentication.AuthenticationInfo;
43 import org.apache.maven.wagon.authorization.AuthorizationException;
44 import org.apache.maven.wagon.proxy.ProxyInfo;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
49 import java.io.IOException;
50 import java.util.List;
53 public class RepositoryModelResolver
54 implements ModelResolver
58 private RepositoryPathTranslator pathTranslator;
60 private WagonFactory wagonFactory;
62 private List<RemoteRepositoryConfiguration> remoteRepositories;
64 private ManagedRepositoryConfiguration targetRepository;
66 private static final Logger log = LoggerFactory.getLogger( RepositoryModelResolver.class );
68 private static final String METADATA_FILENAME = "maven-metadata.xml";
70 // key/value: remote repo ID/network proxy
71 Map<String, ProxyInfo> networkProxyMap;
73 public RepositoryModelResolver( File basedir, RepositoryPathTranslator pathTranslator )
75 this.basedir = basedir;
77 this.pathTranslator = pathTranslator;
80 public RepositoryModelResolver( File basedir, RepositoryPathTranslator pathTranslator, WagonFactory wagonFactory,
81 List<RemoteRepositoryConfiguration> remoteRepositories,
82 Map<String, ProxyInfo> networkProxiesMap,
83 ManagedRepositoryConfiguration targetRepository )
85 this( basedir, pathTranslator );
87 this.wagonFactory = wagonFactory;
89 this.remoteRepositories = remoteRepositories;
91 this.networkProxyMap = networkProxiesMap;
93 this.targetRepository = targetRepository;
96 public ModelSource resolveModel( String groupId, String artifactId, String version )
97 throws UnresolvableModelException
99 String filename = artifactId + "-" + version + ".pom";
100 // TODO: we need to convert 1.0-20091120.112233-1 type paths to baseVersion for the below call - add a test
102 File model = pathTranslator.toFile( basedir, groupId, artifactId, version, filename );
104 if ( !model.exists() )
106 for ( RemoteRepositoryConfiguration remoteRepository : remoteRepositories )
110 boolean success = getModelFromProxy( remoteRepository, groupId, artifactId, version, filename );
111 if ( success && model.exists() )
114 "Model '" + model.getAbsolutePath() + "' successfully retrieved from remote repository '"
115 + remoteRepository.getId() + "'" );
119 catch ( Exception e )
121 log.warn( "An exception was caught while attempting to retrieve model '" + model.getAbsolutePath()
122 + "' from remote repository '" + remoteRepository.getId() + "'.", e );
128 return new FileModelSource( model );
131 public void addRepository( Repository repository )
132 throws InvalidRepositoryException
134 // we just ignore repositories outside of the current one for now
135 // TODO: it'd be nice to look them up from Archiva's set, but we want to do that by URL / mapping, not just the
136 // ID since they will rarely match
139 public ModelResolver newCopy()
141 return new RepositoryModelResolver( basedir, pathTranslator );
144 // FIXME: we need to do some refactoring, we cannot re-use the proxy components of archiva-proxy in maven2-repository
145 // because it's causing a cyclic dependency
146 private boolean getModelFromProxy( RemoteRepositoryConfiguration remoteRepository, String groupId,
147 String artifactId, String version, String filename )
148 throws AuthorizationException, TransferFailedException, ResourceDoesNotExistException, WagonFactoryException,
151 boolean success = false;
154 File tmpResource = null;
155 String artifactPath = pathTranslator.toPath( groupId, artifactId, version, filename );
156 File resource = new File( targetRepository.getLocation(), artifactPath );
158 File workingDirectory = createWorkingDirectory( targetRepository.getLocation() );
164 String protocol = getProtocol( remoteRepository.getUrl() );
166 wagon = wagonFactory.getWagon( "wagon#" + protocol );
169 throw new RuntimeException( "Unsupported remote repository protocol: " + protocol );
172 boolean connected = connectToRepository( wagon, remoteRepository );
175 tmpResource = new File( workingDirectory, filename );
177 if ( VersionUtil.isSnapshot( version ) )
179 // get the metadata first!
180 File tmpMetadataResource = new File( workingDirectory, METADATA_FILENAME );
182 String metadataPath =
183 StringUtils.substringBeforeLast( artifactPath, "/" ) + "/" + METADATA_FILENAME;
185 wagon.get( metadataPath, tmpMetadataResource );
187 log.debug( "Successfully downloaded metadata." );
189 MavenRepositoryMetadata metadata = MavenRepositoryMetadataReader.read( tmpMetadataResource );
191 // re-adjust to timestamp if present, otherwise retain the original -SNAPSHOT filename
192 MavenRepositoryMetadata.Snapshot snapshotVersion = metadata.getSnapshotVersion();
193 String timestampVersion = version;
194 if ( snapshotVersion != null )
196 timestampVersion = timestampVersion.substring( 0, timestampVersion.length()
197 - 8 ); // remove SNAPSHOT from end
198 timestampVersion = timestampVersion + snapshotVersion.getTimestamp() + "-"
199 + snapshotVersion.getBuildNumber();
201 filename = artifactId + "-" + timestampVersion + ".pom";
203 artifactPath = pathTranslator.toPath( groupId, artifactId, version, filename );
205 log.debug( "New artifactPath : " + artifactPath );
209 log.info( "Retrieving " + artifactPath + " from " + remoteRepository.getName() );
211 wagon.get( artifactPath, tmpResource );
213 log.debug( "Downloaded successfully." );
215 tmpSha1 = transferChecksum( wagon, remoteRepository, artifactPath, tmpResource, workingDirectory,
217 tmpMd5 = transferChecksum( wagon, remoteRepository, artifactPath, tmpResource, workingDirectory,
229 catch ( ConnectionException e )
231 log.warn( "Unable to disconnect wagon.", e );
236 if ( resource != null )
238 synchronized ( resource.getAbsolutePath().intern() )
240 File directory = resource.getParentFile();
241 moveFileIfExists( tmpMd5, directory );
242 moveFileIfExists( tmpSha1, directory );
243 moveFileIfExists( tmpResource, directory );
250 FileUtils.deleteQuietly( workingDirectory );
253 // do we still need to execute the consumers?
259 * Using wagon, connect to the remote repository.
261 * @param wagon the wagon instance to establish the connection on.
262 * @return true if the connection was successful. false if not connected.
264 private boolean connectToRepository( Wagon wagon, RemoteRepositoryConfiguration remoteRepository )
268 final ProxyInfo networkProxy;
269 networkProxy = this.networkProxyMap.get( remoteRepository.getId() );
271 if ( networkProxy != null )
273 String msg = "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
274 + " to connect to remote repository " + remoteRepository.getUrl();
275 if ( networkProxy.getNonProxyHosts() != null )
277 msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
280 if ( StringUtils.isNotBlank( networkProxy.getUserName() ) )
282 msg += "; as user: " + networkProxy.getUserName();
288 AuthenticationInfo authInfo = null;
289 String username = remoteRepository.getUsername();
290 String password = remoteRepository.getPassword();
292 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
294 log.debug( "Using username {} to connect to remote repository {}", username, remoteRepository.getUrl() );
295 authInfo = new AuthenticationInfo();
296 authInfo.setUserName( username );
297 authInfo.setPassword( password );
300 // Convert seconds to milliseconds
301 int timeoutInMilliseconds = remoteRepository.getTimeout() * 1000;
304 wagon.setTimeout( timeoutInMilliseconds );
308 org.apache.maven.wagon.repository.Repository wagonRepository =
309 new org.apache.maven.wagon.repository.Repository( remoteRepository.getId(), remoteRepository.getUrl() );
310 wagon.connect( wagonRepository, authInfo, networkProxy );
313 catch ( ConnectionException e )
315 log.error( "Could not connect to " + remoteRepository.getName() + ": " + e.getMessage() );
318 catch ( AuthenticationException e )
320 log.error( "Could not connect to " + remoteRepository.getName() + ": " + e.getMessage() );
327 private File transferChecksum( Wagon wagon, RemoteRepositoryConfiguration remoteRepository, String remotePath,
328 File resource, File tmpDirectory, String ext )
329 throws AuthorizationException, TransferFailedException, ResourceDoesNotExistException
331 File destFile = new File( tmpDirectory, resource.getName() + ext );
333 log.info( "Retrieving " + remotePath + " from " + remoteRepository.getName() );
335 wagon.get( remotePath, destFile );
337 log.debug( "Downloaded successfully." );
342 private String getProtocol( String url )
344 String protocol = StringUtils.substringBefore( url, ":" );
349 private File createWorkingDirectory( String targetRepository )
353 File tmpDir = File.createTempFile( ".workingdirectory", null, new File( targetRepository ) );
358 catch ( IOException e )
360 throw new RuntimeException( "Could not create working directory for this request", e );
364 private void moveFileIfExists( File fileToMove, File directory )
366 if ( fileToMove != null && fileToMove.exists() )
368 File newLocation = new File( directory, fileToMove.getName() );
369 if ( newLocation.exists() && !newLocation.delete() )
371 throw new RuntimeException(
372 "Unable to overwrite existing target file: " + newLocation.getAbsolutePath() );
375 newLocation.getParentFile().mkdirs();
376 if ( !fileToMove.renameTo( newLocation ) )
378 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
382 FileUtils.copyFile( fileToMove, newLocation );
384 catch ( IOException e )
386 if ( newLocation.exists() )
388 log.error( "Tried to copy file " + fileToMove.getName() + " to " + newLocation.getAbsolutePath()
389 + " but file with this name already exists." );
393 throw new RuntimeException(
394 "Cannot copy tmp file " + fileToMove.getAbsolutePath() + " to its final location", e );
399 FileUtils.deleteQuietly( fileToMove );