]> source.dussan.org Git - archiva.git/blob
f2138237f03ec97fa5d258a750dff36278f3ecb6
[archiva.git] /
1 package org.apache.archiva.metadata.repository.storage.maven2;
2
3 /*
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
11  *
12  *   http://www.apache.org/licenses/LICENSE-2.0
13  *
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
19  * under the License.
20  */
21
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;
47
48 import java.io.File;
49 import java.io.IOException;
50 import java.util.List;
51 import java.util.Map;
52
53 public class RepositoryModelResolver
54     implements ModelResolver
55 {
56     private File basedir;
57
58     private RepositoryPathTranslator pathTranslator;
59
60     private WagonFactory wagonFactory;
61
62     private List<RemoteRepositoryConfiguration> remoteRepositories;
63
64     private ManagedRepositoryConfiguration targetRepository;
65
66     private static final Logger log = LoggerFactory.getLogger( RepositoryModelResolver.class );
67
68     private static final String METADATA_FILENAME = "maven-metadata.xml";
69
70     // key/value: remote repo ID/network proxy
71     Map<String, ProxyInfo> networkProxyMap;
72
73     public RepositoryModelResolver( File basedir, RepositoryPathTranslator pathTranslator )
74     {
75         this.basedir = basedir;
76
77         this.pathTranslator = pathTranslator;
78     }
79
80     public RepositoryModelResolver( File basedir, RepositoryPathTranslator pathTranslator, WagonFactory wagonFactory,
81                                     List<RemoteRepositoryConfiguration> remoteRepositories,
82                                     Map<String, ProxyInfo> networkProxiesMap,
83                                     ManagedRepositoryConfiguration targetRepository )
84     {
85         this( basedir, pathTranslator );
86
87         this.wagonFactory = wagonFactory;
88
89         this.remoteRepositories = remoteRepositories;
90
91         this.networkProxyMap = networkProxiesMap;
92
93         this.targetRepository = targetRepository;
94     }
95
96     public ModelSource resolveModel( String groupId, String artifactId, String version )
97         throws UnresolvableModelException
98     {
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
101
102         File model = pathTranslator.toFile( basedir, groupId, artifactId, version, filename );
103
104         if ( !model.exists() )
105         {
106             for ( RemoteRepositoryConfiguration remoteRepository : remoteRepositories )
107             {
108                 try
109                 {
110                     boolean success = getModelFromProxy( remoteRepository, groupId, artifactId, version, filename );
111                     if ( success && model.exists() )
112                     {
113                         log.info(
114                             "Model '" + model.getAbsolutePath() + "' successfully retrieved from remote repository '"
115                                 + remoteRepository.getId() + "'" );
116                         break;
117                     }
118                 }
119                 catch ( Exception e )
120                 {
121                     log.warn( "An exception was caught while attempting to retrieve model '" + model.getAbsolutePath()
122                                   + "' from remote repository '" + remoteRepository.getId() + "'.", e );
123                     continue;
124                 }
125             }
126         }
127
128         return new FileModelSource( model );
129     }
130
131     public void addRepository( Repository repository )
132         throws InvalidRepositoryException
133     {
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
137     }
138
139     public ModelResolver newCopy()
140     {
141         return new RepositoryModelResolver( basedir, pathTranslator );
142     }
143
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,
149         XMLException
150     {
151         boolean success = false;
152         File tmpMd5 = null;
153         File tmpSha1 = null;
154         File tmpResource = null;
155         String artifactPath = pathTranslator.toPath( groupId, artifactId, version, filename );
156         File resource = new File( targetRepository.getLocation(), artifactPath );
157
158         File workingDirectory = createWorkingDirectory( targetRepository.getLocation() );
159         try
160         {
161             Wagon wagon = null;
162             try
163             {
164                 String protocol = getProtocol( remoteRepository.getUrl() );
165
166                 wagon = wagonFactory.getWagon( "wagon#" + protocol );
167                 if ( wagon == null )
168                 {
169                     throw new RuntimeException( "Unsupported remote repository protocol: " + protocol );
170                 }
171
172                 boolean connected = connectToRepository( wagon, remoteRepository );
173                 if ( connected )
174                 {
175                     tmpResource = new File( workingDirectory, filename );
176
177                     if ( VersionUtil.isSnapshot( version ) )
178                     {
179                         // get the metadata first!
180                         File tmpMetadataResource = new File( workingDirectory, METADATA_FILENAME );
181
182                         String metadataPath =
183                             StringUtils.substringBeforeLast( artifactPath, "/" ) + "/" + METADATA_FILENAME;
184
185                         wagon.get( metadataPath, tmpMetadataResource );
186
187                         log.debug( "Successfully downloaded metadata." );
188
189                         MavenRepositoryMetadata metadata = MavenRepositoryMetadataReader.read( tmpMetadataResource );
190
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 )
195                         {
196                             timestampVersion = timestampVersion.substring( 0, timestampVersion.length()
197                                 - 8 ); // remove SNAPSHOT from end
198                             timestampVersion = timestampVersion + snapshotVersion.getTimestamp() + "-"
199                                 + snapshotVersion.getBuildNumber();
200
201                             filename = artifactId + "-" + timestampVersion + ".pom";
202
203                             artifactPath = pathTranslator.toPath( groupId, artifactId, version, filename );
204
205                             log.debug( "New artifactPath : " + artifactPath );
206                         }
207                     }
208
209                     log.info( "Retrieving " + artifactPath + " from " + remoteRepository.getName() );
210
211                     wagon.get( artifactPath, tmpResource );
212
213                     log.debug( "Downloaded successfully." );
214
215                     tmpSha1 = transferChecksum( wagon, remoteRepository, artifactPath, tmpResource, workingDirectory,
216                                                 ".sha1" );
217                     tmpMd5 = transferChecksum( wagon, remoteRepository, artifactPath, tmpResource, workingDirectory,
218                                                ".md5" );
219                 }
220             }
221             finally
222             {
223                 if ( wagon != null )
224                 {
225                     try
226                     {
227                         wagon.disconnect();
228                     }
229                     catch ( ConnectionException e )
230                     {
231                         log.warn( "Unable to disconnect wagon.", e );
232                     }
233                 }
234             }
235
236             if ( resource != null )
237             {
238                 synchronized ( resource.getAbsolutePath().intern() )
239                 {
240                     File directory = resource.getParentFile();
241                     moveFileIfExists( tmpMd5, directory );
242                     moveFileIfExists( tmpSha1, directory );
243                     moveFileIfExists( tmpResource, directory );
244                     success = true;
245                 }
246             }
247         }
248         finally
249         {
250             FileUtils.deleteQuietly( workingDirectory );
251         }
252
253         // do we still need to execute the consumers?
254
255         return success;
256     }
257
258     /**
259      * Using wagon, connect to the remote repository.
260      *
261      * @param wagon the wagon instance to establish the connection on.
262      * @return true if the connection was successful. false if not connected.
263      */
264     private boolean connectToRepository( Wagon wagon, RemoteRepositoryConfiguration remoteRepository )
265     {
266         boolean connected;
267
268         final ProxyInfo networkProxy;
269         networkProxy = this.networkProxyMap.get( remoteRepository.getId() );
270
271         if ( networkProxy != null )
272         {
273             String msg = "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
274                 + " to connect to remote repository " + remoteRepository.getUrl();
275             if ( networkProxy.getNonProxyHosts() != null )
276             {
277                 msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
278             }
279
280             if ( StringUtils.isNotBlank( networkProxy.getUserName() ) )
281             {
282                 msg += "; as user: " + networkProxy.getUserName();
283             }
284
285             log.debug( msg );
286         }
287
288         AuthenticationInfo authInfo = null;
289         String username = remoteRepository.getUsername();
290         String password = remoteRepository.getPassword();
291
292         if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
293         {
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 );
298         }
299
300         // Convert seconds to milliseconds
301         int timeoutInMilliseconds = remoteRepository.getTimeout() * 1000;
302
303         // Set timeout
304         wagon.setTimeout( timeoutInMilliseconds );
305
306         try
307         {
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 );
311             connected = true;
312         }
313         catch ( ConnectionException e )
314         {
315             log.error( "Could not connect to " + remoteRepository.getName() + ": " + e.getMessage() );
316             connected = false;
317         }
318         catch ( AuthenticationException e )
319         {
320             log.error( "Could not connect to " + remoteRepository.getName() + ": " + e.getMessage() );
321             connected = false;
322         }
323
324         return connected;
325     }
326
327     private File transferChecksum( Wagon wagon, RemoteRepositoryConfiguration remoteRepository, String remotePath,
328                                    File resource, File tmpDirectory, String ext )
329         throws AuthorizationException, TransferFailedException, ResourceDoesNotExistException
330     {
331         File destFile = new File( tmpDirectory, resource.getName() + ext );
332
333         log.info( "Retrieving " + remotePath + " from " + remoteRepository.getName() );
334
335         wagon.get( remotePath, destFile );
336
337         log.debug( "Downloaded successfully." );
338
339         return destFile;
340     }
341
342     private String getProtocol( String url )
343     {
344         String protocol = StringUtils.substringBefore( url, ":" );
345
346         return protocol;
347     }
348
349     private File createWorkingDirectory( String targetRepository )
350     {
351         try
352         {
353             File tmpDir = File.createTempFile( ".workingdirectory", null, new File( targetRepository ) );
354             tmpDir.delete();
355             tmpDir.mkdirs();
356             return tmpDir;
357         }
358         catch ( IOException e )
359         {
360             throw new RuntimeException( "Could not create working directory for this request", e );
361         }
362     }
363
364     private void moveFileIfExists( File fileToMove, File directory )
365     {
366         if ( fileToMove != null && fileToMove.exists() )
367         {
368             File newLocation = new File( directory, fileToMove.getName() );
369             if ( newLocation.exists() && !newLocation.delete() )
370             {
371                 throw new RuntimeException(
372                     "Unable to overwrite existing target file: " + newLocation.getAbsolutePath() );
373             }
374
375             newLocation.getParentFile().mkdirs();
376             if ( !fileToMove.renameTo( newLocation ) )
377             {
378                 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
379
380                 try
381                 {
382                     FileUtils.copyFile( fileToMove, newLocation );
383                 }
384                 catch ( IOException e )
385                 {
386                     if ( newLocation.exists() )
387                     {
388                         log.error( "Tried to copy file " + fileToMove.getName() + " to " + newLocation.getAbsolutePath()
389                                        + " but file with this name already exists." );
390                     }
391                     else
392                     {
393                         throw new RuntimeException(
394                             "Cannot copy tmp file " + fileToMove.getAbsolutePath() + " to its final location", e );
395                     }
396                 }
397                 finally
398                 {
399                     FileUtils.deleteQuietly( fileToMove );
400                 }
401             }
402         }
403     }
404 }