Coverage Report - org.apache.maven.shared.runtime.MavenRuntimeVisitorUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
MavenRuntimeVisitorUtils
82%
71/86
89%
25/28
3,308
 
 1  
 package org.apache.maven.shared.runtime;
 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 java.io.IOException;
 23  
 import java.net.MalformedURLException;
 24  
 import java.net.URL;
 25  
 import java.net.URLConnection;
 26  
 import java.util.Enumeration;
 27  
 import java.util.HashSet;
 28  
 import java.util.Set;
 29  
 import java.util.jar.JarEntry;
 30  
 import java.util.jar.JarInputStream;
 31  
 
 32  
 import org.codehaus.plexus.util.IOUtil;
 33  
 
 34  
 /**
 35  
  * Provides various methods of applying Maven runtime visitors.
 36  
  * 
 37  
  * @author <a href="mailto:markh@apache.org">Mark Hobson</a>
 38  
  * @version $Id: MavenRuntimeVisitorUtils.java 1341433 2012-05-22 12:13:18Z markh $
 39  
  * @see MavenRuntimeVisitor
 40  
  */
 41  
 final class MavenRuntimeVisitorUtils
 42  
 {
 43  
     // constants --------------------------------------------------------------
 44  
 
 45  
     /**
 46  
      * The path to Maven's metadata directory.
 47  
      */
 48  
     private static final String MAVEN_PATH = "META-INF/maven";
 49  
 
 50  
     /**
 51  
      * The path elements of a Maven project properties file, where {@code null} is a wildcard.
 52  
      */
 53  4
     private static final String[] PROPERTIES_PATH_TOKENS =
 54  
         new String[] { "META-INF", "maven", null, null, "pom.properties" };
 55  
 
 56  
     /**
 57  
      * The path elements of a Maven project XML file, where {@code null} is a wildcard.
 58  
      */
 59  4
     private static final String[] XML_PATH_TOKENS = new String[] { "META-INF", "maven", null, null, "pom.xml" };
 60  
 
 61  
     /**
 62  
      * The path element index of a Maven project properties/XML file that contains the project group id.
 63  
      */
 64  
     private static final int GROUP_ID_TOKEN_INDEX = 2;
 65  
 
 66  
     /**
 67  
      * The path element index of a Maven project properties/XML file that contains the project artifact id.
 68  
      */
 69  
     private static final int ARTIFACT_ID_TOKEN_INDEX = 3;
 70  
 
 71  
     // constructors -----------------------------------------------------------
 72  
 
 73  
     /**
 74  
      * {@code MavenRuntimeVisitorUtils} is not intended to be instantiated.
 75  
      */
 76  
     private MavenRuntimeVisitorUtils()
 77  0
     {
 78  0
         throw new AssertionError();
 79  
     }
 80  
 
 81  
     // public methods ---------------------------------------------------------
 82  
 
 83  
     /**
 84  
      * Invokes the specified visitor on all Maven projects found within the specified class loader.
 85  
      * 
 86  
      * @param classLoader
 87  
      *            the class loader to introspect
 88  
      * @param visitor
 89  
      *            the visitor to invoke
 90  
      * @throws MavenRuntimeException
 91  
      *             if an error occurs visiting the projects
 92  
      */
 93  
     public static void accept( ClassLoader classLoader, MavenRuntimeVisitor visitor )
 94  
         throws MavenRuntimeException
 95  
     {
 96  
         Enumeration<URL> urls;
 97  
 
 98  
         try
 99  
         {
 100  88
             urls = classLoader.getResources( MAVEN_PATH );
 101  
         }
 102  0
         catch ( IOException exception )
 103  
         {
 104  0
             throw new MavenRuntimeException( "Cannot obtain Maven metadata from class loader: " + classLoader,
 105  
                                              exception );
 106  88
         }
 107  
 
 108  88
         Set<String> visitedProjectProperties = new HashSet<String>();
 109  88
         Set<String> visitedProjectXML = new HashSet<String>();
 110  
 
 111  256
         while ( urls.hasMoreElements() )
 112  
         {
 113  168
             URL url = urls.nextElement();
 114  
 
 115  168
             acceptURL( url, visitor, visitedProjectProperties, visitedProjectXML );
 116  168
         }
 117  88
     }
 118  
 
 119  
     /**
 120  
      * Invokes the specified visitor on the specified class's Maven project.
 121  
      * 
 122  
      * @param klass
 123  
      *            the class to introspect
 124  
      * @param visitor
 125  
      *            the visitor to invoke
 126  
      * @throws MavenRuntimeException
 127  
      *             if an error occurs visiting the projects
 128  
      */
 129  
     public static void accept( Class<?> klass, MavenRuntimeVisitor visitor )
 130  
         throws MavenRuntimeException
 131  
     {
 132  
         try
 133  
         {
 134  56
             URL baseURL = ClassUtils.getBaseURL( klass );
 135  56
             URL url = new URL( baseURL, MAVEN_PATH );
 136  
 
 137  56
             acceptURL( url, visitor, new HashSet<String>(), new HashSet<String>() );
 138  
         }
 139  0
         catch ( MalformedURLException exception )
 140  
         {
 141  0
             throw new MavenRuntimeException( "Cannot obtain URL for class: " + klass.getName(), exception );
 142  56
         }
 143  56
     }
 144  
 
 145  
     /**
 146  
      * Invokes the specified visitor on the specified URL's Maven project.
 147  
      * 
 148  
      * @param url
 149  
      *            the URL to introspect
 150  
      * @param visitor
 151  
      *            the visitor to invoke
 152  
      * @throws MavenRuntimeException
 153  
      *             if an error occurs visiting the projects
 154  
      */
 155  
     public static void accept( URL url, MavenRuntimeVisitor visitor )
 156  
         throws MavenRuntimeException
 157  
     {
 158  
         try
 159  
         {
 160  24
             if ( "jar".equals( url.getProtocol() ) )
 161  
             {
 162  16
                 url = getJarFileURL( url );
 163  
             }
 164  
             
 165  24
             URL baseURL = getJarEntryURL( url, "" );
 166  24
             URL mavenURL = new URL( baseURL, MAVEN_PATH );
 167  
 
 168  24
             acceptURL( mavenURL, visitor, new HashSet<String>(), new HashSet<String>() );
 169  
         }
 170  0
         catch ( MalformedURLException exception )
 171  
         {
 172  0
             throw new MavenRuntimeException( "Cannot obtain URL for Jar: " + url, exception );
 173  24
         }
 174  24
     }
 175  
 
 176  
     // private methods --------------------------------------------------------
 177  
 
 178  
     /**
 179  
      * Invokes the specified visitor on all Maven projects found within the specified Maven metadata URL.
 180  
      * 
 181  
      * @param url
 182  
      *            the URL of the Maven metadata directory to introspect
 183  
      * @param visitor
 184  
      *            the visitor to invoke
 185  
      * @param visitedProjectProperties
 186  
      *            the ids of projects' properties that have been visited
 187  
      * @param visitedProjectXML
 188  
      *            the ids of projects' XML that have been visited
 189  
      * @throws MavenRuntimeException
 190  
      *             if an error occurs visiting the projects
 191  
      */
 192  
     private static void acceptURL( URL url, MavenRuntimeVisitor visitor, Set<String> visitedProjectProperties,
 193  
                                    Set<String> visitedProjectXML ) throws MavenRuntimeException
 194  
     {
 195  248
         if ( "jar".equals( url.getProtocol() ) )
 196  
         {
 197  
             URL jarURL;
 198  
 
 199  
             try
 200  
             {
 201  248
                 jarURL = getJarFileURL( url );
 202  
             }
 203  0
             catch ( MalformedURLException exception )
 204  
             {
 205  0
                 throw new MavenRuntimeException( "Cannot obtain Jar file URL for URL: " + url, exception );
 206  248
             }
 207  
 
 208  248
             acceptJar( jarURL, visitor, visitedProjectProperties, visitedProjectXML );
 209  
         }
 210  248
     }
 211  
 
 212  
     /**
 213  
      * Invokes the specified visitor on all Maven projects found within the specified Jar URL.
 214  
      * 
 215  
      * @param url
 216  
      *            the Jar URL to introspect
 217  
      * @param visitor
 218  
      *            the visitor to invoke
 219  
      * @param visitedProjectProperties
 220  
      *            the ids of projects' properties that have been visited
 221  
      * @param visitedProjectXML
 222  
      *            the ids of projects' XML that have been visited
 223  
      * @throws MavenRuntimeException
 224  
      *             if an error occurs visiting the projects
 225  
      */
 226  
     private static void acceptJar( URL url, MavenRuntimeVisitor visitor, Set<String> visitedProjectProperties,
 227  
                                    Set<String> visitedProjectXML ) throws MavenRuntimeException
 228  
     {
 229  248
         JarInputStream in = null;
 230  
 
 231  
         try
 232  
         {
 233  248
             URLConnection connection = url.openConnection();
 234  248
             connection.setUseCaches( false );
 235  
 
 236  248
             in = new JarInputStream( connection.getInputStream() );
 237  
 
 238  
             JarEntry entry;
 239  
 
 240  2172
             while ( ( entry = in.getNextJarEntry() ) != null )
 241  
             {
 242  1924
                 acceptJarEntry( url, entry, visitor, visitedProjectProperties, visitedProjectXML );
 243  
             }
 244  
         }
 245  0
         catch ( IOException exception )
 246  
         {
 247  0
             throw new MavenRuntimeException( "Cannot read jar", exception );
 248  
         }
 249  
         finally
 250  
         {
 251  248
             IOUtil.close( in );
 252  248
         }
 253  248
     }
 254  
 
 255  
     /**
 256  
      * Invokes the specified visitor on the specified Jar entry if it corresponds to a Maven project XML or properties
 257  
      * file.
 258  
      * 
 259  
      * @param jarURL
 260  
      *            a URL to the Jar file for this entry
 261  
      * @param entry
 262  
      *            the Jar entry to introspect
 263  
      * @param visitor
 264  
      *            the visitor to invoke
 265  
      * @param visitedProjectProperties
 266  
      *            the ids of projects' properties that have been visited
 267  
      * @param visitedProjectXML
 268  
      *            the ids of projects' XML that have been visited
 269  
      * @throws MavenRuntimeException
 270  
      *             if an error occurs visiting the projects
 271  
      */
 272  
     private static void acceptJarEntry( URL jarURL, JarEntry entry, MavenRuntimeVisitor visitor,
 273  
                                         Set<String> visitedProjectProperties, Set<String> visitedProjectXML )
 274  
         throws MavenRuntimeException
 275  
     {
 276  1924
         String name = entry.getName();
 277  
 
 278  
         try
 279  
         {
 280  1924
             URL url = getJarEntryURL( jarURL, entry.getName() );
 281  
 
 282  1924
             if ( isProjectPropertiesPath( name ) )
 283  
             {
 284  248
                 String projectId = getProjectId( name );
 285  
 
 286  248
                 if ( !visitedProjectProperties.contains( projectId ) )
 287  
                 {
 288  200
                     visitor.visitProjectProperties( url );
 289  
 
 290  200
                     visitedProjectProperties.add( projectId );
 291  
                 }
 292  248
             }
 293  1676
             else if ( isProjectXMLPath( name ) )
 294  
             {
 295  248
                 String projectId = getProjectId( name );
 296  
 
 297  248
                 if ( !visitedProjectXML.contains( projectId ) )
 298  
                 {
 299  200
                     visitor.visitProjectXML( url );
 300  
 
 301  200
                     visitedProjectXML.add( projectId );
 302  
                 }
 303  
             }
 304  
         }
 305  0
         catch ( MalformedURLException exception )
 306  
         {
 307  0
             throw new MavenRuntimeException( "Cannot read jar entry", exception );
 308  1924
         }
 309  1924
     }
 310  
 
 311  
     /**
 312  
      * Gets the underlying Jar file URL for the specified Jar entry URL.
 313  
      * 
 314  
      * @param url
 315  
      *            the Jar entry URL
 316  
      * @return the Jar file URL
 317  
      * @throws MalformedURLException
 318  
      *             if an error occurs deriving the Jar file URL
 319  
      */
 320  
     private static URL getJarFileURL( URL url ) throws MalformedURLException
 321  
     {
 322  264
         if ( !"jar".equals( url.getProtocol() ) )
 323  
         {
 324  0
             return url;
 325  
         }
 326  
 
 327  264
         String path = url.getPath();
 328  
 
 329  264
         int index = path.indexOf( "!/" );
 330  
 
 331  264
         if ( index != -1 )
 332  
         {
 333  264
             path = path.substring( 0, index );
 334  
         }
 335  
 
 336  264
         return new URL( path );
 337  
     }
 338  
     
 339  
     private static URL getJarEntryURL( URL jarURL, String entryName ) throws MalformedURLException
 340  
     {
 341  1948
         return new URL( "jar:" + jarURL + "!/" + entryName );
 342  
     }
 343  
 
 344  
     /**
 345  
      * Gets a unique project identifier for the specified Maven project properties/XML file.
 346  
      * 
 347  
      * @param path
 348  
      *            the path to a Maven project properties/XML file
 349  
      * @return the unique project identifier
 350  
      */
 351  
     private static String getProjectId( String path )
 352  
     {
 353  496
         String[] tokens = path.split( "/" );
 354  
 
 355  496
         String groupId = tokens[GROUP_ID_TOKEN_INDEX];
 356  496
         String artifactId = tokens[ARTIFACT_ID_TOKEN_INDEX];
 357  
 
 358  496
         return groupId + ":" + artifactId;
 359  
     }
 360  
 
 361  
     /**
 362  
      * Gets whether the specified path represents a Maven project properties file.
 363  
      * 
 364  
      * @param path
 365  
      *            the path to examine
 366  
      * @return {@code true} if the specified path represents a Maven project properties file
 367  
      */
 368  
     private static boolean isProjectPropertiesPath( String path )
 369  
     {
 370  1924
         return matches( PROPERTIES_PATH_TOKENS, path.split( "/" ) );
 371  
     }
 372  
 
 373  
     /**
 374  
      * Gets whether the specified path represents a Maven project XML file.
 375  
      * 
 376  
      * @param path
 377  
      *            the path to examine
 378  
      * @return {@code true} if the specified path represents a Maven project XML file
 379  
      */
 380  
     private static boolean isProjectXMLPath( String path )
 381  
     {
 382  1676
         return matches( XML_PATH_TOKENS, path.split( "/" ) );
 383  
     }
 384  
 
 385  
     /**
 386  
      * Gets whether the specified string arrays are equal, with wildcard support.
 387  
      * 
 388  
      * @param matchTokens
 389  
      *            the string tokens to match, where {@code null} represents a wildcard
 390  
      * @param tokens
 391  
      *            the string tokens to test
 392  
      * @return {@code true} if the {@code tokens} array equals the {@code matchTokens}, treating any {@code null}
 393  
      *         {@code matchTokens} values as wildcards
 394  
      */
 395  
     private static boolean matches( String[] matchTokens, String[] tokens )
 396  
     {
 397  3600
         if ( tokens.length != matchTokens.length )
 398  
         {
 399  2856
             return false;
 400  
         }
 401  
 
 402  4216
         for ( int i = 0; i < tokens.length; i++ )
 403  
         {
 404  3720
             if ( matchTokens[i] != null && !tokens[i].equals( matchTokens[i] ) )
 405  
             {
 406  248
                 return false;
 407  
             }
 408  
         }
 409  
 
 410  496
         return true;
 411  
     }
 412  
 }