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