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             InputStream is = new BufferedInputStream( new FileInputStream( new File( classDirectory, classFile ) ) );
196             try
197             {
198 
199                 if ( classFile.endsWith( ".class" ) )
200                 {
201                     MojoClassVisitor mojoClassVisitor = new MojoClassVisitor( getLogger() );
202                     ClassReader rdr = new ClassReader( is );
203                     rdr.accept( mojoClassVisitor,
204                                 ClassReader.SKIP_FRAMES | ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG );
205                     analyzeVisitors( mojoClassVisitor );
206                     if ( excludeMojo )
207                     {
208                         mojoClassVisitor.getMojoAnnotatedClass().setMojo( null );
209                     }
210                     if ( isStoreClass( mojoClassVisitor.getMojoAnnotatedClass() ) != null )
211                     {
212                         getLogger().debug(
213                             "found MojoAnnotatedClass:" + mojoClassVisitor.getMojoAnnotatedClass().getClassName() + ":"
214                                 + mojoClassVisitor.getMojoAnnotatedClass() );
215                         mojoClassVisitor.getMojoAnnotatedClass().setArtifact( artifact );
216                         mojoAnnotatedClasses.put( mojoClassVisitor.getMojoAnnotatedClass().getClassName(),
217                                                   mojoClassVisitor.getMojoAnnotatedClass() );
218                     }
219 
220                 }
221             }
222             finally
223             {
224                 IOUtil.close( is );
225             }
226 
227         }
228         return mojoAnnotatedClasses;
229     }
230 
231     private MojoAnnotatedClass isStoreClass( MojoAnnotatedClass mojoAnnotatedClass )
232     {
233         // see MPLUGIN-206 we can have intermediate classes without annotations
234         if ( mojoAnnotatedClass == null )
235         {
236             return null;
237         }
238         return mojoAnnotatedClass;
239         /**
240          if ( !mojoAnnotatedClass.getComponents().isEmpty() || !mojoAnnotatedClass.getParameters().isEmpty()
241          || mojoAnnotatedClass.getExecute() != null || mojoAnnotatedClass.getMojo() != null )
242          {
243          return mojoAnnotatedClass;
244          }
245          return null;
246          **/
247     }
248 
249 
250     protected void analyzeVisitors( MojoClassVisitor mojoClassVisitor )
251         throws ExtractionException
252     {
253 
254         try
255         {
256             MojoAnnotationVisitor mojoAnnotationVisitor =
257                 mojoClassVisitor.getAnnotationVisitorMap().get( Mojo.class.getName() );
258             if ( mojoAnnotationVisitor != null )
259             {
260                 MojoAnnotationContent mojoAnnotationContent = new MojoAnnotationContent();
261                 for ( Map.Entry<String, Object> entry : mojoAnnotationVisitor.getAnnotationValues().entrySet() )
262                 {
263                     reflector.invoke( mojoAnnotationContent, entry.getKey(), new Object[]{ entry.getValue() } );
264                 }
265                 mojoClassVisitor.getMojoAnnotatedClass().setMojo( mojoAnnotationContent );
266             }
267 
268             mojoAnnotationVisitor = mojoClassVisitor.getAnnotationVisitorMap().get( Execute.class.getName() );
269             if ( mojoAnnotationVisitor != null )
270             {
271                 ExecuteAnnotationContent executeAnnotationContent = new ExecuteAnnotationContent();
272 
273                 for ( Map.Entry<String, Object> entry : mojoAnnotationVisitor.getAnnotationValues().entrySet() )
274                 {
275                     reflector.invoke( executeAnnotationContent, entry.getKey(), new Object[]{ entry.getValue() } );
276                 }
277                 mojoClassVisitor.getMojoAnnotatedClass().setExecute( executeAnnotationContent );
278             }
279 
280             List<MojoFieldVisitor> mojoFieldVisitors =
281                 mojoClassVisitor.findFieldWithAnnotationClass( Parameter.class.getName() );
282 
283             for ( MojoFieldVisitor mojoFieldVisitor : mojoFieldVisitors )
284             {
285                 ParameterAnnotationContent parameterAnnotationContent =
286                     new ParameterAnnotationContent( mojoFieldVisitor.getFieldName(), mojoFieldVisitor.getClassName() );
287                 if ( mojoFieldVisitor.getMojoAnnotationVisitor() != null )
288                 {
289                     for ( Map.Entry<String, Object> entry : mojoFieldVisitor.getMojoAnnotationVisitor().getAnnotationValues().entrySet() )
290                     {
291                         reflector.invoke( parameterAnnotationContent, entry.getKey(),
292                                           new Object[]{ entry.getValue() } );
293                     }
294 
295                 }
296                 mojoClassVisitor.getMojoAnnotatedClass().getParameters().put( parameterAnnotationContent.getFieldName(),
297                                                                               parameterAnnotationContent );
298             }
299 
300             mojoFieldVisitors = mojoClassVisitor.findFieldWithAnnotationClass( Component.class.getName() );
301 
302             for ( MojoFieldVisitor mojoFieldVisitor : mojoFieldVisitors )
303             {
304                 ComponentAnnotationContent componentAnnotationContent =
305                     new ComponentAnnotationContent( mojoFieldVisitor.getFieldName() );
306 
307                 if ( mojoFieldVisitor.getMojoAnnotationVisitor() != null )
308                 {
309                     for ( Map.Entry<String, Object> entry : mojoFieldVisitor.getMojoAnnotationVisitor().getAnnotationValues().entrySet() )
310                     {
311                         String methodName = entry.getKey();
312                         if ( StringUtils.equals( "role", methodName ) )
313                         {
314                             Type type = (Type) entry.getValue();
315                             componentAnnotationContent.setRoleClassName( type.getClassName() );
316                         }
317                         else
318                         {
319                             reflector.invoke( componentAnnotationContent, entry.getKey(),
320                                               new Object[]{ entry.getValue() } );
321                         }
322                     }
323                     if ( StringUtils.isEmpty( componentAnnotationContent.getRoleClassName() ) )
324                     {
325                         componentAnnotationContent.setRoleClassName( mojoFieldVisitor.getClassName() );
326                     }
327                 }
328                 mojoClassVisitor.getMojoAnnotatedClass().getComponents().put( componentAnnotationContent.getFieldName(),
329                                                                               componentAnnotationContent );
330             }
331 
332         }
333         catch ( Exception e )
334         {
335             throw new ExtractionException( e.getMessage(), e );
336         }
337     }
338 }