1 package org.apache.maven.shared.repository;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.artifact.Artifact;
23 import org.apache.maven.artifact.ArtifactUtils;
24 import org.apache.maven.artifact.factory.ArtifactFactory;
25 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
26 import org.apache.maven.artifact.repository.ArtifactRepository;
27 import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
28 import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
29 import org.apache.maven.artifact.repository.DefaultArtifactRepository;
30 import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
31 import org.apache.maven.artifact.repository.metadata.ArtifactRepositoryMetadata;
32 import org.apache.maven.artifact.repository.metadata.Versioning;
33 import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer;
34 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
35 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
36 import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
37 import org.apache.maven.artifact.resolver.ArtifactResolver;
38 import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
39 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
40 import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
41 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
42 import org.apache.maven.artifact.versioning.VersionRange;
43 import org.apache.maven.model.Dependency;
44 import org.apache.maven.model.DependencyManagement;
45 import org.apache.maven.model.Exclusion;
46 import org.apache.maven.project.DefaultMavenProjectBuilder;
47 import org.apache.maven.project.MavenProject;
48 import org.apache.maven.project.MavenProjectBuilder;
49 import org.apache.maven.project.ProjectBuildingException;
50 import org.apache.maven.shared.artifact.filter.PatternExcludesArtifactFilter;
51 import org.apache.maven.shared.artifact.filter.PatternIncludesArtifactFilter;
52 import org.apache.maven.shared.artifact.filter.ScopeArtifactFilter;
53 import org.apache.maven.shared.repository.model.GroupVersionAlignment;
54 import org.apache.maven.shared.repository.model.RepositoryInfo;
55 import org.apache.maven.shared.utils.io.FileUtils;
56 import org.apache.maven.shared.utils.io.IOUtil;
57 import org.codehaus.plexus.PlexusConstants;
58 import org.codehaus.plexus.PlexusContainer;
59 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
60 import org.codehaus.plexus.context.Context;
61 import org.codehaus.plexus.context.ContextException;
62 import org.codehaus.plexus.logging.AbstractLogEnabled;
63 import org.codehaus.plexus.logging.Logger;
64 import org.codehaus.plexus.logging.console.ConsoleLogger;
65 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
66
67 import java.io.File;
68 import java.io.FileInputStream;
69 import java.io.FileWriter;
70 import java.io.IOException;
71 import java.io.Writer;
72 import java.lang.reflect.Field;
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.List;
81 import java.util.Map;
82 import java.util.Set;
83 import java.util.TimeZone;
84
85 import static org.apache.commons.codec.digest.DigestUtils.md5Hex;
86 import static org.apache.commons.codec.digest.DigestUtils.shaHex;
87
88
89
90
91
92
93
94 public class DefaultRepositoryAssembler
95 extends AbstractLogEnabled
96 implements RepositoryAssembler, Contextualizable
97 {
98 private static final String[] PREFERRED_RESOLVER_HINTS = { "project-cache-aware",
99 "default" };
100
101 protected static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone( "UTC" );
102
103 protected static final String UTC_TIMESTAMP_PATTERN = "yyyyMMddHHmmss";
104
105
106
107
108 protected ArtifactFactory artifactFactory;
109
110
111
112
113
114 protected ArtifactResolver artifactResolver;
115
116
117
118
119 protected ArtifactRepositoryLayout repositoryLayout;
120
121
122
123
124 protected ArtifactRepositoryFactory artifactRepositoryFactory;
125
126
127
128
129 protected ArtifactMetadataSource metadataSource;
130
131
132
133
134 protected MavenProjectBuilder projectBuilder;
135
136 public DefaultRepositoryAssembler()
137 {
138
139 }
140
141 public DefaultRepositoryAssembler( ArtifactFactory artifactFactory, ArtifactResolver artifactResolver,
142 ArtifactRepositoryLayout repositoryLayout,
143 ArtifactRepositoryFactory artifactRepositoryFactory,
144 ArtifactMetadataSource metadataSource, MavenProjectBuilder projectBuilder )
145 {
146
147 this.artifactFactory = artifactFactory;
148 this.artifactResolver = artifactResolver;
149 this.repositoryLayout = repositoryLayout;
150 this.artifactRepositoryFactory = artifactRepositoryFactory;
151 this.metadataSource = metadataSource;
152 this.projectBuilder = projectBuilder;
153
154 enableLogging( new ConsoleLogger( Logger.LEVEL_DEBUG, getClass().getName() + "::Internal" ) );
155 }
156
157 public void buildRemoteRepository( File repositoryDirectory, RepositoryInfo repository,
158 RepositoryBuilderConfigSource configSource )
159 throws RepositoryAssemblyException
160 {
161 MavenProject project = configSource.getProject();
162 ArtifactRepository localRepository = configSource.getLocalRepository();
163
164 Map groupVersionAlignments = createGroupVersionAlignments( repository.getGroupVersionAlignments() );
165
166 ArtifactRepository targetRepository = createLocalRepository( repositoryDirectory );
167
168 ArtifactResolutionResult result = null;
169
170 Set dependencyArtifacts = project.getDependencyArtifacts();
171
172 if ( dependencyArtifacts == null )
173 {
174 Logger logger = getLogger();
175
176 if ( logger.isDebugEnabled() )
177 {
178 logger.debug( "dependency-artifact set for project: " + project.getId()
179 + " is null. Skipping repository processing." );
180 }
181
182 return;
183 }
184
185 try
186 {
187
188
189
190
191
192
193
194
195 result = artifactResolver.resolveTransitively( dependencyArtifacts, project.getArtifact(),
196 getManagedVersionMap( project ), localRepository,
197 project.getRemoteArtifactRepositories(), metadataSource );
198 }
199 catch ( ArtifactResolutionException e )
200 {
201 throw new RepositoryAssemblyException( "Error resolving artifacts: " + e.getMessage(), e );
202 }
203 catch ( ArtifactNotFoundException e )
204 {
205 throw new RepositoryAssemblyException( "Error resolving artifacts: " + e.getMessage(), e );
206 }
207 catch ( InvalidVersionSpecificationException e )
208 {
209 throw new RepositoryAssemblyException( "Error resolving artifacts: " + e.getMessage(), e );
210 }
211
212 try
213 {
214
215
216 invalidateProccessedProjectCache();
217 }
218 catch ( Exception e )
219 {
220 throw new RepositoryAssemblyException( "Error invalidating the processed project cache.", e );
221 }
222
223 ArtifactFilter filter = buildRepositoryFilter( repository, project );
224
225 assembleRepositoryArtifacts( result, filter, project, localRepository, targetRepository, repositoryDirectory,
226 groupVersionAlignments );
227
228 ArtifactRepository centralRepository = findCentralRepository( project );
229
230 if ( repository.isIncludeMetadata() )
231 {
232 assembleRepositoryMetadata( result, filter, centralRepository, targetRepository );
233 }
234
235 addPomWithAncestry( project.getArtifact(), project.getRemoteArtifactRepositories(), localRepository,
236 targetRepository, groupVersionAlignments, project );
237 }
238
239 private ArtifactFilter buildRepositoryFilter( RepositoryInfo repository, MavenProject project )
240 {
241 AndArtifactFilter filter = new AndArtifactFilter();
242
243 ArtifactFilter scopeFilter = new ScopeArtifactFilter( repository.getScope() );
244 filter.add( scopeFilter );
245
246
247
248
249
250
251
252
253
254
255 List includes = repository.getIncludes();
256
257 if ( ( includes == null ) || includes.isEmpty() )
258 {
259 List<String> patterns = new ArrayList<String>();
260
261 Set projectArtifacts = project.getDependencyArtifacts();
262
263 if ( projectArtifacts != null )
264 {
265 for ( Object projectArtifact : projectArtifacts )
266 {
267 Artifact artifact = (Artifact) projectArtifact;
268
269 patterns.add( artifact.getDependencyConflictId() );
270 }
271 }
272
273 PatternIncludesArtifactFilter includeFilter = new PatternIncludesArtifactFilter( patterns, true );
274
275 filter.add( includeFilter );
276 }
277 else
278 {
279 filter.add( new PatternIncludesArtifactFilter( repository.getIncludes(), true ) );
280 }
281
282
283
284
285
286
287
288
289
290 List excludes = repository.getExcludes();
291
292 if ( ( excludes != null ) && !excludes.isEmpty() )
293 {
294 filter.add( new PatternExcludesArtifactFilter( repository.getExcludes(), true ) );
295 }
296
297 return filter;
298 }
299
300 private void assembleRepositoryArtifacts( ArtifactResolutionResult result, ArtifactFilter filter,
301 MavenProject project, ArtifactRepository localRepository,
302 ArtifactRepository targetRepository, File repositoryDirectory,
303 Map groupVersionAlignments )
304 throws RepositoryAssemblyException
305 {
306 try
307 {
308
309
310
311 FileUtils.deleteDirectory( repositoryDirectory );
312
313 FileUtils.mkdir( repositoryDirectory.getAbsolutePath() );
314
315 for ( Object o : result.getArtifacts() )
316 {
317 Artifact a = (Artifact) o;
318
319 if ( filter.include( a ) )
320 {
321 getLogger().debug( "Re-resolving: " + a + " for repository assembly." );
322
323 setAlignment( a, groupVersionAlignments );
324
325
326
327 a.setResolved( false );
328
329 artifactResolver.resolve( a, project.getRemoteArtifactRepositories(), localRepository );
330
331 a.setVersion( a.getBaseVersion() );
332
333 File targetFile = new File( targetRepository.getBasedir(), targetRepository.pathOf( a ) );
334 FileUtils.copyFile( a.getFile(), targetFile );
335
336 writeChecksums( targetFile );
337
338 addPomWithAncestry( a, project.getRemoteArtifactRepositories(), localRepository, targetRepository,
339 groupVersionAlignments, project );
340 }
341 }
342 }
343 catch ( ArtifactResolutionException e )
344 {
345 throw new RepositoryAssemblyException( "Error resolving artifacts: " + e.getMessage(), e );
346 }
347 catch ( ArtifactNotFoundException e )
348 {
349 throw new RepositoryAssemblyException( "Error resolving artifacts: " + e.getMessage(), e );
350 }
351 catch ( IOException e )
352 {
353 throw new RepositoryAssemblyException( "Error writing artifact metdata.", e );
354 }
355 }
356
357 private void addPomWithAncestry( final Artifact artifact, List remoteArtifactRepositories,
358 ArtifactRepository localRepository, ArtifactRepository targetRepository,
359 Map groupVersionAlignments, MavenProject masterProject )
360 throws RepositoryAssemblyException
361 {
362 String type = artifact.getType();
363 Map refs = masterProject.getProjectReferences();
364
365 String projectKey = ArtifactUtils.versionlessKey( artifact );
366
367 MavenProject p;
368 if ( artifact == masterProject.getArtifact() )
369 {
370 p = masterProject;
371 }
372 else if ( refs.containsKey( projectKey ) )
373 {
374 p = (MavenProject) refs.get( projectKey );
375 }
376 else
377 {
378 try
379 {
380 artifact.isSnapshot();
381
382 Artifact pomArtifact =
383 artifactFactory.createProjectArtifact( artifact.getGroupId(), artifact.getArtifactId(),
384 artifact.getBaseVersion() );
385
386 getLogger().debug( "Building MavenProject instance for: " + pomArtifact
387 + ". 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 =
406 artifactFactory.createProjectArtifact( p.getGroupId(), p.getArtifactId(), p.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 =
417 artifactFactory.createProjectArtifact( p.getGroupId(), p.getArtifactId(), p.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 ( Object o : project.getRemoteArtifactRepositories() )
455 {
456 ArtifactRepository r = (ArtifactRepository) o;
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 ( Object o : result.getArtifacts() )
471 {
472 Artifact a = (Artifact) o;
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(),
513 targetRepository.pathOfRemoteRepositoryMetadata( metadata ) );
514
515 FileUtils.copyFile( metadataFile, metadataFileRemote );
516
517 FileUtils.copyFile( new File( metadataFile.getParentFile(), metadataFile.getName() + ".sha1" ),
518 new File( metadataFileRemote.getParentFile(),
519 metadataFileRemote.getName() + ".sha1" ) );
520
521 FileUtils.copyFile( new File( metadataFile.getParentFile(), metadataFile.getName() + ".md5" ),
522 new File( metadataFileRemote.getParentFile(),
523 metadataFileRemote.getName() + ".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 FileInputStream data = new FileInputStream( file );
537 String md5 = md5Hex( data ).toUpperCase();
538 data.close();
539 FileInputStream data1 = new FileInputStream( file );
540 String sha1 = shaHex( data1 ).toUpperCase();
541 data1.close();
542
543 FileUtils.fileWrite( new File( file.getParentFile(), file.getName() + ".md5" ).getAbsolutePath(),
544 md5.toLowerCase() );
545 FileUtils.fileWrite( new File( file.getParentFile(), file.getName() + ".sha1" ).getAbsolutePath(),
546 sha1.toLowerCase() );
547 }
548
549 protected Map createGroupVersionAlignments( List versionAlignments )
550 {
551 Map groupVersionAlignments = new HashMap();
552
553 if ( versionAlignments != null )
554 {
555 for ( Object versionAlignment : versionAlignments )
556 {
557 GroupVersionAlignment alignment = (GroupVersionAlignment) versionAlignment;
558
559 groupVersionAlignments.put( alignment.getId(), alignment );
560 }
561 }
562
563 return groupVersionAlignments;
564 }
565
566 protected static DateFormat getUtcDateFormatter()
567 {
568 DateFormat utcDateFormatter = new SimpleDateFormat( UTC_TIMESTAMP_PATTERN );
569 utcDateFormatter.setTimeZone( UTC_TIME_ZONE );
570 return utcDateFormatter;
571 }
572
573 protected ArtifactRepository createLocalRepository( File directory )
574 {
575 String localRepositoryUrl = directory.getAbsolutePath();
576
577 if ( !localRepositoryUrl.startsWith( "file:" ) )
578 {
579 localRepositoryUrl = "file://" + localRepositoryUrl;
580 }
581
582 return createRepository( "local", localRepositoryUrl, false, true,
583 ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN );
584 }
585
586 public ArtifactRepository createRepository( String repositoryId, String repositoryUrl, boolean offline,
587 boolean updateSnapshots, String globalChecksumPolicy )
588 {
589 ArtifactRepository localRepository =
590 new DefaultArtifactRepository( repositoryId, repositoryUrl, repositoryLayout );
591
592 boolean snapshotPolicySet = false;
593
594 if ( offline )
595 {
596 snapshotPolicySet = true;
597 }
598
599 if ( !snapshotPolicySet && updateSnapshots )
600 {
601 artifactRepositoryFactory.setGlobalUpdatePolicy( ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS );
602 }
603
604 artifactRepositoryFactory.setGlobalChecksumPolicy( globalChecksumPolicy );
605
606 return localRepository;
607 }
608
609 private void invalidateProccessedProjectCache()
610 throws Exception
611 {
612 Class klass = DefaultMavenProjectBuilder.class;
613
614 try
615 {
616 Field field = klass.getDeclaredField( "processedProjectCache" );
617
618 field.setAccessible( true );
619
620 Object cache = field.get( projectBuilder );
621
622 cache.getClass().getDeclaredMethod( "clear", null ).invoke( cache, null );
623
624 field.setAccessible( false );
625 }
626 catch ( NoSuchFieldException e )
627 {
628
629 }
630 }
631
632 private void setAlignment( Artifact artifact, Map groupVersionAlignments )
633 {
634 GroupVersionAlignment alignment = (GroupVersionAlignment) groupVersionAlignments.get( artifact.getGroupId() );
635
636 if ( alignment != null )
637 {
638 if ( !alignment.getExcludes().contains( artifact.getArtifactId() ) )
639 {
640 artifact.setVersion( alignment.getVersion() );
641 }
642 }
643 }
644
645
646
647 private Map getManagedVersionMap( MavenProject project )
648 throws InvalidVersionSpecificationException
649 {
650 DependencyManagement dependencyManagement = project.getModel().getDependencyManagement();
651
652 Map<String, Artifact> map;
653 List deps = ( dependencyManagement == null ) ? null : dependencyManagement.getDependencies();
654 if ( ( deps != null ) && ( deps.size() > 0 ) )
655 {
656 map = new HashMap<String, Artifact>();
657
658 if ( getLogger().isDebugEnabled() )
659 {
660 getLogger().debug( "Adding managed dependencies for " + project.getId() );
661 }
662
663 for ( Object o1 : dependencyManagement.getDependencies() )
664 {
665 Dependency d = (Dependency) o1;
666
667 VersionRange versionRange = VersionRange.createFromVersionSpec( d.getVersion() );
668 Artifact artifact =
669 artifactFactory.createDependencyArtifact( d.getGroupId(), d.getArtifactId(), versionRange,
670 d.getType(), d.getClassifier(), d.getScope(),
671 d.isOptional() );
672 if ( getLogger().isDebugEnabled() )
673 {
674 getLogger().debug( " " + artifact );
675 }
676
677
678
679
680 if ( ( null != d.getExclusions() ) && !d.getExclusions().isEmpty() )
681 {
682 List<String> exclusions = new ArrayList<String>();
683 for ( Object o : d.getExclusions() )
684 {
685 Exclusion e = (Exclusion) o;
686 exclusions.add( e.getGroupId() + ":" + e.getArtifactId() );
687 }
688 ExcludesArtifactFilter eaf = new ExcludesArtifactFilter( exclusions );
689 artifact.setDependencyFilter( eaf );
690 }
691 else
692 {
693 artifact.setDependencyFilter( null );
694 }
695 map.put( d.getManagementKey(), artifact );
696 }
697 }
698 else
699 {
700 map = Collections.emptyMap();
701 }
702 return map;
703 }
704
705 public void contextualize( Context context )
706 throws ContextException
707 {
708 PlexusContainer container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
709
710 for ( String hint : PREFERRED_RESOLVER_HINTS )
711 {
712 if ( container.hasComponent( ArtifactResolver.ROLE, hint ) )
713 {
714 try
715 {
716 artifactResolver = (ArtifactResolver) container.lookup( ArtifactResolver.ROLE, hint );
717 break;
718 }
719 catch ( ComponentLookupException e )
720 {
721 getLogger().warn( "Cannot find ArtifactResolver with hint: " + hint, e );
722 }
723 }
724 else
725 {
726 getLogger().debug( "No ArtifactResolver with hint " + hint );
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(
745 "Failed to lookup a valid ArtifactResolver implementation. Tried hints:\n" + Arrays.asList(
746 PREFERRED_RESOLVER_HINTS ) );
747 }
748 }
749 }