001 package org.apache.maven.tools.plugin.annotations.scanner;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import org.apache.maven.artifact.Artifact;
023 import org.apache.maven.plugins.annotations.Component;
024 import org.apache.maven.plugins.annotations.Execute;
025 import org.apache.maven.plugins.annotations.Mojo;
026 import org.apache.maven.plugins.annotations.Parameter;
027 import org.apache.maven.tools.plugin.annotations.datamodel.ComponentAnnotationContent;
028 import org.apache.maven.tools.plugin.annotations.datamodel.ExecuteAnnotationContent;
029 import org.apache.maven.tools.plugin.annotations.datamodel.MojoAnnotationContent;
030 import org.apache.maven.tools.plugin.annotations.datamodel.ParameterAnnotationContent;
031 import org.apache.maven.tools.plugin.annotations.scanner.visitors.MojoAnnotationVisitor;
032 import org.apache.maven.tools.plugin.annotations.scanner.visitors.MojoClassVisitor;
033 import org.apache.maven.tools.plugin.annotations.scanner.visitors.MojoFieldVisitor;
034 import org.apache.maven.tools.plugin.extractor.ExtractionException;
035 import org.codehaus.plexus.logging.AbstractLogEnabled;
036 import org.codehaus.plexus.util.DirectoryScanner;
037 import org.codehaus.plexus.util.IOUtil;
038 import org.codehaus.plexus.util.StringUtils;
039 import org.codehaus.plexus.util.reflection.Reflector;
040 import org.objectweb.asm.ClassReader;
041 import org.objectweb.asm.Type;
042
043 import java.io.BufferedInputStream;
044 import java.io.File;
045 import java.io.FileInputStream;
046 import java.io.IOException;
047 import java.io.InputStream;
048 import java.util.Collections;
049 import java.util.HashMap;
050 import java.util.List;
051 import java.util.Map;
052 import java.util.zip.ZipEntry;
053 import java.util.zip.ZipInputStream;
054
055 /**
056 * @author Olivier Lamy
057 * @since 3.0
058 */
059 @org.codehaus.plexus.component.annotations.Component( role = MojoAnnotationsScanner.class )
060 public class DefaultMojoAnnotationsScanner
061 extends AbstractLogEnabled
062 implements MojoAnnotationsScanner
063 {
064 private Reflector reflector = new Reflector();
065
066 public Map<String, MojoAnnotatedClass> scan( MojoAnnotationsScannerRequest request )
067 throws ExtractionException
068 {
069 Map<String, MojoAnnotatedClass> mojoAnnotatedClasses = new HashMap<String, MojoAnnotatedClass>();
070 try
071 {
072
073 for ( Artifact dependency : request.getDependencies() )
074 {
075 File dependencyFile = dependency.getFile();
076 if ( dependencyFile != null && dependencyFile.exists() )
077 {
078 if ( dependencyFile.isDirectory() )
079 {
080 mojoAnnotatedClasses.putAll(
081 scanDirectory( dependencyFile, request.getIncludePatterns(), dependency, true ) );
082 }
083 else
084 {
085 mojoAnnotatedClasses.putAll(
086 scanFile( dependencyFile, request.getIncludePatterns(), dependency, true ) );
087 }
088 }
089 }
090
091 for ( File classDirectory : request.getClassesDirectories() )
092 {
093 if ( classDirectory.exists() && classDirectory.isDirectory() )
094 {
095 mojoAnnotatedClasses.putAll(
096 scanDirectory( classDirectory, request.getIncludePatterns(), request.getProject().getArtifact(),
097 false ) );
098 }
099 }
100
101 return mojoAnnotatedClasses;
102 }
103 catch ( IOException e )
104 {
105 throw new ExtractionException( e.getMessage(), e );
106 }
107 }
108
109 /**
110 * @param archiveFile
111 * @param includePatterns
112 * @param artifact
113 * @param excludeMojo for dependencies we exclude Mojo annotations found
114 * @return
115 * @throws IOException
116 * @throws ExtractionException
117 */
118 protected Map<String, MojoAnnotatedClass> scanFile( File archiveFile, List<String> includePatterns,
119 Artifact artifact, boolean excludeMojo )
120 throws IOException, ExtractionException
121 {
122 if ( !archiveFile.exists() )
123 {
124 return Collections.emptyMap();
125 }
126 Map<String, MojoAnnotatedClass> mojoAnnotatedClasses = new HashMap<String, MojoAnnotatedClass>();
127 ZipInputStream archiveStream = new ZipInputStream( new FileInputStream( archiveFile ) );
128
129 try
130 {
131 for ( ZipEntry zipEntry = archiveStream.getNextEntry(); zipEntry != null;
132 zipEntry = archiveStream.getNextEntry() )
133 {
134 if ( zipEntry.getName().endsWith( ".class" ) )
135 {
136 MojoClassVisitor mojoClassVisitor = new MojoClassVisitor( getLogger() );
137
138 ClassReader rdr = new ClassReader( archiveStream );
139 rdr.accept( mojoClassVisitor,
140 ClassReader.SKIP_FRAMES | ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG );
141 analyzeVisitors( mojoClassVisitor );
142 if ( excludeMojo )
143 {
144 mojoClassVisitor.getMojoAnnotatedClass().setMojo( null );
145 }
146 if ( isStoreClass( mojoClassVisitor.getMojoAnnotatedClass() ) != null )
147 {
148 getLogger().debug(
149 "found MojoAnnotatedClass:" + mojoClassVisitor.getMojoAnnotatedClass().getClassName() + ":"
150 + mojoClassVisitor.getMojoAnnotatedClass() );
151 mojoClassVisitor.getMojoAnnotatedClass().setArtifact( artifact );
152 mojoAnnotatedClasses.put( mojoClassVisitor.getMojoAnnotatedClass().getClassName(),
153 mojoClassVisitor.getMojoAnnotatedClass() );
154 }
155 }
156 }
157 }
158 finally
159 {
160 IOUtil.close( archiveStream );
161 }
162 return mojoAnnotatedClasses;
163 }
164
165 /**
166 * @param classDirectory
167 * @param includePatterns
168 * @param artifact
169 * @param excludeMojo for dependencies we exclude Mojo annotations found
170 * @return
171 * @throws IOException
172 * @throws ExtractionException
173 */
174 protected Map<String, MojoAnnotatedClass> scanDirectory( File classDirectory, List<String> includePatterns,
175 Artifact artifact, boolean excludeMojo )
176 throws IOException, ExtractionException
177 {
178 if ( !classDirectory.exists() )
179 {
180 return Collections.emptyMap();
181 }
182 Map<String, MojoAnnotatedClass> mojoAnnotatedClasses = new HashMap<String, MojoAnnotatedClass>();
183 DirectoryScanner scanner = new DirectoryScanner();
184 scanner.setBasedir( classDirectory );
185 scanner.addDefaultExcludes();
186 if ( includePatterns != null )
187 {
188 scanner.setIncludes( includePatterns.toArray( new String[includePatterns.size()] ) );
189 }
190 scanner.scan();
191 String[] classFiles = scanner.getIncludedFiles();
192
193 for ( String classFile : classFiles )
194 {
195 InputStream is = new BufferedInputStream( new FileInputStream( new File( classDirectory, classFile ) ) );
196 try
197 {
198
199 if ( classFile.endsWith( ".class" ) )
200 {
201 MojoClassVisitor mojoClassVisitor = new MojoClassVisitor( getLogger() );
202 ClassReader rdr = new ClassReader( is );
203 rdr.accept( mojoClassVisitor,
204 ClassReader.SKIP_FRAMES | ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG );
205 analyzeVisitors( mojoClassVisitor );
206 if ( excludeMojo )
207 {
208 mojoClassVisitor.getMojoAnnotatedClass().setMojo( null );
209 }
210 if ( isStoreClass( mojoClassVisitor.getMojoAnnotatedClass() ) != null )
211 {
212 getLogger().debug(
213 "found MojoAnnotatedClass:" + mojoClassVisitor.getMojoAnnotatedClass().getClassName() + ":"
214 + mojoClassVisitor.getMojoAnnotatedClass() );
215 mojoClassVisitor.getMojoAnnotatedClass().setArtifact( artifact );
216 mojoAnnotatedClasses.put( mojoClassVisitor.getMojoAnnotatedClass().getClassName(),
217 mojoClassVisitor.getMojoAnnotatedClass() );
218 }
219
220 }
221 }
222 finally
223 {
224 IOUtil.close( is );
225 }
226
227 }
228 return mojoAnnotatedClasses;
229 }
230
231 private MojoAnnotatedClass isStoreClass( MojoAnnotatedClass mojoAnnotatedClass )
232 {
233 // see MPLUGIN-206 we can have intermediate classes without annotations
234 if ( mojoAnnotatedClass == null )
235 {
236 return null;
237 }
238 return mojoAnnotatedClass;
239 /**
240 if ( !mojoAnnotatedClass.getComponents().isEmpty() || !mojoAnnotatedClass.getParameters().isEmpty()
241 || mojoAnnotatedClass.getExecute() != null || mojoAnnotatedClass.getMojo() != null )
242 {
243 return mojoAnnotatedClass;
244 }
245 return null;
246 **/
247 }
248
249
250 protected void analyzeVisitors( MojoClassVisitor mojoClassVisitor )
251 throws ExtractionException
252 {
253
254 try
255 {
256 MojoAnnotationVisitor mojoAnnotationVisitor =
257 mojoClassVisitor.getAnnotationVisitorMap().get( Mojo.class.getName() );
258 if ( mojoAnnotationVisitor != null )
259 {
260 MojoAnnotationContent mojoAnnotationContent = new MojoAnnotationContent();
261 for ( Map.Entry<String, Object> entry : mojoAnnotationVisitor.getAnnotationValues().entrySet() )
262 {
263 reflector.invoke( mojoAnnotationContent, entry.getKey(), new Object[]{ entry.getValue() } );
264 }
265 mojoClassVisitor.getMojoAnnotatedClass().setMojo( mojoAnnotationContent );
266 }
267
268 mojoAnnotationVisitor = mojoClassVisitor.getAnnotationVisitorMap().get( Execute.class.getName() );
269 if ( mojoAnnotationVisitor != null )
270 {
271 ExecuteAnnotationContent executeAnnotationContent = new ExecuteAnnotationContent();
272
273 for ( Map.Entry<String, Object> entry : mojoAnnotationVisitor.getAnnotationValues().entrySet() )
274 {
275 reflector.invoke( executeAnnotationContent, entry.getKey(), new Object[]{ entry.getValue() } );
276 }
277 mojoClassVisitor.getMojoAnnotatedClass().setExecute( executeAnnotationContent );
278 }
279
280 List<MojoFieldVisitor> mojoFieldVisitors =
281 mojoClassVisitor.findFieldWithAnnotationClass( Parameter.class.getName() );
282
283 for ( MojoFieldVisitor mojoFieldVisitor : mojoFieldVisitors )
284 {
285 ParameterAnnotationContent parameterAnnotationContent =
286 new ParameterAnnotationContent( mojoFieldVisitor.getFieldName(), mojoFieldVisitor.getClassName() );
287 if ( mojoFieldVisitor.getMojoAnnotationVisitor() != null )
288 {
289 for ( Map.Entry<String, Object> entry : mojoFieldVisitor.getMojoAnnotationVisitor().getAnnotationValues().entrySet() )
290 {
291 reflector.invoke( parameterAnnotationContent, entry.getKey(),
292 new Object[]{ entry.getValue() } );
293 }
294
295 }
296 mojoClassVisitor.getMojoAnnotatedClass().getParameters().put( parameterAnnotationContent.getFieldName(),
297 parameterAnnotationContent );
298 }
299
300 mojoFieldVisitors = mojoClassVisitor.findFieldWithAnnotationClass( Component.class.getName() );
301
302 for ( MojoFieldVisitor mojoFieldVisitor : mojoFieldVisitors )
303 {
304 ComponentAnnotationContent componentAnnotationContent =
305 new ComponentAnnotationContent( mojoFieldVisitor.getFieldName() );
306
307 if ( mojoFieldVisitor.getMojoAnnotationVisitor() != null )
308 {
309 for ( Map.Entry<String, Object> entry : mojoFieldVisitor.getMojoAnnotationVisitor().getAnnotationValues().entrySet() )
310 {
311 String methodName = entry.getKey();
312 if ( StringUtils.equals( "role", methodName ) )
313 {
314 Type type = (Type) entry.getValue();
315 componentAnnotationContent.setRoleClassName( type.getClassName() );
316 }
317 else
318 {
319 reflector.invoke( componentAnnotationContent, entry.getKey(),
320 new Object[]{ entry.getValue() } );
321 }
322 }
323 if ( StringUtils.isEmpty( componentAnnotationContent.getRoleClassName() ) )
324 {
325 componentAnnotationContent.setRoleClassName( mojoFieldVisitor.getClassName() );
326 }
327 }
328 mojoClassVisitor.getMojoAnnotatedClass().getComponents().put( componentAnnotationContent.getFieldName(),
329 componentAnnotationContent );
330 }
331
332 }
333 catch ( Exception e )
334 {
335 throw new ExtractionException( e.getMessage(), e );
336 }
337 }
338 }