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