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