| package org.apache.maven.surefire.providerapi; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import javax.annotation.Nonnull; |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.net.URL; |
| import java.util.Enumeration; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import static java.lang.Character.isJavaIdentifierPart; |
| import static java.lang.Character.isJavaIdentifierStart; |
| import static java.util.Collections.emptySet; |
| import static org.apache.maven.surefire.api.util.ReflectionUtils.getConstructor; |
| |
| /** |
| * SPI loader for Surefire/Failsafe should use {@link Thread#getContextClassLoader() current ClassLoader}. |
| * <br> |
| * The {@link java.util.ServiceLoader} embedded in JVM uses |
| * {@link ClassLoader#getSystemClassLoader() System ClassLoader} and cannot be used in Surefire/Failsafe. |
| * |
| * @since 2.20 |
| */ |
| public final class ServiceLoader |
| { |
| |
| @Nonnull |
| @SuppressWarnings( "unchecked" ) |
| public <T> Set<T> load( Class<T> clazz, ClassLoader classLoader ) |
| { |
| try |
| { |
| Set<T> implementations = new HashSet<>(); |
| for ( String fullyQualifiedClassName : lookup( clazz, classLoader ) ) |
| { |
| Class<?> implClass = classLoader.loadClass( fullyQualifiedClassName ); |
| implementations.add( (T) getConstructor( implClass ).newInstance() ); |
| } |
| return implementations; |
| } |
| catch ( IOException | ReflectiveOperationException e ) |
| { |
| throw new IllegalStateException( e.getLocalizedMessage(), e ); |
| } |
| } |
| |
| @Nonnull |
| public Set<String> lookup( Class<?> clazz, ClassLoader classLoader ) |
| throws IOException |
| { |
| final String resourceName = "META-INF/services/" + clazz.getName(); |
| |
| if ( classLoader == null ) |
| { |
| return emptySet(); |
| } |
| final Enumeration<URL> urls = classLoader.getResources( resourceName ); |
| return lookupSpiImplementations( urls ); |
| } |
| |
| /** |
| * Method loadServices loads the services of a class that are |
| * defined using the SPI mechanism. |
| * |
| * @param urlEnumeration The urls from the resource |
| * @return The set of service provider names |
| * @throws IOException When reading the streams fails |
| */ |
| @Nonnull |
| @SuppressWarnings( "checkstyle:innerassignment" ) |
| private static Set<String> lookupSpiImplementations( final Enumeration<URL> urlEnumeration ) |
| throws IOException |
| { |
| final Set<String> names = new HashSet<>(); |
| nextUrl: |
| while ( urlEnumeration.hasMoreElements() ) |
| { |
| final URL url = urlEnumeration.nextElement(); |
| try ( BufferedReader reader = getReader( url ) ) |
| { |
| for ( String line; ( line = reader.readLine() ) != null; ) |
| { |
| int ci = line.indexOf( '#' ); |
| if ( ci >= 0 ) |
| { |
| line = line.substring( 0, ci ); |
| } |
| line = line.trim(); |
| int n = line.length(); |
| if ( n == 0 ) |
| { |
| continue; // next line |
| } |
| |
| if ( line.indexOf( ' ' ) >= 0 || line.indexOf( '\t' ) >= 0 ) |
| { |
| continue nextUrl; // next url |
| } |
| char cp = line.charAt( 0 ); // should use codePointAt but this was JDK1.3 |
| if ( !isJavaIdentifierStart( cp ) ) |
| { |
| continue nextUrl; // next url |
| } |
| for ( int i = 1; i < n; i++ ) |
| { |
| cp = line.charAt( i ); // should use codePointAt but this was JDK1.3 |
| if ( !isJavaIdentifierPart( cp ) && cp != '.' ) |
| { |
| continue nextUrl; // next url |
| } |
| } |
| if ( !names.contains( line ) ) |
| { |
| names.add( line ); |
| } |
| } |
| } |
| } |
| |
| return names; |
| } |
| |
| @Nonnull |
| private static BufferedReader getReader( @Nonnull URL url ) |
| throws IOException |
| { |
| final InputStream inputStream = url.openStream(); |
| final InputStreamReader inputStreamReader = new InputStreamReader( inputStream ); |
| return new BufferedReader( inputStreamReader ); |
| } |
| } |