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