View Javadoc

1   package org.apache.maven.plugin.jar;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.InputStream;
24  import java.util.ArrayList;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.StringTokenizer;
28  
29  import org.apache.commons.lang.SystemUtils;
30  import org.apache.maven.plugin.AbstractMojo;
31  import org.apache.maven.plugin.MojoExecutionException;
32  import org.apache.maven.plugin.logging.Log;
33  import org.codehaus.plexus.util.StringUtils;
34  import org.codehaus.plexus.util.cli.CommandLineException;
35  import org.codehaus.plexus.util.cli.CommandLineUtils;
36  import org.codehaus.plexus.util.cli.Commandline;
37  import org.codehaus.plexus.util.cli.StreamConsumer;
38  
39  /**
40   * Checks the signature of a signed jar using jarsigner.
41   *
42   * @author <a href="jerome@coffeebreaks.org">Jerome Lacoste</a>
43   * @version $Id: JarSignVerifyMojo.java 802529 2009-08-09 12:05:35Z bentmann $
44   * @goal sign-verify
45   * @phase package
46   * @requiresProject
47   * @todo refactor the common code with javadoc plugin
48   * @requiresDependencyResolution runtime
49   * @deprecated As of version 2.3, this goal is no longer supported in favor of the dedicated maven-jarsigner-plugin.
50   */
51  public class JarSignVerifyMojo
52      extends AbstractMojo
53  {
54      /**
55       * The working directory in which the jarsigner executable will be run.
56       *
57       * @parameter expression="${workingdir}" default-value="${basedir}"
58       * @required
59       */
60      private File workingDirectory;
61  
62      /**
63       * Directory containing the generated JAR.
64       *
65       * @parameter expression="${project.build.directory}"
66       * @required
67       * @readonly
68       */
69      private File basedir;
70  
71      /**
72       * Name of the generated JAR (without classifier and extension).
73       *
74       * @parameter alias="jarname" expression="${project.build.finalName}"
75       * @required
76       */
77      private String finalName;
78  
79      /**
80       * Path of the signed jar. When specified, the finalName is ignored.
81       *
82       * @parameter expression="${jarpath}"
83       */
84      private File jarPath;
85  
86      /**
87       * Check certificates. Requires {@link #setVerbose(boolean)}.
88       * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/jarsigner.html#Options">options</a>.
89       *
90       * @parameter expression="${checkcerts}" default-value="false"
91       */
92      private boolean checkCerts;
93  
94      /**
95       * Enable verbose
96       * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/jarsigner.html#Options">options</a>.
97       *
98       * @parameter expression="${verbose}" default-value="false"
99       */
100     private boolean verbose;
101 
102     /** When <code>true</code> this will make the execute() operation fail,
103      * throwing an exception, when verifying a non signed jar.
104      * Primarily to keep backwards compatibility with existing code, and allow reusing the
105      * bean in unattended operations when set to <code>false</code>.
106      *
107      * @parameter expression="${errorWhenNotSigned}" default-value="true"
108      **/
109     private boolean errorWhenNotSigned = true;
110 
111     /**
112      * Is the jar signed ? output property set by the execute call. The value will be accessible
113      * when execute() ends and if errorWhenNotSigned has been set to false.
114      **/
115     private boolean signed;
116 
117     File getJarFile()
118     {
119         if ( jarPath != null )
120         {
121             return jarPath;
122         }
123         else
124         {
125             return AbstractJarMojo.getJarFile( basedir, finalName, null);
126         }
127     }
128 
129     public void execute()
130         throws MojoExecutionException
131     {
132         List arguments = new ArrayList();
133 
134         Commandline commandLine = new Commandline();
135 
136         commandLine.setExecutable( getJarsignerPath() );
137 
138         arguments.add( "-verify" );
139 
140         addArgIf( arguments, this.verbose, "-verbose" );
141         addArgIf( arguments, this.checkCerts, "-certs" );
142 
143         arguments.add( getJarFile() );
144 
145         for ( Iterator it = arguments.iterator() ; it.hasNext() ; )
146         {
147             commandLine.createArgument().setValue( it.next().toString() );
148         }
149 
150         commandLine.setWorkingDirectory( workingDirectory.getAbsolutePath() );
151 
152         getLog().debug("Executing: " + commandLine );
153 
154         LineMatcherStreamConsumer outConsumer = new LineMatcherStreamConsumer( "jar verified." );
155 
156         final StringBuffer errBuffer = new StringBuffer();
157         StreamConsumer errConsumer = new StreamConsumer()
158         {
159             public void consumeLine(String line)
160             {
161                  errBuffer.append( line );
162                  getLog().warn( line );
163             }
164         };
165 
166 
167         try
168         {
169             int result = executeCommandLine( commandLine, null, outConsumer, errConsumer );
170 
171             if ( result != 0 )
172             {
173                 throw new MojoExecutionException("Result of " + commandLine
174                     + " execution is: \'" + result + "\'." );
175             }
176 
177             signed = outConsumer.matched;
178 
179             if ( !signed && errorWhenNotSigned )
180             {
181                 throw new MojoExecutionException( "Verify failed: " + outConsumer.firstOutLine );
182             }
183         }
184         catch ( CommandLineException e )
185         {
186             throw new MojoExecutionException( "command execution failed", e );
187         }
188     }
189 
190     // checks if a consumed line matches
191     // also keeps track of the first consumed line.
192     class LineMatcherStreamConsumer
193         implements StreamConsumer
194     {
195         private String toMatch;
196         private boolean matched;
197         private String firstOutLine;
198 
199         LineMatcherStreamConsumer( String toMatch )
200         {
201              this.toMatch = toMatch;
202         }
203 
204         public void consumeLine(String line)
205         {
206             if ( firstOutLine == null )
207             {
208                  firstOutLine = line;
209             }
210             matched = matched || toMatch.equals( line );
211 
212             getLog().info( line );
213         }
214     }
215 
216   // taken from JavadocReport then slightly refactored
217     // should probably share with other plugins that use $JAVA_HOME/bin tools
218 
219     /**
220      * Get the path of jarsigner tool depending the OS.
221      *
222      * @return the path of the jarsigner tool
223      */
224     private String getJarsignerPath()
225     {
226         return getJDKCommandPath( "jarsigner", getLog() );
227     }
228 
229     private static String getJDKCommandPath( String command, Log logger )
230     {
231         String path = getJDKCommandExe(command).getAbsolutePath();
232         logger.debug( command + " executable=[" + path + "]" );
233         return path;
234     }
235 
236     private static File getJDKCommandExe( String command )
237     {
238         String fullCommand = command + ( SystemUtils.IS_OS_WINDOWS ? ".exe" : "" );
239 
240         File exe;
241 
242         // For IBM's JDK 1.2
243         if ( SystemUtils.IS_OS_AIX )
244         {
245             exe = new File( SystemUtils.getJavaHome() + "/../sh", fullCommand );
246         }
247         else if ( SystemUtils.IS_OS_MAC_OSX )
248         {
249             exe = new File( SystemUtils.getJavaHome() + "/bin", fullCommand );
250         }
251         else
252         {
253             exe = new File( SystemUtils.getJavaHome() + "/../bin", fullCommand );
254         }
255 
256         return exe;
257     }
258 
259 
260     // Helper methods. Could/should be shared e.g. with JavadocReport
261 
262     /**
263      * Convenience method to add an argument to the <code>command line</code>
264      * conditionally based on the given flag.
265      *
266      * @param arguments
267      * @param b the flag which controls if the argument is added or not.
268      * @param value the argument value to be added.
269      */
270     private void addArgIf( List arguments, boolean b, String value )
271     {
272         if ( b )
273         {
274             arguments.add( value );
275         }
276     }
277 
278     /**
279      * Convenience method to add an argument to the <code>command line</code>
280      * if the the value is not null or empty.
281      * <p>
282      * Moreover, the value could be comma separated.
283      *
284      * @param arguments
285      * @param key the argument name.
286      * @param value the argument value to be added.
287      * @see #addArgIfNotEmpty(java.util.List,String,String,boolean)
288      */
289     private void addArgIfNotEmpty( List arguments, String key, String value )
290     {
291         addArgIfNotEmpty( arguments, key, value, false );
292     }
293 
294     /**
295      * Convenience method to add an argument to the <code>command line</code>
296      * if the the value is not null or empty.
297      * <p>
298      * Moreover, the value could be comma separated.
299      *
300      * @param arguments
301      * @param key the argument name.
302      * @param value the argument value to be added.
303      * @param repeatKey repeat or not the key in the command line
304      */
305     private void addArgIfNotEmpty( List arguments, String key, String value, boolean repeatKey )
306     {
307         if ( !StringUtils.isEmpty( value ) )
308         {
309             arguments.add( key );
310 
311             StringTokenizer token = new StringTokenizer( value, "," );
312             while ( token.hasMoreTokens() )
313             {
314                 String current = token.nextToken().trim();
315 
316                 if ( !StringUtils.isEmpty( current ) )
317                 {
318                     arguments.add( current );
319 
320                     if ( token.hasMoreTokens() && repeatKey )
321                     {
322                         arguments.add( key );
323                     }
324                 }
325             }
326         }
327     }
328 
329     //
330     // methods used for tests purposes - allow mocking and simulate automatic setters
331     //
332 
333     protected int executeCommandLine( Commandline commandLine, InputStream inputStream,
334                                       StreamConsumer systemOut, StreamConsumer systemErr )
335         throws CommandLineException
336     {
337         return CommandLineUtils.executeCommandLine( commandLine, inputStream, systemOut, systemErr );
338     }
339 
340     public void setWorkingDir( File workingDir )
341     {
342         this.workingDirectory = workingDir;
343     }
344 
345     public void setBasedir( File basedir )
346     {
347         this.basedir = basedir;
348     }
349 
350     // hiding for now - I don't think this is required to be seen
351     /*
352     public void setFinalName( String finalName )
353     {
354         this.finalName = finalName;
355     }
356     */
357 
358     public void setJarPath( File jarPath )
359     {
360         this.jarPath = jarPath;
361     }
362 
363     public void setCheckCerts( boolean checkCerts )
364     {
365         this.checkCerts = checkCerts;
366     }
367 
368     public void setVerbose( boolean verbose )
369     {
370         this.verbose = verbose;
371     }
372 
373     /**
374      * Is the JAR file signed ? Output property set by the {@link #execute()} call.
375      *
376      * @return <code>true</code> if the jar was signed, <code>false</code> otherwise.
377      */
378     public boolean isSigned()
379     {
380         return signed;
381     }
382 
383     /**
384      * Sets a boolean that is to determine if an exception should be thrown when
385      * the JAR file being verified is unsigned. If you just what to check if a
386      * JAR is unsigned and then act on the result, then you probably want to
387      * set this to <code>true</code>.
388      */
389     public void setErrorWhenNotSigned( boolean errorWhenNotSigned )
390     {
391         this.errorWhenNotSigned = errorWhenNotSigned;
392     }
393 }