/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.gradle.util;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
import org.apache.ivy.Ivy;
import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.core.report.ResolveReport;
import org.apache.ivy.core.settings.IvySettings;
import org.apache.ivy.util.AbstractMessageLogger;
import org.apache.ivy.util.Message;
import org.apache.ivy.util.MessageLogger;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.internal.artifacts.IvyService;
import org.gradle.api.internal.artifacts.configurations.DefaultConfigurationContainer;
import org.gradle.api.internal.artifacts.configurations.ResolverProvider;
import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
import org.gradle.api.internal.artifacts.ivyservice.DefaultIvyService;
import org.gradle.api.internal.artifacts.ivyservice.ErrorHandlingIvyService;
import org.gradle.api.internal.artifacts.ivyservice.IvyFactory;
import org.gradle.api.internal.artifacts.ivyservice.SettingsConverter;
import org.gradle.api.internal.artifacts.ivyservice.ShortcircuitEmptyConfigsIvyService;
import org.gradle.api.internal.artifacts.repositories.InternalRepository;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;
import org.gradle.invocation.DefaultGradle;
/**
* A helper for handling resolution of ivy.xml files through Ivy calls using the bits and pieces of Ivy config
* from Gradle.
*
* @author Steve Ebersole
*/
public class IvyResolutionHelper {
private static final Logger log = Logging.getLogger( IvyResolutionHelper.class );
private final Project project;
/**
* Creates an Ivy resolution helper for the given project. The project will be the one on
* which the requested configurations are created/used.
*
* @param project The project into which to inject the resolved dependencies.
*/
public IvyResolutionHelper(Project project) {
this.project = project;
}
/**
* Constitutes the main API. Resolve the dependencies named in the given ivy.xml file into the named
* configuration.
*
* @param ivyXmlFile The ivy.xml file
* @param configurationName The name of the configuration into which to place the resolved dependencies.
*
* @return The configuration. Returns null to indicate a problem doing the resolution.
*/
public Configuration resolve(File ivyXmlFile, String configurationName) {
log.debug( "Resolving file[{}] by configuration[{}]", ivyXmlFile, configurationName );
ResolveReport resolveReport = performResolve( getIvy(), ivyXmlFile );
ModuleDescriptor moduleDescriptor = resolveReport.getModuleDescriptor();
Configuration configuration = getOrCreateConfiguration( configurationName );
for ( DependencyDescriptor dependencyDescriptor : moduleDescriptor.getDependencies() ) {
final ModuleRevisionId info = dependencyDescriptor.getDynamicConstraintDependencyRevisionId();
final Dependency dependency = new DefaultExternalModuleDependency(
info.getOrganisation(),
info.getName(),
info.getRevision()
);
configuration.addDependency( dependency );
log.debug( "Added dependency {} to configuration {}", dependency, configurationName );
}
return configuration;
}
public Configuration getOrCreateConfiguration(String configurationName) {
Configuration configuration = project.getConfigurations().findByName( configurationName );
if ( configuration == null ) {
configuration = project.getConfigurations().add( configurationName );
}
return configuration;
}
@SuppressWarnings( { "unchecked" })
protected ResolveReport performResolve(Ivy ivy, File ivyXmlFile) {
final MessageLogger originalMessageLogger = Message.getDefaultLogger();
Message.setDefaultLogger( new NoOpMessageLogger() );
try {
ResolveReport resolveReport = ivy.resolve( ivyXmlFile );
if ( resolveReport.hasError() ) {
throw new ResolutionException(
resolveReport.getModuleDescriptor().getModuleRevisionId().toString(),
resolveReport.getAllProblemMessages()
);
}
return resolveReport;
}
catch ( ParseException e ) {
throw new BuildException( "Malformed ivy dependency file [" + ivyXmlFile.getName() + "]", e );
}
catch ( IOException e ) {
throw new BuildException( "Problem reading ivy dependency file [" + ivyXmlFile.getName() + "]", e );
}
finally {
Message.setDefaultLogger( originalMessageLogger );
}
}
/**
* Create an {@link Ivy} instance based on the running Gradle instance.
* <p/>
* Much of this is copied from {@link org.gradle.api.internal.artifacts.ivyservice.DefaultIvyService} because it
* unfortunately does not expose the needed information.
*
* @return The generated {@link Ivy} instance
*/
private Ivy getIvy() {
DefaultIvyService ivyService = unwrap(
( (DefaultConfigurationContainer) project.getConfigurations() ).getIvyService()
);
final IvyFactory ivyFactory = ivyService.getIvyFactory();
final SettingsConverter settingsConverter = ivyService.getSettingsConverter();
final ResolverProvider resolverProvider = ivyService.getResolverProvider();
// Ugh, can absolutely find no way to access this other than a bunch of recursive reflection calls to
// locate the build's org.gradle.api.internal.project.TopLevelBuildServiceRegistry and access its
// private org.gradle.api.internal.project.TopLevelBuildServiceRegistry.clientModuleRegistry field.
//
// Not sure if it is critical to reuse that map instance or not. seems to work without, but i have no
// idea about the ramifications.
Map<String, ModuleDescriptor> clientModuleRegistry = new HashMap<String, ModuleDescriptor>();
// this part is mainly DefaultIvyService#ivyForResolve
IvySettings ivySettings = settingsConverter.convertForResolve(
resolverProvider.getResolvers(),
project.getGradle().getGradleUserHomeDir(),
getGradeService( InternalRepository.class ),
clientModuleRegistry
);
return ivyFactory.createIvy( ivySettings );
}
private DefaultIvyService unwrap(IvyService ivyService) {
if ( DefaultIvyService.class.isInstance( ivyService ) ) {
return (DefaultIvyService) ivyService;
}
if ( ErrorHandlingIvyService.class.isInstance( ivyService ) ) {
return unwrap( ( (ErrorHandlingIvyService) ivyService ).getIvyService() );
}
if ( ShortcircuitEmptyConfigsIvyService.class.isInstance( ivyService ) ) {
return unwrap( ( (ShortcircuitEmptyConfigsIvyService) ivyService ).getIvyService() );
}
throw new BuildException(
"Do not know how to extract needed ivy config from ivy service of type " + ivyService.getClass()
.getName()
);
}
protected <T> T getGradeService(Class<T> type) {
return ( (DefaultGradle) project.getGradle() ).getServices().get( type );
}
private static class NoOpMessageLogger extends AbstractMessageLogger implements MessageLogger {
public void log(String s, int i) {
}
public void rawlog(String s, int i) {
}
public void sumupProblems() {
}
protected void doProgress() {
}
protected void doEndProgress(String s) {
}
}
}