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  
32  import org.eclipse.aether.RepositorySystem;
33  import org.eclipse.aether.internal.impl.DefaultArtifactResolver;
34  import org.eclipse.aether.internal.impl.DefaultChecksumPolicyProvider;
35  import org.eclipse.aether.internal.impl.DefaultDependencyCollector;
36  import org.eclipse.aether.internal.impl.DefaultDeployer;
37  import org.eclipse.aether.internal.impl.DefaultFileProcessor;
38  import org.eclipse.aether.internal.impl.DefaultInstaller;
39  import org.eclipse.aether.internal.impl.DefaultLocalRepositoryProvider;
40  import org.eclipse.aether.internal.impl.DefaultMetadataResolver;
41  import org.eclipse.aether.internal.impl.DefaultOfflineController;
42  import org.eclipse.aether.internal.impl.DefaultRemoteRepositoryManager;
43  import org.eclipse.aether.internal.impl.DefaultRepositoryConnectorProvider;
44  import org.eclipse.aether.internal.impl.DefaultRepositoryEventDispatcher;
45  import org.eclipse.aether.internal.impl.DefaultRepositoryLayoutProvider;
46  import org.eclipse.aether.internal.impl.DefaultRepositorySystem;
47  import org.eclipse.aether.internal.impl.DefaultSyncContextFactory;
48  import org.eclipse.aether.internal.impl.DefaultTransporterProvider;
49  import org.eclipse.aether.internal.impl.DefaultUpdateCheckManager;
50  import org.eclipse.aether.internal.impl.DefaultUpdatePolicyAnalyzer;
51  import org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManagerFactory;
52  import org.eclipse.aether.internal.impl.Maven2RepositoryLayoutFactory;
53  import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
54  import org.eclipse.aether.internal.impl.slf4j.Slf4jLoggerFactory;
55  import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider;
56  import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory;
57  import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider;
58  import org.eclipse.aether.spi.connector.transport.TransporterProvider;
59  import org.eclipse.aether.spi.io.FileProcessor;
60  import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
61  import org.eclipse.aether.spi.locator.Service;
62  import org.eclipse.aether.spi.locator.ServiceLocator;
63  import org.eclipse.aether.spi.log.LoggerFactory;
64  
65  /**
66   * A simple service locator that is already setup with all components from this library. To acquire a complete
67   * repository system, clients need to add an artifact descriptor reader, a version resolver, a version range resolver
68   * and optionally some repository connector and transporter factories to access remote repositories. Once the locator is
69   * fully populated, the repository system can be created like this:
70   * 
71   * <pre>
72   * RepositorySystem repoSystem = serviceLocator.getService( RepositorySystem.class );
73   * </pre>
74   * 
75   * <em>Note:</em> This class is not thread-safe. Clients are expected to create the service locator and the repository
76   * system on a single thread.
77   */
78  public final class DefaultServiceLocator
79      implements ServiceLocator
80  {
81  
82      private class Entry<T>
83      {
84  
85          private final Class<T> type;
86  
87          private final Collection<Object> providers;
88  
89          private List<T> instances;
90  
91          public Entry( Class<T> type )
92          {
93              if ( type == null )
94              {
95                  throw new IllegalArgumentException( "service type not specified" );
96              }
97              this.type = type;
98              providers = new LinkedHashSet<Object>( 8 );
99          }
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 }