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

trip.spi.DefaultServiceProvider Maven / Gradle / Ivy

package trip.spi;

import static trip.spi.helpers.filter.Filter.filter;
import static trip.spi.helpers.filter.Filter.first;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceConfigurationError;

import trip.spi.helpers.EmptyIterable;
import trip.spi.helpers.EmptyProviderContext;
import trip.spi.helpers.FieldQualifierExtractor;
import trip.spi.helpers.ProducerFactoryMap;
import trip.spi.helpers.ProvidableClass;
import trip.spi.helpers.QualifierExtractor;
import trip.spi.helpers.ServiceLoader;
import trip.spi.helpers.SingleObjectIterable;
import trip.spi.helpers.filter.AnyObject;
import trip.spi.helpers.filter.Condition;

@SuppressWarnings( { "rawtypes", "unchecked" } )
public class DefaultServiceProvider implements ServiceProvider {

	final Map, Iterable>> implementedClasses = new HashMap<>();
	final SingletonContext singletonContext = new SingletonContext();

	final Map, Iterable> providers;
	final ProducerFactoryMap producers;

	public DefaultServiceProvider() {
		this.providers = createDefaultProvidedData();
		singletonContext.setQualifierExtractor( createQualifierExtractor() );
		runHookBeforeProducersAreReady();
		this.producers = loadAllProducers();
		runAllStartupListeners();
	}

	private QualifierExtractor createQualifierExtractor() {
		final Iterable extractors = loadAll(FieldQualifierExtractor.class);
		return new QualifierExtractor( extractors );
	}

	private void runHookBeforeProducersAreReady() {
		final Iterable startupListeners = loadAll( StartupListener.class );
		for ( final StartupListener listener : startupListeners )
			listener.beforeProducersReady( this );
	}

	private void runAllStartupListeners() {
		final Iterable startupListeners = loadAll( StartupListener.class );
		for ( final StartupListener listener : startupListeners )
			listener.onStartup( this );
	}

	protected Map, Iterable> createDefaultProvidedData() {
		final Map, Iterable> injectables = new HashMap, Iterable>();
		injectables.put( ServiceProvider.class, new SingleObjectIterable( this ) );
		return injectables;
	}

	protected ProducerFactoryMap loadAllProducers() {
		return ProducerFactoryMap.from( loadAll( ProducerFactory.class ) );
	}

	@Override
	public  T load( final Class interfaceClazz ) {
		return load( interfaceClazz, AnyObject.instance() );
	}

	@Override
	public  T load( final Class interfaceClazz, final Condition condition ) {
		return load( interfaceClazz, condition, EmptyProviderContext.INSTANCE );
	}

	@Override
	public  T load( final Class interfaceClazz, final ProviderContext context ) {
		return load( interfaceClazz, AnyObject.instance(), context );
	}

	@Override
	public  T load( final Class interfaceClazz, final Condition condition, final ProviderContext context )
			throws ServiceProviderException {
		final T produced = produceFromFactory( interfaceClazz, condition, context );
		if ( produced != null )
			return produced;
		return first( loadAll( interfaceClazz, condition ), condition );
	}

	@Override
	public  Iterable loadAll( final Class interfaceClazz, final Condition condition ) {
		return filter( loadAll( interfaceClazz ), condition );
	}

	@Override
	public  Iterable loadAll( final Class interfaceClazz ) {
		Iterable iterable = (Iterable)this.providers.get( interfaceClazz );
		if ( iterable == null )
			synchronized ( providers ) {
				iterable = (Iterable)this.providers.get( interfaceClazz );
				if ( iterable == null )
					iterable = loadAllServicesImplementingTheInterface( interfaceClazz );
			}
		return iterable;
	}

	private  Iterable loadAllServicesImplementingTheInterface( final Class interfaceClazz ) {
		try {
			return loadServiceFor( interfaceClazz );
		} catch ( final StackOverflowError cause ) {
			throw new ServiceConfigurationError(
				"Could not load implementations of " + interfaceClazz.getCanonicalName() +
					": Recursive dependency injection detected." );
		}
	}

	private  Iterable loadServiceFor( final Class interfaceClazz ) {
		final List> iterableInterfaces = loadClassesImplementing( interfaceClazz );
		Iterable instances = null;
		if ( !iterableInterfaces.isEmpty() ){
			instances = singletonContext.instantiate( iterableInterfaces );
			provideOn( instances );
			providerFor( interfaceClazz, instances );
		} else {
			final T instance = singletonContext.instantiate( interfaceClazz );
			instances = instance == null ? EmptyIterable.instance() : new SingleObjectIterable<>( instance );
			provideOn( instances );
		}
		return instances;
	}

	public  List> loadClassesImplementing( final Class interfaceClazz ) {
		List> implementations = (List)implementedClasses.get( interfaceClazz );
		if ( implementations == null )
			synchronized ( implementedClasses ) {
				implementations = (List)implementedClasses.get( interfaceClazz );
				if ( implementations == null ) {
					implementations = ServiceLoader.loadImplementationsFor( interfaceClazz );
					implementedClasses.put( (Class)interfaceClazz, (Iterable)implementations );
				}
			}
		return implementations;
	}

	@Override
	public  void providerFor( final Class interfaceClazz, final ProducerFactory provider ) {
			this.producers.memorizeProviderForClazz( provider, interfaceClazz );
	}

	@Override
	public  void providerFor( final Class interfaceClazz, final T object ) {
		providerFor( interfaceClazz, new SingleObjectIterable( object ) );
	}

	protected  void providerFor( final Class interfaceClazz, final Iterable iterable ) {
		this.providers.put( interfaceClazz, iterable );
	}

	@Override
	public  void provideOn( final Iterable iterable ) {
		for ( final T object : iterable )
			provideOn( object );
	}

	@Override
	public void provideOn( final Object object ) {
		try {
			final ProvidableClass providableClass = singletonContext.retrieveProvidableClass( object.getClass() );
			providableClass.provide( object, this );
		} catch ( final Exception cause ) {
			throw new ServiceProviderException( cause );
		}
	}

	private  T produceFromFactory( final Class interfaceClazz, final Condition condition, final ProviderContext context )
	{
		final ProducerFactory provider = getProviderFor( interfaceClazz, condition );
		if ( provider != null )
			return provider.provide( context );
		return null;
	}

	public  ProducerFactory getProviderFor( final Class interfaceClazz, final Condition condition ) {
		if ( this.producers == null )
			return null;
		return (ProducerFactory)this.producers.get( interfaceClazz, condition );
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy