1 package org.apache.archiva.metadata.repository.storage.maven2;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import com.google.common.io.Files;
23 import org.apache.archiva.admin.model.beans.ManagedRepository;
24 import org.apache.archiva.admin.model.beans.NetworkProxy;
25 import org.apache.archiva.admin.model.beans.RemoteRepository;
26 import org.apache.archiva.common.utils.VersionUtil;
27 import org.apache.archiva.maven2.metadata.MavenMetadataReader;
28 import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
29 import org.apache.archiva.model.ArchivaRepositoryMetadata;
30 import org.apache.archiva.model.SnapshotVersion;
31 import org.apache.archiva.proxy.common.WagonFactory;
32 import org.apache.archiva.proxy.common.WagonFactoryException;
33 import org.apache.archiva.proxy.common.WagonFactoryRequest;
34 import org.apache.archiva.xml.XMLException;
35 import org.apache.commons.io.FileUtils;
36 import org.apache.commons.lang.StringUtils;
37 import org.apache.maven.model.Repository;
38 import org.apache.maven.model.building.FileModelSource;
39 import org.apache.maven.model.building.ModelSource;
40 import org.apache.maven.model.resolution.InvalidRepositoryException;
41 import org.apache.maven.model.resolution.ModelResolver;
42 import org.apache.maven.model.resolution.UnresolvableModelException;
43 import org.apache.maven.wagon.ConnectionException;
44 import org.apache.maven.wagon.ResourceDoesNotExistException;
45 import org.apache.maven.wagon.TransferFailedException;
46 import org.apache.maven.wagon.Wagon;
47 import org.apache.maven.wagon.authentication.AuthenticationException;
48 import org.apache.maven.wagon.authentication.AuthenticationInfo;
49 import org.apache.maven.wagon.authorization.AuthorizationException;
50 import org.apache.maven.wagon.proxy.ProxyInfo;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 import java.io.File;
55 import java.io.IOException;
56 import java.util.List;
57 import java.util.Map;
58
59 public class RepositoryModelResolver
60 implements ModelResolver
61 {
62 private File basedir;
63
64 private RepositoryPathTranslator pathTranslator;
65
66 private WagonFactory wagonFactory;
67
68 private List<RemoteRepository> remoteRepositories;
69
70 private ManagedRepository targetRepository;
71
72 private static final Logger log = LoggerFactory.getLogger( RepositoryModelResolver.class );
73
74 private static final String METADATA_FILENAME = "maven-metadata.xml";
75
76
77 Map<String, NetworkProxy> networkProxyMap;
78
79 private ManagedRepository managedRepository;
80
81 public RepositoryModelResolver( File basedir, RepositoryPathTranslator pathTranslator )
82 {
83 this.basedir = basedir;
84
85 this.pathTranslator = pathTranslator;
86 }
87
88 public RepositoryModelResolver( ManagedRepository managedRepository, RepositoryPathTranslator pathTranslator,
89 WagonFactory wagonFactory, List<RemoteRepository> remoteRepositories,
90 Map<String, NetworkProxy> networkProxiesMap, ManagedRepository targetRepository )
91 {
92 this( new File( managedRepository.getLocation() ), pathTranslator );
93
94 this.managedRepository = managedRepository;
95
96 this.wagonFactory = wagonFactory;
97
98 this.remoteRepositories = remoteRepositories;
99
100 this.networkProxyMap = networkProxiesMap;
101
102 this.targetRepository = targetRepository;
103 }
104
105 public ModelSource resolveModel( String groupId, String artifactId, String version )
106 throws UnresolvableModelException
107 {
108 String filename = artifactId + "-" + version + ".pom";
109
110
111 File model = pathTranslator.toFile( basedir, groupId, artifactId, version, filename );
112
113 if ( !model.exists() )
114 {
115
116
117
118
119 if ( StringUtils.contains( version, VersionUtil.SNAPSHOT ) )
120 {
121 File localSnapshotModel = findTimeStampedSnapshotPom( groupId, artifactId, version, model.getParent() );
122 if ( localSnapshotModel != null )
123 {
124 return new FileModelSource( localSnapshotModel );
125 }
126
127 }
128
129 for ( RemoteRepository remoteRepository : remoteRepositories )
130 {
131 try
132 {
133 boolean success = getModelFromProxy( remoteRepository, groupId, artifactId, version, filename );
134 if ( success && model.exists() )
135 {
136 log.info( "Model '{}' successfully retrieved from remote repository '{}'",
137 model.getAbsolutePath(), remoteRepository.getId() );
138 break;
139 }
140 }
141 catch ( ResourceDoesNotExistException e )
142 {
143 log.info(
144 "An exception was caught while attempting to retrieve model '{}' from remote repository '{}'.Reason:{}",
145 new Object[]{ model.getAbsolutePath(), remoteRepository.getId(), e.getMessage() } );
146 }
147 catch ( Exception e )
148 {
149 log.warn(
150 "An exception was caught while attempting to retrieve model '{}' from remote repository '{}'.Reason:{}",
151 new Object[]{ model.getAbsolutePath(), remoteRepository.getId(), e.getMessage() } );
152
153 continue;
154 }
155 }
156 }
157
158 return new FileModelSource( model );
159 }
160
161 protected File findTimeStampedSnapshotPom( String groupId, String artifactId, String version,
162 String parentDirectory )
163 {
164
165
166 File mavenMetadata = new File( parentDirectory, METADATA_FILENAME );
167 if ( mavenMetadata.exists() )
168 {
169 try
170 {
171 ArchivaRepositoryMetadata archivaRepositoryMetadata = MavenMetadataReader.read( mavenMetadata );
172 SnapshotVersion snapshotVersion = archivaRepositoryMetadata.getSnapshotVersion();
173 if ( snapshotVersion != null )
174 {
175 String lastVersion = snapshotVersion.getTimestamp();
176 int buildNumber = snapshotVersion.getBuildNumber();
177 String snapshotPath =
178 StringUtils.replaceChars( groupId, '.', '/' ) + '/' + artifactId + '/' + version + '/'
179 + artifactId + '-' + StringUtils.remove( version, "-SNAPSHOT" ) + '-' + lastVersion + '-'
180 + buildNumber + ".pom";
181
182 log.debug( "use snapshot path {} for maven coordinate {}:{}:{}", snapshotPath, groupId, artifactId,
183 version );
184
185 File model = new File( basedir, snapshotPath );
186
187 if ( model.exists() )
188 {
189 return model;
190 }
191 }
192 }
193 catch ( XMLException e )
194 {
195 log.warn( "fail to read {}, {}", mavenMetadata.getAbsolutePath(), e.getCause() );
196 }
197 }
198
199 return null;
200 }
201
202 public void addRepository( Repository repository )
203 throws InvalidRepositoryException
204 {
205
206
207
208 }
209
210 public ModelResolver newCopy()
211 {
212 return new RepositoryModelResolver( basedir, pathTranslator );
213 }
214
215
216
217 private boolean getModelFromProxy( RemoteRepository remoteRepository, String groupId, String artifactId,
218 String version, String filename )
219 throws AuthorizationException, TransferFailedException, ResourceDoesNotExistException, WagonFactoryException,
220 XMLException
221 {
222 boolean success = false;
223 File tmpMd5 = null;
224 File tmpSha1 = null;
225 File tmpResource = null;
226 String artifactPath = pathTranslator.toPath( groupId, artifactId, version, filename );
227 File resource = new File( targetRepository.getLocation(), artifactPath );
228
229 File workingDirectory = createWorkingDirectory( targetRepository.getLocation() );
230 try
231 {
232 Wagon wagon = null;
233 try
234 {
235 String protocol = getProtocol( remoteRepository.getUrl() );
236 final NetworkProxy networkProxy = this.networkProxyMap.get( remoteRepository.getId() );
237
238 wagon = wagonFactory.getWagon(
239 new WagonFactoryRequest( "wagon#" + protocol, remoteRepository.getExtraHeaders() ).networkProxy(
240 networkProxy ) );
241
242 if ( wagon == null )
243 {
244 throw new RuntimeException( "Unsupported remote repository protocol: " + protocol );
245 }
246
247 boolean connected = connectToRepository( wagon, remoteRepository );
248 if ( connected )
249 {
250 tmpResource = new File( workingDirectory, filename );
251
252 if ( VersionUtil.isSnapshot( version ) )
253 {
254
255 File tmpMetadataResource = new File( workingDirectory, METADATA_FILENAME );
256
257 String metadataPath =
258 StringUtils.substringBeforeLast( artifactPath, "/" ) + "/" + METADATA_FILENAME;
259
260 wagon.get( addParameters( metadataPath, remoteRepository ), tmpMetadataResource );
261
262 log.debug( "Successfully downloaded metadata." );
263
264 ArchivaRepositoryMetadata metadata = MavenMetadataReader.read( tmpMetadataResource );
265
266
267 SnapshotVersion snapshotVersion = metadata.getSnapshotVersion();
268 String timestampVersion = version;
269 if ( snapshotVersion != null )
270 {
271 timestampVersion = timestampVersion.substring( 0, timestampVersion.length()
272 - 8 );
273 timestampVersion = timestampVersion + snapshotVersion.getTimestamp() + "-"
274 + snapshotVersion.getBuildNumber();
275
276 filename = artifactId + "-" + timestampVersion + ".pom";
277
278 artifactPath = pathTranslator.toPath( groupId, artifactId, version, filename );
279
280 log.debug( "New artifactPath :{}", artifactPath );
281 }
282 }
283
284 log.info( "Retrieving {} from {}", artifactPath, remoteRepository.getName() );
285
286 wagon.get( addParameters( artifactPath, remoteRepository ), tmpResource );
287
288 log.debug( "Downloaded successfully." );
289
290 tmpSha1 = transferChecksum( wagon, remoteRepository, artifactPath, tmpResource, workingDirectory,
291 ".sha1" );
292 tmpMd5 = transferChecksum( wagon, remoteRepository, artifactPath, tmpResource, workingDirectory,
293 ".md5" );
294 }
295 }
296 finally
297 {
298 if ( wagon != null )
299 {
300 try
301 {
302 wagon.disconnect();
303 }
304 catch ( ConnectionException e )
305 {
306 log.warn( "Unable to disconnect wagon.", e );
307 }
308 }
309 }
310
311 if ( resource != null )
312 {
313 synchronized ( resource.getAbsolutePath().intern() )
314 {
315 File directory = resource.getParentFile();
316 moveFileIfExists( tmpMd5, directory );
317 moveFileIfExists( tmpSha1, directory );
318 moveFileIfExists( tmpResource, directory );
319 success = true;
320 }
321 }
322 }
323 finally
324 {
325 FileUtils.deleteQuietly( workingDirectory );
326 }
327
328
329
330 return success;
331 }
332
333
334
335
336
337
338
339 private boolean connectToRepository( Wagon wagon, RemoteRepository remoteRepository )
340 {
341 boolean connected;
342
343 final NetworkProxy proxyConnector = this.networkProxyMap.get( remoteRepository.getId() );
344 ProxyInfo networkProxy = null;
345 if ( proxyConnector != null )
346 {
347 networkProxy = new ProxyInfo();
348 networkProxy.setType( proxyConnector.getProtocol() );
349 networkProxy.setHost( proxyConnector.getHost() );
350 networkProxy.setPort( proxyConnector.getPort() );
351 networkProxy.setUserName( proxyConnector.getUsername() );
352 networkProxy.setPassword( proxyConnector.getPassword() );
353
354 String msg = "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
355 + " to connect to remote repository " + remoteRepository.getUrl();
356 if ( networkProxy.getNonProxyHosts() != null )
357 {
358 msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
359 }
360
361 if ( StringUtils.isNotBlank( networkProxy.getUserName() ) )
362 {
363 msg += "; as user: " + networkProxy.getUserName();
364 }
365
366 log.debug( msg );
367 }
368
369 AuthenticationInfo authInfo = null;
370 String username = remoteRepository.getUserName();
371 String password = remoteRepository.getPassword();
372
373 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
374 {
375 log.debug( "Using username {} to connect to remote repository {}", username, remoteRepository.getUrl() );
376 authInfo = new AuthenticationInfo();
377 authInfo.setUserName( username );
378 authInfo.setPassword( password );
379 }
380
381
382 int timeoutInMilliseconds = remoteRepository.getTimeout() * 1000;
383
384
385 wagon.setReadTimeout( timeoutInMilliseconds );
386 wagon.setTimeout( timeoutInMilliseconds );
387
388 try
389 {
390 org.apache.maven.wagon.repository.Repository wagonRepository =
391 new org.apache.maven.wagon.repository.Repository( remoteRepository.getId(), remoteRepository.getUrl() );
392 if ( networkProxy != null )
393 {
394 wagon.connect( wagonRepository, authInfo, networkProxy );
395 }
396 else
397 {
398 wagon.connect( wagonRepository, authInfo );
399 }
400 connected = true;
401 }
402 catch ( ConnectionException e )
403 {
404 log.error( "Could not connect to {}:{} ", remoteRepository.getName(), e.getMessage() );
405 connected = false;
406 }
407 catch ( AuthenticationException e )
408 {
409 log.error( "Could not connect to {}:{} ", remoteRepository.getName(), e.getMessage() );
410 connected = false;
411 }
412
413 return connected;
414 }
415
416 private File transferChecksum( Wagon wagon, RemoteRepository remoteRepository, String remotePath, File resource,
417 File tmpDirectory, String ext )
418 throws AuthorizationException, TransferFailedException, ResourceDoesNotExistException
419 {
420 File destFile = new File( tmpDirectory, resource.getName() + ext );
421
422 log.info( "Retrieving {} from {}", remotePath, remoteRepository.getName() );
423
424 wagon.get( addParameters( remotePath, remoteRepository ), destFile );
425
426 log.debug( "Downloaded successfully." );
427
428 return destFile;
429 }
430
431 private String getProtocol( String url )
432 {
433 String protocol = StringUtils.substringBefore( url, ":" );
434
435 return protocol;
436 }
437
438 private File createWorkingDirectory( String targetRepository )
439 {
440 return Files.createTempDir();
441 }
442
443 private void moveFileIfExists( File fileToMove, File directory )
444 {
445 if ( fileToMove != null && fileToMove.exists() )
446 {
447 File newLocation = new File( directory, fileToMove.getName() );
448 if ( newLocation.exists() && !newLocation.delete() )
449 {
450 throw new RuntimeException(
451 "Unable to overwrite existing target file: " + newLocation.getAbsolutePath() );
452 }
453
454 newLocation.getParentFile().mkdirs();
455 if ( !fileToMove.renameTo( newLocation ) )
456 {
457 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
458
459 try
460 {
461 FileUtils.copyFile( fileToMove, newLocation );
462 }
463 catch ( IOException e )
464 {
465 if ( newLocation.exists() )
466 {
467 log.error( "Tried to copy file {} to {} but file with this name already exists.",
468 fileToMove.getName(), newLocation.getAbsolutePath() );
469 }
470 else
471 {
472 throw new RuntimeException(
473 "Cannot copy tmp file " + fileToMove.getAbsolutePath() + " to its final location", e );
474 }
475 }
476 finally
477 {
478 FileUtils.deleteQuietly( fileToMove );
479 }
480 }
481 }
482 }
483
484 protected String addParameters( String path, RemoteRepository remoteRepository )
485 {
486 if ( remoteRepository.getExtraParameters().isEmpty() )
487 {
488 return path;
489 }
490
491 boolean question = false;
492
493 StringBuilder res = new StringBuilder( path == null ? "" : path );
494
495 for ( Map.Entry<String, String> entry : remoteRepository.getExtraParameters().entrySet() )
496 {
497 if ( !question )
498 {
499 res.append( '?' ).append( entry.getKey() ).append( '=' ).append( entry.getValue() );
500 }
501 }
502
503 return res.toString();
504 }
505 }