View Javadoc

1   package org.apache.maven;
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 org.apache.maven.artifact.ArtifactUtils;
23  import org.apache.maven.project.MavenProject;
24  import org.sonatype.aether.artifact.Artifact;
25  import org.sonatype.aether.repository.WorkspaceReader;
26  import org.sonatype.aether.repository.WorkspaceRepository;
27  
28  import java.io.File;
29  import java.util.ArrayList;
30  import java.util.Collection;
31  import java.util.Collections;
32  import java.util.HashMap;
33  import java.util.HashSet;
34  import java.util.List;
35  import java.util.Map;
36  
37  /**
38   * An implementation of a workspace reader that knows how to search the Maven reactor for artifacts.
39   * 
40   * @author Jason van Zyl
41   */
42  class ReactorReader
43      implements WorkspaceReader
44  {
45  
46      private Map<String, MavenProject> projectsByGAV;
47  
48      private Map<String, List<MavenProject>> projectsByGA;
49  
50      private WorkspaceRepository repository;
51  
52      @SuppressWarnings( { "ConstantConditions" } )
53      public ReactorReader( Map<String, MavenProject> reactorProjects )
54      {
55          projectsByGAV = reactorProjects;
56  
57          projectsByGA = new HashMap<String, List<MavenProject>>( reactorProjects.size() * 2 );
58          for ( MavenProject project : reactorProjects.values() )
59          {
60              String key = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
61  
62              List<MavenProject> projects = projectsByGA.get( key );
63  
64              if ( projects == null )
65              {
66                  projects = new ArrayList<MavenProject>( 1 );
67                  projectsByGA.put( key, projects );
68              }
69  
70              projects.add( project );
71          }
72  
73          repository = new WorkspaceRepository( "reactor", new HashSet<String>( projectsByGAV.keySet() ) );
74      }
75  
76      private File find( MavenProject project, Artifact artifact )
77      {
78          if ( "pom".equals( artifact.getExtension() ) )
79          {
80              return project.getFile();
81          }
82  
83          org.apache.maven.artifact.Artifact projectArtifact = findMatchingArtifact( project, artifact );
84  
85          if ( hasArtifactFileFromPackagePhase( projectArtifact ) )
86          {
87              return projectArtifact.getFile();
88          }
89          else if ( !hasBeenPackaged( project ) )
90          {
91              // fallback to loose class files only if artifacts haven't been packaged yet
92  
93              if ( isTestArtifact( artifact ) )
94              {
95                  if ( project.hasLifecyclePhase( "test-compile" ) )
96                  {
97                      return new File( project.getBuild().getTestOutputDirectory() );
98                  }
99              }
100             else
101             {
102                 if ( project.hasLifecyclePhase( "compile" ) )
103                 {
104                     return new File( project.getBuild().getOutputDirectory() );
105                 }
106             }
107         }
108 
109         // The fall-through indicates that the artifact cannot be found;
110         // for instance if package produced nothing or classifier problems.
111         return null;
112     }
113 
114     private boolean hasArtifactFileFromPackagePhase( org.apache.maven.artifact.Artifact projectArtifact )
115     {
116         return projectArtifact != null && projectArtifact.getFile() != null && projectArtifact.getFile().exists();
117     }
118 
119     private boolean hasBeenPackaged( MavenProject project )
120     {
121         return project.hasLifecyclePhase( "package" ) || project.hasLifecyclePhase( "install" )
122             || project.hasLifecyclePhase( "deploy" );
123     }
124 
125     /**
126      * Tries to resolve the specified artifact from the artifacts of the given project.
127      * 
128      * @param project The project to try to resolve the artifact from, must not be <code>null</code>.
129      * @param requestedArtifact The artifact to resolve, must not be <code>null</code>.
130      * @return The matching artifact from the project or <code>null</code> if not found.
131      */
132     private org.apache.maven.artifact.Artifact findMatchingArtifact( MavenProject project, Artifact requestedArtifact )
133     {
134         String requestedRepositoryConflictId = getConflictId( requestedArtifact );
135 
136         org.apache.maven.artifact.Artifact mainArtifact = project.getArtifact();
137         if ( requestedRepositoryConflictId.equals( getConflictId( mainArtifact ) ) )
138         {
139             return mainArtifact;
140         }
141 
142         Collection<org.apache.maven.artifact.Artifact> attachedArtifacts = project.getAttachedArtifacts();
143         if ( attachedArtifacts != null && !attachedArtifacts.isEmpty() )
144         {
145             for ( org.apache.maven.artifact.Artifact attachedArtifact : attachedArtifacts )
146             {
147                 if ( requestedRepositoryConflictId.equals( getConflictId( attachedArtifact ) ) )
148                 {
149                     return attachedArtifact;
150                 }
151             }
152         }
153 
154         return null;
155     }
156 
157     /**
158      * Gets the repository conflict id of the specified artifact. Unlike the dependency conflict id, the repository
159      * conflict id uses the artifact file extension instead of the artifact type. Hence, the repository conflict id more
160      * closely reflects the identity of artifacts as perceived by a repository.
161      * 
162      * @param artifact The artifact, must not be <code>null</code>.
163      * @return The repository conflict id, never <code>null</code>.
164      */
165     private String getConflictId( org.apache.maven.artifact.Artifact artifact )
166     {
167         StringBuilder buffer = new StringBuilder( 128 );
168         buffer.append( artifact.getGroupId() );
169         buffer.append( ':' ).append( artifact.getArtifactId() );
170         if ( artifact.getArtifactHandler() != null )
171         {
172             buffer.append( ':' ).append( artifact.getArtifactHandler().getExtension() );
173         }
174         else
175         {
176             buffer.append( ':' ).append( artifact.getType() );
177         }
178         if ( artifact.hasClassifier() )
179         {
180             buffer.append( ':' ).append( artifact.getClassifier() );
181         }
182         return buffer.toString();
183     }
184 
185     private String getConflictId( Artifact artifact )
186     {
187         StringBuilder buffer = new StringBuilder( 128 );
188         buffer.append( artifact.getGroupId() );
189         buffer.append( ':' ).append( artifact.getArtifactId() );
190         buffer.append( ':' ).append( artifact.getExtension() );
191         if ( artifact.getClassifier().length() > 0 )
192         {
193             buffer.append( ':' ).append( artifact.getClassifier() );
194         }
195         return buffer.toString();
196     }
197 
198     /**
199      * Determines whether the specified artifact refers to test classes.
200      * 
201      * @param artifact The artifact to check, must not be {@code null}.
202      * @return {@code true} if the artifact refers to test classes, {@code false} otherwise.
203      */
204     private static boolean isTestArtifact( Artifact artifact )
205     {
206         if ( "test-jar".equals( artifact.getProperty( "type", "" ) ) )
207         {
208             return true;
209         }
210         else if ( "jar".equals( artifact.getExtension() ) && "tests".equals( artifact.getClassifier() ) )
211         {
212             return true;
213         }
214         return false;
215     }
216 
217     public File findArtifact( Artifact artifact )
218     {
219         String projectKey = artifact.getGroupId() + ':' + artifact.getArtifactId() + ':' + artifact.getVersion();
220 
221         MavenProject project = projectsByGAV.get( projectKey );
222 
223         if ( project != null )
224         {
225             return find( project, artifact );
226         }
227 
228         return null;
229     }
230 
231     public List<String> findVersions( Artifact artifact )
232     {
233         String key = artifact.getGroupId() + ':' + artifact.getArtifactId();
234 
235         List<MavenProject> projects = projectsByGA.get( key );
236         if ( projects == null || projects.isEmpty() )
237         {
238             return Collections.emptyList();
239         }
240 
241         List<String> versions = new ArrayList<String>();
242 
243         for ( MavenProject project : projects )
244         {
245             if ( find( project, artifact ) != null )
246             {
247                 versions.add( project.getVersion() );
248             }
249         }
250 
251         return Collections.unmodifiableList( versions );
252     }
253 
254     public WorkspaceRepository getRepository()
255     {
256         return repository;
257     }
258 
259 }