View Javadoc

1   package org.codehaus.mojo.minijar;
2   
3   /*
4    * Copyright 2005 The Apache Software Foundation.
5    * 
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    * 
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.io.File;
20  import java.io.IOException;
21  import java.util.HashSet;
22  import java.util.Iterator;
23  import java.util.Map;
24  import java.util.Set;
25  
26  import org.apache.maven.artifact.Artifact;
27  import org.apache.maven.plugin.AbstractMojo;
28  import org.apache.maven.plugin.MojoExecutionException;
29  import org.apache.maven.project.MavenProject;
30  import org.vafer.dependency.Clazzpath;
31  import org.vafer.dependency.ClazzpathUnit;
32  
33  /**
34   * Common class for the minijar mojos 
35   */
36  public abstract class AbstractPluginMojo
37      extends AbstractMojo
38  {
39      /**
40       * @parameter expression="${project}"
41       * @required
42       * @readonly
43       */
44      private MavenProject project;
45  
46      
47      
48      /**
49       * @parameter expression="${project.build.directory}"
50       * @required
51       * @readonly
52       */
53      protected File buildDirectory;
54  
55      
56      
57      /**
58       * If provided the default is to relocate no dependencies
59       * but the ones specified. This parameter is mutual
60       * exclusive to excludeDependenciesInRelocation.
61       * 
62       * @parameter expression="${includeDependenciesInRelocation}"
63       */
64      protected HashSet includeDependenciesInRelocation = null;
65  
66      /**
67       * If provided the default is to relocate all dependencies
68       * but exclude the ones specified. This parameter is mutual
69       * exclusive to includeDependenciesInRelocation.
70  	 *
71       * @parameter expression="${excludeDependenciesInRelocation}"
72       */
73      protected HashSet excludeDependenciesInRelocation = null;
74  
75  
76      
77      /**
78       * If provided the default is to include no dependencies
79       * but the ones specified. This parameter is mutual
80       * exclusive to excludeDependencies.
81       * 
82       * @parameter expression="${includeDependencies}"
83       */
84      protected HashSet includeDependencies = null;
85  
86      /**
87       * If provided the default is to include all dependencies
88       * but remove the ones specified. This parameter is mutual
89       * exclusive to includeDependencies.
90  	 *
91       * @parameter expression="${excludeDependencies}"
92       */
93      protected HashSet excludeDependencies = null;
94          
95      
96      
97      /**
98       * By default minijar will analyse the class dependencies and remove
99       * classes that are not required for the execution of the project.
100      * See the "keep.." parameters to explicitly override the behaviour
101      * for classes or resource that only loaded via reflection or set
102      * this parameter to false to turn off the magic.
103      * 
104      * @parameter expression="${stripUnusedClasses}" default-value="true"
105      */
106     protected boolean stripUnusedClasses;
107 
108     
109     /**
110      * Explicitly mark all classes from the specified artifacts to be
111      * kept - no matter whether the analysis of minijar would suggest
112      * to remove (some of) them.
113      *  
114      * @parameter expression="${keepUnusedClassesFromArtifacts}"
115      */
116     protected HashSet keepUnusedClassesFromArtifacts = new HashSet();
117     
118     /**
119      * Explicitly mark classes matching the given patterns to be
120      * kept - no matter whether the analysis of minijar suggests
121      * otherwise.
122      * 
123      * @parameter expression="${keepUnusedClasses}"
124      */
125     protected HashSet keepUnusedClasses = new HashSet();
126 
127     
128     protected MavenProject getProject()
129     {
130         if ( project.getExecutionProject() != null )
131         {
132             return project.getExecutionProject();
133         }
134         else
135         {
136             return project;
137         }
138     }
139     
140     /**
141      * Substitute the variables in the given expression with the
142      * values from the Map.
143      * 
144      * @param pVariables
145      * @param pExpression
146      * @return
147      */
148     protected String replaceVariables( final Map pVariables, final String pExpression )
149     {
150     	
151     	final char[] s = pExpression.toCharArray();
152     	
153     	final char[] open = "[".toCharArray();
154     	final char[] close = "]".toCharArray();
155     	
156     	final StringBuffer out = new StringBuffer();
157     	StringBuffer sb = new StringBuffer();
158     	char[] watch = open;
159     	int w = 0;
160     	for (int i = 0; i < s.length; i++)
161     	{
162 			char c = s[i];
163 			
164 			if ( c == watch[w] )
165 			{
166 				w++;
167 				if (watch.length == w)
168 				{					
169 					if(watch == open)
170 					{
171 						// found open
172 						out.append( sb );						
173 						sb = new StringBuffer();
174 						watch = close;
175 					}
176 					else if ( watch == close )
177 					{
178 						// found close
179 						final String variable = (String) pVariables.get( sb.toString() ); 
180 						if ( variable != null )
181 						{
182 							out.append( variable );
183 						}
184 						else
185 						{
186 							getLog().error( "Unknown variable " + sb );
187 						}
188 						sb = new StringBuffer();
189 						watch = open;
190 					}					
191 					w = 0;
192 				}
193 			}
194 			else
195 			{				
196 				if  (w > 0 )
197 				{
198 					sb.append( watch, 0, w );
199 				}
200 
201 				sb.append( c );
202 				
203 				w = 0;
204 			}
205 		}
206     	
207     	if ( watch == open )
208     	{
209     		out.append( sb );
210     	}
211 
212     	return out.toString();    	
213     }
214 
215 
216     public boolean isInKeepUnusedClassesFromArtifacts( final Artifact artifact ) {
217     	
218     	final String id = artifact.getGroupId() + ":" + artifact.getArtifactId();
219 
220     	return keepUnusedClassesFromArtifacts.contains(id);
221     }
222 
223     public boolean isInKeepUnusedClasses( final String clazzname ) {
224 
225     	// FIXME: do matching
226     	
227     	return keepUnusedClasses.contains(clazzname);
228     }
229 
230     /**
231      * Converts a Set of artifact id's into a Set of artifacts
232      * @param artifacts
233      * @param ids
234      * @return null if no ids were given otherwise the Set of artifacts
235      */
236     private Set getArtifactsFromIds( final Set artifacts, final Set ids )
237     {
238     	if ( ids == null )
239     	{
240     		return null;
241     	}
242     	
243     	final Set result = new HashSet();
244     	
245     	for ( Iterator it = artifacts.iterator(); it.hasNext(); )
246     	{
247 			final Artifact artifact = (Artifact) it.next();
248 			
249 			final String id = artifact.getGroupId() + ":" + artifact.getArtifactId();
250 			
251 			if ( ids.contains( id ) )
252 			{
253 				result.add( artifact );
254 			}
255 		}
256     	
257     	return result;
258     }
259     
260     
261     private Set combine( final Set all, final Set included, final Set excluded, final Set defaults )
262     {
263     	final Set result = new HashSet();
264     	
265     	if ( excluded != null )
266     	{
267     		result.addAll(all);
268     		result.removeAll(excluded);
269     	}
270 
271     	if ( included != null )
272     	{
273     		result.addAll(included);
274     	}
275 
276     	if ( excluded == null && included == null)
277     	{
278     		result.addAll(defaults);
279     	}
280     	
281     	return result;
282     }
283     
284     
285     public abstract void execute( final Set removable, final Set dependencies, final Set relocateDependencies )
286 		throws MojoExecutionException;
287 
288 
289     /**
290      * Main entry point
291      * @throws MojoExecutionException on error
292      */
293     public void execute()
294         throws MojoExecutionException
295     {
296         final Set removable = new HashSet();
297 
298         /** START - THIS SHOULD NOT BE REQUIRED **/
299         if ( excludeDependencies != null && excludeDependencies.size() == 0 )
300         {
301         	excludeDependencies = null;
302         }
303         if ( includeDependencies != null && includeDependencies.size() == 0 )
304         {
305         	includeDependencies = null;
306         }
307         if ( excludeDependenciesInRelocation != null && excludeDependenciesInRelocation.size() == 0 )
308         {
309         	excludeDependenciesInRelocation = null;
310         }
311         if ( includeDependenciesInRelocation != null && includeDependenciesInRelocation.size() == 0 )
312         {
313         	includeDependenciesInRelocation = null;
314         }
315         /** STOP - THIS SHOULD NOT BE REQUIRED **/
316         
317         if ( excludeDependencies != null && includeDependencies != null ) {
318         	throw new MojoExecutionException( "Both parameters excludeDependencies and includeDependencies are mutual exclusive" );
319         }
320 
321         if ( excludeDependenciesInRelocation != null && includeDependenciesInRelocation != null ) {
322         	throw new MojoExecutionException( "Both parameters excludeDependenciesInRelocation and includeDependenciesInRelocation are mutual exclusive" );
323         }
324 
325         final Set projectArtifacts = getProject().getArtifacts(); 
326         
327         final Set dependencies = combine(
328         		projectArtifacts,
329         		getArtifactsFromIds( projectArtifacts, includeDependencies ),
330         		getArtifactsFromIds( projectArtifacts, excludeDependencies ),
331         		projectArtifacts
332         		);
333         
334         final Set relocateDependencies = combine(
335         		projectArtifacts,
336         		getArtifactsFromIds( projectArtifacts, includeDependenciesInRelocation ),
337         		getArtifactsFromIds( projectArtifacts, excludeDependenciesInRelocation ),
338         		projectArtifacts
339         		);
340 
341         getLog().debug( "dependencies: " + dependencies );
342         getLog().debug( "relocateDependencies: " + relocateDependencies );
343         
344         if ( stripUnusedClasses )
345         {
346             getLog().info( "Calculating transitive hull of class dependencies." );
347 
348             try
349             {
350                 final Artifact projectArtifact = getProject().getArtifact();
351                 
352                 if ( projectArtifact == null )
353                 {
354                     throw new MojoExecutionException( "No project artifact" );
355                 }
356                 
357                 final File projectArtifactFile = projectArtifact.getFile();
358                 
359                 if ( projectArtifactFile == null )
360                 {
361                     throw new MojoExecutionException( "No project artifact file" );
362                 }
363                 
364             	final Clazzpath clazzpath = new Clazzpath();
365                 final ClazzpathUnit jar = new ClazzpathUnit( clazzpath, projectArtifactFile.getAbsolutePath() );
366             
367                 for ( Iterator i = project.getArtifacts().iterator(); i.hasNext(); )
368                 {
369                     final Artifact dependency = (Artifact) i.next();
370                     
371                     if ( !dependencies.contains( dependency ) )
372                     {
373                     	getLog().info( "Ignoring " + dependency );
374                     	continue;
375                     }                    
376 
377                     new ClazzpathUnit( clazzpath, dependency.getFile().getAbsolutePath() );                    	
378                 }
379             
380                 removable.addAll( clazzpath.getClazzes() );
381                 
382                 final int total = removable.size();
383                 
384                 removable.removeAll( jar.getClazzes() );
385                 removable.removeAll( jar.getTransitiveDependencies() );
386                 
387                 getLog().info( "Can remove " + removable.size() + " of " + total + " classes (" + (int) ( 100 * removable.size() / total ) + "%)." );                                
388 
389                 //getLog().debug( "Can remove " + removable );                                
390 
391             }
392             catch ( IOException e )
393             {
394                 throw new MojoExecutionException( "Could not analyse classpath dependencies", e );
395             }
396         }
397 
398         execute( removable, dependencies, relocateDependencies );
399     }
400 
401 }