// Copyright FreeHEP, 2005-2006. package org.freehep.maven.jarjar; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.archiver.ArchiverException; import org.codehaus.plexus.archiver.UnArchiver; import org.codehaus.plexus.archiver.manager.ArchiverManager; import org.codehaus.plexus.archiver.manager.NoSuchArchiverException; /** * Unpacks jar dependencies and creates one jar file out of it. * @description Unpacks jar dependencies and creates one jar file out of it. * @goal jarjar * @phase process-resources * @requiresProject * @requiresDependencyResolution * @author <a href="Mark.Donszelmann@slac.stanford.edu">Mark Donszelmann</a> * @version $Id: JarJarMojo.java 8947 2006-09-12 18:16:26Z duns $ */ public class JarJarMojo extends AbstractMojo { /** * Add also all transitive dependencies * * @parameter expression="${jarjar.transitive}" default-value="false" */ protected boolean addTransitiveDependencies; /** * Sets the scope * * @parameter expression="${jarjar.scope}" default-value="runtime" */ protected String scope; /** * A list of inclusion filters in the format groupId:artifactId. * Defaults to all included. * * @parameter */ protected Set includes = null; /** * A list of exclusion filters in the format groupId:artifactId. * Defaults to none excluded. * * @parameter */ protected Set excludes = new HashSet(); /** * @parameter expression="${project}" * @readonly * @required */ protected MavenProject project; /** * @parameter expression="${localRepository}" * @required * @readonly */ protected ArtifactRepository localRepository; /** * To look up Archiver/UnArchiver implementations * * @parameter expression="${component.org.codehaus.plexus.archiver.manager.ArchiverManager}" * @required */ protected ArchiverManager archiverManager; /** * @parameter expression="${project.build.outputDirectory}" * @readonly */ protected File outputDirectory; private String metainfServices = "META-INF/services"; public void execute() throws MojoExecutionException, MojoFailureException { execute(false); } protected void execute(boolean showInfo) throws MojoExecutionException, MojoFailureException { if (!showInfo && !outputDirectory.exists()) outputDirectory.mkdirs(); if (showInfo) { getLog().info("Resulting jar contains the following dependencies for scope='"+scope+"'"); getLog().info(" A = added; N = not included; X = excluded; O = out of scope;"); } if (addTransitiveDependencies) { unpack(showInfo, project.getArtifacts()); } else { unpack(showInfo, project.getDependencyArtifacts()); } } private void unpack(boolean showInfo, Collection artifacts) throws MojoExecutionException, MojoFailureException { Map/*<String, Set<String>>*/ services = new HashMap(); mergeServices(services, outputDirectory); for (Iterator i=artifacts.iterator(); i.hasNext(); ) { Artifact dependency = (Artifact)i.next(); String id = dependency.getGroupId()+":"+dependency.getArtifactId(); if ((includes != null) && !includes.contains(id)) { if (showInfo) getLog().info("N "+id); continue; } if (excludes.contains(id)) { if (showInfo) getLog().info("X "+id); continue; } /* * Scope handling * * scope depScopes * COMPILE COMPILE, SYSTEM, PROVIDED * TEST all * RUNTIME COMPILE, RUNTIME * SYSTEM SYSTEM * PROVIDED PROVIDED */ String depScope = dependency.getScope(); if (scope.equals(Artifact.SCOPE_COMPILE)) { if (!depScope.equals(Artifact.SCOPE_COMPILE) && !depScope.equals(Artifact.SCOPE_SYSTEM) && !depScope.equals(Artifact.SCOPE_PROVIDED)) { if (showInfo) getLog().info("O " + id + " scope=" + depScope); continue; } } else if (scope.equals(Artifact.SCOPE_TEST)) { // always added } else if (scope.equals(Artifact.SCOPE_RUNTIME)) { if (!depScope.equals(Artifact.SCOPE_COMPILE) && !depScope.equals(Artifact.SCOPE_RUNTIME)) { if (showInfo) getLog().info("O " + id + " scope=" + depScope); continue; } } else if (scope.equals(Artifact.SCOPE_SYSTEM)) { if (!depScope.equals(Artifact.SCOPE_SYSTEM)) { if (showInfo) getLog().info("O " + id + " scope=" + depScope); continue; } } else if (scope.equals(Artifact.SCOPE_PROVIDED)) { if (!depScope.equals(Artifact.SCOPE_PROVIDED)) { if (showInfo) getLog().info("O " + id + " scope=" + depScope); continue; } } else { throw new MojoFailureException("Invalid requested scope '" + scope + "'"); } if (showInfo) { getLog().info("A "+id+" "+dependency.getVersion()); continue; } // FIXME reported to maven developer list, isSnapshot changes behaviour of getBaseVersion, called in pathOf. if (dependency.isSnapshot()); File file = new File(localRepository.getBasedir(), localRepository.pathOf(dependency)); unpack(file, outputDirectory); mergeServices(services, outputDirectory); } writeServices(services, outputDirectory); } private void unpack(File file, File location) throws MojoExecutionException { try { UnArchiver unArchiver; unArchiver = archiverManager.getUnArchiver("jar"); unArchiver.setSourceFile(file); unArchiver.setDestDirectory(location); unArchiver.extract(); } catch (IOException e) { throw new MojoExecutionException("Error unpacking file: "+file+" to: "+location, e); } catch (NoSuchArchiverException e) { throw new MojoExecutionException("Error unpacking file: "+file+" to: "+location, e); } catch (ArchiverException e) { throw new MojoExecutionException("Error unpacking file: "+file+" to: "+location, e); } } private void mergeServices(Map/*<String, Set<String>>*/ services, File location) throws MojoExecutionException { File servicesDirectory = new File(location, metainfServices); if (servicesDirectory.exists() && servicesDirectory.isDirectory()) { File[] files = servicesDirectory.listFiles(); for (int i=0; i<files.length; i++) { String fileName = files[i].getName(); Set/*<String>*/ classes = (Set)services.get(fileName); if (classes == null) { classes = new HashSet(); services.put(fileName, classes); System.err.println("Adding "+fileName); } try { BufferedReader reader = new BufferedReader(new FileReader(files[i])); String line = null; while ((line = reader.readLine()) != null) { classes.add(line); } reader.close(); } catch (IOException e) { throw new MojoExecutionException("Problem reading services file '"+fileName+"'", e); } } } } private void writeServices(Map/*<String, Set<String>>*/ services, File location) throws MojoExecutionException { File servicesDirectory = new File(location, metainfServices); if (!servicesDirectory.exists()) servicesDirectory.mkdirs(); for (Iterator i=services.keySet().iterator(); i.hasNext(); ) { String fileName = (String)i.next(); File file = new File(servicesDirectory, fileName); try { PrintWriter writer = new PrintWriter(new FileWriter(file)); Set/*<String>*/ classes = (Set)services.get(fileName); for (Iterator j=classes.iterator(); j.hasNext(); ) { String className = (String)j.next(); writer.println(className); } writer.close(); } catch (IOException e) { throw new MojoExecutionException("Problem writing services file '"+fileName+"'", e); } } } }