1 package org.apache.maven.plugins.jarsigner;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.artifact.Artifact;
23 import org.apache.maven.execution.MavenSession;
24 import org.apache.maven.plugin.AbstractMojo;
25 import org.apache.maven.plugin.MojoExecutionException;
26 import org.apache.maven.plugins.annotations.Component;
27 import org.apache.maven.plugins.annotations.Parameter;
28 import org.apache.maven.project.MavenProject;
29 import org.apache.maven.settings.Settings;
30 import org.apache.maven.shared.jarsigner.JarSigner;
31 import org.apache.maven.shared.jarsigner.JarSignerRequest;
32 import org.apache.maven.shared.jarsigner.JarSignerUtil;
33 import org.apache.maven.shared.utils.StringUtils;
34 import org.apache.maven.shared.utils.cli.Commandline;
35 import org.apache.maven.shared.utils.cli.javatool.JavaToolException;
36 import org.apache.maven.shared.utils.cli.javatool.JavaToolResult;
37 import org.apache.maven.shared.utils.io.FileUtils;
38 import org.apache.maven.toolchain.Toolchain;
39 import org.apache.maven.toolchain.ToolchainManager;
40 import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
41 import org.sonatype.plexus.components.sec.dispatcher.SecDispatcherException;
42
43 import java.io.File;
44 import java.io.IOException;
45 import java.text.MessageFormat;
46 import java.util.ArrayList;
47 import java.util.Arrays;
48 import java.util.Collection;
49 import java.util.HashSet;
50 import java.util.List;
51 import java.util.ResourceBundle;
52 import org.apache.maven.shared.utils.ReaderFactory;
53
54
55
56
57
58
59
60 public abstract class AbstractJarsignerMojo
61 extends AbstractMojo
62 {
63
64
65
66
67 @Parameter( property = "jarsigner.verbose", defaultValue = "false" )
68 private boolean verbose;
69
70
71
72
73 @Parameter( property = "jarsigner.keystore" )
74 private String keystore;
75
76
77
78
79 @Parameter( property = "jarsigner.storetype" )
80 private String storetype;
81
82
83
84
85 @Parameter( property = "jarsigner.storepass" )
86 private String storepass;
87
88
89
90
91 @Parameter( property = "jarsigner.providerName" )
92 private String providerName;
93
94
95
96
97 @Parameter( property = "jarsigner.providerClass" )
98 private String providerClass;
99
100
101
102
103 @Parameter( property = "jarsigner.providerArg" )
104 private String providerArg;
105
106
107
108
109 @Parameter( property = "jarsigner.alias" )
110 private String alias;
111
112
113
114
115
116 @Parameter( property = "jarsigner.maxMemory" )
117 private String maxMemory;
118
119
120
121
122 @Parameter( property = "jarsigner.archive" )
123 private File archive;
124
125
126
127
128
129
130 @Parameter( property = "jarsigner.archiveDirectory" )
131 private File archiveDirectory;
132
133
134
135
136
137
138
139
140 @Parameter
141 private String[] includes = { "**/*.?ar" };
142
143
144
145
146
147
148
149 @Parameter
150 private String[] excludes = {};
151
152
153
154
155 @Parameter( property = "jarsigner.arguments" )
156 private String[] arguments;
157
158
159
160
161 @Parameter( property = "jarsigner.skip", defaultValue = "false" )
162 private boolean skip;
163
164
165
166
167
168
169 @Parameter( property = "jarsigner.processMainArtifact", defaultValue = "true" )
170 private boolean processMainArtifact;
171
172
173
174
175
176
177
178 @Parameter( property = "jarsigner.processAttachedArtifacts", defaultValue = "true" )
179 private boolean processAttachedArtifacts;
180
181
182
183
184
185
186
187 @Parameter( property = "jarsigner.protectedAuthenticationPath", defaultValue = "false" )
188 private boolean protectedAuthenticationPath;
189
190
191
192
193
194
195
196 @Parameter
197 private String[] includeClassifiers;
198
199
200
201
202
203
204
205 @Parameter
206 private String[] excludeClassifiers;
207
208
209
210
211 @Parameter( defaultValue = "${project}", readonly = true, required = true )
212 private MavenProject project;
213
214
215
216
217
218
219 @Parameter( defaultValue = "${settings}", readonly = true, required = true )
220 private Settings settings;
221
222
223
224
225
226
227 @Parameter( defaultValue = "${project.basedir}" )
228 private File workingDirectory;
229
230
231
232 @Component
233 private JarSigner jarSigner;
234
235
236
237
238
239
240
241 @Parameter( defaultValue = "${session}", readonly = true, required = true )
242 private MavenSession session;
243
244
245
246
247
248
249 @Component
250 private ToolchainManager toolchainManager;
251
252
253
254
255 @Component( hint = "mng-4384" )
256 private SecDispatcher securityDispatcher;
257
258 public final void execute()
259 throws MojoExecutionException
260 {
261 if ( !this.skip )
262 {
263 Toolchain toolchain = getToolchain();
264
265 if ( toolchain != null )
266 {
267 getLog().info( "Toolchain in maven-jarsigner-plugin: " + toolchain );
268 jarSigner.setToolchain( toolchain );
269 }
270
271 int processed = 0;
272
273 if ( this.archive != null )
274 {
275 processArchive( this.archive );
276 processed++;
277 }
278 else
279 {
280 if ( processMainArtifact )
281 {
282 processed += processArtifact( this.project.getArtifact() ) ? 1 : 0;
283 }
284
285 if ( processAttachedArtifacts )
286 {
287 Collection<String> includes = new HashSet<>();
288 if ( includeClassifiers != null )
289 {
290 includes.addAll( Arrays.asList( includeClassifiers ) );
291 }
292
293 Collection<String> excludes = new HashSet<>();
294 if ( excludeClassifiers != null )
295 {
296 excludes.addAll( Arrays.asList( excludeClassifiers ) );
297 }
298
299 for ( Artifact artifact : this.project.getAttachedArtifacts() )
300 {
301 if ( !includes.isEmpty() && !includes.contains( artifact.getClassifier() ) )
302 {
303 continue;
304 }
305
306 if ( excludes.contains( artifact.getClassifier() ) )
307 {
308 continue;
309 }
310
311 processed += processArtifact( artifact ) ? 1 : 0;
312 }
313 }
314 else
315 {
316 if ( verbose )
317 {
318 getLog().info( getMessage( "ignoringAttachments" ) );
319 }
320 else
321 {
322 getLog().debug( getMessage( "ignoringAttachments" ) );
323 }
324 }
325
326 if ( archiveDirectory != null )
327 {
328 String includeList = ( includes != null ) ? StringUtils.join( includes, "," ) : null;
329 String excludeList = ( excludes != null ) ? StringUtils.join( excludes, "," ) : null;
330
331 List<File> jarFiles;
332 try
333 {
334 jarFiles = FileUtils.getFiles( archiveDirectory, includeList, excludeList );
335 }
336 catch ( IOException e )
337 {
338 throw new MojoExecutionException( "Failed to scan archive directory for JARs: "
339 + e.getMessage(), e );
340 }
341
342 for ( File jarFile : jarFiles )
343 {
344 processArchive( jarFile );
345 processed++;
346 }
347 }
348 }
349
350 getLog().info( getMessage( "processed", processed ) );
351 }
352 else
353 {
354 getLog().info( getMessage( "disabled", null ) );
355 }
356 }
357
358
359
360
361
362
363
364
365
366 protected abstract JarSignerRequest createRequest( File archive )
367 throws MojoExecutionException;
368
369
370
371
372
373
374
375
376
377
378
379 protected String getCommandlineInfo( final Commandline commandLine )
380 {
381 if ( commandLine == null )
382 {
383 throw new NullPointerException( "commandLine" );
384 }
385
386 String commandLineInfo = commandLine.toString();
387 commandLineInfo = StringUtils.replace( commandLineInfo, this.storepass, "'*****'" );
388 return commandLineInfo;
389 }
390
391 public String getStoretype()
392 {
393 return storetype;
394 }
395
396 public String getStorepass()
397 {
398 return storepass;
399 }
400
401
402
403
404
405
406
407 private boolean isZipFile( final Artifact artifact )
408 {
409 return artifact != null && artifact.getFile() != null && JarSignerUtil.isZipFile( artifact.getFile() );
410 }
411
412
413
414
415
416
417
418
419
420 private boolean processArtifact( final Artifact artifact )
421 throws MojoExecutionException
422 {
423 if ( artifact == null )
424 {
425 throw new NullPointerException( "artifact" );
426 }
427
428 boolean processed = false;
429
430 if ( isZipFile( artifact ) )
431 {
432 processArchive( artifact.getFile() );
433
434 processed = true;
435 }
436 else
437 {
438 if ( this.verbose )
439 {
440 getLog().info( getMessage( "unsupported", artifact ) );
441 }
442 else if ( getLog().isDebugEnabled() )
443 {
444 getLog().debug( getMessage( "unsupported", artifact ) );
445 }
446 }
447
448 return processed;
449 }
450
451
452
453
454
455
456
457 protected void preProcessArchive( final File archive )
458 throws MojoExecutionException
459 {
460
461 }
462
463
464
465
466
467
468
469
470 private void processArchive( final File archive )
471 throws MojoExecutionException
472 {
473 if ( archive == null )
474 {
475 throw new NullPointerException( "archive" );
476 }
477
478 preProcessArchive( archive );
479
480 if ( this.verbose )
481 {
482 getLog().info( getMessage( "processing", archive ) );
483 }
484 else if ( getLog().isDebugEnabled() )
485 {
486 getLog().debug( getMessage( "processing", archive ) );
487 }
488
489 JarSignerRequest request = createRequest( archive );
490 request.setVerbose( verbose );
491 request.setAlias( alias );
492 request.setArchive( archive );
493 request.setKeystore( keystore );
494 request.setStoretype( storetype );
495 request.setProviderArg( providerArg );
496 request.setProviderClass( providerClass );
497 request.setProviderName( providerName );
498 request.setWorkingDirectory( workingDirectory );
499 request.setMaxMemory( maxMemory );
500 request.setProtectedAuthenticationPath( protectedAuthenticationPath );
501
502
503 final List<String> additionalArguments = new ArrayList<>();
504
505 boolean fileEncodingSeen = false;
506
507 if ( this.arguments != null )
508 {
509 for ( final String argument : this.arguments )
510 {
511 if ( argument.trim().startsWith( "-J-Dfile.encoding=" ) )
512 {
513 fileEncodingSeen = true;
514 }
515
516 additionalArguments.add( argument );
517 }
518 }
519
520 if ( !fileEncodingSeen )
521 {
522 additionalArguments.add( "-J-Dfile.encoding=" + ReaderFactory.FILE_ENCODING );
523 }
524
525
526 if ( this.settings != null && this.settings.getActiveProxy() != null
527 && StringUtils.isNotEmpty( this.settings.getActiveProxy().getHost() ) )
528 {
529 additionalArguments.add( "-J-Dhttp.proxyHost=" + this.settings.getActiveProxy().getHost() );
530 additionalArguments.add( "-J-Dhttps.proxyHost=" + this.settings.getActiveProxy().getHost() );
531 additionalArguments.add( "-J-Dftp.proxyHost=" + this.settings.getActiveProxy().getHost() );
532
533 if ( this.settings.getActiveProxy().getPort() > 0 )
534 {
535 additionalArguments.add( "-J-Dhttp.proxyPort=" + this.settings.getActiveProxy().getPort() );
536 additionalArguments.add( "-J-Dhttps.proxyPort=" + this.settings.getActiveProxy().getPort() );
537 additionalArguments.add( "-J-Dftp.proxyPort=" + this.settings.getActiveProxy().getPort() );
538 }
539
540 if ( StringUtils.isNotEmpty( this.settings.getActiveProxy().getNonProxyHosts() ) )
541 {
542 additionalArguments.add( "-J-Dhttp.nonProxyHosts=\""
543 + this.settings.getActiveProxy().getNonProxyHosts() + "\"" );
544
545 additionalArguments.add( "-J-Dftp.nonProxyHosts=\""
546 + this.settings.getActiveProxy().getNonProxyHosts() + "\"" );
547
548 }
549 }
550
551 request.setArguments( !additionalArguments.isEmpty()
552 ? additionalArguments.toArray( new String[ additionalArguments.size() ] )
553 : null );
554
555
556 request.setStorepass( decrypt( storepass ) );
557
558 try
559 {
560 JavaToolResult result = jarSigner.execute( request );
561
562 Commandline commandLine = result.getCommandline();
563
564 int resultCode = result.getExitCode();
565
566 if ( resultCode != 0 )
567 {
568
569 throw new MojoExecutionException( getMessage( "failure", getCommandlineInfo( commandLine ), resultCode ) );
570
571 }
572
573 }
574 catch ( JavaToolException e )
575 {
576 throw new MojoExecutionException( getMessage( "commandLineException", e.getMessage() ), e );
577 }
578 }
579
580 protected String decrypt( String encoded )
581 throws MojoExecutionException
582 {
583 try
584 {
585 return securityDispatcher.decrypt( encoded );
586 }
587 catch ( SecDispatcherException e )
588 {
589 getLog().error( "error using security dispatcher: " + e.getMessage(), e );
590 throw new MojoExecutionException( "error using security dispatcher: " + e.getMessage(), e );
591 }
592 }
593
594
595
596
597
598
599
600
601
602
603
604
605 private String getMessage( final String key, final Object[] args )
606 {
607 if ( key == null )
608 {
609 throw new NullPointerException( "key" );
610 }
611
612 return new MessageFormat( ResourceBundle.getBundle( "jarsigner" ).getString( key ) ).format( args );
613 }
614
615 private String getMessage( final String key )
616 {
617 return getMessage( key, null );
618 }
619
620 String getMessage( final String key, final Object arg )
621 {
622 return getMessage( key, new Object[] { arg } );
623 }
624
625 private String getMessage( final String key, final Object arg1, final Object arg2 )
626 {
627 return getMessage( key, new Object[] { arg1, arg2 } );
628 }
629
630
631
632
633
634
635
636
637 private Toolchain getToolchain()
638 {
639 Toolchain tc = null;
640 if ( toolchainManager != null )
641 {
642 tc = toolchainManager.getToolchainFromBuildContext( "jdk", session );
643 }
644
645 return tc;
646 }
647 }