/* * JBoss, Home of Professional Open Source * Copyright 2014, Red Hat Middleware LLC, and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jboss.shrinkwrap.resolver.impl.maven.bootstrap; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.maven.model.building.DefaultModelBuilderFactory; import org.apache.maven.model.building.ModelBuilder; import org.apache.maven.repository.internal.DefaultArtifactDescriptorReader; import org.apache.maven.repository.internal.DefaultVersionRangeResolver; import org.apache.maven.repository.internal.DefaultVersionResolver; import org.apache.maven.repository.internal.SnapshotMetadataGeneratorFactory; import org.apache.maven.repository.internal.VersionsMetadataGeneratorFactory; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory; import org.eclipse.aether.impl.ArtifactDescriptorReader; import org.eclipse.aether.impl.ArtifactResolver; import org.eclipse.aether.impl.DependencyCollector; import org.eclipse.aether.impl.Deployer; import org.eclipse.aether.impl.Installer; import org.eclipse.aether.impl.LocalRepositoryProvider; import org.eclipse.aether.impl.MetadataGeneratorFactory; import org.eclipse.aether.impl.MetadataResolver; import org.eclipse.aether.impl.OfflineController; import org.eclipse.aether.impl.RemoteRepositoryManager; import org.eclipse.aether.impl.RepositoryConnectorProvider; import org.eclipse.aether.impl.RepositoryEventDispatcher; import org.eclipse.aether.impl.SyncContextFactory; import org.eclipse.aether.impl.UpdateCheckManager; import org.eclipse.aether.impl.UpdatePolicyAnalyzer; import org.eclipse.aether.impl.VersionRangeResolver; import org.eclipse.aether.impl.VersionResolver; import org.eclipse.aether.internal.impl.DefaultArtifactResolver; import org.eclipse.aether.internal.impl.DefaultChecksumPolicyProvider; import org.eclipse.aether.internal.impl.DefaultDependencyCollector; import org.eclipse.aether.internal.impl.DefaultDeployer; import org.eclipse.aether.internal.impl.DefaultFileProcessor; import org.eclipse.aether.internal.impl.DefaultInstaller; import org.eclipse.aether.internal.impl.DefaultLocalRepositoryProvider; import org.eclipse.aether.internal.impl.DefaultMetadataResolver; import org.eclipse.aether.internal.impl.DefaultOfflineController; import org.eclipse.aether.internal.impl.DefaultRemoteRepositoryManager; import org.eclipse.aether.internal.impl.DefaultRepositoryConnectorProvider; import org.eclipse.aether.internal.impl.DefaultRepositoryEventDispatcher; import org.eclipse.aether.internal.impl.DefaultRepositoryLayoutProvider; import org.eclipse.aether.internal.impl.DefaultRepositorySystem; import org.eclipse.aether.internal.impl.DefaultSyncContextFactory; import org.eclipse.aether.internal.impl.DefaultTransporterProvider; import org.eclipse.aether.internal.impl.DefaultUpdateCheckManager; import org.eclipse.aether.internal.impl.DefaultUpdatePolicyAnalyzer; import org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManagerFactory; import org.eclipse.aether.internal.impl.Maven2RepositoryLayoutFactory; import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory; import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider; import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory; import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider; import org.eclipse.aether.spi.connector.transport.TransporterFactory; import org.eclipse.aether.spi.connector.transport.TransporterProvider; import org.eclipse.aether.spi.io.FileProcessor; import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory; import org.eclipse.aether.spi.locator.Service; import org.eclipse.aether.spi.locator.ServiceLocator; import org.eclipse.aether.spi.log.LoggerFactory; import org.eclipse.aether.transport.wagon.WagonProvider; import org.eclipse.aether.transport.wagon.WagonTransporterFactory; import org.jboss.shrinkwrap.resolver.impl.maven.logging.AetherLoggerFactory; /** * A service locator for bootstrapping repository system * * @author <a href="kpiwko@redhat.com">Karel Piwko</a> * */ class ShrinkWrapResolverServiceLocator implements ServiceLocator { private static final Logger log = Logger.getLogger(ShrinkWrapResolverServiceLocator.class.getName()); private final Map<Class<?>, CacheItem> cache; /** * Representation of either implementation class or instance for given service to allow lazy-loading of the services * * @author <a href="kpiwko@redhat.com">Karel Piwko</a> * */ class CacheItem { Class<?> type; List<Class<?>> implementations; List<Object> instances; CacheItem(Class<?> type) { this.type = type; this.implementations = new ArrayList<Class<?>>(); this.instances = new ArrayList<Object>(); } void addImplementation(Class<?> classImpl) { implementations.add(classImpl); } synchronized List<Object> instantiate() { for (Class<?> impl : implementations) { try { Object instance = SecurityActions.newInstance(impl, new Class<?>[0], new Object[0]); // lazy load other services if required if (instance instanceof Service) { ((Service) instance).initService(ShrinkWrapResolverServiceLocator.this); } instances.add(type.cast(instance)); } catch (Exception e) { log.log(Level.SEVERE, MessageFormat.format("Failed instantiating {0}, implementation of {1}", impl.getName(), type.getName()), e); } } return instances; } synchronized void replaceInstances(Object... newInstaces) { this.implementations.clear(); this.instances.clear(); this.instances.addAll(Arrays.asList(newInstaces)); } } ShrinkWrapResolverServiceLocator() { this.cache = new HashMap<Class<?>, CacheItem>(); addService(RepositorySystem.class, DefaultRepositorySystem.class); addService(ArtifactResolver.class, DefaultArtifactResolver.class); addService(DependencyCollector.class, DefaultDependencyCollector.class); addService(Deployer.class, DefaultDeployer.class); addService(Installer.class, DefaultInstaller.class); addService(MetadataResolver.class, DefaultMetadataResolver.class); addService(RepositoryConnectorProvider.class, DefaultRepositoryConnectorProvider.class); addService(RemoteRepositoryManager.class, DefaultRemoteRepositoryManager.class); addService(UpdateCheckManager.class, DefaultUpdateCheckManager.class); addService(UpdatePolicyAnalyzer.class, DefaultUpdatePolicyAnalyzer.class); addService(FileProcessor.class, DefaultFileProcessor.class); addService(SyncContextFactory.class, DefaultSyncContextFactory.class); addService(RepositoryEventDispatcher.class, DefaultRepositoryEventDispatcher.class); addService(OfflineController.class, DefaultOfflineController.class); addService(LocalRepositoryProvider.class, DefaultLocalRepositoryProvider.class); addService(LocalRepositoryManagerFactory.class, SimpleLocalRepositoryManagerFactory.class); addService(LocalRepositoryManagerFactory.class, EnhancedLocalRepositoryManagerFactory.class); // add Maven supported services, we are not using MavenServiceLocator as it should not be used from // Maven plugins, however we need to do that for dependency tree output // class names for internal aether classes we need to register implementations for addService(ArtifactDescriptorReader.class, DefaultArtifactDescriptorReader.class); addService(VersionResolver.class, DefaultVersionResolver.class); addService(VersionRangeResolver.class, DefaultVersionRangeResolver.class); addService(MetadataGeneratorFactory.class, SnapshotMetadataGeneratorFactory.class); addService(MetadataGeneratorFactory.class, VersionsMetadataGeneratorFactory.class); // add our own services setServices(ModelBuilder.class, new DefaultModelBuilderFactory().newInstance()); setServices(WagonProvider.class, new ManualWagonProvider()); // add default services introduced after aether 0.9.0.M2 addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class); addService(TransporterProvider.class, DefaultTransporterProvider.class); addService(TransporterFactory.class, WagonTransporterFactory.class); addService(RepositoryLayoutProvider.class, DefaultRepositoryLayoutProvider.class); addService(RepositoryLayoutFactory.class, Maven2RepositoryLayoutFactory.class); addService(ChecksumPolicyProvider.class, DefaultChecksumPolicyProvider.class); // to avoid problems with SLF4J, we are having a JUL bridge setServices(LoggerFactory.class, new AetherLoggerFactory()); } private <T> ShrinkWrapResolverServiceLocator addService(Class<T> type, Class<? extends T> implementationType) { CacheItem item = cache.get(type); if (item == null) { item = new CacheItem(type); } item.addImplementation(implementationType); cache.put(type, item); return this; } /** * Sets the instances for a service. * * @param <T> The service type. * @param type The interface describing the service, must not be {@code null}. * @param services The instances of the service, may be {@code null} but must not contain {@code null} elements. * @return This locator for chaining, never {@code null}. */ @SuppressWarnings("unchecked") private <T> ShrinkWrapResolverServiceLocator setServices(Class<T> type, T... services) { CacheItem item = cache.get(type); if (item == null) { item = new CacheItem(type); } item.replaceInstances(services); cache.put(type, item); return this; } @Override public <T> T getService(Class<T> serviceType) { List<T> services = getServices(serviceType); if (services.size() == 1) { return services.iterator().next(); } if (services.size() > 1) { throw new IllegalStateException(MessageFormat.format( "Unable to identify service for {0}, multiple ({1}) services implementations were registered.", serviceType.getName(), services.size())); } // this represents and exception we can't recover from if (serviceType.isAssignableFrom(RepositorySystem.class)) { // zero services available throw new IllegalStateException( "Unable to boostrap Aether repository system, missing RepositoryService " + serviceType.getName() + ", probably due to missing or invalid Aether dependencies. " + " You are either running from within Maven plugin with Maven 3.0.x version (make sure to update to Maven 3.1.0 or newer) or " + " you have org.apache.maven:maven-aether-provider:3.0.x on classpath shading required binding (make sure to update dependencies in your project)."); } // there might be some services, however we can live with them being null return null; } @SuppressWarnings("unchecked") @Override public <T> List<T> getServices(Class<T> serviceType) { CacheItem item = cache.get(serviceType); if (item == null) { // this represents and exception we can't recover from if (serviceType.isAssignableFrom(RepositorySystem.class)) { throw new IllegalStateException( "Unable to boostrap Aether repository system, missing RepositoryService " + serviceType.getName() + ", probably due to missing or invalid Aether dependencies. " + " You are either running from within Maven plugin with Maven 3.0.x version (make sure to update to Maven 3.1.0 or newer) or " + " you have org.apache.maven:maven-aether-provider:3.0.x on classpath shading required binding (make sure to update dependencies in your project)."); } return Collections.emptyList(); } // classes were not yet instantiated if (item.instances.isEmpty()) { return (List<T>) item.instantiate(); } // we already have instances, so let's return them return (List<T>) item.instances; } }