1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.shared.repository;
20
21 import org.apache.maven.artifact.Artifact;
22 import org.apache.maven.artifact.ArtifactUtils;
23 import org.apache.maven.artifact.factory.ArtifactFactory;
24 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
25 import org.apache.maven.artifact.repository.ArtifactRepository;
26 import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
27 import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
28 import org.apache.maven.artifact.repository.DefaultArtifactRepository;
29 import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
30 import org.apache.maven.artifact.repository.metadata.ArtifactRepositoryMetadata;
31 import org.apache.maven.artifact.repository.metadata.Versioning;
32 import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer;
33 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
34 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
35 import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
36 import org.apache.maven.artifact.resolver.ArtifactResolver;
37 import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
38 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
39 import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
40 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
41 import org.apache.maven.artifact.versioning.VersionRange;
42 import org.apache.maven.model.Dependency;
43 import org.apache.maven.model.DependencyManagement;
44 import org.apache.maven.model.Exclusion;
45 import org.apache.maven.project.DefaultMavenProjectBuilder;
46 import org.apache.maven.project.MavenProject;
47 import org.apache.maven.project.MavenProjectBuilder;
48 import org.apache.maven.project.ProjectBuildingException;
49 import org.apache.maven.shared.artifact.filter.PatternExcludesArtifactFilter;
50 import org.apache.maven.shared.artifact.filter.PatternIncludesArtifactFilter;
51 import org.apache.maven.shared.artifact.filter.ScopeArtifactFilter;
52 import org.apache.maven.shared.repository.model.GroupVersionAlignment;
53 import org.apache.maven.shared.repository.model.RepositoryInfo;
54 import org.apache.maven.shared.repository.utils.DigestUtils;
55 import org.codehaus.plexus.PlexusConstants;
56 import org.codehaus.plexus.PlexusContainer;
57 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
58 import org.codehaus.plexus.context.Context;
59 import org.codehaus.plexus.context.ContextException;
60 import org.codehaus.plexus.logging.AbstractLogEnabled;
61 import org.codehaus.plexus.logging.Logger;
62 import org.codehaus.plexus.logging.console.ConsoleLogger;
63 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
64 import org.codehaus.plexus.util.FileUtils;
65 import org.codehaus.plexus.util.IOUtil;
66
67 import java.io.File;
68 import java.io.FileWriter;
69 import java.io.IOException;
70 import java.io.Writer;
71 import java.lang.reflect.Field;
72 import java.security.NoSuchAlgorithmException;
73 import java.text.DateFormat;
74 import java.text.SimpleDateFormat;
75 import java.util.ArrayList;
76 import java.util.Arrays;
77 import java.util.Collections;
78 import java.util.Date;
79 import java.util.HashMap;
80 import java.util.Iterator;
81 import java.util.List;
82 import java.util.Map;
83 import java.util.Set;
84 import java.util.TimeZone;
85
86
87
88
89
90
91
92 public class DefaultRepositoryAssembler
93 extends AbstractLogEnabled
94 implements RepositoryAssembler, Contextualizable
95 {
96 private static final String[] PREFERRED_RESOLVER_HINTS = { "project-cache-aware",
97 "default" };
98
99 protected static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone( "UTC" );
100
101 protected static final String UTC_TIMESTAMP_PATTERN = "yyyyMMddHHmmss";
102
103
104
105
106 protected ArtifactFactory artifactFactory;
107
108
109
110
111
112 protected ArtifactResolver artifactResolver;
113
114
115
116
117 protected ArtifactRepositoryLayout repositoryLayout;
118
119
120
121
122 protected ArtifactRepositoryFactory artifactRepositoryFactory;
123
124
125
126
127 protected ArtifactMetadataSource metadataSource;
128
129
130
131
132 protected MavenProjectBuilder projectBuilder;
133
134 public DefaultRepositoryAssembler()
135 {
136
137 }
138
139 public DefaultRepositoryAssembler( ArtifactFactory artifactFactory, ArtifactResolver artifactResolver,
140 ArtifactRepositoryLayout repositoryLayout,
141 ArtifactRepositoryFactory artifactRepositoryFactory,
142 ArtifactMetadataSource metadataSource, MavenProjectBuilder projectBuilder )
143 {
144
145 this.artifactFactory = artifactFactory;
146 this.artifactResolver = artifactResolver;
147 this.repositoryLayout = repositoryLayout;
148 this.artifactRepositoryFactory = artifactRepositoryFactory;
149 this.metadataSource = metadataSource;
150 this.projectBuilder = projectBuilder;
151
152 enableLogging( new ConsoleLogger( Logger.LEVEL_DEBUG, getClass().getName() + "::Internal" ) );
153 }
154
155 public void buildRemoteRepository( File repositoryDirectory, RepositoryInfo repository,
156 RepositoryBuilderConfigSource configSource )
157 throws RepositoryAssemblyException
158 {
159 MavenProject project = configSource.getProject();
160 ArtifactRepository localRepository = configSource.getLocalRepository();
161
162 Map groupVersionAlignments = createGroupVersionAlignments( repository.getGroupVersionAlignments() );
163
164 ArtifactRepository targetRepository = createLocalRepository( repositoryDirectory );
165
166 ArtifactResolutionResult result = null;
167
168 Set dependencyArtifacts = project.getDependencyArtifacts();
169
170 if ( dependencyArtifacts == null )
171 {
172 Logger logger = getLogger();
173
174 if ( logger.isDebugEnabled() )
175 {
176 logger.debug( "dependency-artifact set for project: " + project.getId()
177 + " is null. Skipping repository processing." );
178 }
179
180 return;
181 }
182
183 try
184 {
185
186
187
188
189
190
191
192
193 result = artifactResolver.resolveTransitively( dependencyArtifacts, project.getArtifact(),
194 getManagedVersionMap( project ), localRepository,
195 project.getRemoteArtifactRepositories(),
196 metadataSource );
197 }
198 catch ( ArtifactResolutionException e )
199 {
200 throw new RepositoryAssemblyException( "Error resolving artifacts: " + e.getMessage(), e );
201 }
202 catch ( ArtifactNotFoundException e )
203 {
204 throw new RepositoryAssemblyException( "Error resolving artifacts: " + e.getMessage(), e );
205 }
206 catch ( InvalidVersionSpecificationException e )
207 {
208 throw new RepositoryAssemblyException( "Error resolving artifacts: " + e.getMessage(), e );
209 }
210
211 try
212 {
213
214
215 invalidateProccessedProjectCache();
216 }
217 catch ( Exception e )
218 {
219 throw new RepositoryAssemblyException( "Error invalidating the processed project cache.", e );
220 }
221
222 ArtifactFilter filter = buildRepositoryFilter( repository, project );
223
224 assembleRepositoryArtifacts( result, filter, project, localRepository, targetRepository, repositoryDirectory,
225 groupVersionAlignments );
226
227 ArtifactRepository centralRepository = findCentralRepository( project );
228
229 if ( repository.isIncludeMetadata() )
230 {
231 assembleRepositoryMetadata( result, filter, centralRepository, targetRepository );
232 }
233
234 addPomWithAncestry( project.getArtifact(), project.getRemoteArtifactRepositories(), localRepository,
235 targetRepository, groupVersionAlignments, project );
236 }
237
238 private ArtifactFilter buildRepositoryFilter( RepositoryInfo repository, MavenProject project )
239 {
240 AndArtifactFilter filter = new AndArtifactFilter();
241
242 ArtifactFilter scopeFilter = new ScopeArtifactFilter( repository.getScope() );
243 filter.add( scopeFilter );
244
245
246
247
248
249
250
251
252
253
254 List includes = repository.getIncludes();
255
256 if ( ( includes == null ) || includes.isEmpty() )
257 {
258 List patterns = new ArrayList();
259
260 Set projectArtifacts = project.getDependencyArtifacts();
261
262 if ( projectArtifacts != null )
263 {
264 for ( Iterator it = projectArtifacts.iterator(); it.hasNext(); )
265 {
266 Artifact artifact = (Artifact) it.next();
267
268 patterns.add( artifact.getDependencyConflictId() );
269 }
270 }
271
272 PatternIncludesArtifactFilter includeFilter = new PatternIncludesArtifactFilter( patterns, true );
273
274 filter.add( includeFilter );
275 }
276 else
277 {
278 filter.add( new PatternIncludesArtifactFilter( repository.getIncludes(), true ) );
279 }
280
281
282
283
284
285
286
287
288
289 List excludes = repository.getExcludes();
290
291 if ( ( excludes != null ) && !excludes.isEmpty() )
292 {
293 filter.add( new PatternExcludesArtifactFilter( repository.getExcludes(), true ) );
294 }
295
296 return filter;
297 }
298
299 private void assembleRepositoryArtifacts( ArtifactResolutionResult result, ArtifactFilter filter,
300 MavenProject project, ArtifactRepository localRepository,
301 ArtifactRepository targetRepository, File repositoryDirectory,
302 Map groupVersionAlignments )
303 throws RepositoryAssemblyException
304 {
305 try
306 {
307
308
309
310 FileUtils.deleteDirectory( repositoryDirectory );
311
312 FileUtils.mkdir( repositoryDirectory.getAbsolutePath() );
313
314 for ( Iterator i = result.getArtifacts().iterator(); i.hasNext(); )
315 {
316 Artifact a = (Artifact) i.next();
317
318 if ( filter.include( a ) )
319 {
320 getLogger().debug( "Re-resolving: " + a + " for repository assembly." );
321
322 setAlignment( a, groupVersionAlignments );
323
324
325
326 a.setResolved( false );
327
328 artifactResolver.resolve( a, project.getRemoteArtifactRepositories(), localRepository );
329
330 a.setVersion( a.getBaseVersion() );
331
332 File targetFile = new File( targetRepository.getBasedir(), targetRepository.pathOf( a ) );
333 FileUtils.copyFile( a.getFile(), targetFile );
334
335 writeChecksums( targetFile );
336
337 addPomWithAncestry( a, project.getRemoteArtifactRepositories(), localRepository, targetRepository, groupVersionAlignments, project );
338 }
339 }
340 }
341 catch ( ArtifactResolutionException e )
342 {
343 throw new RepositoryAssemblyException( "Error resolving artifacts: " + e.getMessage(), e );
344 }
345 catch ( ArtifactNotFoundException e )
346 {
347 throw new RepositoryAssemblyException( "Error resolving artifacts: " + e.getMessage(), e );
348 }
349 catch ( IOException e )
350 {
351 throw new RepositoryAssemblyException( "Error writing artifact metdata.", e );
352 }
353 }
354
355
356
357
358
359
360 private void addPomWithAncestry( final Artifact artifact, List remoteArtifactRepositories,
361 ArtifactRepository localRepository, ArtifactRepository targetRepository,
362 Map groupVersionAlignments, MavenProject masterProject )
363 throws RepositoryAssemblyException
364 {
365 String type = artifact.getType();
366 Map refs = masterProject.getProjectReferences();
367
368 String projectKey = ArtifactUtils.versionlessKey( artifact );
369
370 MavenProject p;
371 if ( artifact == masterProject.getArtifact() )
372 {
373 p = masterProject;
374 }
375 else if ( refs.containsKey( projectKey ) )
376 {
377 p = (MavenProject) refs.get( projectKey );
378 }
379 else
380 {
381 try
382 {
383 artifact.isSnapshot();
384
385 Artifact pomArtifact = artifactFactory.createProjectArtifact( artifact.getGroupId(), artifact.getArtifactId(), artifact.getBaseVersion() );
386
387 getLogger().debug( "Building MavenProject instance for: " + pomArtifact + ". NOTE: This SHOULD BE available in the Artifact API! ...but it's not." );
388 p = projectBuilder.buildFromRepository( pomArtifact, remoteArtifactRepositories, localRepository );
389 }
390 catch ( ProjectBuildingException e )
391 {
392 throw new RepositoryAssemblyException( "Error reading POM for: " + artifact.getId(), e );
393 }
394 }
395
396
397
398 if ( "pom".equals( type ) )
399 {
400 p = p.getParent();
401 }
402
403 while( p != null )
404 {
405 Artifact destArtifact = artifactFactory.createProjectArtifact( p.getGroupId(), p.getArtifactId(), p
406 .getVersion() );
407
408 setAlignment( destArtifact, groupVersionAlignments );
409
410 File sourceFile = p.getFile();
411
412
413 if ( ( sourceFile == null ) || !sourceFile.exists() )
414 {
415
416 Artifact srcArtifact = artifactFactory.createProjectArtifact( p.getGroupId(), p.getArtifactId(), p
417 .getVersion() );
418
419 sourceFile = new File( localRepository.getBasedir(), localRepository.pathOf( srcArtifact ) );
420 }
421
422 if ( !sourceFile.exists() )
423 {
424 break;
425 }
426
427 File targetFile = new File( targetRepository.getBasedir(), targetRepository.pathOf( destArtifact ) );
428
429 try
430 {
431 FileUtils.copyFile( sourceFile, targetFile );
432 }
433 catch ( IOException e )
434 {
435 throw new RepositoryAssemblyException( "Error writing POM metdata: " + destArtifact.getId(), e );
436 }
437
438 try
439 {
440 writeChecksums( targetFile );
441 }
442 catch ( IOException e )
443 {
444 throw new RepositoryAssemblyException( "Error writing checksums for POM: " + destArtifact.getId(), e );
445 }
446
447 p = p.getParent();
448 }
449 }
450
451 private ArtifactRepository findCentralRepository( MavenProject project )
452 {
453 ArtifactRepository centralRepository = null;
454 for ( Iterator i = project.getRemoteArtifactRepositories().iterator(); i.hasNext(); )
455 {
456 ArtifactRepository r = (ArtifactRepository) i.next();
457 if ( "central".equals( r.getId() ) )
458 {
459 centralRepository = r;
460 }
461 }
462
463 return centralRepository;
464 }
465
466 private void assembleRepositoryMetadata( ArtifactResolutionResult result, ArtifactFilter filter,
467 ArtifactRepository centralRepository, ArtifactRepository targetRepository )
468 throws RepositoryAssemblyException
469 {
470 for ( Iterator i = result.getArtifacts().iterator(); i.hasNext(); )
471 {
472 Artifact a = (Artifact) i.next();
473
474 if ( filter.include( a ) )
475 {
476 Versioning v = new Versioning();
477
478 v.setRelease( a.getVersion() );
479
480 v.setLatest( a.getVersion() );
481
482 v.addVersion( a.getVersion() );
483
484 v.setLastUpdated( getUtcDateFormatter().format( new Date() ) );
485
486 ArtifactRepositoryMetadata metadata = new ArtifactRepositoryMetadata( a, v );
487 String path = targetRepository.pathOfLocalRepositoryMetadata( metadata, centralRepository );
488 File metadataFile = new File( targetRepository.getBasedir(), path );
489
490 MetadataXpp3Writer metadataWriter = new MetadataXpp3Writer();
491
492 Writer writer = null;
493 try
494 {
495 writer = new FileWriter( metadataFile );
496
497 metadataWriter.write( writer, metadata.getMetadata() );
498 }
499 catch ( IOException e )
500 {
501 throw new RepositoryAssemblyException( "Error writing artifact metdata.", e );
502 }
503 finally
504 {
505 IOUtil.close( writer );
506 }
507
508 try
509 {
510 writeChecksums( metadataFile );
511
512 File metadataFileRemote = new File( targetRepository.getBasedir(), targetRepository
513 .pathOfRemoteRepositoryMetadata( metadata ) );
514
515 FileUtils.copyFile( metadataFile, metadataFileRemote );
516
517 FileUtils.copyFile( new File( metadataFile.getParentFile(), metadataFile.getName() + ".sha1" ),
518 new File( metadataFileRemote.getParentFile(), metadataFileRemote.getName()
519 + ".sha1" ) );
520
521 FileUtils.copyFile( new File( metadataFile.getParentFile(), metadataFile.getName() + ".md5" ),
522 new File( metadataFileRemote.getParentFile(), metadataFileRemote.getName()
523 + ".md5" ) );
524 }
525 catch ( IOException e )
526 {
527 throw new RepositoryAssemblyException( "Error writing artifact metdata.", e );
528 }
529 }
530 }
531 }
532
533 private void writeChecksums( File file )
534 throws IOException, RepositoryAssemblyException
535 {
536 try
537 {
538 String md5 = DigestUtils.createChecksum( file, "MD5" );
539 String sha1 = DigestUtils.createChecksum( file, "SHA-1" );
540
541 FileUtils.fileWrite( new File( file.getParentFile(), file.getName() + ".md5" ).getAbsolutePath(), md5
542 .toLowerCase() );
543 FileUtils.fileWrite( new File( file.getParentFile(), file.getName() + ".sha1" ).getAbsolutePath(), sha1
544 .toLowerCase() );
545 }
546 catch ( NoSuchAlgorithmException e )
547 {
548 throw new RepositoryAssemblyException( "Unable to get write checksums: " + e.getMessage(), e );
549 }
550 }
551
552 protected Map createGroupVersionAlignments( List versionAlignments )
553 {
554 Map groupVersionAlignments = new HashMap();
555
556 if ( versionAlignments != null )
557 {
558 for ( Iterator i = versionAlignments.iterator(); i.hasNext(); )
559 {
560 GroupVersionAlignment alignment = (GroupVersionAlignment) i.next();
561
562 groupVersionAlignments.put( alignment.getId(), alignment );
563 }
564 }
565
566 return groupVersionAlignments;
567 }
568
569 protected static DateFormat getUtcDateFormatter()
570 {
571 DateFormat utcDateFormatter = new SimpleDateFormat( UTC_TIMESTAMP_PATTERN );
572 utcDateFormatter.setTimeZone( UTC_TIME_ZONE );
573 return utcDateFormatter;
574 }
575
576 protected ArtifactRepository createLocalRepository( File directory )
577 {
578 String localRepositoryUrl = directory.getAbsolutePath();
579
580 if ( !localRepositoryUrl.startsWith( "file:" ) )
581 {
582 localRepositoryUrl = "file://" + localRepositoryUrl;
583 }
584
585 return createRepository( "local", localRepositoryUrl, false, true,
586 ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN );
587 }
588
589 public ArtifactRepository createRepository( String repositoryId, String repositoryUrl, boolean offline,
590 boolean updateSnapshots, String globalChecksumPolicy )
591 {
592 ArtifactRepository localRepository = new DefaultArtifactRepository( repositoryId, repositoryUrl,
593 repositoryLayout );
594
595 boolean snapshotPolicySet = false;
596
597 if ( offline )
598 {
599 snapshotPolicySet = true;
600 }
601
602 if ( !snapshotPolicySet && updateSnapshots )
603 {
604 artifactRepositoryFactory.setGlobalUpdatePolicy( ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS );
605 }
606
607 artifactRepositoryFactory.setGlobalChecksumPolicy( globalChecksumPolicy );
608
609 return localRepository;
610 }
611
612 private void invalidateProccessedProjectCache()
613 throws Exception
614 {
615 Class klass = DefaultMavenProjectBuilder.class;
616
617 try
618 {
619 Field field = klass.getDeclaredField( "processedProjectCache" );
620
621 field.setAccessible( true );
622
623 Object cache = field.get( projectBuilder );
624
625 cache.getClass().getDeclaredMethod( "clear", null ).invoke( cache, null );
626
627 field.setAccessible( false );
628 }
629 catch( NoSuchFieldException e )
630 {
631
632 }
633 }
634
635 private void setAlignment( Artifact artifact, Map groupVersionAlignments )
636 {
637 GroupVersionAlignment alignment = (GroupVersionAlignment) groupVersionAlignments.get( artifact.getGroupId() );
638
639 if ( alignment != null )
640 {
641 if ( !alignment.getExcludes().contains( artifact.getArtifactId() ) )
642 {
643 artifact.setVersion( alignment.getVersion() );
644 }
645 }
646 }
647
648
649
650 private Map getManagedVersionMap( MavenProject project )
651 throws InvalidVersionSpecificationException
652 {
653 DependencyManagement dependencyManagement = project.getModel().getDependencyManagement();
654
655 Map map = null;
656 List deps;
657 if ( ( dependencyManagement != null ) && ( ( deps = dependencyManagement.getDependencies() ) != null )
658 && ( deps.size() > 0 ) )
659 {
660 map = new HashMap();
661
662 if ( getLogger().isDebugEnabled() )
663 {
664 getLogger().debug( "Adding managed dependencies for " + project.getId() );
665 }
666
667 for ( Iterator i = dependencyManagement.getDependencies().iterator(); i.hasNext(); )
668 {
669 Dependency d = (Dependency) i.next();
670
671 VersionRange versionRange = VersionRange.createFromVersionSpec( d.getVersion() );
672 Artifact artifact = artifactFactory.createDependencyArtifact( d.getGroupId(), d.getArtifactId(),
673 versionRange, d.getType(),
674 d.getClassifier(), d.getScope(),
675 d.isOptional() );
676 if ( getLogger().isDebugEnabled() )
677 {
678 getLogger().debug( " " + artifact );
679 }
680
681
682
683
684 if ( ( null != d.getExclusions() ) && !d.getExclusions().isEmpty() )
685 {
686 List exclusions = new ArrayList();
687 Iterator exclItr = d.getExclusions().iterator();
688 while ( exclItr.hasNext() )
689 {
690 Exclusion e = (Exclusion) exclItr.next();
691 exclusions.add( e.getGroupId() + ":" + e.getArtifactId() );
692 }
693 ExcludesArtifactFilter eaf = new ExcludesArtifactFilter( exclusions );
694 artifact.setDependencyFilter( eaf );
695 }
696 else
697 {
698 artifact.setDependencyFilter( null );
699 }
700 map.put( d.getManagementKey(), artifact );
701 }
702 }
703 else if ( map == null )
704 {
705 map = Collections.EMPTY_MAP;
706 }
707 return map;
708 }
709
710 public void contextualize( Context context )
711 throws ContextException
712 {
713 PlexusContainer container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
714
715 for ( int i = 0; i < PREFERRED_RESOLVER_HINTS.length; i++ )
716 {
717 String hint = PREFERRED_RESOLVER_HINTS[i];
718
719 try
720 {
721 artifactResolver = (ArtifactResolver) container.lookup( ArtifactResolver.ROLE, hint );
722 break;
723 }
724 catch ( ComponentLookupException e )
725 {
726 getLogger().debug( "Cannot find ArtifactResolver with hint: " + hint, e );
727 }
728 }
729
730 if ( artifactResolver == null )
731 {
732 try
733 {
734 artifactResolver = (ArtifactResolver) container.lookup( ArtifactResolver.ROLE );
735 }
736 catch ( ComponentLookupException e )
737 {
738 getLogger().debug( "Cannot find ArtifactResolver with no hint.", e );
739 }
740 }
741
742 if ( artifactResolver == null )
743 {
744 throw new ContextException( "Failed to lookup a valid ArtifactResolver implementation. Tried hints:\n"
745 + Arrays.asList( PREFERRED_RESOLVER_HINTS ) );
746 }
747 }
748 }