View Javadoc

1   /**
2    * Copyright 2007 Google Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.tonicsystems.jarjar;
18  
19  import java.io.File;
20  import java.util.List;
21  import java.util.Set;
22  
23  import org.apache.maven.artifact.Artifact;
24  import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
25  import org.apache.maven.plugin.AbstractMojo;
26  import org.apache.maven.plugin.MojoExecutionException;
27  import org.apache.maven.project.MavenProject;
28  import org.apache.maven.shared.artifact.filter.StrictPatternExcludesArtifactFilter;
29  import org.apache.maven.shared.artifact.filter.StrictPatternIncludesArtifactFilter;
30  import org.codehaus.plexus.archiver.Archiver;
31  import org.codehaus.plexus.archiver.UnArchiver;
32  import org.codehaus.plexus.archiver.manager.ArchiverManager;
33  import org.codehaus.plexus.util.FileUtils;
34  
35  import com.tonicsystems.jarjar.util.StandaloneJarProcessor;
36  
37  /**
38   * Repackage dependencies and embed them into the final artifact.
39   * 
40   * @goal jarjar
41   * @phase package
42   * @requiresDependencyResolution test
43   */
44  public class JarJarMojo
45      extends AbstractMojo
46  {
47      /**
48       * Exclude the following META-INF files when jarjar'ing project dependencies.
49       */
50      private static final String[] META_INF_EXCLUDES = { "META-INF/MANIFEST.MF", //
51          "META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA" }; // signature files
52  
53      /**
54       * @parameter expression="${project}"
55       * @required
56       * @readonly
57       */
58      private MavenProject project;
59  
60      /**
61       * @parameter default-value="${project.build.directory}/jarjar"
62       * @required
63       */
64      private File workingDirectory;
65  
66      /**
67       * List of JarJar rules.
68       * 
69       * @parameter
70       * @required
71       */
72      private List<PatternElement> rules;
73  
74      /**
75       * Where to get the original classes.
76       * 
77       * @parameter
78       */
79      private String input;
80  
81      /**
82       * Where to put the JarJar'd classes.
83       * 
84       * @parameter
85       */
86      private String output;
87  
88      /**
89       * List of "groupId:artifactId" dependencies to include.
90       * 
91       * @parameter
92       */
93      private List<String> includes;
94  
95      /**
96       * List of "groupId:artifactId" dependencies to exclude.
97       * 
98       * @parameter
99       */
100     private List<String> excludes;
101 
102     /**
103      * When true, don't JarJar the manifest.
104      * 
105      * @parameter
106      */
107     private boolean skipManifest;
108 
109     /**
110      * When true, apply JarJar even if output already exists.
111      * 
112      * @parameter
113      */
114     private boolean overwrite;
115 
116     /**
117      * @component
118      */
119     private ArchiverManager archiverManager;
120 
121     @SuppressWarnings( "unchecked" )
122     public void execute()
123         throws MojoExecutionException
124     {
125         try
126         {
127             // VALIDATE INPUT / OUTPUT
128 
129             if ( null == input && null != project.getArtifact() )
130             {
131                 input = project.getArtifact().getFile().getAbsolutePath();
132             }
133             if ( "{classes}".equals( input ) )
134             {
135                 input = project.getBuild().getOutputDirectory();
136             }
137             if ( "{test-classes}".equals( input ) )
138             {
139                 input = project.getBuild().getTestOutputDirectory();
140             }
141             if ( null == input || !new File( input ).exists() )
142             {
143                 getLog().info( "Nothing to process" );
144                 return;
145             }
146 
147             if ( null == output )
148             {
149                 output = input;
150             }
151 
152             final File inputFile = new File( input );
153             final File outputFile = new File( output );
154 
155             final boolean inPlaceJarJar = inputFile.equals( outputFile );
156 
157             final File backupFile = new File( outputFile.getParentFile(), "original-" + outputFile.getName() );
158             if ( backupFile.isDirectory() && backupFile.list().length == 0 )
159             {
160                 backupFile.delete();
161             }
162             if ( !overwrite && ( inPlaceJarJar && backupFile.exists() || !inPlaceJarJar && outputFile.exists() ) )
163             {
164                 getLog().info( "Already processed" );
165                 return;
166             }
167 
168             // SETUP JARJAR
169 
170             final MainProcessor processor = new MainProcessor( rules, getLog().isDebugEnabled(), skipManifest );
171             final AndArtifactFilter filter = new AndArtifactFilter();
172             if ( null != includes )
173             {
174                 filter.add( new StrictPatternIncludesArtifactFilter( includes ) );
175             }
176             if ( null != excludes )
177             {
178                 filter.add( new StrictPatternExcludesArtifactFilter( excludes ) );
179             }
180 
181             // BUILD UBER-ZIP OF ARTIFACT + DEPENDENCIES
182 
183             getLog().info( "Processing: " + inputFile );
184 
185             final File uberZip = new File( workingDirectory, "uber-" + inputFile.getName() );
186             final Archiver archiver = archiverManager.getArchiver( "zip" );
187 
188             archiver.setDestFile( uberZip );
189 
190             if ( inputFile.isDirectory() )
191             {
192                 archiver.addDirectory( inputFile );
193             }
194             else
195             {
196                 archiver.addArchivedFileSet( inputFile );
197             }
198 
199             for ( final Artifact a : (Set<Artifact>) project.getArtifacts() )
200             {
201                 if ( filter.include( a ) )
202                 {
203                     try
204                     {
205                         archiver.addArchivedFileSet( a.getFile(), null, META_INF_EXCLUDES );
206                     }
207                     catch ( final Throwable e )
208                     {
209                         getLog().info( "Ignoring: " + a );
210                         getLog().debug( e );
211                     }
212                 }
213             }
214 
215             if ( !archiver.getResources().hasNext() )
216             {
217                 getLog().info( "Nothing to JarJar" );
218                 return;
219             }
220 
221             archiver.createArchive();
222 
223             // JARJAR UBER-ZIP
224 
225             getLog().info( "JarJar'ing to: " + outputFile );
226 
227             final File hullZip = new File( workingDirectory, "hull-" + inputFile.getName() );
228 
229             StandaloneJarProcessor.run( uberZip, hullZip, processor, true );
230             processor.strip( hullZip );
231 
232             final boolean toDirectory = outputFile.isDirectory() || !outputFile.exists() && inputFile.isDirectory();
233 
234             if ( inPlaceJarJar )
235             {
236                 try
237                 {
238                     getLog().info( "Original: " + backupFile );
239                     FileUtils.rename( outputFile, backupFile );
240                 }
241                 catch ( final Throwable e )
242                 {
243                     getLog().warn( e.toString() );
244                 }
245             }
246 
247             if ( toDirectory )
248             {
249                 outputFile.mkdirs();
250                 final UnArchiver unarchiver = archiverManager.getUnArchiver( "zip" );
251                 unarchiver.setDestDirectory( outputFile );
252                 unarchiver.setSourceFile( hullZip );
253                 unarchiver.extract();
254             }
255             else
256             {
257                 FileUtils.copyFile( hullZip, outputFile );
258             }
259         }
260         catch ( final Throwable e )
261         {
262             throw new MojoExecutionException( "Unable to JarJar: " + input + " cause: " + e.getMessage(), e );
263         }
264     }
265 }