Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
JarSignVerifyMojo |
|
| 2.1052631578947367;2.105 | ||||
JarSignVerifyMojo$1 |
|
| 2.1052631578947367;2.105 | ||||
JarSignVerifyMojo$LineMatcherStreamConsumer |
|
| 2.1052631578947367;2.105 |
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 | 17 | 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 | 17 | 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 | 7 | if ( jarPath != null ) |
120 | { | |
121 | 7 | return jarPath; |
122 | } | |
123 | else | |
124 | { | |
125 | 0 | return AbstractJarMojo.getJarFile( basedir, finalName, null); |
126 | } | |
127 | } | |
128 | ||
129 | public void execute() | |
130 | throws MojoExecutionException | |
131 | { | |
132 | 7 | List arguments = new ArrayList(); |
133 | ||
134 | 7 | Commandline commandLine = new Commandline(); |
135 | ||
136 | 7 | commandLine.setExecutable( getJarsignerPath() ); |
137 | ||
138 | 7 | arguments.add( "-verify" ); |
139 | ||
140 | 7 | addArgIf( arguments, this.verbose, "-verbose" ); |
141 | 7 | addArgIf( arguments, this.checkCerts, "-certs" ); |
142 | ||
143 | 7 | arguments.add( getJarFile() ); |
144 | ||
145 | 7 | for ( Iterator it = arguments.iterator() ; it.hasNext() ; ) |
146 | { | |
147 | 16 | commandLine.createArgument().setValue( it.next().toString() ); |
148 | } | |
149 | ||
150 | 7 | commandLine.setWorkingDirectory( workingDirectory.getAbsolutePath() ); |
151 | ||
152 | 7 | getLog().debug("Executing: " + commandLine ); |
153 | ||
154 | 7 | LineMatcherStreamConsumer outConsumer = new LineMatcherStreamConsumer( "jar verified." ); |
155 | ||
156 | 7 | final StringBuffer errBuffer = new StringBuffer(); |
157 | 7 | StreamConsumer errConsumer = new StreamConsumer() |
158 | { | |
159 | 7 | public void consumeLine(String line) |
160 | { | |
161 | 0 | errBuffer.append( line ); |
162 | 0 | getLog().warn( line ); |
163 | 0 | } |
164 | }; | |
165 | ||
166 | ||
167 | try | |
168 | { | |
169 | 7 | int result = executeCommandLine( commandLine, null, outConsumer, errConsumer ); |
170 | ||
171 | 6 | if ( result != 0 ) |
172 | { | |
173 | 1 | throw new MojoExecutionException("Result of " + commandLine |
174 | + " execution is: \'" + result + "\'." ); | |
175 | } | |
176 | ||
177 | 5 | signed = outConsumer.matched; |
178 | ||
179 | 5 | if ( !signed && errorWhenNotSigned ) |
180 | { | |
181 | 1 | throw new MojoExecutionException( "Verify failed: " + outConsumer.firstOutLine ); |
182 | } | |
183 | } | |
184 | 1 | catch ( CommandLineException e ) |
185 | { | |
186 | 1 | throw new MojoExecutionException( "command execution failed", e ); |
187 | 4 | } |
188 | 4 | } |
189 | ||
190 | // checks if a consumed line matches | |
191 | // also keeps track of the first consumed line. | |
192 | 17 | class LineMatcherStreamConsumer |
193 | implements StreamConsumer | |
194 | { | |
195 | private String toMatch; | |
196 | private boolean matched; | |
197 | private String firstOutLine; | |
198 | ||
199 | LineMatcherStreamConsumer( String toMatch ) | |
200 | 7 | { |
201 | 7 | this.toMatch = toMatch; |
202 | 7 | } |
203 | ||
204 | public void consumeLine(String line) | |
205 | { | |
206 | 5 | if ( firstOutLine == null ) |
207 | { | |
208 | 5 | firstOutLine = line; |
209 | } | |
210 | 5 | matched = matched || toMatch.equals( line ); |
211 | ||
212 | 5 | getLog().info( line ); |
213 | 5 | } |
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 | 7 | return getJDKCommandPath( "jarsigner", getLog() ); |
227 | } | |
228 | ||
229 | private static String getJDKCommandPath( String command, Log logger ) | |
230 | { | |
231 | 7 | String path = getJDKCommandExe(command).getAbsolutePath(); |
232 | 7 | logger.debug( command + " executable=[" + path + "]" ); |
233 | 7 | return path; |
234 | } | |
235 | ||
236 | private static File getJDKCommandExe( String command ) | |
237 | { | |
238 | 7 | String fullCommand = command + ( SystemUtils.IS_OS_WINDOWS ? ".exe" : "" ); |
239 | ||
240 | File exe; | |
241 | ||
242 | // For IBM's JDK 1.2 | |
243 | 7 | if ( SystemUtils.IS_OS_AIX ) |
244 | { | |
245 | 0 | exe = new File( SystemUtils.getJavaHome() + "/../sh", fullCommand ); |
246 | } | |
247 | 7 | else if ( SystemUtils.IS_OS_MAC_OSX ) |
248 | { | |
249 | 0 | exe = new File( SystemUtils.getJavaHome() + "/bin", fullCommand ); |
250 | } | |
251 | else | |
252 | { | |
253 | 7 | exe = new File( SystemUtils.getJavaHome() + "/../bin", fullCommand ); |
254 | } | |
255 | ||
256 | 7 | 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 | 14 | if ( b ) |
273 | { | |
274 | 2 | arguments.add( value ); |
275 | } | |
276 | 14 | } |
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 | 0 | addArgIfNotEmpty( arguments, key, value, false ); |
292 | 0 | } |
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 | 0 | if ( !StringUtils.isEmpty( value ) ) |
308 | { | |
309 | 0 | arguments.add( key ); |
310 | ||
311 | 0 | StringTokenizer token = new StringTokenizer( value, "," ); |
312 | 0 | while ( token.hasMoreTokens() ) |
313 | { | |
314 | 0 | String current = token.nextToken().trim(); |
315 | ||
316 | 0 | if ( !StringUtils.isEmpty( current ) ) |
317 | { | |
318 | 0 | arguments.add( current ); |
319 | ||
320 | 0 | if ( token.hasMoreTokens() && repeatKey ) |
321 | { | |
322 | 0 | arguments.add( key ); |
323 | } | |
324 | } | |
325 | 0 | } |
326 | } | |
327 | 0 | } |
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 | 0 | return CommandLineUtils.executeCommandLine( commandLine, inputStream, systemOut, systemErr ); |
338 | } | |
339 | ||
340 | public void setWorkingDir( File workingDir ) | |
341 | { | |
342 | 13 | this.workingDirectory = workingDir; |
343 | 13 | } |
344 | ||
345 | public void setBasedir( File basedir ) | |
346 | { | |
347 | 13 | this.basedir = basedir; |
348 | 13 | } |
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 | 13 | this.jarPath = jarPath; |
361 | 13 | } |
362 | ||
363 | public void setCheckCerts( boolean checkCerts ) | |
364 | { | |
365 | 1 | this.checkCerts = checkCerts; |
366 | 1 | } |
367 | ||
368 | public void setVerbose( boolean verbose ) | |
369 | { | |
370 | 8 | this.verbose = verbose; |
371 | 8 | } |
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 | 6 | 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 | 2 | this.errorWhenNotSigned = errorWhenNotSigned; |
392 | 2 | } |
393 | } |