All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nnsoft.commons.bspi.ServiceLoader Maven / Gradle / Ivy

package org.nnsoft.commons.bspi;

/*
 *    Copyright 2010-2011 The 99 Software Foundation
 *
 *    Licensed 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 java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;

/**
 * A simple service-provider loading facility.
 *
 * @param  The type of the service to be loaded by this loader.
 */
public final class ServiceLoader
    implements Iterable
{

    private static final String SERVICE_PREFIX = "META-INF/services/";

    /**
     * Creates a new service loader for the given service type, using the current thread's
     * {@linkplain java.lang.Thread#getContextClassLoader context class loader}.
     *
     * @param  The type of the service to be loaded by this loader.
     * @param service The class or interface representing the service being loaded.
     * @return A new service loader.
     */
    public static  ServiceLoader load( Class service )
    {
        return load( service, Thread.currentThread().getContextClassLoader() );
    }

    /**
     * Creates a new service loader for the given service type and class loader.
     *
     * @param  The type of the service to be loaded by this loader.
     * @param service The class or interface representing the service being loaded.
     * @param classLoader The class loader used to locate, load, and instantiate providers.
     * @return A new service loader.
     */
    public static  ServiceLoader load( Class service, ClassLoader classLoader )
    {
        if ( service == null )
        {
            throw new IllegalArgumentException( "Parameter 'service' must not be null" );
        }
        if ( classLoader == null )
        {
            throw new IllegalArgumentException( "Parameter 'classLoader' must not be null" );
        }
        return new ServiceLoader( service, classLoader );
    }

    /**
     * Creates a new service loader for the given service type, using the extension class loader.
     *
     * @param  The type of the service to be loaded by this loader.
     * @param service The class or interface representing the service being loaded.
     * @return A new service loader.
     */
    public static  ServiceLoader loadInstalled( Class service )
    {
        ClassLoader parent = ClassLoader.getSystemClassLoader();
        ClassLoader current = null;
        while ( parent != null )
        {
            current = parent;
            parent = parent.getParent();
        }
        return load( service, current );
    }

    /**
     * Cached services types, in instantiation order.
     */
    private final LinkedHashMap> servicesTypes =
        new LinkedHashMap>();

    /**
     * Cached services, in instantiation order.
     */
    private final LinkedHashMap, S> services = new LinkedHashMap, S>();

    /**
     * The class or interface representing the service being loaded.
     */
    private final Class service;

    /**
     * The class loader used to locate, load, and instantiate providers.
     */
    private final ClassLoader classLoader;

    /**
     * The current lazy-lookup class iterator.
     */
    private ServiceClassIterator serviceClassIterator;

    /**
     * This class can't be instantiate directly, use static methods instead.
     *
     * @param service the class or interface representing the service being loaded.
     * @param classLoader the class loader used to locate, load, and instantiate providers.
     */
    private ServiceLoader( Class service, ClassLoader classLoader )
    {
        this.service = service;
        this.classLoader = classLoader;
        reload();
    }

    /**
     * Clear this loader's provider cache so that all providers will be reloaded.
     */
    public void reload()
    {
        servicesTypes.clear();
        services.clear();

        String fullName = SERVICE_PREFIX + service.getName();
        try
        {
            Enumeration serviceResources = classLoader.getResources( fullName );
            serviceClassIterator =
                new ServiceClassIterator( service, classLoader, serviceResources, servicesTypes );
        }
        catch ( IOException e )
        {
            throw new ServiceConfigurationError( "An error occurred while loading '"
                                                 + fullName
                                                 + "' Service resource(s) from clabackport-spiath", e );
        }
    }

    /**
     * {@inheritDoc}
     */
    public Iterator iterator()
    {
        return new ServiceInstancesIterator( services.entrySet().iterator(),
                                                new ServiceIterator( typesIterator(), services ) );
    }

    /**
     * Returns an iterator over a set of elements of type {@code Class}.
     *
     * @return an iterator over a set of elements of type {@code Class}.
     * @since 1.0.1
     */
    public Iterator> typesIterator()
    {
        return new ServiceTypesIterator( servicesTypes.entrySet().iterator(), serviceClassIterator );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString()
    {
        return getClass().getName() + "[" + service.getName() + "]";
    }

}