1 package org.apache.maven.tools.plugin.extractor.annotations.scanner;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import org.apache.maven.artifact.Artifact;
26 import org.apache.maven.plugins.annotations.Component;
27 import org.apache.maven.plugins.annotations.Execute;
28 import org.apache.maven.plugins.annotations.Mojo;
29 import org.apache.maven.plugins.annotations.Parameter;
30 import org.apache.maven.tools.plugin.extractor.ExtractionException;
31 import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ComponentAnnotationContent;
32 import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ExecuteAnnotationContent;
33 import org.apache.maven.tools.plugin.extractor.annotations.datamodel.MojoAnnotationContent;
34 import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ParameterAnnotationContent;
35 import org.apache.maven.tools.plugin.extractor.annotations.scanner.visitors.MojoAnnotationVisitor;
36 import org.apache.maven.tools.plugin.extractor.annotations.scanner.visitors.MojoClassVisitor;
37 import org.apache.maven.tools.plugin.extractor.annotations.scanner.visitors.MojoFieldVisitor;
38 import org.apache.maven.tools.plugin.extractor.annotations.scanner.visitors.MojoParameterVisitor;
39 import org.codehaus.plexus.logging.AbstractLogEnabled;
40 import org.codehaus.plexus.util.DirectoryScanner;
41 import org.codehaus.plexus.util.StringUtils;
42 import org.codehaus.plexus.util.reflection.Reflector;
43 import org.codehaus.plexus.util.reflection.ReflectorException;
44 import org.objectweb.asm.ClassReader;
45 import org.objectweb.asm.Type;
46
47 import java.io.BufferedInputStream;
48 import java.io.File;
49 import java.io.FileInputStream;
50 import java.io.IOException;
51 import java.io.InputStream;
52 import java.util.HashMap;
53 import java.util.List;
54 import java.util.Map;
55 import java.util.regex.Pattern;
56 import java.util.zip.ZipEntry;
57 import java.util.zip.ZipInputStream;
58
59
60
61
62
63
64
65 @Named
66 @Singleton
67 public class DefaultMojoAnnotationsScanner
68 extends AbstractLogEnabled
69 implements MojoAnnotationsScanner
70 {
71
72 private static final Pattern SCANNABLE_CLASS = Pattern.compile( "[^-]+\\.class" );
73 private static final String EMPTY = "";
74
75 private Reflector reflector = new Reflector();
76
77 @Override
78 public Map<String, MojoAnnotatedClass> scan( MojoAnnotationsScannerRequest request )
79 throws ExtractionException
80 {
81 Map<String, MojoAnnotatedClass> mojoAnnotatedClasses = new HashMap<>();
82
83 try
84 {
85 for ( Artifact dependency : request.getDependencies() )
86 {
87 scan( mojoAnnotatedClasses, dependency.getFile(), request.getIncludePatterns(), dependency, true );
88 }
89
90 for ( File classDirectory : request.getClassesDirectories() )
91 {
92 scan( mojoAnnotatedClasses, classDirectory, request.getIncludePatterns(),
93 request.getProject().getArtifact(), false );
94 }
95 }
96 catch ( IOException e )
97 {
98 throw new ExtractionException( e.getMessage(), e );
99 }
100
101 return mojoAnnotatedClasses;
102 }
103
104 protected void scan( Map<String, MojoAnnotatedClass> mojoAnnotatedClasses, File source,
105 List<String> includePatterns, Artifact artifact, boolean excludeMojo )
106 throws IOException, ExtractionException
107 {
108 if ( source == null || ! source.exists() )
109 {
110 return;
111 }
112
113 Map<String, MojoAnnotatedClass> scanResult;
114 if ( source.isDirectory() )
115 {
116 scanResult = scanDirectory( source, includePatterns, artifact, excludeMojo );
117 }
118 else
119 {
120 scanResult = scanArchive( source, artifact, excludeMojo );
121 }
122
123 mojoAnnotatedClasses.putAll( scanResult );
124 }
125
126
127
128
129
130
131
132
133
134 protected Map<String, MojoAnnotatedClass> scanArchive( File archiveFile, Artifact artifact, boolean excludeMojo )
135 throws IOException, ExtractionException
136 {
137 Map<String, MojoAnnotatedClass> mojoAnnotatedClasses = new HashMap<>();
138
139 String zipEntryName = null;
140 try ( ZipInputStream archiveStream = new ZipInputStream( new FileInputStream( archiveFile ) ) )
141 {
142 String archiveFilename = archiveFile.getAbsolutePath();
143 for ( ZipEntry zipEntry = archiveStream.getNextEntry(); zipEntry != null;
144 zipEntry = archiveStream.getNextEntry() )
145 {
146 zipEntryName = zipEntry.getName();
147 if ( !SCANNABLE_CLASS.matcher( zipEntryName ).matches() )
148 {
149 continue;
150 }
151 analyzeClassStream( mojoAnnotatedClasses, archiveStream, artifact, excludeMojo, archiveFilename,
152 zipEntry.getName() );
153 }
154 }
155 catch ( IllegalArgumentException e )
156 {
157
158 getLogger().error( "Failed to analyze " + archiveFile.getAbsolutePath() + "!/" + zipEntryName );
159
160 throw e;
161 }
162
163 return mojoAnnotatedClasses;
164 }
165
166
167
168
169
170
171
172
173
174
175 protected Map<String, MojoAnnotatedClass> scanDirectory( File classDirectory, List<String> includePatterns,
176 Artifact artifact, boolean excludeMojo )
177 throws IOException, ExtractionException
178 {
179 Map<String, MojoAnnotatedClass> mojoAnnotatedClasses = new HashMap<>();
180
181 DirectoryScanner scanner = new DirectoryScanner();
182 scanner.setBasedir( classDirectory );
183 scanner.addDefaultExcludes();
184 if ( includePatterns != null )
185 {
186 scanner.setIncludes( includePatterns.toArray( new String[includePatterns.size()] ) );
187 }
188 scanner.scan();
189 String[] classFiles = scanner.getIncludedFiles();
190 String classDirname = classDirectory.getAbsolutePath();
191
192 for ( String classFile : classFiles )
193 {
194 if ( !SCANNABLE_CLASS.matcher( classFile ).matches() )
195 {
196 continue;
197 }
198
199 try ( InputStream is =
200 new BufferedInputStream( new FileInputStream( new File( classDirectory, classFile ) ) ) )
201 {
202 analyzeClassStream( mojoAnnotatedClasses, is, artifact, excludeMojo, classDirname, classFile );
203 }
204 }
205 return mojoAnnotatedClasses;
206 }
207
208 private void analyzeClassStream( Map<String, MojoAnnotatedClass> mojoAnnotatedClasses, InputStream is,
209 Artifact artifact, boolean excludeMojo, String source, String file )
210 throws IOException, ExtractionException
211 {
212 MojoClassVisitor mojoClassVisitor = new MojoClassVisitor( );
213
214 try
215 {
216 ClassReader rdr = new ClassReader( is );
217 rdr.accept( mojoClassVisitor, ClassReader.SKIP_FRAMES | ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG );
218 }
219 catch ( ArrayIndexOutOfBoundsException aiooe )
220 {
221 getLogger().warn( "Error analyzing class " + file + " in " + source + ": ignoring class",
222 getLogger().isDebugEnabled() ? aiooe : null );
223 return;
224 }
225 catch ( IllegalArgumentException iae )
226 {
227 if ( iae.getMessage() == null )
228 {
229 getLogger().warn( "Error analyzing class " + file + " in " + source + ": ignoring class",
230 getLogger().isDebugEnabled() ? iae : null );
231 return;
232 }
233 else
234 {
235 throw iae;
236 }
237 }
238
239 analyzeVisitors( mojoClassVisitor );
240
241 MojoAnnotatedClass mojoAnnotatedClass = mojoClassVisitor.getMojoAnnotatedClass();
242
243 if ( excludeMojo )
244 {
245 mojoAnnotatedClass.setMojo( null );
246 }
247
248 if ( mojoAnnotatedClass != null )
249 {
250 if ( getLogger().isDebugEnabled() && mojoAnnotatedClass.hasAnnotations() )
251 {
252 getLogger().debug( "found MojoAnnotatedClass:" + mojoAnnotatedClass.getClassName() + ":"
253 + mojoAnnotatedClass );
254 }
255 mojoAnnotatedClass.setArtifact( artifact );
256 mojoAnnotatedClasses.put( mojoAnnotatedClass.getClassName(), mojoAnnotatedClass );
257 }
258 }
259
260 protected void populateAnnotationContent( Object content, MojoAnnotationVisitor mojoAnnotationVisitor )
261 throws ReflectorException
262 {
263 for ( Map.Entry<String, Object> entry : mojoAnnotationVisitor.getAnnotationValues().entrySet() )
264 {
265 reflector.invoke( content, entry.getKey(), new Object[] { entry.getValue() } );
266 }
267 }
268
269 protected void analyzeVisitors( MojoClassVisitor mojoClassVisitor )
270 throws ExtractionException
271 {
272 final MojoAnnotatedClass mojoAnnotatedClass = mojoClassVisitor.getMojoAnnotatedClass();
273
274 try
275 {
276
277 MojoAnnotationVisitor mojoAnnotationVisitor = mojoClassVisitor.getAnnotationVisitor( Mojo.class );
278 if ( mojoAnnotationVisitor != null )
279 {
280 MojoAnnotationContent mojoAnnotationContent = new MojoAnnotationContent();
281 populateAnnotationContent( mojoAnnotationContent, mojoAnnotationVisitor );
282
283 if ( mojoClassVisitor.getAnnotationVisitor( Deprecated.class ) != null )
284 {
285 mojoAnnotationContent.setDeprecated( EMPTY );
286 }
287
288 mojoAnnotatedClass.setMojo( mojoAnnotationContent );
289 }
290
291
292 mojoAnnotationVisitor = mojoClassVisitor.getAnnotationVisitor( Execute.class );
293 if ( mojoAnnotationVisitor != null )
294 {
295 ExecuteAnnotationContent executeAnnotationContent = new ExecuteAnnotationContent();
296 populateAnnotationContent( executeAnnotationContent, mojoAnnotationVisitor );
297 mojoAnnotatedClass.setExecute( executeAnnotationContent );
298 }
299
300
301 List<MojoParameterVisitor> mojoParameterVisitors = mojoClassVisitor.findParameterVisitors();
302 for ( MojoParameterVisitor parameterVisitor : mojoParameterVisitors )
303 {
304 ParameterAnnotationContent parameterAnnotationContent =
305 new ParameterAnnotationContent( parameterVisitor.getFieldName(), parameterVisitor.getClassName(),
306 parameterVisitor.getTypeParameters(),
307 parameterVisitor.isAnnotationOnMethod() );
308
309 Map<String, MojoAnnotationVisitor> annotationVisitorMap = parameterVisitor.getAnnotationVisitorMap();
310 MojoAnnotationVisitor fieldAnnotationVisitor = annotationVisitorMap.get( Parameter.class.getName() );
311
312 populateAnnotationContent( parameterAnnotationContent, fieldAnnotationVisitor );
313
314 if ( annotationVisitorMap.containsKey( Deprecated.class.getName() ) )
315 {
316 parameterAnnotationContent.setDeprecated( EMPTY );
317 }
318
319 mojoAnnotatedClass.getParameters().put( parameterAnnotationContent.getFieldName(),
320 parameterAnnotationContent );
321 }
322
323
324 List<MojoFieldVisitor> mojoFieldVisitors = mojoClassVisitor.findFieldWithAnnotation( Component.class );
325 for ( MojoFieldVisitor mojoFieldVisitor : mojoFieldVisitors )
326 {
327 ComponentAnnotationContent componentAnnotationContent =
328 new ComponentAnnotationContent( mojoFieldVisitor.getFieldName() );
329
330 Map<String, MojoAnnotationVisitor> annotationVisitorMap = mojoFieldVisitor.getAnnotationVisitorMap();
331 MojoAnnotationVisitor annotationVisitor = annotationVisitorMap.get( Component.class.getName() );
332
333 if ( annotationVisitor != null )
334 {
335 for ( Map.Entry<String, Object> entry : annotationVisitor.getAnnotationValues().entrySet() )
336 {
337 String methodName = entry.getKey();
338 if ( "role".equals( methodName ) )
339 {
340 Type type = (Type) entry.getValue();
341 componentAnnotationContent.setRoleClassName( type.getClassName() );
342 }
343 else
344 {
345 reflector.invoke( componentAnnotationContent, entry.getKey(),
346 new Object[]{ entry.getValue() } );
347 }
348 }
349
350 if ( StringUtils.isEmpty( componentAnnotationContent.getRoleClassName() ) )
351 {
352 componentAnnotationContent.setRoleClassName( mojoFieldVisitor.getClassName() );
353 }
354 }
355 mojoAnnotatedClass.getComponents().put( componentAnnotationContent.getFieldName(),
356 componentAnnotationContent );
357 }
358 }
359 catch ( ReflectorException e )
360 {
361 throw new ExtractionException( e.getMessage(), e );
362 }
363 }
364 }