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
23 import java.io.IOException;
24 import java.util.List;
27 import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
28 import org.apache.archiva.proxy.common.WagonFactory;
29 import org.apache.archiva.proxy.common.WagonFactoryException;
30 import org.apache.commons.io.FileUtils;
31 import org.apache.commons.lang.StringUtils;
32 import org.apache.archiva.common.utils.VersionUtil;
33 import org.apache.archiva.configuration.ManagedRepositoryConfiguration;
34 import org.apache.archiva.configuration.RemoteRepositoryConfiguration;
35 import org.apache.archiva.xml.XMLException;
36 import org.apache.maven.model.Repository;
37 import org.apache.maven.model.building.FileModelSource;
38 import org.apache.maven.model.building.ModelSource;
39 import org.apache.maven.model.resolution.InvalidRepositoryException;
40 import org.apache.maven.model.resolution.ModelResolver;
41 import org.apache.maven.model.resolution.UnresolvableModelException;
42 import org.apache.maven.wagon.ConnectionException;
43 import org.apache.maven.wagon.ResourceDoesNotExistException;
44 import org.apache.maven.wagon.TransferFailedException;
45 import org.apache.maven.wagon.Wagon;
46 import org.apache.maven.wagon.authentication.AuthenticationException;
47 import org.apache.maven.wagon.authentication.AuthenticationInfo;
48 import org.apache.maven.wagon.authorization.AuthorizationException;
49 import org.apache.maven.wagon.proxy.ProxyInfo;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
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,
81 WagonFactory wagonFactory, List<RemoteRepositoryConfiguration> remoteRepositories,
82 Map<String, ProxyInfo> networkProxiesMap, ManagedRepositoryConfiguration targetRepository )
84 this( basedir, pathTranslator );
86 this.wagonFactory = wagonFactory;
88 this.remoteRepositories = remoteRepositories;
90 this.networkProxyMap = networkProxiesMap;
92 this.targetRepository = targetRepository;
95 public ModelSource resolveModel( String groupId, String artifactId, String version )
96 throws UnresolvableModelException
98 String filename = artifactId + "-" + version + ".pom";
99 // TODO: we need to convert 1.0-20091120.112233-1 type paths to baseVersion for the below call - add a test
101 File model = pathTranslator.toFile( basedir, groupId, artifactId, version, filename );
103 if( !model.exists() )
105 for( RemoteRepositoryConfiguration remoteRepository : remoteRepositories )
109 boolean success = getModelFromProxy( remoteRepository, groupId, artifactId, version, filename );
110 if( success && model.exists() )
112 log.info( "Model '" + model.getAbsolutePath() + "' successfully retrieved from remote repository '"
113 + remoteRepository.getId() + "'" );
119 log.warn( "An exception was caught while attempting to retrieve model '" + model.getAbsolutePath()
120 + "' from remote repository '" + remoteRepository.getId() + "'.", e );
126 return new FileModelSource( model );
129 public void addRepository( Repository repository )
130 throws InvalidRepositoryException
132 // we just ignore repositories outside of the current one for now
133 // 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
134 // ID since they will rarely match
137 public ModelResolver newCopy()
139 return new RepositoryModelResolver( basedir, pathTranslator );
142 // FIXME: we need to do some refactoring, we cannot re-use the proxy components of archiva-proxy in maven2-repository
143 // because it's causing a cyclic dependency
144 private boolean getModelFromProxy( RemoteRepositoryConfiguration remoteRepository, String groupId,
145 String artifactId, String version, String filename )
146 throws AuthorizationException, TransferFailedException, ResourceDoesNotExistException, WagonFactoryException,
149 boolean success = false;
152 File tmpResource = null;
153 String artifactPath = pathTranslator.toPath( groupId, artifactId, version, filename );
154 File resource = new File( targetRepository.getLocation(), artifactPath );
156 File workingDirectory = createWorkingDirectory( targetRepository.getLocation() );
162 String protocol = getProtocol( remoteRepository.getUrl() );
164 wagon = wagonFactory.getWagon( "wagon#" + protocol );
167 throw new RuntimeException( "Unsupported remote repository protocol: " + protocol );
170 boolean connected = connectToRepository( wagon, remoteRepository );
173 tmpResource = new File( workingDirectory, filename );
175 if ( VersionUtil.isSnapshot( version ) )
177 // get the metadata first!
178 File tmpMetadataResource = new File( workingDirectory, METADATA_FILENAME );
180 String metadataPath = StringUtils.substringBeforeLast( artifactPath, "/" ) + "/" + METADATA_FILENAME;
182 wagon.get( metadataPath, tmpMetadataResource );
184 log.debug( "Successfully downloaded metadata." );
186 MavenRepositoryMetadata metadata = MavenRepositoryMetadataReader.read( tmpMetadataResource );
188 // re-adjust to timestamp if present, otherwise retain the original -SNAPSHOT filename
189 MavenRepositoryMetadata.Snapshot snapshotVersion = metadata.getSnapshotVersion();
190 String timestampVersion = version;
191 if ( snapshotVersion != null )
194 timestampVersion.substring( 0, timestampVersion.length() - 8 ); // remove SNAPSHOT from end
196 timestampVersion + snapshotVersion.getTimestamp() + "-" + snapshotVersion.getBuildNumber();
198 filename = artifactId + "-" + timestampVersion + ".pom";
200 artifactPath = pathTranslator.toPath( groupId, artifactId, version, filename );
202 log.debug( "New artifactPath : " + artifactPath );
206 log.info( "Retrieving " + artifactPath + " from " + remoteRepository.getName() );
208 wagon.get( artifactPath, tmpResource );
210 log.debug( "Downloaded successfully." );
213 transferChecksum( wagon, remoteRepository, artifactPath, tmpResource, workingDirectory, ".sha1" );
215 transferChecksum( wagon, remoteRepository, artifactPath, tmpResource, workingDirectory, ".md5" );
226 catch ( ConnectionException e )
228 log.warn( "Unable to disconnect wagon.", e );
233 if ( resource != null )
235 synchronized ( resource.getAbsolutePath().intern() )
237 File directory = resource.getParentFile();
238 moveFileIfExists( tmpMd5, directory );
239 moveFileIfExists( tmpSha1, directory );
240 moveFileIfExists( tmpResource, directory );
247 FileUtils.deleteQuietly( workingDirectory );
250 // do we still need to execute the consumers?
256 * Using wagon, connect to the remote repository.
258 * @param wagon the wagon instance to establish the connection on.
259 * @return true if the connection was successful. false if not connected.
261 private boolean connectToRepository( Wagon wagon, RemoteRepositoryConfiguration remoteRepository )
265 final ProxyInfo networkProxy;
266 networkProxy = this.networkProxyMap.get( remoteRepository.getId() );
268 if ( networkProxy != null )
271 "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
272 + " to connect to remote repository " + remoteRepository.getUrl();
273 if ( networkProxy.getNonProxyHosts() != null )
275 msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
278 if ( StringUtils.isNotBlank( networkProxy.getUserName() ) )
280 msg += "; as user: " + networkProxy.getUserName();
286 AuthenticationInfo authInfo = null;
287 String username = remoteRepository.getUsername();
288 String password = remoteRepository.getPassword();
290 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
292 log.debug( "Using username " + username + " to connect to remote repository " + remoteRepository.getUrl() );
293 authInfo = new AuthenticationInfo();
294 authInfo.setUserName( username );
295 authInfo.setPassword( password );
298 // Convert seconds to milliseconds
299 int timeoutInMilliseconds = remoteRepository.getTimeout() * 1000;
302 wagon.setTimeout( timeoutInMilliseconds );
306 org.apache.maven.wagon.repository.Repository wagonRepository =
307 new org.apache.maven.wagon.repository.Repository( remoteRepository.getId(), remoteRepository.getUrl() );
308 wagon.connect( wagonRepository, authInfo, networkProxy );
311 catch ( ConnectionException e )
313 log.error( "Could not connect to " + remoteRepository.getName() + ": " + e.getMessage() );
316 catch ( AuthenticationException e )
318 log.error( "Could not connect to " + remoteRepository.getName() + ": " + e.getMessage() );
325 private File transferChecksum( Wagon wagon, RemoteRepositoryConfiguration remoteRepository, String remotePath,
326 File resource, File tmpDirectory, String ext )
327 throws AuthorizationException, TransferFailedException, ResourceDoesNotExistException
329 File destFile = new File( tmpDirectory, resource.getName() + ext );
331 log.info( "Retrieving " + remotePath + " from " + remoteRepository.getName() );
333 wagon.get( remotePath, destFile );
335 log.debug( "Downloaded successfully." );
340 private String getProtocol( String url )
342 String protocol = StringUtils.substringBefore( url, ":" );
347 private File createWorkingDirectory( String targetRepository )
351 File tmpDir = File.createTempFile( ".workingdirectory", null, new File( targetRepository ) );
356 catch ( IOException e )
358 throw new RuntimeException( "Could not create working directory for this request", e );
362 private void moveFileIfExists( File fileToMove, File directory )
364 if ( fileToMove != null && fileToMove.exists() )
366 File newLocation = new File( directory, fileToMove.getName() );
367 if ( newLocation.exists() && !newLocation.delete() )
369 throw new RuntimeException( "Unable to overwrite existing target file: " + newLocation.getAbsolutePath() );
372 newLocation.getParentFile().mkdirs();
373 if ( !fileToMove.renameTo( newLocation ) )
375 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
379 FileUtils.copyFile( fileToMove, newLocation );
381 catch ( IOException e )
383 if ( newLocation.exists() )
385 log.error( "Tried to copy file " + fileToMove.getName() + " to " + newLocation.getAbsolutePath()
386 + " but file with this name already exists." );
390 throw new RuntimeException( "Cannot copy tmp file " + fileToMove.getAbsolutePath()
391 + " to its final location", e );
396 FileUtils.deleteQuietly( fileToMove );