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