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
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 );
146 }
147 }
148 catch ( IllegalArgumentException e )
149 {
150
151 getLogger().error( "Failed to analyze " + archiveFile.getAbsolutePath() + "!/" + zipEntryName );
152
153 throw e;
154 }
155 finally
156 {
157 IOUtil.close( archiveStream );
158 }
159
160 return mojoAnnotatedClasses;
161 }
162
163
164
165
166
167
168
169
170
171
172 protected Map<String, MojoAnnotatedClass> scanDirectory( File classDirectory, List<String> includePatterns,
173 Artifact artifact, boolean excludeMojo )
174 throws IOException, ExtractionException
175 {
176 Map<String, MojoAnnotatedClass> mojoAnnotatedClasses = new HashMap<String, MojoAnnotatedClass>();
177
178 DirectoryScanner scanner = new DirectoryScanner();
179 scanner.setBasedir( classDirectory );
180 scanner.addDefaultExcludes();
181 if ( includePatterns != null )
182 {
183 scanner.setIncludes( includePatterns.toArray( new String[includePatterns.size()] ) );
184 }
185 scanner.scan();
186 String[] classFiles = scanner.getIncludedFiles();
187
188 for ( String classFile : classFiles )
189 {
190 if ( !SCANNABLE_CLASS.matcher( classFile ).matches() )
191 {
192 continue;
193 }
194
195 InputStream is = new BufferedInputStream( new FileInputStream( new File( classDirectory, classFile ) ) );
196 try
197 {
198 analyzeClassStream( mojoAnnotatedClasses, is, artifact, excludeMojo );
199 }
200 finally
201 {
202 IOUtil.close( is );
203 }
204 }
205 return mojoAnnotatedClasses;
206 }
207
208 private void analyzeClassStream( Map<String, MojoAnnotatedClass> mojoAnnotatedClasses, InputStream is,
209 Artifact artifact, boolean excludeMojo )
210 throws IOException, ExtractionException
211 {
212 MojoClassVisitor mojoClassVisitor = new MojoClassVisitor( getLogger() );
213
214 ClassReader rdr = new ClassReader( is );
215 rdr.accept( mojoClassVisitor, ClassReader.SKIP_FRAMES | ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG );
216
217 analyzeVisitors( mojoClassVisitor );
218
219 MojoAnnotatedClass mojoAnnotatedClass = mojoClassVisitor.getMojoAnnotatedClass();
220
221 if ( excludeMojo )
222 {
223 mojoAnnotatedClass.setMojo( null );
224 }
225
226 if ( mojoAnnotatedClass != null )
227 {
228 if ( getLogger().isDebugEnabled() && mojoAnnotatedClass.hasAnnotations() )
229 {
230 getLogger().debug( "found MojoAnnotatedClass:" + mojoAnnotatedClass.getClassName() + ":"
231 + mojoAnnotatedClass );
232 }
233 mojoAnnotatedClass.setArtifact( artifact );
234 mojoAnnotatedClasses.put( mojoAnnotatedClass.getClassName(), mojoAnnotatedClass );
235 }
236 }
237
238 protected void populateAnnotationContent( Object content, MojoAnnotationVisitor mojoAnnotationVisitor )
239 throws ReflectorException
240 {
241 for ( Map.Entry<String, Object> entry : mojoAnnotationVisitor.getAnnotationValues().entrySet() )
242 {
243 reflector.invoke( content, entry.getKey(), new Object[] { entry.getValue() } );
244 }
245 }
246
247 protected void analyzeVisitors( MojoClassVisitor mojoClassVisitor )
248 throws ExtractionException
249 {
250 final MojoAnnotatedClass mojoAnnotatedClass = mojoClassVisitor.getMojoAnnotatedClass();
251
252 try
253 {
254
255 MojoAnnotationVisitor mojoAnnotationVisitor = mojoClassVisitor.getAnnotationVisitor( Mojo.class );
256 if ( mojoAnnotationVisitor != null )
257 {
258 MojoAnnotationContent mojoAnnotationContent = new MojoAnnotationContent();
259 populateAnnotationContent( mojoAnnotationContent, mojoAnnotationVisitor );
260 mojoAnnotatedClass.setMojo( mojoAnnotationContent );
261 }
262
263
264 mojoAnnotationVisitor = mojoClassVisitor.getAnnotationVisitor( Execute.class );
265 if ( mojoAnnotationVisitor != null )
266 {
267 ExecuteAnnotationContent executeAnnotationContent = new ExecuteAnnotationContent();
268 populateAnnotationContent( executeAnnotationContent, mojoAnnotationVisitor );
269 mojoAnnotatedClass.setExecute( executeAnnotationContent );
270 }
271
272
273 List<MojoFieldVisitor> mojoFieldVisitors = mojoClassVisitor.findFieldWithAnnotation( Parameter.class );
274 for ( MojoFieldVisitor mojoFieldVisitor : mojoFieldVisitors )
275 {
276 ParameterAnnotationContent parameterAnnotationContent =
277 new ParameterAnnotationContent( mojoFieldVisitor.getFieldName(), mojoFieldVisitor.getClassName() );
278 if ( mojoFieldVisitor.getMojoAnnotationVisitor() != null )
279 {
280 populateAnnotationContent( parameterAnnotationContent,
281 mojoFieldVisitor.getMojoAnnotationVisitor() );
282 }
283
284 mojoAnnotatedClass.getParameters().put( parameterAnnotationContent.getFieldName(),
285 parameterAnnotationContent );
286 }
287
288
289 mojoFieldVisitors = mojoClassVisitor.findFieldWithAnnotation( Component.class );
290 for ( MojoFieldVisitor mojoFieldVisitor : mojoFieldVisitors )
291 {
292 ComponentAnnotationContent componentAnnotationContent =
293 new ComponentAnnotationContent( mojoFieldVisitor.getFieldName() );
294
295 MojoAnnotationVisitor annotationVisitor = mojoFieldVisitor.getMojoAnnotationVisitor();
296 if ( annotationVisitor != null )
297 {
298 for ( Map.Entry<String, Object> entry : annotationVisitor.getAnnotationValues().entrySet() )
299 {
300 String methodName = entry.getKey();
301 if ( StringUtils.equals( "role", methodName ) )
302 {
303 Type type = (Type) entry.getValue();
304 componentAnnotationContent.setRoleClassName( type.getClassName() );
305 }
306 else
307 {
308 reflector.invoke( componentAnnotationContent, entry.getKey(),
309 new Object[]{ entry.getValue() } );
310 }
311 }
312
313 if ( StringUtils.isEmpty( componentAnnotationContent.getRoleClassName() ) )
314 {
315 componentAnnotationContent.setRoleClassName( mojoFieldVisitor.getClassName() );
316 }
317 }
318 mojoAnnotatedClass.getComponents().put( componentAnnotationContent.getFieldName(),
319 componentAnnotationContent );
320 }
321 }
322 catch ( ReflectorException e )
323 {
324 throw new ExtractionException( e.getMessage(), e );
325 }
326 }
327 }