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