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