1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.maven.plugin;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.net.URL;
24 import java.util.ArrayList;
25 import java.util.Date;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Set;
30
31 import org.apache.maven.artifact.Artifact;
32 import org.apache.maven.plugin.MojoExecutionException;
33 import org.apache.maven.plugin.MojoFailureException;
34 import org.eclipse.jetty.util.PathWatcher;
35 import org.eclipse.jetty.util.PathWatcher.PathWatchEvent;
36 import org.eclipse.jetty.util.resource.Resource;
37 import org.eclipse.jetty.webapp.WebAppContext;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public class JettyRunMojo extends AbstractJettyMojo
62 {
63 public static final String DEFAULT_WEBAPP_SRC = "src"+File.separator+"main"+File.separator+"webapp";
64 public static final String FAKE_WEBAPP = "webapp-tmp";
65
66
67
68
69
70
71
72
73
74
75 protected boolean useTestScope;
76
77
78
79
80
81
82
83
84
85 protected String webXml;
86
87
88
89
90
91
92
93
94
95 protected File classesDirectory;
96
97
98
99
100
101 protected ScanPattern scanClassesPattern;
102
103
104
105
106
107
108
109
110
111
112 protected File testClassesDirectory;
113
114
115
116
117
118 protected ScanPattern scanTestClassesPattern;
119
120
121
122
123
124
125
126
127
128 protected File webAppSourceDirectory;
129
130
131
132
133
134
135 protected File[] scanTargets;
136
137
138
139
140
141
142
143
144 protected ScanTargetPattern[] scanTargetPatterns;
145
146
147
148
149
150 protected WarPluginInfo warPluginInfo;
151
152
153
154
155
156 protected List<Artifact> warArtifacts;
157
158
159
160
161
162
163
164
165
166 @Override
167 public void execute() throws MojoExecutionException, MojoFailureException
168 {
169 warPluginInfo = new WarPluginInfo(project);
170 super.execute();
171 }
172
173
174
175
176
177
178
179
180
181 public void checkPomConfiguration () throws MojoExecutionException
182 {
183
184 try
185 {
186 if ((webAppSourceDirectory == null) || !webAppSourceDirectory.exists())
187 {
188 getLog().info("webAppSourceDirectory"+(webAppSourceDirectory == null ? " not set." : (webAppSourceDirectory.getAbsolutePath()+" does not exist."))+" Trying "+DEFAULT_WEBAPP_SRC);
189 webAppSourceDirectory = new File (project.getBasedir(), DEFAULT_WEBAPP_SRC);
190 if (!webAppSourceDirectory.exists())
191 {
192 getLog().info("webAppSourceDirectory "+webAppSourceDirectory.getAbsolutePath()+" does not exist. Trying "+project.getBuild().getDirectory()+File.separator+FAKE_WEBAPP);
193
194
195 File target = new File(project.getBuild().getDirectory());
196 webAppSourceDirectory = new File(target, FAKE_WEBAPP);
197 if (!webAppSourceDirectory.exists())
198 webAppSourceDirectory.mkdirs();
199 }
200 }
201 else
202 getLog().info( "Webapp source directory = " + webAppSourceDirectory.getCanonicalPath());
203 }
204 catch (IOException e)
205 {
206 throw new MojoExecutionException("Webapp source directory does not exist", e);
207 }
208
209
210 if ( !"automatic".equalsIgnoreCase( reload ) && !"manual".equalsIgnoreCase( reload ) )
211 {
212 throw new MojoExecutionException( "invalid reload mechanic specified, must be 'automatic' or 'manual'" );
213 }
214 else
215 {
216 getLog().info("Reload Mechanic: " + reload );
217 }
218
219
220
221 try
222 {
223
224 if (classesDirectory != null)
225 {
226 if (!classesDirectory.exists())
227 getLog().info( "Classes directory "+ classesDirectory.getCanonicalPath()+ " does not exist");
228 else
229 getLog().info("Classes = " + classesDirectory.getCanonicalPath());
230 }
231 else
232 getLog().info("Classes directory not set");
233 }
234 catch (IOException e)
235 {
236 throw new MojoExecutionException("Location of classesDirectory does not exist");
237 }
238 }
239
240
241
242
243 @Override
244 public void finishConfigurationBeforeStart() throws Exception
245 {
246 server.setStopAtShutdown(true);
247 super.finishConfigurationBeforeStart();
248 }
249
250
251
252
253
254
255
256 public void configureWebApplication() throws Exception
257 {
258 super.configureWebApplication();
259
260
261
262
263
264
265 Resource webAppSourceDirectoryResource = Resource.newResource(webAppSourceDirectory.getCanonicalPath());
266 if (webApp.getWar() == null)
267 webApp.setWar(webAppSourceDirectoryResource.toString());
268
269 if (webApp.getBaseResource() == null)
270 webApp.setBaseResource(webAppSourceDirectoryResource);
271
272 if (classesDirectory != null)
273 webApp.setClasses (classesDirectory);
274 if (useTestScope && (testClassesDirectory != null))
275 webApp.setTestClasses (testClassesDirectory);
276
277 webApp.setWebInfLib (getDependencyFiles());
278
279
280 Set<Artifact> matchedWarArtifacts = new HashSet<Artifact>();
281
282
283
284
285 List<Overlay> overlays = new ArrayList<Overlay>();
286 for (OverlayConfig config:warPluginInfo.getMavenWarOverlayConfigs())
287 {
288
289 if (config.isSkip())
290 continue;
291
292
293 if (config.isCurrentProject())
294 {
295 Overlay overlay = new Overlay(config, null);
296 overlays.add(overlay);
297 continue;
298 }
299
300
301 Artifact a = getArtifactForOverlay(config, getWarArtifacts());
302 if (a != null)
303 {
304 matchedWarArtifacts.add(a);
305 SelectiveJarResource r = new SelectiveJarResource(new URL("jar:"+Resource.toURL(a.getFile()).toString()+"!/"));
306 r.setIncludes(config.getIncludes());
307 r.setExcludes(config.getExcludes());
308 Overlay overlay = new Overlay(config, r);
309 overlays.add(overlay);
310 }
311 }
312
313
314 for (Artifact a: getWarArtifacts())
315 {
316 if (!matchedWarArtifacts.contains(a))
317 {
318 Overlay overlay = new Overlay(null, Resource.newResource(new URL("jar:"+Resource.toURL(a.getFile()).toString()+"!/")));
319 overlays.add(overlay);
320 }
321 }
322
323 webApp.setOverlays(overlays);
324
325
326 if (webApp.getDescriptor() == null)
327 {
328
329 if (webXml != null)
330 {
331 Resource r = Resource.newResource(webXml);
332 if (r.exists() && !r.isDirectory())
333 {
334 webApp.setDescriptor(r.toString());
335 }
336 }
337
338
339 if (webApp.getDescriptor() == null && webApp.getBaseResource() != null)
340 {
341 Resource r = webApp.getBaseResource().addPath("WEB-INF/web.xml");
342 if (r.exists() && !r.isDirectory())
343 {
344 webApp.setDescriptor(r.toString());
345 }
346 }
347
348
349 if (webApp.getDescriptor() == null && (webAppSourceDirectory != null))
350 {
351 File f = new File (new File (webAppSourceDirectory, "WEB-INF"), "web.xml");
352 if (f.exists() && f.isFile())
353 {
354 webApp.setDescriptor(f.getCanonicalPath());
355 }
356 }
357 }
358 getLog().info( "web.xml file = "+webApp.getDescriptor());
359 getLog().info("Webapp directory = " + webAppSourceDirectory.getCanonicalPath());
360 }
361
362
363
364
365
366
367
368 public void configureScanner ()
369 throws MojoExecutionException
370 {
371 try
372 {
373 gatherScannables();
374 }
375 catch (Exception e)
376 {
377 throw new MojoExecutionException("Error forming scan list", e);
378 }
379
380 scanner.addListener(new PathWatcher.EventListListener()
381 {
382
383 @Override
384 public void onPathWatchEvents(List<PathWatchEvent> events)
385 {
386 try
387 {
388 boolean reconfigure = false;
389 if (events != null)
390 {
391 for (PathWatchEvent e:events)
392 {
393 if (e.getPath().equals(project.getFile().toPath()))
394 {
395 reconfigure = true;
396 break;
397 }
398 }
399 }
400
401 restartWebApp(reconfigure);
402 }
403 catch (Exception e)
404 {
405 getLog().error("Error reconfiguring/restarting webapp after change in watched files",e);
406 }
407 }
408 });
409 }
410
411
412 public void gatherScannables() throws Exception
413 {
414 if (webApp.getDescriptor() != null)
415 {
416 Resource r = Resource.newResource(webApp.getDescriptor());
417 scanner.watch(r.getFile().toPath());
418 }
419
420 if (webApp.getJettyEnvXml() != null)
421 scanner.watch(new File(webApp.getJettyEnvXml()).toPath());
422
423 if (webApp.getDefaultsDescriptor() != null)
424 {
425 if (!WebAppContext.WEB_DEFAULTS_XML.equals(webApp.getDefaultsDescriptor()))
426 scanner.watch(new File(webApp.getDefaultsDescriptor()).toPath());
427 }
428
429 if (webApp.getOverrideDescriptor() != null)
430 {
431 scanner.watch(new File(webApp.getOverrideDescriptor()).toPath());
432 }
433
434 File jettyWebXmlFile = findJettyWebXmlFile(new File(webAppSourceDirectory,"WEB-INF"));
435 if (jettyWebXmlFile != null)
436 {
437 scanner.watch(jettyWebXmlFile.toPath());
438 }
439
440
441 for (Artifact a:getWarArtifacts())
442 {
443 scanner.watch(a.getFile().toPath());
444 }
445
446
447 if (scanTargets != null)
448 {
449 for (File f:scanTargets)
450 {
451 if (f.isDirectory())
452 {
453 PathWatcher.Config config = new PathWatcher.Config(f.toPath());
454 config.setRecurseDepth(PathWatcher.Config.UNLIMITED_DEPTH);
455 scanner.watch(config);
456 }
457 else
458 scanner.watch(f.toPath());
459 }
460 }
461
462
463 if (scanTargetPatterns != null)
464 {
465 for (ScanTargetPattern p:scanTargetPatterns)
466 {
467 PathWatcher.Config config = new PathWatcher.Config(p.getDirectory().toPath());
468 config.setRecurseDepth(PathWatcher.Config.UNLIMITED_DEPTH);
469 for (String pattern:p.getExcludes())
470 config.addExcludeGlobRelative(pattern);
471 for (String pattern:p.getIncludes())
472 config.addIncludeGlobRelative(pattern);
473 scanner.watch(config);
474 }
475 }
476
477
478 scanner.watch(project.getFile().toPath());
479
480 if (webApp.getTestClasses() != null)
481 {
482 PathWatcher.Config config = new PathWatcher.Config(webApp.getTestClasses().toPath());
483 config.setRecurseDepth(PathWatcher.Config.UNLIMITED_DEPTH);
484 if (scanTestClassesPattern != null)
485 {
486 for (String p:scanTestClassesPattern.getExcludes())
487 config.addExcludeGlobRelative(p);
488 for (String p:scanTestClassesPattern.getIncludes())
489 config.addIncludeGlobRelative(p);
490 }
491 scanner.watch(config);
492 }
493
494 if (webApp.getClasses() != null)
495 {
496 PathWatcher.Config config = new PathWatcher.Config(webApp.getClasses().toPath());
497 config.setRecurseDepth(PathWatcher.Config.UNLIMITED_DEPTH);
498 if (scanClassesPattern != null)
499 {
500 for (String p:scanClassesPattern.getExcludes())
501 config.addExcludeGlobRelative(p);
502
503 for (String p:scanClassesPattern.getIncludes())
504 config.addIncludeGlobRelative(p);
505
506 }
507 scanner.watch(config);
508 }
509
510 if (webApp.getWebInfLib() != null)
511 {
512 for (File f:webApp.getWebInfLib())
513 {
514 PathWatcher.Config config = new PathWatcher.Config(f.toPath());
515 config.setRecurseDepth(PathWatcher.Config.UNLIMITED_DEPTH);
516 scanner.watch(config);
517 }
518 }
519 }
520
521
522
523
524
525 public void restartWebApp(boolean reconfigureScanner) throws Exception
526 {
527 getLog().info("restarting "+webApp);
528 getLog().debug("Stopping webapp ...");
529 stopScanner();
530 webApp.stop();
531
532 getLog().debug("Reconfiguring webapp ...");
533
534 checkPomConfiguration();
535 configureWebApplication();
536
537
538
539 if (reconfigureScanner)
540 {
541 getLog().info("Reconfiguring scanner after change to pom.xml ...");
542 scanner.reset();
543 warArtifacts = null;
544 configureScanner();
545 }
546
547 getLog().debug("Restarting webapp ...");
548 webApp.start();
549 startScanner();
550 getLog().info("Restart completed at "+new Date().toString());
551 }
552
553
554
555
556
557
558
559 private List<File> getDependencyFiles ()
560 {
561 List<File> dependencyFiles = new ArrayList<File>();
562 for ( Iterator<Artifact> iter = projectArtifacts.iterator(); iter.hasNext(); )
563 {
564 Artifact artifact = (Artifact) iter.next();
565
566
567 if(artifact.getType().equals("war"))
568 {
569 continue;
570 }
571
572 if (Artifact.SCOPE_PROVIDED.equals(artifact.getScope()))
573 continue;
574
575 if (Artifact.SCOPE_TEST.equals(artifact.getScope()) && !useTestScope)
576 continue;
577
578 dependencyFiles.add(artifact.getFile());
579 getLog().debug( "Adding artifact " + artifact.getFile().getName() + " with scope "+artifact.getScope()+" for WEB-INF/lib " );
580 }
581
582 return dependencyFiles;
583 }
584
585
586
587
588
589
590
591 private List<Artifact> getWarArtifacts ()
592 {
593 if (warArtifacts != null)
594 return warArtifacts;
595
596 warArtifacts = new ArrayList<Artifact>();
597 for ( Iterator<Artifact> iter = projectArtifacts.iterator(); iter.hasNext(); )
598 {
599 Artifact artifact = (Artifact) iter.next();
600 if (artifact.getType().equals("war") || artifact.getType().equals("zip"))
601 {
602 try
603 {
604 warArtifacts.add(artifact);
605 getLog().info("Dependent war artifact "+artifact.getId());
606 }
607 catch(Exception e)
608 {
609 throw new RuntimeException(e);
610 }
611 }
612 }
613 return warArtifacts;
614 }
615
616 protected Artifact getArtifactForOverlay (OverlayConfig o, List<Artifact> warArtifacts)
617 {
618 if (o == null || warArtifacts == null || warArtifacts.isEmpty())
619 return null;
620
621 for (Artifact a:warArtifacts)
622 {
623 if (o.matchesArtifact (a.getGroupId(), a.getArtifactId(), a.getClassifier()))
624 {
625 return a;
626 }
627 }
628
629 return null;
630 }
631 }