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.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 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}