/******************************************************************************* * Copyright (c) 2010, 2014 Sonatype, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Sonatype, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.aether.internal.ant.tasks; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.ProjectComponent; import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.types.Reference; import org.apache.tools.ant.types.resources.FileResource; import org.apache.tools.ant.types.resources.Resources; import org.apache.tools.ant.util.FileUtils; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.graph.DependencyFilter; import org.eclipse.aether.graph.DependencyNode; import org.eclipse.aether.internal.ant.AntRepoSys; import org.eclipse.aether.internal.ant.Names; import org.eclipse.aether.internal.ant.types.Dependencies; import org.eclipse.aether.internal.ant.types.Pom; import org.eclipse.aether.resolution.ArtifactRequest; import org.eclipse.aether.resolution.ArtifactResolutionException; import org.eclipse.aether.resolution.ArtifactResult; import org.eclipse.aether.util.artifact.SubArtifact; import org.eclipse.aether.util.filter.ScopeDependencyFilter; /** */ public class Resolve extends AbstractResolvingTask { private List<ArtifactConsumer> consumers = new ArrayList<ArtifactConsumer>(); private boolean failOnMissingAttachments; public void setFailOnMissingAttachments( boolean failOnMissingAttachments ) { this.failOnMissingAttachments = failOnMissingAttachments; } public Path createPath() { Path path = new Path(); consumers.add( path ); return path; } public Files createFiles() { Files files = new Files(); consumers.add( files ); return files; } public Props createProperties() { Props props = new Props(); consumers.add( props ); return props; } private void validate() { for ( ArtifactConsumer consumer : consumers ) { consumer.validate(); } Pom pom = AntRepoSys.getInstance( getProject() ).getDefaultPom(); if ( dependencies == null && pom != null ) { log( "Using default pom for dependency resolution (" + pom.toString() + ")", Project.MSG_INFO ); dependencies = new Dependencies(); dependencies.setProject( getProject() ); getProject().addReference( Names.ID_DEFAULT_POM, pom ); dependencies.setPomRef( new Reference( getProject(), Names.ID_DEFAULT_POM ) ); } if ( dependencies != null ) { dependencies.validate( this ); } else { throw new BuildException( "No <dependencies> set for resolution" ); } } @Override public void execute() throws BuildException { validate(); AntRepoSys sys = AntRepoSys.getInstance( getProject() ); RepositorySystemSession session = sys.getSession( this, localRepository ); RepositorySystem system = sys.getSystem(); log( "Using local repository " + session.getLocalRepository(), Project.MSG_VERBOSE ); DependencyNode root = collectDependencies().getRoot(); root.accept( new DependencyGraphLogger( this ) ); Map<String, Group> groups = new HashMap<String, Group>(); for ( ArtifactConsumer consumer : consumers ) { String classifier = consumer.getClassifier(); Group group = groups.get( classifier ); if ( group == null ) { group = new Group( classifier ); groups.put( classifier, group ); } group.add( consumer ); } for ( Group group : groups.values() ) { group.createRequests( root ); } log( "Resolving artifacts", Project.MSG_INFO ); for ( Group group : groups.values() ) { List<ArtifactResult> results; try { results = system.resolveArtifacts( session, group.getRequests() ); } catch ( ArtifactResolutionException e ) { if ( !group.isAttachments() || failOnMissingAttachments ) { throw new BuildException( "Could not resolve artifacts: " + e.getMessage(), e ); } results = e.getResults(); for ( ArtifactResult result : results ) { if ( result.isMissing() ) { log( "Ignoring missing attachment " + result.getRequest().getArtifact(), Project.MSG_VERBOSE ); } else if ( !result.isResolved() ) { throw new BuildException( "Could not resolve artifacts: " + e.getMessage(), e ); } } } group.processResults( results, session ); } } public static abstract class ArtifactConsumer extends ProjectComponent { private DependencyFilter filter; public boolean accept( org.eclipse.aether.graph.DependencyNode node, List<DependencyNode> parents ) { return filter == null || filter.accept( node, parents ); } public String getClassifier() { return null; } public void validate() { } public abstract void process( Artifact artifact, RepositorySystemSession session ); public void setScopes( String scopes ) { if ( filter != null ) { throw new BuildException( "You must not specify both 'scopes' and 'classpath'" ); } Collection<String> included = new HashSet<String>(); Collection<String> excluded = new HashSet<String>(); String[] split = scopes.split( "[, ]" ); for ( String scope : split ) { scope = scope.trim(); Collection<String> dst; if ( scope.startsWith( "-" ) || scope.startsWith( "!" ) ) { dst = excluded; scope = scope.substring( 1 ); } else { dst = included; } if ( scope.length() > 0 ) { dst.add( scope ); } } filter = new ScopeDependencyFilter( included, excluded ); } public void setClasspath( String classpath ) { if ( "compile".equals( classpath ) ) { setScopes( "provided,system,compile" ); } else if ( "runtime".equals( classpath ) ) { setScopes( "compile,runtime" ); } else if ( "test".equals( classpath ) ) { setScopes( "provided,system,compile,runtime,test" ); } else { throw new BuildException( "The classpath '" + classpath + "' is not defined" + ", must be one of 'compile', 'runtime' or 'test'" ); } } } public class Path extends ArtifactConsumer { private String refid; private org.apache.tools.ant.types.Path path; public void setRefId( String refId ) { this.refid = refId; } public void validate() { if ( refid == null ) { throw new BuildException( "You must specify the 'refid' for the path" ); } } public void process( Artifact artifact, RepositorySystemSession session ) { if ( path == null ) { path = new org.apache.tools.ant.types.Path( getProject() ); getProject().addReference( refid, path ); } path.setLocation( artifact.getFile() ); } } public class Files extends ArtifactConsumer { private static final String DEFAULT_LAYOUT = Layout.GID_DIRS + "/" + Layout.AID + "/" + Layout.BVER + "/" + Layout.AID + "-" + Layout.VER + "-" + Layout.CLS + "." + Layout.EXT; private String refid; private String classifier; private File dir; private Layout layout; private FileSet fileset; private Resources resources; public void setRefId( String refId ) { this.refid = refId; } public String getClassifier() { return classifier; } public void setAttachments( String attachments ) { if ( "sources".equals( attachments ) ) { classifier = "*-sources"; } else if ( "javadoc".equals( attachments ) ) { classifier = "*-javadoc"; } else { throw new BuildException( "The attachment type '" + attachments + "' is not defined, must be one of 'sources' or 'javadoc'" ); } } public void setDir( File dir ) { this.dir = dir; if ( dir != null && layout == null ) { layout = new Layout( DEFAULT_LAYOUT ); } } public void setLayout( String layout ) { this.layout = new Layout( layout ); } public void validate() { if ( refid == null && dir == null ) { throw new BuildException( "You must either specify the 'refid' for the resource collection" + " or a 'dir' to copy the files to" ); } if ( dir == null && layout != null ) { throw new BuildException( "You must not specify a 'layout' unless 'dir' is also specified" ); } } public void process( Artifact artifact, RepositorySystemSession session ) { if ( dir != null ) { if ( refid != null && fileset == null ) { fileset = new FileSet(); fileset.setProject( getProject() ); fileset.setDir( dir ); getProject().addReference( refid, fileset ); } String path = layout.getPath( artifact ); if ( fileset != null ) { fileset.createInclude().setName( path ); } File src = artifact.getFile(); File dst = new File( dir, path ); if ( src.lastModified() != dst.lastModified() || src.length() != dst.length() ) { try { Resolve.this.log( "Copy " + src + " to " + dst, Project.MSG_VERBOSE ); FileUtils.getFileUtils().copyFile( src, dst, null, true, true ); } catch ( IOException e ) { throw new BuildException( "Failed to copy artifact file " + src + " to " + dst + ": " + e.getMessage(), e ); } } else { Resolve.this.log( "Omit to copy " + src + " to " + dst + ", seems unchanged", Project.MSG_VERBOSE ); } } else { if ( resources == null ) { resources = new Resources(); resources.setProject( getProject() ); getProject().addReference( refid, resources ); } FileResource resource = new FileResource( artifact.getFile() ); resource.setBaseDir( session.getLocalRepository().getBasedir() ); resource.setProject( getProject() ); resources.add( resource ); } } } public class Props extends ArtifactConsumer { private String prefix; private String classifier; public void setPrefix( String prefix ) { this.prefix = prefix; } public String getClassifier() { return classifier; } public void setAttachments( String attachments ) { if ( "sources".equals( attachments ) ) { classifier = "*-sources"; } else if ( "javadoc".equals( attachments ) ) { classifier = "*-javadoc"; } else { throw new BuildException( "The attachment type '" + attachments + "' is not defined, must be one of 'sources' or 'javadoc'" ); } } public void process( Artifact artifact, RepositorySystemSession session ) { StringBuilder buffer = new StringBuilder( 256 ); if ( prefix != null && prefix.length() > 0 ) { buffer.append( prefix ); if ( !prefix.endsWith( "." ) ) { buffer.append( '.' ); } } buffer.append( artifact.getGroupId() ); buffer.append( ':' ); buffer.append( artifact.getArtifactId() ); buffer.append( ':' ); buffer.append( artifact.getExtension() ); if ( artifact.getClassifier().length() > 0 ) { buffer.append( ':' ); buffer.append( artifact.getClassifier() ); } String path = artifact.getFile().getAbsolutePath(); getProject().setProperty( buffer.toString(), path ); } } private static class Group { private String classifier; private List<ArtifactConsumer> consumers = new ArrayList<ArtifactConsumer>(); private List<ArtifactRequest> requests = new ArrayList<ArtifactRequest>(); public Group( String classifier ) { this.classifier = classifier; } public boolean isAttachments() { return classifier != null; } public void add( ArtifactConsumer consumer ) { consumers.add( consumer ); } public void createRequests( DependencyNode node ) { createRequests( node, new LinkedList<DependencyNode>() ); } private void createRequests( DependencyNode node, LinkedList<DependencyNode> parents ) { if ( node.getDependency() != null ) { for ( ArtifactConsumer consumer : consumers ) { if ( consumer.accept( node, parents ) ) { ArtifactRequest request = new ArtifactRequest( node ); if ( classifier != null ) { request.setArtifact( new SubArtifact( request.getArtifact(), classifier, "jar" ) ); } requests.add( request ); break; } } } parents.addFirst( node ); for ( DependencyNode child : node.getChildren() ) { createRequests( child, parents ); } parents.removeFirst(); } public List<ArtifactRequest> getRequests() { return requests; } public void processResults( List<ArtifactResult> results, RepositorySystemSession session ) { for ( ArtifactResult result : results ) { if ( !result.isResolved() ) { continue; } for ( ArtifactConsumer consumer : consumers ) { if ( consumer.accept( result.getRequest().getDependencyNode(), Collections.<DependencyNode> emptyList() ) ) { consumer.process( result.getArtifact(), session ); } } } } } }