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}