This project has retired. For details please refer to its
Attic page.
RepositoryModelResolver xref
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 org.apache.archiva.common.utils.VersionUtil;
23 import org.apache.archiva.maven2.metadata.MavenMetadataReader;
24 import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
25 import org.apache.archiva.model.ArchivaRepositoryMetadata;
26 import org.apache.archiva.model.SnapshotVersion;
27 import org.apache.archiva.proxy.maven.WagonFactory;
28 import org.apache.archiva.proxy.maven.WagonFactoryException;
29 import org.apache.archiva.proxy.maven.WagonFactoryRequest;
30 import org.apache.archiva.proxy.model.NetworkProxy;
31 import org.apache.archiva.repository.ManagedRepository;
32 import org.apache.archiva.repository.RemoteRepository;
33 import org.apache.archiva.repository.RepositoryCredentials;
34 import org.apache.archiva.repository.maven2.MavenSystemManager;
35 import org.apache.archiva.repository.storage.StorageAsset;
36 import org.apache.archiva.xml.XMLException;
37 import org.apache.commons.lang3.StringUtils;
38 import org.apache.http.auth.UsernamePasswordCredentials;
39 import org.apache.maven.model.Dependency;
40 import org.apache.maven.model.Parent;
41 import org.apache.maven.model.Repository;
42 import org.apache.maven.model.building.FileModelSource;
43 import org.apache.maven.model.building.ModelSource;
44 import org.apache.maven.model.resolution.InvalidRepositoryException;
45 import org.apache.maven.model.resolution.ModelResolver;
46 import org.apache.maven.model.resolution.UnresolvableModelException;
47 import org.apache.maven.wagon.ConnectionException;
48 import org.apache.maven.wagon.ResourceDoesNotExistException;
49 import org.apache.maven.wagon.TransferFailedException;
50 import org.apache.maven.wagon.Wagon;
51 import org.apache.maven.wagon.authentication.AuthenticationException;
52 import org.apache.maven.wagon.authentication.AuthenticationInfo;
53 import org.apache.maven.wagon.authorization.AuthorizationException;
54 import org.apache.maven.wagon.proxy.ProxyInfo;
55 import org.eclipse.aether.RepositorySystemSession;
56 import org.eclipse.aether.artifact.Artifact;
57 import org.eclipse.aether.artifact.DefaultArtifact;
58 import org.eclipse.aether.impl.VersionRangeResolver;
59 import org.eclipse.aether.resolution.VersionRangeRequest;
60 import org.eclipse.aether.resolution.VersionRangeResolutionException;
61 import org.eclipse.aether.resolution.VersionRangeResult;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 import java.io.IOException;
66 import java.nio.file.Files;
67 import java.nio.file.Path;
68 import java.nio.file.Paths;
69 import java.util.HashMap;
70 import java.util.List;
71 import java.util.Map;
72
73 public class RepositoryModelResolver
74 implements ModelResolver
75 {
76
77 private final Map<String, NetworkProxy> networkProxyMap = new HashMap<>();
78
79 private RepositorySystemSession session;
80 private VersionRangeResolver versionRangeResolver;
81
82 private StorageAsset basedir;
83
84 private RepositoryPathTranslator pathTranslator;
85
86 private WagonFactory wagonFactory;
87
88 private List<RemoteRepository> remoteRepositories;
89
90 private ManagedRepository targetRepository;
91
92 private static final Logger log = LoggerFactory.getLogger( RepositoryModelResolver.class );
93
94 private static final String METADATA_FILENAME = "maven-metadata.xml";
95
96 private MavenSystemManager mavenSystemManager;
97
98
99
100 private ManagedRepository managedRepository;
101
102 public RepositoryModelResolver(StorageAsset basedir, RepositoryPathTranslator pathTranslator )
103 {
104 this.basedir = basedir;
105
106 this.pathTranslator = pathTranslator;
107 }
108
109 public RepositoryModelResolver(ManagedRepository managedRepository, RepositoryPathTranslator pathTranslator,
110 WagonFactory wagonFactory, List<RemoteRepository> remoteRepositories,
111 Map<String, NetworkProxy> networkProxiesMap, ManagedRepository targetRepository,
112 MavenSystemManager mavenSystemManager)
113 {
114 this( managedRepository.getAsset(""), pathTranslator );
115
116 this.managedRepository = managedRepository;
117
118 this.wagonFactory = wagonFactory;
119
120 this.remoteRepositories = remoteRepositories;
121
122 this.networkProxyMap.clear();
123 this.networkProxyMap.putAll(networkProxiesMap);
124
125 this.targetRepository = targetRepository;
126
127 this.session = MavenSystemManager.newRepositorySystemSession( managedRepository.getAsset("").getFilePath().toString() );
128
129 this.versionRangeResolver = mavenSystemManager.getLocator().getService(VersionRangeResolver.class);
130
131 this.mavenSystemManager = mavenSystemManager;
132 }
133
134
135 @Override
136 public ModelSource resolveModel( String groupId, String artifactId, String version )
137 throws UnresolvableModelException
138 {
139 String filename = artifactId + "-" + version + ".pom";
140
141
142 StorageAsset model = pathTranslator.toFile( basedir, groupId, artifactId, version, filename );
143
144 if ( !model.exists() )
145 {
146
147
148
149
150 if ( StringUtils.contains( version, VersionUtil.SNAPSHOT ) )
151 {
152 Path localSnapshotModel = findTimeStampedSnapshotPom( groupId, artifactId, version, model.getParent().getFilePath() );
153 if ( localSnapshotModel != null )
154 {
155 return new FileModelSource( localSnapshotModel.toFile() );
156 }
157
158 }
159
160 for ( RemoteRepository remoteRepository : remoteRepositories )
161 {
162 try
163 {
164 boolean success = getModelFromProxy( remoteRepository, groupId, artifactId, version, filename );
165 if ( success && model.exists() )
166 {
167 log.info( "Model '{}' successfully retrieved from remote repository '{}'",
168 model.getPath(), remoteRepository.getId() );
169 break;
170 }
171 }
172 catch ( ResourceDoesNotExistException e )
173 {
174 log.info(
175 "An exception was caught while attempting to retrieve model '{}' from remote repository '{}'.Reason:{}",
176 model.getPath(), remoteRepository.getId(), e.getMessage() );
177 }
178 catch ( Exception e )
179 {
180 log.warn(
181 "An exception was caught while attempting to retrieve model '{}' from remote repository '{}'.Reason:{}",
182 model.getPath(), remoteRepository.getId(), e.getMessage() );
183
184 continue;
185 }
186 }
187 }
188
189 return new FileModelSource( model.getFilePath().toFile() );
190 }
191
192 public ModelSource resolveModel(Parent parent) throws UnresolvableModelException {
193 try {
194 Artifact artifact = new DefaultArtifact(parent.getGroupId(), parent.getArtifactId(), "", "pom", parent.getVersion());
195 VersionRangeRequest versionRangeRequest;
196 versionRangeRequest = new VersionRangeRequest(artifact, null, null);
197 VersionRangeResult versionRangeResult = this.versionRangeResolver.resolveVersionRange(this.session, versionRangeRequest);
198 if (versionRangeResult.getHighestVersion() == null) {
199 throw new UnresolvableModelException(String.format("No versions matched the requested parent version range '%s'", parent.getVersion()), parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
200 } else if (versionRangeResult.getVersionConstraint() != null && versionRangeResult.getVersionConstraint().getRange() != null && versionRangeResult.getVersionConstraint().getRange().getUpperBound() == null) {
201 throw new UnresolvableModelException(String.format("The requested parent version range '%s' does not specify an upper bound", parent.getVersion()), parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
202 } else {
203 parent.setVersion(versionRangeResult.getHighestVersion().toString());
204 return this.resolveModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
205 }
206 } catch ( VersionRangeResolutionException var5) {
207 throw new UnresolvableModelException(var5.getMessage(), parent.getGroupId(), parent.getArtifactId(), parent.getVersion(), var5);
208 }
209 }
210
211 public ModelSource resolveModel(Dependency dependency) throws UnresolvableModelException {
212 try {
213 Artifact artifact = new DefaultArtifact(dependency.getGroupId(), dependency.getArtifactId(), "", "pom", dependency.getVersion());
214 VersionRangeRequest versionRangeRequest = new VersionRangeRequest(artifact, null, null);
215 VersionRangeResult versionRangeResult = this.versionRangeResolver.resolveVersionRange(this.session, versionRangeRequest);
216 if (versionRangeResult.getHighestVersion() == null) {
217 throw new UnresolvableModelException(String.format("No versions matched the requested dependency version range '%s'", dependency.getVersion()), dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion());
218 } else if (versionRangeResult.getVersionConstraint() != null && versionRangeResult.getVersionConstraint().getRange() != null && versionRangeResult.getVersionConstraint().getRange().getUpperBound() == null) {
219 throw new UnresolvableModelException(String.format("The requested dependency version range '%s' does not specify an upper bound", dependency.getVersion()), dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion());
220 } else {
221 dependency.setVersion(versionRangeResult.getHighestVersion().toString());
222 return this.resolveModel(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion());
223 }
224 } catch (VersionRangeResolutionException var5) {
225 throw new UnresolvableModelException(var5.getMessage(), dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(), var5);
226 }
227 }
228
229 protected Path findTimeStampedSnapshotPom( String groupId, String artifactId, String version,
230 Path parentDirectory )
231 {
232
233
234 Path mavenMetadata = parentDirectory.resolve( METADATA_FILENAME );
235 if ( Files.exists(mavenMetadata) )
236 {
237 try
238 {
239 ArchivaRepositoryMetadata archivaRepositoryMetadata = MavenMetadataReader.read( mavenMetadata);
240 SnapshotVersion snapshotVersion = archivaRepositoryMetadata.getSnapshotVersion();
241 if ( snapshotVersion != null )
242 {
243 String lastVersion = snapshotVersion.getTimestamp();
244 int buildNumber = snapshotVersion.getBuildNumber();
245 String snapshotPath =
246 StringUtils.replaceChars( groupId, '.', '/' ) + '/' + artifactId + '/' + version + '/'
247 + artifactId + '-' + StringUtils.remove( version, "-" + VersionUtil.SNAPSHOT ) + '-'
248 + lastVersion + '-' + buildNumber + ".pom";
249
250 log.debug( "use snapshot path {} for maven coordinate {}:{}:{}", snapshotPath, groupId, artifactId,
251 version );
252
253 StorageAsset model = basedir.resolve( snapshotPath );
254
255 if ( model.exists() )
256 {
257 return model.getFilePath();
258 }
259 }
260 }
261 catch (XMLException e )
262 {
263 log.warn( "fail to read {}, {}", mavenMetadata.toAbsolutePath(), e.getCause() );
264 }
265 }
266
267 return null;
268 }
269
270 @Override
271 public void addRepository( Repository repository )
272 throws InvalidRepositoryException
273 {
274
275
276
277 }
278
279 @Override
280 public void addRepository( Repository repository, boolean b ) throws InvalidRepositoryException
281 {
282
283 }
284
285 @Override
286 public ModelResolver newCopy()
287 {
288 return new RepositoryModelResolver( managedRepository, pathTranslator, wagonFactory, remoteRepositories,
289 networkProxyMap, targetRepository, mavenSystemManager);
290 }
291
292
293
294 private boolean getModelFromProxy( RemoteRepository remoteRepository, String groupId, String artifactId,
295 String version, String filename )
296 throws AuthorizationException, TransferFailedException, ResourceDoesNotExistException, WagonFactoryException,
297 XMLException, IOException
298 {
299 boolean success = false;
300 Path tmpMd5 = null;
301 Path tmpSha1 = null;
302 Path tmpResource = null;
303 String artifactPath = pathTranslator.toPath( groupId, artifactId, version, filename );
304 Path resource = targetRepository.getAsset("").getFilePath().resolve( artifactPath );
305
306 Path workingDirectory = createWorkingDirectory( targetRepository.getLocation().toString() );
307 try
308 {
309 Wagon wagon = null;
310 try
311 {
312 String protocol = getProtocol( remoteRepository.getLocation().toString() );
313 final NetworkProxy networkProxy = this.networkProxyMap.get( remoteRepository.getId() );
314
315 wagon = wagonFactory.getWagon(
316 new WagonFactoryRequest( "wagon#" + protocol, remoteRepository.getExtraHeaders() ).networkProxy(
317 networkProxy )
318 );
319
320 if ( wagon == null )
321 {
322 throw new RuntimeException( "Unsupported remote repository protocol: " + protocol );
323 }
324
325 boolean connected = connectToRepository( wagon, remoteRepository );
326 if ( connected )
327 {
328 tmpResource = workingDirectory.resolve( filename );
329
330 if ( VersionUtil.isSnapshot( version ) )
331 {
332
333 Path tmpMetadataResource = workingDirectory.resolve( METADATA_FILENAME );
334
335 String metadataPath =
336 StringUtils.substringBeforeLast( artifactPath, "/" ) + "/" + METADATA_FILENAME;
337
338 wagon.get( addParameters( metadataPath, remoteRepository ), tmpMetadataResource.toFile() );
339
340 log.debug( "Successfully downloaded metadata." );
341
342 ArchivaRepositoryMetadata metadata = MavenMetadataReader.read( tmpMetadataResource );
343
344
345 SnapshotVersion snapshotVersion = metadata.getSnapshotVersion();
346 String timestampVersion = version;
347 if ( snapshotVersion != null )
348 {
349 timestampVersion = timestampVersion.substring( 0, timestampVersion.length()
350 - 8 );
351 timestampVersion = timestampVersion + snapshotVersion.getTimestamp() + "-"
352 + snapshotVersion.getBuildNumber();
353
354 filename = artifactId + "-" + timestampVersion + ".pom";
355
356 artifactPath = pathTranslator.toPath( groupId, artifactId, version, filename );
357
358 log.debug( "New artifactPath :{}", artifactPath );
359 }
360 }
361
362 log.info( "Retrieving {} from {}", artifactPath, remoteRepository.getName() );
363
364 wagon.get( addParameters( artifactPath, remoteRepository ), tmpResource.toFile() );
365
366 log.debug( "Downloaded successfully." );
367
368 tmpSha1 = transferChecksum( wagon, remoteRepository, artifactPath, tmpResource, workingDirectory,
369 ".sha1" );
370 tmpMd5 = transferChecksum( wagon, remoteRepository, artifactPath, tmpResource, workingDirectory,
371 ".md5" );
372 }
373 }
374 finally
375 {
376 if ( wagon != null )
377 {
378 try
379 {
380 wagon.disconnect();
381 }
382 catch ( ConnectionException e )
383 {
384 log.warn( "Unable to disconnect wagon.", e );
385 }
386 }
387 }
388
389 if ( resource != null )
390 {
391 synchronized ( resource.toAbsolutePath().toString().intern() )
392 {
393 Path directory = resource.getParent();
394 moveFileIfExists( tmpMd5, directory );
395 moveFileIfExists( tmpSha1, directory );
396 moveFileIfExists( tmpResource, directory );
397 success = true;
398 }
399 }
400 }
401 finally
402 {
403 org.apache.archiva.common.utils.FileUtils.deleteQuietly( workingDirectory );
404 }
405
406
407
408 return success;
409 }
410
411
412
413
414
415
416
417 private boolean connectToRepository( Wagon wagon, RemoteRepository remoteRepository )
418 {
419 boolean connected;
420
421 final NetworkProxy proxyConnector = this.networkProxyMap.get( remoteRepository.getId() );
422 ProxyInfo networkProxy = null;
423 if ( proxyConnector != null )
424 {
425 networkProxy = new ProxyInfo();
426 networkProxy.setType( proxyConnector.getProtocol() );
427 networkProxy.setHost( proxyConnector.getHost() );
428 networkProxy.setPort( proxyConnector.getPort() );
429 networkProxy.setUserName( proxyConnector.getUsername() );
430 networkProxy.setPassword( new String(proxyConnector.getPassword()) );
431
432 String msg = "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
433 + " to connect to remote repository " + remoteRepository.getLocation();
434 if ( networkProxy.getNonProxyHosts() != null )
435 {
436 msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
437 }
438
439 if ( StringUtils.isNotBlank( networkProxy.getUserName() ) )
440 {
441 msg += "; as user: " + networkProxy.getUserName();
442 }
443
444 log.debug( msg );
445 }
446
447 AuthenticationInfo authInfo = null;
448 RepositoryCredentials creds = remoteRepository.getLoginCredentials();
449 String username = "";
450 String password = "";
451 if (creds instanceof UsernamePasswordCredentials) {
452 UsernamePasswordCredentials c = (UsernamePasswordCredentials) creds;
453 username = c.getUserName();
454 password = c.getPassword();
455 }
456
457 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
458 {
459 log.debug( "Using username {} to connect to remote repository {}", username, remoteRepository.getLocation() );
460 authInfo = new AuthenticationInfo();
461 authInfo.setUserName( username );
462 authInfo.setPassword( password );
463 }
464
465 int timeoutInMilliseconds = ((int)remoteRepository.getTimeout().getSeconds())*1000;
466
467
468 wagon.setReadTimeout( timeoutInMilliseconds );
469 wagon.setTimeout( timeoutInMilliseconds );
470
471 try
472 {
473 org.apache.maven.wagon.repository.Repository wagonRepository =
474 new org.apache.maven.wagon.repository.Repository( remoteRepository.getId(), remoteRepository.getLocation().toString() );
475 if ( networkProxy != null )
476 {
477 wagon.connect( wagonRepository, authInfo, networkProxy );
478 }
479 else
480 {
481 wagon.connect( wagonRepository, authInfo );
482 }
483 connected = true;
484 }
485 catch ( ConnectionException | AuthenticationException e )
486 {
487 log.error( "Could not connect to {}:{} ", remoteRepository.getName(), e.getMessage() );
488 connected = false;
489 }
490
491 return connected;
492 }
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507 private Path transferChecksum( final Wagon wagon, final RemoteRepository remoteRepository,
508 final String remotePath, final Path resource,
509 final Path workingDir, final String ext )
510 throws AuthorizationException, TransferFailedException, ResourceDoesNotExistException
511 {
512 Path destFile = workingDir.resolve( resource.getFileName() + ext );
513 String remoteChecksumPath = remotePath + ext;
514
515 log.info( "Retrieving {} from {}", remoteChecksumPath, remoteRepository.getName() );
516
517 wagon.get( addParameters( remoteChecksumPath, remoteRepository ), destFile.toFile() );
518
519 log.debug( "Downloaded successfully." );
520
521 return destFile;
522 }
523
524 private String getProtocol( String url )
525 {
526 String protocol = StringUtils.substringBefore( url, ":" );
527
528 return protocol;
529 }
530
531 private Path createWorkingDirectory( String targetRepository )
532 throws IOException
533 {
534 return Files.createTempDirectory( "temp" );
535 }
536
537 private void moveFileIfExists( Path fileToMove, Path directory )
538 {
539 if ( fileToMove != null && Files.exists(fileToMove) )
540 {
541 Path newLocation = directory.resolve( fileToMove.getFileName() );
542 try {
543 Files.deleteIfExists(newLocation);
544 } catch (IOException e) {
545 throw new RuntimeException(
546 "Unable to overwrite existing target file: " + newLocation.toAbsolutePath(), e );
547 }
548
549 try {
550 Files.createDirectories(newLocation.getParent());
551 } catch (IOException e) {
552 e.printStackTrace();
553 }
554 try {
555 Files.move(fileToMove, newLocation );
556 } catch (IOException e) {
557 try {
558 Files.copy(fileToMove, newLocation);
559 } catch (IOException e1) {
560 if (Files.exists(newLocation)) {
561 log.error( "Tried to copy file {} to {} but file with this name already exists.",
562 fileToMove.getFileName(), newLocation.toAbsolutePath() );
563 } else {
564 throw new RuntimeException(
565 "Cannot copy tmp file " + fileToMove.toAbsolutePath() + " to its final location", e );
566 }
567 }
568 } finally {
569 org.apache.archiva.common.utils.FileUtils.deleteQuietly(fileToMove);
570 }
571 }
572 }
573
574 protected String addParameters( String path, RemoteRepository remoteRepository )
575 {
576 if ( remoteRepository.getExtraParameters().isEmpty() )
577 {
578 return path;
579 }
580
581 boolean question = false;
582
583 StringBuilder res = new StringBuilder( path == null ? "" : path );
584
585 for ( Map.Entry<String, String> entry : remoteRepository.getExtraParameters().entrySet() )
586 {
587 if ( !question )
588 {
589 res.append( '?' ).append( entry.getKey() ).append( '=' ).append( entry.getValue() );
590 }
591 }
592
593 return res.toString();
594 }
595 }