package org.codehaus.mojo.minijar; /* * Copyright 2005 The Apache Software Foundation. * * 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. */ import java.io.File; import java.io.IOException; 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.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.project.MavenProject; import org.vafer.dependency.Clazzpath; import org.vafer.dependency.ClazzpathUnit; /** * Common class for the minijar mojos */ public abstract class AbstractPluginMojo extends AbstractMojo { /** * @parameter expression="${project}" * @required * @readonly */ private MavenProject project; /** * @parameter expression="${project.build.directory}" * @required * @readonly */ protected File buildDirectory; /** * If provided the default is to relocate no dependencies * but the ones specified. This parameter is mutual * exclusive to excludeDependenciesInRelocation. * * @parameter expression="${includeDependenciesInRelocation}" */ protected HashSet includeDependenciesInRelocation = null; /** * If provided the default is to relocate all dependencies * but exclude the ones specified. This parameter is mutual * exclusive to includeDependenciesInRelocation. * * @parameter expression="${excludeDependenciesInRelocation}" */ protected HashSet excludeDependenciesInRelocation = null; /** * If provided the default is to include no dependencies * but the ones specified. This parameter is mutual * exclusive to excludeDependencies. * * @parameter expression="${includeDependencies}" */ protected HashSet includeDependencies = null; /** * If provided the default is to include all dependencies * but remove the ones specified. This parameter is mutual * exclusive to includeDependencies. * * @parameter expression="${excludeDependencies}" */ protected HashSet excludeDependencies = null; /** * By default minijar will analyse the class dependencies and remove * classes that are not required for the execution of the project. * See the "keep.." parameters to explicitly override the behaviour * for classes or resource that only loaded via reflection or set * this parameter to false to turn off the magic. * * @parameter expression="${stripUnusedClasses}" default-value="true" */ protected boolean stripUnusedClasses; /** * Explicitly mark all classes from the specified artifacts to be * kept - no matter whether the analysis of minijar would suggest * to remove (some of) them. * * @parameter expression="${keepUnusedClassesFromArtifacts}" */ protected HashSet keepUnusedClassesFromArtifacts = new HashSet(); /** * Explicitly mark classes matching the given patterns to be * kept - no matter whether the analysis of minijar suggests * otherwise. * * @parameter expression="${keepUnusedClasses}" */ protected HashSet keepUnusedClasses = new HashSet(); protected MavenProject getProject() { if ( project.getExecutionProject() != null ) { return project.getExecutionProject(); } else { return project; } } /** * Substitute the variables in the given expression with the * values from the Map. * * @param pVariables * @param pExpression * @return */ protected String replaceVariables( final Map pVariables, final String pExpression ) { final char[] s = pExpression.toCharArray(); final char[] open = "[".toCharArray(); final char[] close = "]".toCharArray(); final StringBuffer out = new StringBuffer(); StringBuffer sb = new StringBuffer(); char[] watch = open; int w = 0; for (int i = 0; i < s.length; i++) { char c = s[i]; if ( c == watch[w] ) { w++; if (watch.length == w) { if(watch == open) { // found open out.append( sb ); sb = new StringBuffer(); watch = close; } else if ( watch == close ) { // found close final String variable = (String) pVariables.get( sb.toString() ); if ( variable != null ) { out.append( variable ); } else { getLog().error( "Unknown variable " + sb ); } sb = new StringBuffer(); watch = open; } w = 0; } } else { if (w > 0 ) { sb.append( watch, 0, w ); } sb.append( c ); w = 0; } } if ( watch == open ) { out.append( sb ); } return out.toString(); } public boolean isInKeepUnusedClassesFromArtifacts( final Artifact artifact ) { final String id = artifact.getGroupId() + ":" + artifact.getArtifactId(); return keepUnusedClassesFromArtifacts.contains(id); } public boolean isInKeepUnusedClasses( final String clazzname ) { // FIXME: do matching return keepUnusedClasses.contains(clazzname); } /** * Converts a Set of artifact id's into a Set of artifacts * @param artifacts * @param ids * @return null if no ids were given otherwise the Set of artifacts */ private Set getArtifactsFromIds( final Set artifacts, final Set ids ) { if ( ids == null ) { return null; } final Set result = new HashSet(); for ( Iterator it = artifacts.iterator(); it.hasNext(); ) { final Artifact artifact = (Artifact) it.next(); final String id = artifact.getGroupId() + ":" + artifact.getArtifactId(); if ( ids.contains( id ) ) { result.add( artifact ); } } return result; } private Set combine( final Set all, final Set included, final Set excluded, final Set defaults ) { final Set result = new HashSet(); if ( excluded != null ) { result.addAll(all); result.removeAll(excluded); } if ( included != null ) { result.addAll(included); } if ( excluded == null && included == null) { result.addAll(defaults); } return result; } public abstract void execute( final Set removable, final Set dependencies, final Set relocateDependencies ) throws MojoExecutionException; /** * Main entry point * @throws MojoExecutionException on error */ public void execute() throws MojoExecutionException { final Set removable = new HashSet(); /** START - THIS SHOULD NOT BE REQUIRED **/ if ( excludeDependencies != null && excludeDependencies.size() == 0 ) { excludeDependencies = null; } if ( includeDependencies != null && includeDependencies.size() == 0 ) { includeDependencies = null; } if ( excludeDependenciesInRelocation != null && excludeDependenciesInRelocation.size() == 0 ) { excludeDependenciesInRelocation = null; } if ( includeDependenciesInRelocation != null && includeDependenciesInRelocation.size() == 0 ) { includeDependenciesInRelocation = null; } /** STOP - THIS SHOULD NOT BE REQUIRED **/ if ( excludeDependencies != null && includeDependencies != null ) { throw new MojoExecutionException( "Both parameters excludeDependencies and includeDependencies are mutual exclusive" ); } if ( excludeDependenciesInRelocation != null && includeDependenciesInRelocation != null ) { throw new MojoExecutionException( "Both parameters excludeDependenciesInRelocation and includeDependenciesInRelocation are mutual exclusive" ); } final Set projectArtifacts = getProject().getArtifacts(); final Set dependencies = combine( projectArtifacts, getArtifactsFromIds( projectArtifacts, includeDependencies ), getArtifactsFromIds( projectArtifacts, excludeDependencies ), projectArtifacts ); final Set relocateDependencies = combine( projectArtifacts, getArtifactsFromIds( projectArtifacts, includeDependenciesInRelocation ), getArtifactsFromIds( projectArtifacts, excludeDependenciesInRelocation ), projectArtifacts ); getLog().debug( "dependencies: " + dependencies ); getLog().debug( "relocateDependencies: " + relocateDependencies ); if ( stripUnusedClasses ) { getLog().info( "Calculating transitive hull of class dependencies." ); try { final Artifact projectArtifact = getProject().getArtifact(); if ( projectArtifact == null ) { throw new MojoExecutionException( "No project artifact" ); } final File projectArtifactFile = projectArtifact.getFile(); if ( projectArtifactFile == null ) { throw new MojoExecutionException( "No project artifact file" ); } final Clazzpath clazzpath = new Clazzpath(); final ClazzpathUnit jar = new ClazzpathUnit( clazzpath, projectArtifactFile.getAbsolutePath() ); for ( Iterator i = project.getArtifacts().iterator(); i.hasNext(); ) { final Artifact dependency = (Artifact) i.next(); if ( !dependencies.contains( dependency ) ) { getLog().info( "Ignoring " + dependency ); continue; } new ClazzpathUnit( clazzpath, dependency.getFile().getAbsolutePath() ); } removable.addAll( clazzpath.getClazzes() ); final int total = removable.size(); removable.removeAll( jar.getClazzes() ); removable.removeAll( jar.getTransitiveDependencies() ); getLog().info( "Can remove " + removable.size() + " of " + total + " classes (" + (int) ( 100 * removable.size() / total ) + "%)." ); //getLog().debug( "Can remove " + removable ); } catch ( IOException e ) { throw new MojoExecutionException( "Could not analyse classpath dependencies", e ); } } execute( removable, dependencies, relocateDependencies ); } }