blob: 809d802e3785659c2efb6be50c7606ef6edee300 [file] [log] [blame]
package org.eclipse.aether.impl;
/*
* 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 java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import static java.util.Objects.requireNonNull;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.internal.impl.DefaultArtifactResolver;
import org.eclipse.aether.internal.impl.DefaultChecksumPolicyProvider;
import org.eclipse.aether.internal.impl.DefaultDependencyCollector;
import org.eclipse.aether.internal.impl.DefaultDeployer;
import org.eclipse.aether.internal.impl.DefaultFileProcessor;
import org.eclipse.aether.internal.impl.DefaultInstaller;
import org.eclipse.aether.internal.impl.DefaultLocalRepositoryProvider;
import org.eclipse.aether.internal.impl.DefaultMetadataResolver;
import org.eclipse.aether.internal.impl.DefaultOfflineController;
import org.eclipse.aether.internal.impl.DefaultRemoteRepositoryManager;
import org.eclipse.aether.internal.impl.DefaultRepositoryConnectorProvider;
import org.eclipse.aether.internal.impl.DefaultRepositoryEventDispatcher;
import org.eclipse.aether.internal.impl.DefaultRepositoryLayoutProvider;
import org.eclipse.aether.internal.impl.DefaultRepositorySystem;
import org.eclipse.aether.internal.impl.DefaultSyncContextFactory;
import org.eclipse.aether.internal.impl.DefaultTransporterProvider;
import org.eclipse.aether.internal.impl.DefaultUpdateCheckManager;
import org.eclipse.aether.internal.impl.DefaultUpdatePolicyAnalyzer;
import org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManagerFactory;
import org.eclipse.aether.internal.impl.Maven2RepositoryLayoutFactory;
import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
import org.eclipse.aether.internal.impl.slf4j.Slf4jLoggerFactory;
import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider;
import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory;
import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider;
import org.eclipse.aether.spi.connector.transport.TransporterProvider;
import org.eclipse.aether.spi.io.FileProcessor;
import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
import org.eclipse.aether.spi.locator.Service;
import org.eclipse.aether.spi.locator.ServiceLocator;
import org.eclipse.aether.spi.log.LoggerFactory;
/**
* A simple service locator that is already setup with all components from this library. To acquire a complete
* repository system, clients need to add an artifact descriptor reader, a version resolver, a version range resolver
* and optionally some repository connector and transporter factories to access remote repositories. Once the locator is
* fully populated, the repository system can be created like this:
*
* <pre>
* RepositorySystem repoSystem = serviceLocator.getService( RepositorySystem.class );
* </pre>
*
* <em>Note:</em> This class is not thread-safe. Clients are expected to create the service locator and the repository
* system on a single thread.
*/
public final class DefaultServiceLocator
implements ServiceLocator
{
private class Entry<T>
{
private final Class<T> type;
private final Collection<Object> providers;
private List<T> instances;
Entry( Class<T> type )
{
this.type = requireNonNull( type, "service type cannot be null" );
providers = new LinkedHashSet<Object>( 8 );
}
public synchronized void setServices( T... services )
{
providers.clear();
if ( services != null )
{
for ( T service : services )
{
providers.add( requireNonNull( service, "service instance cannot be null" ) );
}
}
instances = null;
}
public synchronized void setService( Class<? extends T> impl )
{
providers.clear();
addService( impl );
}
public synchronized void addService( Class<? extends T> impl )
{
providers.add( requireNonNull( impl, "implementation class cannot be null" ) );
instances = null;
}
public T getInstance()
{
List<T> instances = getInstances();
return instances.isEmpty() ? null : instances.get( 0 );
}
public synchronized List<T> getInstances()
{
if ( instances == null )
{
instances = new ArrayList<T>( providers.size() );
for ( Object provider : providers )
{
T instance;
if ( provider instanceof Class )
{
instance = newInstance( (Class<?>) provider );
}
else
{
instance = type.cast( provider );
}
if ( instance != null )
{
instances.add( instance );
}
}
instances = Collections.unmodifiableList( instances );
}
return instances;
}
private T newInstance( Class<?> impl )
{
try
{
Constructor<?> constr = impl.getDeclaredConstructor();
if ( !Modifier.isPublic( constr.getModifiers() ) )
{
constr.setAccessible( true );
}
Object obj = constr.newInstance();
T instance = type.cast( obj );
if ( instance instanceof Service )
{
( (Service) instance ).initService( DefaultServiceLocator.this );
}
return instance;
}
catch ( Exception e )
{
serviceCreationFailed( type, impl, e );
}
catch ( LinkageError e )
{
serviceCreationFailed( type, impl, e );
}
return null;
}
}
private final Map<Class<?>, Entry<?>> entries;
private ErrorHandler errorHandler;
/**
* Creates a new service locator that already knows about all service implementations included this library.
*/
public DefaultServiceLocator()
{
entries = new HashMap<Class<?>, Entry<?>>();
addService( RepositorySystem.class, DefaultRepositorySystem.class );
addService( ArtifactResolver.class, DefaultArtifactResolver.class );
addService( DependencyCollector.class, DefaultDependencyCollector.class );
addService( Deployer.class, DefaultDeployer.class );
addService( Installer.class, DefaultInstaller.class );
addService( MetadataResolver.class, DefaultMetadataResolver.class );
addService( RepositoryLayoutProvider.class, DefaultRepositoryLayoutProvider.class );
addService( RepositoryLayoutFactory.class, Maven2RepositoryLayoutFactory.class );
addService( TransporterProvider.class, DefaultTransporterProvider.class );
addService( ChecksumPolicyProvider.class, DefaultChecksumPolicyProvider.class );
addService( RepositoryConnectorProvider.class, DefaultRepositoryConnectorProvider.class );
addService( RemoteRepositoryManager.class, DefaultRemoteRepositoryManager.class );
addService( UpdateCheckManager.class, DefaultUpdateCheckManager.class );
addService( UpdatePolicyAnalyzer.class, DefaultUpdatePolicyAnalyzer.class );
addService( FileProcessor.class, DefaultFileProcessor.class );
addService( SyncContextFactory.class, DefaultSyncContextFactory.class );
addService( RepositoryEventDispatcher.class, DefaultRepositoryEventDispatcher.class );
addService( OfflineController.class, DefaultOfflineController.class );
addService( LocalRepositoryProvider.class, DefaultLocalRepositoryProvider.class );
addService( LocalRepositoryManagerFactory.class, SimpleLocalRepositoryManagerFactory.class );
addService( LocalRepositoryManagerFactory.class, EnhancedLocalRepositoryManagerFactory.class );
if ( Slf4jLoggerFactory.isSlf4jAvailable() )
{
addService( LoggerFactory.class, Slf4jLoggerFactory.class );
}
}
private <T> Entry<T> getEntry( Class<T> type, boolean create )
{
@SuppressWarnings( "unchecked" )
Entry<T> entry = (Entry<T>) entries.get( requireNonNull( type, "service type cannot be null" ) );
if ( entry == null && create )
{
entry = new Entry<T>( type );
entries.put( type, entry );
}
return entry;
}
/**
* Sets the implementation class for a service. The specified class must have a no-arg constructor (of any
* visibility). If the service implementation itself requires other services for its operation, it should implement
* {@link Service} to gain access to this service locator.
*
* @param <T> The service type.
* @param type The interface describing the service, must not be {@code null}.
* @param impl The implementation class of the service, must not be {@code null}.
* @return This locator for chaining, never {@code null}.
*/
public <T> DefaultServiceLocator setService( Class<T> type, Class<? extends T> impl )
{
getEntry( type, true ).setService( impl );
return this;
}
/**
* Adds an implementation class for a service. The specified class must have a no-arg constructor (of any
* visibility). If the service implementation itself requires other services for its operation, it should implement
* {@link Service} to gain access to this service locator.
*
* @param <T> The service type.
* @param type The interface describing the service, must not be {@code null}.
* @param impl The implementation class of the service, must not be {@code null}.
* @return This locator for chaining, never {@code null}.
*/
public <T> DefaultServiceLocator addService( Class<T> type, Class<? extends T> impl )
{
getEntry( type, true ).addService( impl );
return this;
}
/**
* Sets the instances for a service.
*
* @param <T> The service type.
* @param type The interface describing the service, must not be {@code null}.
* @param services The instances of the service, may be {@code null} but must not contain {@code null} elements.
* @return This locator for chaining, never {@code null}.
*/
public <T> DefaultServiceLocator setServices( Class<T> type, T... services )
{
getEntry( type, true ).setServices( services );
return this;
}
public <T> T getService( Class<T> type )
{
Entry<T> entry = getEntry( type, false );
return ( entry != null ) ? entry.getInstance() : null;
}
public <T> List<T> getServices( Class<T> type )
{
Entry<T> entry = getEntry( type, false );
return ( entry != null ) ? entry.getInstances() : null;
}
private void serviceCreationFailed( Class<?> type, Class<?> impl, Throwable exception )
{
if ( errorHandler != null )
{
errorHandler.serviceCreationFailed( type, impl, exception );
}
}
/**
* Sets the error handler to use.
*
* @param errorHandler The error handler to use, may be {@code null} to ignore/swallow errors.
*/
public void setErrorHandler( ErrorHandler errorHandler )
{
this.errorHandler = errorHandler;
}
/**
* A hook to customize the handling of errors encountered while locating a service implementation.
*/
public abstract static class ErrorHandler
{
/**
* Handles errors during creation of a service. The default implemention does nothing.
*
* @param type The interface describing the service, must not be {@code null}.
* @param impl The implementation class of the service, must not be {@code null}.
* @param exception The error that occurred while trying to instantiate the implementation class, must not be
* {@code null}.
*/
public void serviceCreationFailed( Class<?> type, Class<?> impl, Throwable exception )
{
}
}
}