View Javadoc
1   package org.eclipse.aether.impl;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   * 
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   * 
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.lang.reflect.Constructor;
23  import java.lang.reflect.Modifier;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.HashMap;
28  import java.util.LinkedHashSet;
29  import java.util.List;
30  import java.util.Map;
31  import static java.util.Objects.requireNonNull;
32  
33  import org.eclipse.aether.RepositorySystem;
34  import org.eclipse.aether.internal.impl.DefaultArtifactResolver;
35  import org.eclipse.aether.internal.impl.DefaultChecksumPolicyProvider;
36  import org.eclipse.aether.internal.impl.DefaultDependencyCollector;
37  import org.eclipse.aether.internal.impl.DefaultDeployer;
38  import org.eclipse.aether.internal.impl.DefaultFileProcessor;
39  import org.eclipse.aether.internal.impl.DefaultInstaller;
40  import org.eclipse.aether.internal.impl.DefaultLocalRepositoryProvider;
41  import org.eclipse.aether.internal.impl.DefaultMetadataResolver;
42  import org.eclipse.aether.internal.impl.DefaultOfflineController;
43  import org.eclipse.aether.internal.impl.DefaultRemoteRepositoryManager;
44  import org.eclipse.aether.internal.impl.DefaultRepositoryConnectorProvider;
45  import org.eclipse.aether.internal.impl.DefaultRepositoryEventDispatcher;
46  import org.eclipse.aether.internal.impl.DefaultRepositoryLayoutProvider;
47  import org.eclipse.aether.internal.impl.DefaultRepositorySystem;
48  import org.eclipse.aether.internal.impl.DefaultSyncContextFactory;
49  import org.eclipse.aether.internal.impl.DefaultTransporterProvider;
50  import org.eclipse.aether.internal.impl.DefaultUpdateCheckManager;
51  import org.eclipse.aether.internal.impl.DefaultUpdatePolicyAnalyzer;
52  import org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManagerFactory;
53  import org.eclipse.aether.internal.impl.Maven2RepositoryLayoutFactory;
54  import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
55  import org.eclipse.aether.internal.impl.slf4j.Slf4jLoggerFactory;
56  import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider;
57  import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory;
58  import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider;
59  import org.eclipse.aether.spi.connector.transport.TransporterProvider;
60  import org.eclipse.aether.spi.io.FileProcessor;
61  import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
62  import org.eclipse.aether.spi.locator.Service;
63  import org.eclipse.aether.spi.locator.ServiceLocator;
64  import org.eclipse.aether.spi.log.LoggerFactory;
65  
66  /**
67   * A simple service locator that is already setup with all components from this library. To acquire a complete
68   * repository system, clients need to add an artifact descriptor reader, a version resolver, a version range resolver
69   * and optionally some repository connector and transporter factories to access remote repositories. Once the locator is
70   * fully populated, the repository system can be created like this:
71   * 
72   * <pre>
73   * RepositorySystem repoSystem = serviceLocator.getService( RepositorySystem.class );
74   * </pre>
75   * 
76   * <em>Note:</em> This class is not thread-safe. Clients are expected to create the service locator and the repository
77   * system on a single thread.
78   */
79  public final class DefaultServiceLocator
80      implements ServiceLocator
81  {
82  
83      private class Entry<T>
84      {
85  
86          private final Class<T> type;
87  
88          private final Collection<Object> providers;
89  
90          private List<T> instances;
91  
92          Entry( Class<T> type )
93          {
94              this.type = requireNonNull( type, "service type cannot be null" );
95              providers = new LinkedHashSet<Object>( 8 );
96          }
97  
98          public synchronized void setServices( T... services )
99          {
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         if ( Slf4jLoggerFactory.isSlf4jAvailable() )
219         {
220             addService( LoggerFactory.class, Slf4jLoggerFactory.class );
221         }
222     }
223 
224     private <T> Entry<T> getEntry( Class<T> type, boolean create )
225     {
226         @SuppressWarnings( "unchecked" )
227         Entry<T> entry = (Entry<T>) entries.get( requireNonNull( type, "service type cannot be null" ) );
228         if ( entry == null && create )
229         {
230             entry = new Entry<T>( type );
231             entries.put( type, entry );
232         }
233         return entry;
234     }
235 
236     /**
237      * Sets the implementation class for a service. The specified class must have a no-arg constructor (of any
238      * visibility). If the service implementation itself requires other services for its operation, it should implement
239      * {@link Service} to gain access to this service locator.
240      * 
241      * @param <T> The service type.
242      * @param type The interface describing the service, must not be {@code null}.
243      * @param impl The implementation class of the service, must not be {@code null}.
244      * @return This locator for chaining, never {@code null}.
245      */
246     public <T> DefaultServiceLocator setService( Class<T> type, Class<? extends T> impl )
247     {
248         getEntry( type, true ).setService( impl );
249         return this;
250     }
251 
252     /**
253      * Adds an implementation class for a service. The specified class must have a no-arg constructor (of any
254      * visibility). If the service implementation itself requires other services for its operation, it should implement
255      * {@link Service} to gain access to this service locator.
256      * 
257      * @param <T> The service type.
258      * @param type The interface describing the service, must not be {@code null}.
259      * @param impl The implementation class of the service, must not be {@code null}.
260      * @return This locator for chaining, never {@code null}.
261      */
262     public <T> DefaultServiceLocator addService( Class<T> type, Class<? extends T> impl )
263     {
264         getEntry( type, true ).addService( impl );
265         return this;
266     }
267 
268     /**
269      * Sets the instances for a service.
270      * 
271      * @param <T> The service type.
272      * @param type The interface describing the service, must not be {@code null}.
273      * @param services The instances of the service, may be {@code null} but must not contain {@code null} elements.
274      * @return This locator for chaining, never {@code null}.
275      */
276     public <T> DefaultServiceLocator setServices( Class<T> type, T... services )
277     {
278         getEntry( type, true ).setServices( services );
279         return this;
280     }
281 
282     public <T> T getService( Class<T> type )
283     {
284         Entry<T> entry = getEntry( type, false );
285         return ( entry != null ) ? entry.getInstance() : null;
286     }
287 
288     public <T> List<T> getServices( Class<T> type )
289     {
290         Entry<T> entry = getEntry( type, false );
291         return ( entry != null ) ? entry.getInstances() : null;
292     }
293 
294     private void serviceCreationFailed( Class<?> type, Class<?> impl, Throwable exception )
295     {
296         if ( errorHandler != null )
297         {
298             errorHandler.serviceCreationFailed( type, impl, exception );
299         }
300     }
301 
302     /**
303      * Sets the error handler to use.
304      * 
305      * @param errorHandler The error handler to use, may be {@code null} to ignore/swallow errors.
306      */
307     public void setErrorHandler( ErrorHandler errorHandler )
308     {
309         this.errorHandler = errorHandler;
310     }
311 
312     /**
313      * A hook to customize the handling of errors encountered while locating a service implementation.
314      */
315     public abstract static class ErrorHandler
316     {
317 
318         /**
319          * Handles errors during creation of a service. The default implemention does nothing.
320          * 
321          * @param type The interface describing the service, must not be {@code null}.
322          * @param impl The implementation class of the service, must not be {@code null}.
323          * @param exception The error that occurred while trying to instantiate the implementation class, must not be
324          *            {@code null}.
325          */
326         public void serviceCreationFailed( Class<?> type, Class<?> impl, Throwable exception )
327         {
328         }
329 
330     }
331 
332 }