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 if ( !classFile.endsWith( ".class" ) )
196 {
197 continue;
198 }
199
200 InputStream is = new BufferedInputStream( new FileInputStream( new File( classDirectory, classFile ) ) );
201 try
202 {
203 MojoClassVisitor mojoClassVisitor = new MojoClassVisitor( getLogger() );
204 ClassReader rdr = new ClassReader( is );
205 rdr.accept( mojoClassVisitor,
206 ClassReader.SKIP_FRAMES | ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG );
207 analyzeVisitors( mojoClassVisitor );
208
209 MojoAnnotatedClass mojoAnnotatedClass = mojoClassVisitor.getMojoAnnotatedClass();
210
211 if ( excludeMojo )
212 {
213 mojoAnnotatedClass.setMojo( null );
214 }
215
216 if ( isStoreClass( mojoAnnotatedClass ) != null )
217 {
218 getLogger().debug( "found MojoAnnotatedClass:" + mojoAnnotatedClass.getClassName() + ":"
219 + mojoAnnotatedClass );
220 mojoAnnotatedClass.setArtifact( artifact );
221 mojoAnnotatedClasses.put( mojoAnnotatedClass.getClassName(), mojoAnnotatedClass );
222 }
223 }
224 finally
225 {
226 IOUtil.close( is );
227 }
228 }
229 return mojoAnnotatedClasses;
230 }
231
232 private MojoAnnotatedClass isStoreClass( MojoAnnotatedClass mojoAnnotatedClass )
233 {
234 // see MPLUGIN-206 we can have intermediate classes without annotations
235 if ( mojoAnnotatedClass == null )
236 {
237 return null;
238 }
239 return mojoAnnotatedClass;
240 /**
241 if ( !mojoAnnotatedClass.getComponents().isEmpty() || !mojoAnnotatedClass.getParameters().isEmpty()
242 || mojoAnnotatedClass.getExecute() != null || mojoAnnotatedClass.getMojo() != null )
243 {
244 return mojoAnnotatedClass;
245 }
246 return null;
247 **/
248 }
249
250
251 protected void analyzeVisitors( MojoClassVisitor mojoClassVisitor )
252 throws ExtractionException
253 {
254
255 try
256 {
257 MojoAnnotationVisitor mojoAnnotationVisitor =
258 mojoClassVisitor.getAnnotationVisitorMap().get( Mojo.class.getName() );
259 if ( mojoAnnotationVisitor != null )
260 {
261 MojoAnnotationContent mojoAnnotationContent = new MojoAnnotationContent();
262 for ( Map.Entry<String, Object> entry : mojoAnnotationVisitor.getAnnotationValues().entrySet() )
263 {
264 reflector.invoke( mojoAnnotationContent, entry.getKey(), new Object[]{ entry.getValue() } );
265 }
266 mojoClassVisitor.getMojoAnnotatedClass().setMojo( mojoAnnotationContent );
267 }
268
269 mojoAnnotationVisitor = mojoClassVisitor.getAnnotationVisitorMap().get( Execute.class.getName() );
270 if ( mojoAnnotationVisitor != null )
271 {
272 ExecuteAnnotationContent executeAnnotationContent = new ExecuteAnnotationContent();
273
274 for ( Map.Entry<String, Object> entry : mojoAnnotationVisitor.getAnnotationValues().entrySet() )
275 {
276 reflector.invoke( executeAnnotationContent, entry.getKey(), new Object[]{ entry.getValue() } );
277 }
278 mojoClassVisitor.getMojoAnnotatedClass().setExecute( executeAnnotationContent );
279 }
280
281 List<MojoFieldVisitor> mojoFieldVisitors =
282 mojoClassVisitor.findFieldWithAnnotationClass( Parameter.class.getName() );
283
284 for ( MojoFieldVisitor mojoFieldVisitor : mojoFieldVisitors )
285 {
286 ParameterAnnotationContent parameterAnnotationContent =
287 new ParameterAnnotationContent( mojoFieldVisitor.getFieldName(), mojoFieldVisitor.getClassName() );
288 if ( mojoFieldVisitor.getMojoAnnotationVisitor() != null )
289 {
290 for ( Map.Entry<String, Object> entry : mojoFieldVisitor.getMojoAnnotationVisitor().getAnnotationValues().entrySet() )
291 {
292 reflector.invoke( parameterAnnotationContent, entry.getKey(),
293 new Object[]{ entry.getValue() } );
294 }
295
296 }
297 mojoClassVisitor.getMojoAnnotatedClass().getParameters().put( parameterAnnotationContent.getFieldName(),
298 parameterAnnotationContent );
299 }
300
301 mojoFieldVisitors = mojoClassVisitor.findFieldWithAnnotationClass( Component.class.getName() );
302
303 for ( MojoFieldVisitor mojoFieldVisitor : mojoFieldVisitors )
304 {
305 ComponentAnnotationContent componentAnnotationContent =
306 new ComponentAnnotationContent( mojoFieldVisitor.getFieldName() );
307
308 if ( mojoFieldVisitor.getMojoAnnotationVisitor() != null )
309 {
310 for ( Map.Entry<String, Object> entry : mojoFieldVisitor.getMojoAnnotationVisitor().getAnnotationValues().entrySet() )
311 {
312 String methodName = entry.getKey();
313 if ( StringUtils.equals( "role", methodName ) )
314 {
315 Type type = (Type) entry.getValue();
316 componentAnnotationContent.setRoleClassName( type.getClassName() );
317 }
318 else
319 {
320 reflector.invoke( componentAnnotationContent, entry.getKey(),
321 new Object[]{ entry.getValue() } );
322 }
323 }
324 if ( StringUtils.isEmpty( componentAnnotationContent.getRoleClassName() ) )
325 {
326 componentAnnotationContent.setRoleClassName( mojoFieldVisitor.getClassName() );
327 }
328 }
329 mojoClassVisitor.getMojoAnnotatedClass().getComponents().put( componentAnnotationContent.getFieldName(),
330 componentAnnotationContent );
331 }
332
333 }
334 catch ( Exception e )
335 {
336 throw new ExtractionException( e.getMessage(), e );
337 }
338 }
339 }