View Javadoc
1   package org.apache.maven.plugin.surefire.util;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import static org.apache.maven.plugin.surefire.util.ScannerUtil.convertJarFileResourceToJavaClassName;
23  import static org.apache.maven.plugin.surefire.util.ScannerUtil.convertSlashToSystemFileSeparator;
24  import static org.apache.maven.plugin.surefire.util.ScannerUtil.processIncludesExcludes;
25  
26  import java.io.File;
27  import java.io.IOException;
28  import java.util.ArrayList;
29  import java.util.Enumeration;
30  import java.util.List;
31  import java.util.jar.JarEntry;
32  import java.util.jar.JarFile;
33  
34  import org.apache.maven.artifact.Artifact;
35  import org.apache.maven.plugin.MojoExecutionException;
36  import org.apache.maven.shared.utils.io.MatchPatterns;
37  import org.apache.maven.surefire.util.DefaultScanResult;
38  
39  import javax.annotation.Nonnull;
40  import javax.annotation.Nullable;
41  
42  /**
43   * Scans dependencies looking for tests.
44   *
45   * @author Aslak Knutsen
46   */
47  public class DependencyScanner
48  {
49  
50      private final List<File> dependenciesToScan;
51  
52      protected final List<String> includes;
53  
54      @SuppressWarnings( "checkstyle:modifierorder" )
55      protected final @Nonnull List<String> excludes;
56  
57      protected final List<String> specificTests;
58  
59      public DependencyScanner( List<File> dependenciesToScan, List<String> includes, @Nonnull List<String> excludes,
60                                List<String> specificTests )
61      {
62          this.dependenciesToScan = dependenciesToScan;
63          this.includes = includes;
64          this.excludes = excludes;
65          this.specificTests = specificTests;
66      }
67  
68      public DefaultScanResult scan()
69          throws MojoExecutionException
70      {
71          Matcher matcher = new Matcher( includes, excludes, specificTests );
72          List<String> found = new ArrayList<String>();
73          for ( File artifact : dependenciesToScan )
74          {
75              try
76              {
77                  found.addAll( scanArtifact( artifact, matcher ) );
78              }
79              catch ( IOException e )
80              {
81                  throw new MojoExecutionException( "Could not scan dependency " + artifact.toString(), e );
82              }
83          }
84          return new DefaultScanResult( found );
85      }
86  
87      private List<String> scanArtifact( File artifact, Matcher matcher )
88          throws IOException
89      {
90          List<String> found = new ArrayList<String>();
91  
92          if ( artifact != null )
93          {
94              if ( artifact.isFile() )
95              {
96                  JarFile jar = null;
97                  try
98                  {
99                      jar = new JarFile( artifact );
100                     Enumeration<JarEntry> entries = jar.entries();
101                     while ( entries.hasMoreElements() )
102                     {
103                         JarEntry entry = entries.nextElement();
104                         if ( matcher.shouldInclude( entry.getName() ) )
105                         {
106                             found.add( convertJarFileResourceToJavaClassName( entry.getName() ) );
107                         }
108                     }
109                 }
110                 finally
111                 {
112                     if ( jar != null )
113                     {
114                         jar.close();
115                     }
116                 }
117             }
118         }
119         return found;
120     }
121 
122     public static List<File> filter( List<Artifact> artifacts, List<String> groupArtifactIds )
123     {
124         List<File> matches = new ArrayList<File>();
125         if ( groupArtifactIds == null || artifacts == null )
126         {
127             return matches;
128         }
129         for ( Artifact artifact : artifacts )
130         {
131             for ( String groups : groupArtifactIds )
132             {
133                 String[] groupArtifact = groups.split( ":" );
134                 if ( groupArtifact.length != 2 )
135                 {
136                     throw new IllegalArgumentException( "dependencyToScan argument should be in format"
137                         + " 'groupid:artifactid': " + groups );
138                 }
139                 if ( artifact.getGroupId().matches( groupArtifact[0] )
140                     && artifact.getArtifactId().matches( groupArtifact[1] ) )
141                 {
142                     matches.add( artifact.getFile() );
143                 }
144             }
145         }
146         return matches;
147     }
148 
149     private class Matcher
150     {
151 
152         private MatchPatterns includes;
153 
154         private MatchPatterns excludes;
155 
156         private SpecificFileFilter specificTestFilter;
157 
158         public Matcher( @Nullable List<String> includes, @Nonnull List<String> excludes,
159                         @Nullable List<String> specificTests )
160         {
161             String[] specific = specificTests == null ? new String[0] : processIncludesExcludes( specificTests );
162             specificTestFilter = new SpecificFileFilter( specific );
163 
164             if ( includes != null && includes.size() > 0 )
165             {
166                 this.includes = MatchPatterns.from( processIncludesExcludes( includes ) );
167             }
168             else
169             {
170                 this.includes = MatchPatterns.from( "**" );
171             }
172             this.excludes = MatchPatterns.from( processIncludesExcludes( excludes ) );
173         }
174 
175         public boolean shouldInclude( String name )
176         {
177             if ( !name.endsWith( ".class" ) )
178             {
179                 return false;
180             }
181             name = convertSlashToSystemFileSeparator( name );
182             boolean isIncluded = includes.matches( name, false );
183             boolean isExcluded = excludes.matches( name, false );
184 
185             return isIncluded && !isExcluded && specificTestFilter.accept( name );
186         }
187     }
188 }