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 }