001package org.eclipse.aether.impl;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 * 
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 * 
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.lang.reflect.Constructor;
023import java.lang.reflect.Modifier;
024import java.util.ArrayList;
025import java.util.Collection;
026import java.util.Collections;
027import java.util.HashMap;
028import java.util.LinkedHashSet;
029import java.util.List;
030import java.util.Map;
031
032import org.eclipse.aether.RepositorySystem;
033import org.eclipse.aether.internal.impl.DefaultArtifactResolver;
034import org.eclipse.aether.internal.impl.DefaultChecksumPolicyProvider;
035import org.eclipse.aether.internal.impl.DefaultDependencyCollector;
036import org.eclipse.aether.internal.impl.DefaultDeployer;
037import org.eclipse.aether.internal.impl.DefaultFileProcessor;
038import org.eclipse.aether.internal.impl.DefaultInstaller;
039import org.eclipse.aether.internal.impl.DefaultLocalRepositoryProvider;
040import org.eclipse.aether.internal.impl.DefaultMetadataResolver;
041import org.eclipse.aether.internal.impl.DefaultOfflineController;
042import org.eclipse.aether.internal.impl.DefaultRemoteRepositoryManager;
043import org.eclipse.aether.internal.impl.DefaultRepositoryConnectorProvider;
044import org.eclipse.aether.internal.impl.DefaultRepositoryEventDispatcher;
045import org.eclipse.aether.internal.impl.DefaultRepositoryLayoutProvider;
046import org.eclipse.aether.internal.impl.DefaultRepositorySystem;
047import org.eclipse.aether.internal.impl.DefaultSyncContextFactory;
048import org.eclipse.aether.internal.impl.DefaultTransporterProvider;
049import org.eclipse.aether.internal.impl.DefaultUpdateCheckManager;
050import org.eclipse.aether.internal.impl.DefaultUpdatePolicyAnalyzer;
051import org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManagerFactory;
052import org.eclipse.aether.internal.impl.Maven2RepositoryLayoutFactory;
053import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
054import org.eclipse.aether.internal.impl.slf4j.Slf4jLoggerFactory;
055import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider;
056import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory;
057import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider;
058import org.eclipse.aether.spi.connector.transport.TransporterProvider;
059import org.eclipse.aether.spi.io.FileProcessor;
060import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
061import org.eclipse.aether.spi.locator.Service;
062import org.eclipse.aether.spi.locator.ServiceLocator;
063import org.eclipse.aether.spi.log.LoggerFactory;
064
065/**
066 * A simple service locator that is already setup with all components from this library. To acquire a complete
067 * repository system, clients need to add an artifact descriptor reader, a version resolver, a version range resolver
068 * and optionally some repository connector and transporter factories to access remote repositories. Once the locator is
069 * fully populated, the repository system can be created like this:
070 * 
071 * <pre>
072 * RepositorySystem repoSystem = serviceLocator.getService( RepositorySystem.class );
073 * </pre>
074 * 
075 * <em>Note:</em> This class is not thread-safe. Clients are expected to create the service locator and the repository
076 * system on a single thread.
077 */
078public final class DefaultServiceLocator
079    implements ServiceLocator
080{
081
082    private class Entry<T>
083    {
084
085        private final Class<T> type;
086
087        private final Collection<Object> providers;
088
089        private List<T> instances;
090
091        public Entry( Class<T> type )
092        {
093            if ( type == null )
094            {
095                throw new IllegalArgumentException( "service type not specified" );
096            }
097            this.type = type;
098            providers = new LinkedHashSet<Object>( 8 );
099        }
100
101        public synchronized void setServices( T... services )
102        {
103            providers.clear();
104            if ( services != null )
105            {
106                for ( T service : services )
107                {
108                    if ( service == null )
109                    {
110                        throw new IllegalArgumentException( "service instance not specified" );
111                    }
112                    providers.add( service );
113                }
114            }
115            instances = null;
116        }
117
118        public synchronized void setService( Class<? extends T> impl )
119        {
120            providers.clear();
121            addService( impl );
122        }
123
124        public synchronized void addService( Class<? extends T> impl )
125        {
126            if ( impl == null )
127            {
128                throw new IllegalArgumentException( "implementation class not specified" );
129            }
130            providers.add( impl );
131            instances = null;
132        }
133
134        public T getInstance()
135        {
136            List<T> instances = getInstances();
137            return instances.isEmpty() ? null : instances.get( 0 );
138        }
139
140        public synchronized List<T> getInstances()
141        {
142            if ( instances == null )
143            {
144                instances = new ArrayList<T>( providers.size() );
145                for ( Object provider : providers )
146                {
147                    T instance;
148                    if ( provider instanceof Class )
149                    {
150                        instance = newInstance( (Class<?>) provider );
151                    }
152                    else
153                    {
154                        instance = type.cast( provider );
155                    }
156                    if ( instance != null )
157                    {
158                        instances.add( instance );
159                    }
160                }
161                instances = Collections.unmodifiableList( instances );
162            }
163            return instances;
164        }
165
166        private T newInstance( Class<?> impl )
167        {
168            try
169            {
170                Constructor<?> constr = impl.getDeclaredConstructor();
171                if ( !Modifier.isPublic( constr.getModifiers() ) )
172                {
173                    constr.setAccessible( true );
174                }
175                Object obj = constr.newInstance();
176
177                T instance = type.cast( obj );
178                if ( instance instanceof Service )
179                {
180                    ( (Service) instance ).initService( DefaultServiceLocator.this );
181                }
182                return instance;
183            }
184            catch ( Exception e )
185            {
186                serviceCreationFailed( type, impl, e );
187            }
188            catch ( LinkageError e )
189            {
190                serviceCreationFailed( type, impl, e );
191            }
192            return null;
193        }
194
195    }
196
197    private final Map<Class<?>, Entry<?>> entries;
198
199    private ErrorHandler errorHandler;
200
201    /**
202     * Creates a new service locator that already knows about all service implementations included this library.
203     */
204    public DefaultServiceLocator()
205    {
206        entries = new HashMap<Class<?>, Entry<?>>();
207
208        addService( RepositorySystem.class, DefaultRepositorySystem.class );
209        addService( ArtifactResolver.class, DefaultArtifactResolver.class );
210        addService( DependencyCollector.class, DefaultDependencyCollector.class );
211        addService( Deployer.class, DefaultDeployer.class );
212        addService( Installer.class, DefaultInstaller.class );
213        addService( MetadataResolver.class, DefaultMetadataResolver.class );
214        addService( RepositoryLayoutProvider.class, DefaultRepositoryLayoutProvider.class );
215        addService( RepositoryLayoutFactory.class, Maven2RepositoryLayoutFactory.class );
216        addService( TransporterProvider.class, DefaultTransporterProvider.class );
217        addService( ChecksumPolicyProvider.class, DefaultChecksumPolicyProvider.class );
218        addService( RepositoryConnectorProvider.class, DefaultRepositoryConnectorProvider.class );
219        addService( RemoteRepositoryManager.class, DefaultRemoteRepositoryManager.class );
220        addService( UpdateCheckManager.class, DefaultUpdateCheckManager.class );
221        addService( UpdatePolicyAnalyzer.class, DefaultUpdatePolicyAnalyzer.class );
222        addService( FileProcessor.class, DefaultFileProcessor.class );
223        addService( SyncContextFactory.class, DefaultSyncContextFactory.class );
224        addService( RepositoryEventDispatcher.class, DefaultRepositoryEventDispatcher.class );
225        addService( OfflineController.class, DefaultOfflineController.class );
226        addService( LocalRepositoryProvider.class, DefaultLocalRepositoryProvider.class );
227        addService( LocalRepositoryManagerFactory.class, SimpleLocalRepositoryManagerFactory.class );
228        addService( LocalRepositoryManagerFactory.class, EnhancedLocalRepositoryManagerFactory.class );
229        if ( Slf4jLoggerFactory.isSlf4jAvailable() )
230        {
231            addService( LoggerFactory.class, Slf4jLoggerFactory.class );
232        }
233    }
234
235    private <T> Entry<T> getEntry( Class<T> type, boolean create )
236    {
237        if ( type == null )
238        {
239            throw new IllegalArgumentException( "service type not specified" );
240        }
241        @SuppressWarnings( "unchecked" )
242        Entry<T> entry = (Entry<T>) entries.get( type );
243        if ( entry == null && create )
244        {
245            entry = new Entry<T>( type );
246            entries.put( type, entry );
247        }
248        return entry;
249    }
250
251    /**
252     * Sets the implementation class for a service. The specified class must have a no-arg constructor (of any
253     * visibility). If the service implementation itself requires other services for its operation, it should implement
254     * {@link Service} to gain access to this service locator.
255     * 
256     * @param <T> The service type.
257     * @param type The interface describing the service, must not be {@code null}.
258     * @param impl The implementation class of the service, must not be {@code null}.
259     * @return This locator for chaining, never {@code null}.
260     */
261    public <T> DefaultServiceLocator setService( Class<T> type, Class<? extends T> impl )
262    {
263        getEntry( type, true ).setService( impl );
264        return this;
265    }
266
267    /**
268     * Adds an implementation class for a service. The specified class must have a no-arg constructor (of any
269     * visibility). If the service implementation itself requires other services for its operation, it should implement
270     * {@link Service} to gain access to this service locator.
271     * 
272     * @param <T> The service type.
273     * @param type The interface describing the service, must not be {@code null}.
274     * @param impl The implementation class of the service, must not be {@code null}.
275     * @return This locator for chaining, never {@code null}.
276     */
277    public <T> DefaultServiceLocator addService( Class<T> type, Class<? extends T> impl )
278    {
279        getEntry( type, true ).addService( impl );
280        return this;
281    }
282
283    /**
284     * Sets the instances for a service.
285     * 
286     * @param <T> The service type.
287     * @param type The interface describing the service, must not be {@code null}.
288     * @param services The instances of the service, may be {@code null} but must not contain {@code null} elements.
289     * @return This locator for chaining, never {@code null}.
290     */
291    public <T> DefaultServiceLocator setServices( Class<T> type, T... services )
292    {
293        getEntry( type, true ).setServices( services );
294        return this;
295    }
296
297    public <T> T getService( Class<T> type )
298    {
299        Entry<T> entry = getEntry( type, false );
300        return ( entry != null ) ? entry.getInstance() : null;
301    }
302
303    public <T> List<T> getServices( Class<T> type )
304    {
305        Entry<T> entry = getEntry( type, false );
306        return ( entry != null ) ? entry.getInstances() : null;
307    }
308
309    private void serviceCreationFailed( Class<?> type, Class<?> impl, Throwable exception )
310    {
311        if ( errorHandler != null )
312        {
313            errorHandler.serviceCreationFailed( type, impl, exception );
314        }
315    }
316
317    /**
318     * Sets the error handler to use.
319     * 
320     * @param errorHandler The error handler to use, may be {@code null} to ignore/swallow errors.
321     */
322    public void setErrorHandler( ErrorHandler errorHandler )
323    {
324        this.errorHandler = errorHandler;
325    }
326
327    /**
328     * A hook to customize the handling of errors encountered while locating a service implementation.
329     */
330    public abstract static class ErrorHandler
331    {
332
333        /**
334         * Handles errors during creation of a service. The default implemention does nothing.
335         * 
336         * @param type The interface describing the service, must not be {@code null}.
337         * @param impl The implementation class of the service, must not be {@code null}.
338         * @param exception The error that occurred while trying to instantiate the implementation class, must not be
339         *            {@code null}.
340         */
341        public void serviceCreationFailed( Class<?> type, Class<?> impl, Throwable exception )
342        {
343        }
344
345    }
346
347}