1 package org.codehaus.mojo.webstart;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.commons.collections.CollectionUtils;
23 import org.apache.commons.lang.StringUtils;
24 import org.apache.maven.artifact.Artifact;
25 import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
26 import org.apache.maven.artifact.resolver.filter.InversionArtifactFilter;
27 import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
28 import org.apache.maven.artifact.resolver.filter.TypeArtifactFilter;
29 import org.apache.maven.plugin.MojoExecutionException;
30 import org.apache.maven.plugin.MojoFailureException;
31 import org.apache.maven.plugins.annotations.Component;
32 import org.apache.maven.plugins.annotations.Mojo;
33 import org.apache.maven.plugins.annotations.Parameter;
34 import org.apache.maven.plugins.annotations.ResolutionScope;
35 import org.apache.maven.project.MavenProject;
36 import org.codehaus.mojo.webstart.generator.GeneratorTechnicalConfig;
37 import org.codehaus.mojo.webstart.generator.JarResourceGeneratorConfig;
38 import org.codehaus.mojo.webstart.generator.JarResourcesGenerator;
39 import org.codehaus.mojo.webstart.generator.VersionXmlGenerator;
40 import org.codehaus.mojo.webstart.util.ArtifactUtil;
41 import org.codehaus.mojo.webstart.util.IOUtil;
42
43 import java.io.File;
44 import java.util.Collection;
45 import java.util.Collections;
46 import java.util.LinkedHashSet;
47 import java.util.List;
48 import java.util.Set;
49
50
51
52
53
54
55
56
57
58 @Mojo( name = "jnlp-download-servlet", requiresProject = true, inheritByDefault = true,
59 requiresDependencyResolution = ResolutionScope.RUNTIME )
60 public class JnlpDownloadServletMojo
61 extends AbstractBaseJnlpMojo
62 {
63
64
65
66
67
68
69
70
71 private static final String BUILT_IN_SERVLET_TEMPLATE_FILENAME = "default-jnlp-servlet-template.vm";
72
73
74
75
76 private static final String SERVLET_TEMPLATE_FILENAME = "servlet-template.vm";
77
78
79
80
81
82
83
84
85
86
87 @Parameter( property = "jnlp.outputDirectoryName", defaultValue = "webstart" )
88 private String outputDirectoryName;
89
90
91
92
93
94
95 @Parameter( required = true )
96 private List<JnlpFile> jnlpFiles;
97
98
99
100
101
102
103
104 @Parameter
105 private List<JarResource> commonJarResources;
106
107
108
109 @Parameter( defaultValue = "${reactorProjects}", required = true, readonly = true )
110 private List<MavenProject> reactorProjects;
111
112
113
114
115
116
117
118
119 @Component
120 private MavenProject project;
121
122
123
124
125
126
127
128
129 public void execute()
130 throws MojoExecutionException, MojoFailureException
131 {
132
133
134
135
136
137 checkConfiguration();
138
139
140
141
142
143 IOUtil ioUtil = getIoUtil();
144
145 ioUtil.makeDirectoryIfNecessary( getWorkDirectory() );
146 ioUtil.copyResources( getResourcesDirectory(), getWorkDirectory() );
147
148
149
150
151
152 getLog().info( "-- Prepare commons jar resources" );
153 Set<ResolvedJarResource> resolvedCommonJarResources;
154
155 if ( CollectionUtils.isEmpty( commonJarResources ) )
156 {
157 resolvedCommonJarResources = Collections.emptySet();
158 }
159 else
160 {
161 resolvedCommonJarResources = resolveJarResources( commonJarResources, null );
162 }
163
164 Set<ResolvedJarResource> allResolvedJarResources = new LinkedHashSet<ResolvedJarResource>();
165 allResolvedJarResources.addAll( resolvedCommonJarResources );
166
167
168
169
170
171 getLog().info( "-- Prepare jnlp files" );
172 Set<ResolvedJnlpFile> resolvedJnlpFiles = new LinkedHashSet<ResolvedJnlpFile>();
173
174 for ( JnlpFile jnlpFile : jnlpFiles )
175 {
176 verboseLog( "prepare jnlp " + jnlpFile );
177
178
179 Set<ResolvedJarResource> resolvedJarResources =
180 resolveJarResources( jnlpFile.getJarResources(), resolvedCommonJarResources );
181
182
183 allResolvedJarResources.addAll( resolvedJarResources );
184
185
186 ResolvedJnlpFile resolvedJnlpFile = new ResolvedJnlpFile( jnlpFile, resolvedJarResources );
187 resolvedJnlpFiles.add( resolvedJnlpFile );
188 }
189
190
191
192
193
194 signOrRenameJars();
195
196
197
198
199
200 for ( ResolvedJnlpFile jnlpFile : resolvedJnlpFiles )
201 {
202 generateJnlpFile( jnlpFile, getLibPath() );
203 }
204
205
206
207
208
209 generateVersionXml( allResolvedJarResources );
210
211
212
213
214
215
216 File outputDir = new File( getProject().getBuild().getDirectory(),
217 getProject().getBuild().getFinalName() + File.separator + outputDirectoryName );
218
219 ioUtil.copyDirectoryStructure( getWorkDirectory(), outputDir );
220 }
221
222
223
224
225
226
227
228
229 public MavenProject getProject()
230 {
231 return project;
232 }
233
234
235
236
237
238
239
240
241
242
243
244 private void checkConfiguration()
245 throws MojoExecutionException
246 {
247 checkDependencyFilenameStrategy();
248
249 if ( CollectionUtils.isEmpty( jnlpFiles ) )
250 {
251 throw new MojoExecutionException(
252 "Configuration error: At least one <jnlpFile> element must be specified" );
253 }
254
255 if ( jnlpFiles.size() == 1 && StringUtils.isEmpty( jnlpFiles.get( 0 ).getOutputFilename() ) )
256 {
257 getLog().debug( "Jnlp output file name not specified in single set of jnlpFiles. " +
258 "Using default output file name: launch.jnlp." );
259 jnlpFiles.get( 0 ).setOutputFilename( "launch.jnlp" );
260 }
261
262
263
264
265
266 Set<String> filenames = new LinkedHashSet<String>( jnlpFiles.size() );
267
268 for ( JnlpFile jnlpFile : jnlpFiles )
269 {
270 if ( !filenames.add( jnlpFile.getOutputFilename() ) )
271 {
272 throw new MojoExecutionException( "Configuration error: Unique JNLP filenames must be provided. " +
273 "The following file name appears more than once [" +
274 jnlpFile.getOutputFilename() + "]." );
275 }
276
277 checkJnlpFileConfiguration( jnlpFile );
278 }
279
280 if ( CollectionUtils.isNotEmpty( commonJarResources ) )
281 {
282
283
284
285
286
287 for ( JarResource jarResource : commonJarResources )
288 {
289 checkMandatoryJarResourceFields( jarResource );
290
291 if ( jarResource.getMainClass() != null )
292 {
293 throw new MojoExecutionException( "Configuration Error: A mainClass must not be specified " +
294 "on a JarResource in the commonJarResources collection." );
295 }
296 }
297
298
299
300
301
302
303
304 for ( JnlpFile jnlpFile : jnlpFiles )
305 {
306 for ( JarResource jarResource : jnlpFile.getJarResources() )
307 {
308 if ( commonJarResources.contains( jarResource ) )
309 {
310 String message = "Configuration Error: The jar resource element for artifact " + jarResource +
311 " defined in common jar resources is duplicated in the jar " +
312 "resources configuration of the jnlp file identified by the template file " +
313 jnlpFile.getInputTemplate() + ".";
314
315 throw new MojoExecutionException( message );
316 }
317 }
318 }
319 }
320 }
321
322
323
324
325
326
327
328 private void checkJnlpFileConfiguration( JnlpFile jnlpFile )
329 throws MojoExecutionException
330 {
331
332 if ( StringUtils.isBlank( jnlpFile.getOutputFilename() ) )
333 {
334 throw new MojoExecutionException(
335 "Configuration error: An outputFilename must be specified for each jnlpFile element" );
336 }
337
338 if ( StringUtils.isNotBlank( jnlpFile.getTemplateFilename() ) )
339 {
340 getLog().warn(
341 "jnlpFile.templateFilename is deprecated (since 1.0-beta-5), use now the jnlpFile.inputTemplate instead." );
342 jnlpFile.setInputTemplate( jnlpFile.getTemplateFilename() );
343 }
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360 List<JarResource> jnlpJarResources = jnlpFile.getJarResources();
361
362 if ( CollectionUtils.isEmpty( jnlpJarResources ) )
363 {
364 throw new MojoExecutionException(
365 "Configuration error: A non-empty <jarResources> element must be specified in the plugin " +
366 "configuration for the JNLP file named [" + jnlpFile.getOutputFilename() + "]" );
367 }
368
369
370
371
372
373 JarResource mainJarResource = null;
374
375 for ( JarResource jarResource : jnlpJarResources )
376 {
377 checkMandatoryJarResourceFields( jarResource );
378
379 if ( jarResource.getMainClass() != null )
380 {
381 if ( mainJarResource != null )
382 {
383
384
385 throw new MojoExecutionException(
386 "Configuration error: More than one <jarResource> element has been declared " +
387 "with a <mainClass> element in the configuration for JNLP file [" +
388 jnlpFile.getOutputFilename() +
389 "]" );
390 }
391
392 jnlpFile.setMainClass( jarResource.getMainClass() );
393 mainJarResource = jarResource;
394 }
395 }
396
397 if ( mainJarResource == null )
398 {
399 throw new MojoExecutionException( "Configuration error: Exactly one <jarResource> element must " +
400 "be declared with a <mainClass> element in the configuration for JNLP file [" +
401 jnlpFile.getOutputFilename() + "]" );
402 }
403 }
404
405
406
407
408
409
410
411 private void checkMandatoryJarResourceFields( JarResource jarResource )
412 throws MojoExecutionException
413 {
414
415 if ( !jarResource.isMandatoryField() )
416 {
417 throw new MojoExecutionException(
418 "Configuration error: groupId, artifactId or version missing for jarResource[" + jarResource + "]." );
419 }
420
421 }
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436 private Set<ResolvedJarResource> resolveJarResources( Collection<JarResource> configuredJarResources,
437 Set<ResolvedJarResource> commonJarResources )
438 throws MojoExecutionException
439 {
440
441 Set<ResolvedJarResource> collectedJarResources = new LinkedHashSet<ResolvedJarResource>();
442
443 if ( commonJarResources != null )
444 {
445 collectedJarResources.addAll( commonJarResources );
446 }
447
448 ArtifactUtil artifactUtil = getArtifactUtil();
449
450
451 Set<Artifact> artifacts = new LinkedHashSet<Artifact>();
452
453
454 Set<MavenProject> siblingProjects = new LinkedHashSet<MavenProject>();
455
456
457
458 for ( JarResource jarResource : configuredJarResources )
459 {
460 Artifact artifact = artifactUtil.createArtifact( jarResource );
461
462
463 MavenProject siblingProject = artifactUtil.resolveFromReactor( artifact, getProject(), reactorProjects );
464 if ( siblingProject == null )
465 {
466
467 artifactUtil.resolveFromRepositories( artifact, getRemoteRepositories(), getLocalRepository() );
468 artifacts.add( artifact );
469 }
470 else
471 {
472 artifact = siblingProject.getArtifact();
473 siblingProjects.add( siblingProject );
474 artifacts.add( artifact );
475 artifact.setResolved( true );
476 }
477
478 if ( StringUtils.isNotBlank( jarResource.getMainClass() ) )
479 {
480
481
482 if ( artifact == null )
483 {
484 throw new IllegalStateException(
485 "Implementation Error: The given jarResource cannot be checked for " +
486 "a main class until the underlying artifact has been resolved: [" +
487 jarResource + "]" );
488 }
489
490 boolean containsMainClass = artifactUtil.artifactContainsClass( artifact, jarResource.getMainClass() );
491 if ( !containsMainClass )
492 {
493 throw new MojoExecutionException(
494 "The jar specified by the following jarResource does not contain the declared main class:" +
495 jarResource );
496 }
497 }
498 ResolvedJarResource resolvedJarResource = new ResolvedJarResource( jarResource, artifact );
499 getLog().debug( "Add jarResource (configured): " + jarResource );
500 collectedJarResources.add( resolvedJarResource );
501 }
502
503 if ( !isExcludeTransitive() )
504 {
505
506
507
508 AndArtifactFilter artifactFilter = new AndArtifactFilter();
509
510 artifactFilter.add( new ScopeArtifactFilter( Artifact.SCOPE_RUNTIME ) );
511
512 artifactFilter.add( new InversionArtifactFilter( new TypeArtifactFilter( "pom" ) ) );
513
514
515
516 Set<Artifact> transitiveArtifacts =
517 getArtifactUtil().resolveTransitively( artifacts, siblingProjects, getProject().getArtifact(),
518 getLocalRepository(), getRemoteRepositories(), artifactFilter, getProject().getManagedVersionMap());
519
520
521
522 for ( Artifact resolvedArtifact : transitiveArtifacts )
523 {
524
525 ResolvedJarResource newJarResource = new ResolvedJarResource( resolvedArtifact );
526
527 if ( !collectedJarResources.contains( newJarResource ) )
528 {
529 getLog().debug( "Add jarResource (transitive): " + newJarResource );
530 collectedJarResources.add( newJarResource );
531 }
532 }
533 }
534
535
536 for ( ResolvedJarResource jarResource : collectedJarResources )
537 {
538 Artifact artifact = jarResource.getArtifact();
539
540 String filenameWithVersion =
541 getDependencyFilenameStrategy().getDependencyFilename( artifact, false, isUseUniqueVersions() );
542
543 boolean copied = copyJarAsUnprocessedToDirectoryIfNecessary( artifact.getFile(), getLibDirectory(),
544 filenameWithVersion );
545
546 if ( copied )
547 {
548 String name = artifact.getFile().getName();
549
550 verboseLog( "Adding " + name + " to modifiedJnlpArtifacts list." );
551 getModifiedJnlpArtifacts().add( name.substring( 0, name.lastIndexOf( '.' ) ) );
552 }
553
554 String filename = getDependencyFilenameStrategy().getDependencyFilename( artifact,
555 jarResource.isOutputJarVersion()
556 ? null
557 : false,
558 isUseUniqueVersions() );
559 jarResource.setHrefValue( filename );
560 }
561 return collectedJarResources;
562 }
563
564 private void generateJnlpFile( ResolvedJnlpFile jnlpFile, String libPath )
565 throws MojoExecutionException
566 {
567
568 File jnlpOutputFile = new File( getWorkDirectory(), jnlpFile.getOutputFilename() );
569
570 Set<ResolvedJarResource> jarResources = jnlpFile.getJarResources();
571
572
573
574
575
576 File templateDirectory;
577
578 if ( StringUtils.isNotBlank( jnlpFile.getInputTemplateResourcePath() ) )
579 {
580 templateDirectory = new File( jnlpFile.getInputTemplateResourcePath() );
581 getLog().debug( "Use jnlp directory : " + templateDirectory );
582 }
583 else
584 {
585
586 templateDirectory = getTemplateDirectory();
587 getLog().debug( "Use default template directory : " + templateDirectory );
588 }
589
590
591
592
593
594 if ( StringUtils.isBlank( jnlpFile.getInputTemplate() ) )
595 {
596 getLog().debug(
597 "Jnlp servlet template file name not specified. Checking if default output file name exists: " +
598 SERVLET_TEMPLATE_FILENAME );
599
600 File templateFile = new File( templateDirectory, SERVLET_TEMPLATE_FILENAME );
601
602 if ( templateFile.isFile() )
603 {
604 jnlpFile.setInputTemplate( SERVLET_TEMPLATE_FILENAME );
605 }
606 else
607 {
608 getLog().debug( "Jnlp servlet template file not found in default location. Using inbuilt one." );
609 }
610 }
611 else
612 {
613 File templateFile = new File( templateDirectory, jnlpFile.getInputTemplate() );
614
615 if ( !templateFile.isFile() )
616 {
617 throw new MojoExecutionException(
618 "The specified JNLP servlet template does not exist: [" + templateFile + "]" );
619 }
620 }
621
622 String templateFileName = jnlpFile.getInputTemplate();
623
624 GeneratorTechnicalConfig generatorTechnicalConfig =
625 new GeneratorTechnicalConfig( getProject(), templateDirectory, BUILT_IN_SERVLET_TEMPLATE_FILENAME,
626 jnlpOutputFile, templateFileName, jnlpFile.getMainClass(),
627 getWebstartJarURLForVelocity(), getEncoding() );
628 JarResourceGeneratorConfig jarResourceGeneratorConfig = new JarResourceGeneratorConfig( jarResources, libPath, getCodebase(), jnlpFile.getProperties(), jnlpFile.getArguments() );
629 JarResourcesGenerator jnlpGenerator =
630 new JarResourcesGenerator( getLog(), generatorTechnicalConfig, jarResourceGeneratorConfig );
631
632
633
634 try
635 {
636 jnlpGenerator.generate();
637 }
638 catch ( Exception e )
639 {
640 throw new MojoExecutionException(
641 "The following error occurred attempting to generate " + "the JNLP deployment descriptor: " + e, e );
642 }
643
644 }
645
646
647
648
649
650
651
652 private void generateVersionXml( Set<ResolvedJarResource> jarResources )
653 throws MojoExecutionException
654 {
655
656 VersionXmlGenerator generator = new VersionXmlGenerator( getEncoding() );
657 generator.generate( getLibDirectory(), jarResources );
658 }
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682 }