View Javadoc

1   package org.apache.maven.tools.plugin.annotations.scanner;
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.Artifact;
23  import org.apache.maven.plugins.annotations.Component;
24  import org.apache.maven.plugins.annotations.Execute;
25  import org.apache.maven.plugins.annotations.Mojo;
26  import org.apache.maven.plugins.annotations.Parameter;
27  import org.apache.maven.tools.plugin.annotations.datamodel.ComponentAnnotationContent;
28  import org.apache.maven.tools.plugin.annotations.datamodel.ExecuteAnnotationContent;
29  import org.apache.maven.tools.plugin.annotations.datamodel.MojoAnnotationContent;
30  import org.apache.maven.tools.plugin.annotations.datamodel.ParameterAnnotationContent;
31  import org.apache.maven.tools.plugin.annotations.scanner.visitors.MojoAnnotationVisitor;
32  import org.apache.maven.tools.plugin.annotations.scanner.visitors.MojoClassVisitor;
33  import org.apache.maven.tools.plugin.annotations.scanner.visitors.MojoFieldVisitor;
34  import org.apache.maven.tools.plugin.extractor.ExtractionException;
35  import org.codehaus.plexus.logging.AbstractLogEnabled;
36  import org.codehaus.plexus.util.DirectoryScanner;
37  import org.codehaus.plexus.util.IOUtil;
38  import org.codehaus.plexus.util.StringUtils;
39  import org.codehaus.plexus.util.reflection.Reflector;
40  import org.objectweb.asm.ClassReader;
41  import org.objectweb.asm.Type;
42  
43  import java.io.BufferedInputStream;
44  import java.io.File;
45  import java.io.FileInputStream;
46  import java.io.IOException;
47  import java.io.InputStream;
48  import java.util.Collections;
49  import java.util.HashMap;
50  import java.util.List;
51  import java.util.Map;
52  import java.util.zip.ZipEntry;
53  import java.util.zip.ZipInputStream;
54  
55  /**
56   * @author Olivier Lamy
57   * @since 3.0
58   */
59  @org.codehaus.plexus.component.annotations.Component( role = MojoAnnotationsScanner.class )
60  public class DefaultMojoAnnotationsScanner
61      extends AbstractLogEnabled
62      implements MojoAnnotationsScanner
63  {
64      private Reflector reflector = new Reflector();
65  
66      public Map<String, MojoAnnotatedClass> scan( MojoAnnotationsScannerRequest request )
67          throws ExtractionException
68      {
69          Map<String, MojoAnnotatedClass> mojoAnnotatedClasses = new HashMap<String, MojoAnnotatedClass>();
70          try
71          {
72  
73              for ( Artifact dependency : request.getDependencies() )
74              {
75                  File dependencyFile = dependency.getFile();
76                  if ( dependencyFile != null && dependencyFile.exists() )
77                  {
78                      if ( dependencyFile.isDirectory() )
79                      {
80                          mojoAnnotatedClasses.putAll(
81                              scanDirectory( dependencyFile, request.getIncludePatterns(), dependency, true ) );
82                      }
83                      else
84                      {
85                          mojoAnnotatedClasses.putAll(
86                              scanFile( dependencyFile, request.getIncludePatterns(), dependency, true ) );
87                      }
88                  }
89              }
90  
91              for ( File classDirectory : request.getClassesDirectories() )
92              {
93                  if ( classDirectory.exists() && classDirectory.isDirectory() )
94                  {
95                      mojoAnnotatedClasses.putAll(
96                          scanDirectory( classDirectory, request.getIncludePatterns(), request.getProject().getArtifact(),
97                                         false ) );
98                  }
99              }
100 
101             return mojoAnnotatedClasses;
102         }
103         catch ( IOException e )
104         {
105             throw new ExtractionException( e.getMessage(), e );
106         }
107     }
108 
109     /**
110      * @param archiveFile
111      * @param includePatterns
112      * @param artifact
113      * @param excludeMojo     for dependencies we exclude Mojo annotations found
114      * @return
115      * @throws IOException
116      * @throws ExtractionException
117      */
118     protected Map<String, MojoAnnotatedClass> scanFile( File archiveFile, List<String> includePatterns,
119                                                         Artifact artifact, boolean excludeMojo )
120         throws IOException, ExtractionException
121     {
122         if ( !archiveFile.exists() )
123         {
124             return Collections.emptyMap();
125         }
126         Map<String, MojoAnnotatedClass> mojoAnnotatedClasses = new HashMap<String, MojoAnnotatedClass>();
127         ZipInputStream archiveStream = new ZipInputStream( new FileInputStream( archiveFile ) );
128 
129         try
130         {
131             for ( ZipEntry zipEntry = archiveStream.getNextEntry(); zipEntry != null;
132                   zipEntry = archiveStream.getNextEntry() )
133             {
134                 if ( zipEntry.getName().endsWith( ".class" ) )
135                 {
136                     MojoClassVisitor mojoClassVisitor = new MojoClassVisitor( getLogger() );
137 
138                     ClassReader rdr = new ClassReader( archiveStream );
139                     rdr.accept( mojoClassVisitor,
140                                 ClassReader.SKIP_FRAMES | ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG );
141                     analyzeVisitors( mojoClassVisitor );
142                     if ( excludeMojo )
143                     {
144                         mojoClassVisitor.getMojoAnnotatedClass().setMojo( null );
145                     }
146                     if ( isStoreClass( mojoClassVisitor.getMojoAnnotatedClass() ) != null )
147                     {
148                         getLogger().debug(
149                             "found MojoAnnotatedClass:" + mojoClassVisitor.getMojoAnnotatedClass().getClassName() + ":"
150                                 + mojoClassVisitor.getMojoAnnotatedClass() );
151                         mojoClassVisitor.getMojoAnnotatedClass().setArtifact( artifact );
152                         mojoAnnotatedClasses.put( mojoClassVisitor.getMojoAnnotatedClass().getClassName(),
153                                                   mojoClassVisitor.getMojoAnnotatedClass() );
154                     }
155                 }
156             }
157         }
158         finally
159         {
160             IOUtil.close( archiveStream );
161         }
162         return mojoAnnotatedClasses;
163     }
164 
165     /**
166      * @param classDirectory
167      * @param includePatterns
168      * @param artifact
169      * @param excludeMojo     for dependencies we exclude Mojo annotations found
170      * @return
171      * @throws IOException
172      * @throws ExtractionException
173      */
174     protected Map<String, MojoAnnotatedClass> scanDirectory( File classDirectory, List<String> includePatterns,
175                                                              Artifact artifact, boolean excludeMojo )
176         throws IOException, ExtractionException
177     {
178         if ( !classDirectory.exists() )
179         {
180             return Collections.emptyMap();
181         }
182         Map<String, MojoAnnotatedClass> mojoAnnotatedClasses = new HashMap<String, MojoAnnotatedClass>();
183         DirectoryScanner scanner = new DirectoryScanner();
184         scanner.setBasedir( classDirectory );
185         scanner.addDefaultExcludes();
186         if ( includePatterns != null )
187         {
188             scanner.setIncludes( includePatterns.toArray( new String[includePatterns.size()] ) );
189         }
190         scanner.scan();
191         String[] classFiles = scanner.getIncludedFiles();
192 
193         for ( String classFile : classFiles )
194         {
195             if ( !classFile.endsWith( ".class" ) )
196             {
197                 continue;
198             }
199 
200             InputStream is = new BufferedInputStream( new FileInputStream( new File( classDirectory, classFile ) ) );
201             try
202             {
203                 MojoClassVisitor mojoClassVisitor = new MojoClassVisitor( getLogger() );
204                 ClassReader rdr = new ClassReader( is );
205                 rdr.accept( mojoClassVisitor,
206                             ClassReader.SKIP_FRAMES | ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG );
207                 analyzeVisitors( mojoClassVisitor );
208 
209                 MojoAnnotatedClass mojoAnnotatedClass = mojoClassVisitor.getMojoAnnotatedClass();
210 
211                 if ( excludeMojo )
212                 {
213                     mojoAnnotatedClass.setMojo( null );
214                 }
215 
216                 if ( isStoreClass( mojoAnnotatedClass ) != null )
217                 {
218                     getLogger().debug( "found MojoAnnotatedClass:" + mojoAnnotatedClass.getClassName() + ":"
219                                            + mojoAnnotatedClass );
220                     mojoAnnotatedClass.setArtifact( artifact );
221                     mojoAnnotatedClasses.put( mojoAnnotatedClass.getClassName(), mojoAnnotatedClass );
222                 }
223             }
224             finally
225             {
226                 IOUtil.close( is );
227             }
228         }
229         return mojoAnnotatedClasses;
230     }
231 
232     private MojoAnnotatedClass isStoreClass( MojoAnnotatedClass mojoAnnotatedClass )
233     {
234         // see MPLUGIN-206 we can have intermediate classes without annotations
235         if ( mojoAnnotatedClass == null )
236         {
237             return null;
238         }
239         return mojoAnnotatedClass;
240         /**
241          if ( !mojoAnnotatedClass.getComponents().isEmpty() || !mojoAnnotatedClass.getParameters().isEmpty()
242          || mojoAnnotatedClass.getExecute() != null || mojoAnnotatedClass.getMojo() != null )
243          {
244          return mojoAnnotatedClass;
245          }
246          return null;
247          **/
248     }
249 
250 
251     protected void analyzeVisitors( MojoClassVisitor mojoClassVisitor )
252         throws ExtractionException
253     {
254 
255         try
256         {
257             MojoAnnotationVisitor mojoAnnotationVisitor =
258                 mojoClassVisitor.getAnnotationVisitorMap().get( Mojo.class.getName() );
259             if ( mojoAnnotationVisitor != null )
260             {
261                 MojoAnnotationContent mojoAnnotationContent = new MojoAnnotationContent();
262                 for ( Map.Entry<String, Object> entry : mojoAnnotationVisitor.getAnnotationValues().entrySet() )
263                 {
264                     reflector.invoke( mojoAnnotationContent, entry.getKey(), new Object[]{ entry.getValue() } );
265                 }
266                 mojoClassVisitor.getMojoAnnotatedClass().setMojo( mojoAnnotationContent );
267             }
268 
269             mojoAnnotationVisitor = mojoClassVisitor.getAnnotationVisitorMap().get( Execute.class.getName() );
270             if ( mojoAnnotationVisitor != null )
271             {
272                 ExecuteAnnotationContent executeAnnotationContent = new ExecuteAnnotationContent();
273 
274                 for ( Map.Entry<String, Object> entry : mojoAnnotationVisitor.getAnnotationValues().entrySet() )
275                 {
276                     reflector.invoke( executeAnnotationContent, entry.getKey(), new Object[]{ entry.getValue() } );
277                 }
278                 mojoClassVisitor.getMojoAnnotatedClass().setExecute( executeAnnotationContent );
279             }
280 
281             List<MojoFieldVisitor> mojoFieldVisitors =
282                 mojoClassVisitor.findFieldWithAnnotationClass( Parameter.class.getName() );
283 
284             for ( MojoFieldVisitor mojoFieldVisitor : mojoFieldVisitors )
285             {
286                 ParameterAnnotationContent parameterAnnotationContent =
287                     new ParameterAnnotationContent( mojoFieldVisitor.getFieldName(), mojoFieldVisitor.getClassName() );
288                 if ( mojoFieldVisitor.getMojoAnnotationVisitor() != null )
289                 {
290                     for ( Map.Entry<String, Object> entry : mojoFieldVisitor.getMojoAnnotationVisitor().getAnnotationValues().entrySet() )
291                     {
292                         reflector.invoke( parameterAnnotationContent, entry.getKey(),
293                                           new Object[]{ entry.getValue() } );
294                     }
295 
296                 }
297                 mojoClassVisitor.getMojoAnnotatedClass().getParameters().put( parameterAnnotationContent.getFieldName(),
298                                                                               parameterAnnotationContent );
299             }
300 
301             mojoFieldVisitors = mojoClassVisitor.findFieldWithAnnotationClass( Component.class.getName() );
302 
303             for ( MojoFieldVisitor mojoFieldVisitor : mojoFieldVisitors )
304             {
305                 ComponentAnnotationContent componentAnnotationContent =
306                     new ComponentAnnotationContent( mojoFieldVisitor.getFieldName() );
307 
308                 if ( mojoFieldVisitor.getMojoAnnotationVisitor() != null )
309                 {
310                     for ( Map.Entry<String, Object> entry : mojoFieldVisitor.getMojoAnnotationVisitor().getAnnotationValues().entrySet() )
311                     {
312                         String methodName = entry.getKey();
313                         if ( StringUtils.equals( "role", methodName ) )
314                         {
315                             Type type = (Type) entry.getValue();
316                             componentAnnotationContent.setRoleClassName( type.getClassName() );
317                         }
318                         else
319                         {
320                             reflector.invoke( componentAnnotationContent, entry.getKey(),
321                                               new Object[]{ entry.getValue() } );
322                         }
323                     }
324                     if ( StringUtils.isEmpty( componentAnnotationContent.getRoleClassName() ) )
325                     {
326                         componentAnnotationContent.setRoleClassName( mojoFieldVisitor.getClassName() );
327                     }
328                 }
329                 mojoClassVisitor.getMojoAnnotatedClass().getComponents().put( componentAnnotationContent.getFieldName(),
330                                                                               componentAnnotationContent );
331             }
332 
333         }
334         catch ( Exception e )
335         {
336             throw new ExtractionException( e.getMessage(), e );
337         }
338     }
339 }