View Javadoc
1   package org.apache.maven.surefire.spi;
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 javax.annotation.Nonnull;
23  import java.io.BufferedReader;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.InputStreamReader;
27  import java.lang.reflect.InvocationTargetException;
28  import java.net.URL;
29  import java.util.Enumeration;
30  import java.util.HashSet;
31  import java.util.Set;
32  
33  import static java.lang.Character.isJavaIdentifierPart;
34  import static java.lang.Character.isJavaIdentifierStart;
35  import static java.util.Collections.emptySet;
36  import static org.apache.maven.surefire.util.ReflectionUtils.getConstructor;
37  
38  /**
39   * SPI loader for Java 1.5.
40   *
41   * @since 2.20
42   */
43  public class ServiceLoader
44  {
45  
46      @Nonnull
47      @SuppressWarnings( "unchecked" )
48      public <T> Set<T> load( Class<T> clazz, ClassLoader classLoader )
49      {
50          try
51          {
52              Set<T> implementations = new HashSet<T>();
53              for ( String fullyQualifiedClassName : lookup( clazz, classLoader ) )
54              {
55                  Class<?> implClass = classLoader.loadClass( fullyQualifiedClassName );
56                  implementations.add( (T) getConstructor( implClass ).newInstance() );
57              }
58              return implementations;
59          }
60          catch ( IOException e )
61          {
62              throw new IllegalStateException( e.getLocalizedMessage(), e );
63          }
64          catch ( ClassNotFoundException e )
65          {
66              throw new IllegalStateException( e.getLocalizedMessage(), e );
67          }
68          catch ( InvocationTargetException e )
69          {
70              throw new IllegalStateException( e.getLocalizedMessage(), e );
71          }
72          catch ( InstantiationException e )
73          {
74              throw new IllegalStateException( e.getLocalizedMessage(), e );
75          }
76          catch ( IllegalAccessException e )
77          {
78              throw new IllegalStateException( e.getLocalizedMessage(), e );
79          }
80      }
81  
82      @Nonnull
83      public Set<String> lookup( Class<?> clazz, ClassLoader classLoader )
84              throws IOException
85      {
86          final String resourceName = "META-INF/services/" + clazz.getName();
87  
88          if ( classLoader == null )
89          {
90              return emptySet();
91          }
92          final Enumeration<URL> urls = classLoader.getResources( resourceName );
93          return lookupSpiImplementations( urls );
94      }
95  
96      /**
97       * Method loadServices loads the services of a class that are
98       * defined using the SPI mechanism.
99       *
100      * @param urlEnumeration The urls from the resource
101      * @return The set of service provider names
102      * @throws IOException When reading the streams fails
103      */
104     @Nonnull
105     @SuppressWarnings( "checkstyle:innerassignment" )
106     private static Set<String> lookupSpiImplementations( final Enumeration<URL> urlEnumeration )
107             throws IOException
108     {
109         final Set<String> names = new HashSet<String>();
110         nextUrl:
111         while ( urlEnumeration.hasMoreElements() )
112         {
113             final URL url = urlEnumeration.nextElement();
114             final BufferedReader reader = getReader( url );
115             try
116             {
117                 for ( String line; ( line = reader.readLine() ) != null; )
118                 {
119                     int ci = line.indexOf( '#' );
120                     if ( ci >= 0 )
121                     {
122                         line = line.substring( 0, ci );
123                     }
124                     line = line.trim();
125                     int n = line.length();
126                     if ( n == 0 )
127                     {
128                         continue; // next line
129                     }
130 
131                     if ( line.indexOf( ' ' ) >= 0 || line.indexOf( '\t' ) >= 0 )
132                     {
133                         continue nextUrl; // next url
134                     }
135                     char cp = line.charAt( 0 ); // should use codePointAt but this was JDK1.3
136                     if ( !isJavaIdentifierStart( cp ) )
137                     {
138                         continue nextUrl; // next url
139                     }
140                     for ( int i = 1; i < n; i++ )
141                     {
142                         cp = line.charAt( i );  // should use codePointAt but this was JDK1.3
143                         if ( !isJavaIdentifierPart( cp ) && cp != '.' )
144                         {
145                             continue nextUrl; // next url
146                         }
147                     }
148                     if ( !names.contains( line ) )
149                     {
150                         names.add( line );
151                     }
152                 }
153             }
154             finally
155             {
156                 reader.close();
157             }
158         }
159 
160         return names;
161     }
162 
163     @Nonnull
164     private static BufferedReader getReader( @Nonnull URL url )
165             throws IOException
166     {
167         final InputStream inputStream = url.openStream();
168         final InputStreamReader inputStreamReader = new InputStreamReader( inputStream );
169         return new BufferedReader( inputStreamReader );
170     }
171 }