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.FileInputStream;
23 import java.io.InputStream;
24 import java.net.MalformedURLException;
25 import java.net.URL;
26 import java.net.URLClassLoader;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Enumeration;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Properties;
33 import java.util.Set;
34
35 import org.apache.maven.artifact.Artifact;
36 import org.apache.maven.plugin.AbstractMojo;
37 import org.apache.maven.plugin.MojoExecutionException;
38 import org.apache.maven.plugin.MojoFailureException;
39 import org.apache.maven.project.MavenProject;
40 import org.codehaus.plexus.util.FileUtils;
41 import org.eclipse.jetty.security.LoginService;
42 import org.eclipse.jetty.server.RequestLog;
43 import org.eclipse.jetty.server.Server;
44 import org.eclipse.jetty.server.ShutdownMonitor;
45 import org.eclipse.jetty.server.handler.ContextHandler;
46 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
47 import org.eclipse.jetty.server.handler.HandlerCollection;
48 import org.eclipse.jetty.util.PathWatcher;
49 import org.eclipse.jetty.util.StringUtil;
50 import org.eclipse.jetty.util.resource.Resource;
51 import org.eclipse.jetty.xml.XmlConfiguration;
52
53
54
55
56 public abstract class AbstractJettyMojo extends AbstractMojo
57 {
58
59
60
61
62
63
64
65 protected boolean useProvidedScope;
66
67
68
69
70
71
72
73 protected String[] excludedGoals;
74
75
76
77
78
79
80
81
82
83 protected ContextHandler[] contextHandlers;
84
85
86
87
88
89
90
91
92
93 protected LoginService[] loginServices;
94
95
96
97
98
99
100
101
102
103 protected RequestLog requestLog;
104
105
106
107
108
109
110
111
112
113 protected JettyWebAppContext webApp;
114
115
116
117
118
119
120
121
122
123 protected int scanIntervalSeconds;
124
125
126
127
128
129
130
131
132
133 protected String reload;
134
135
136
137
138
139
140
141
142
143
144
145 protected File systemPropertiesFile;
146
147
148
149
150
151
152
153
154
155
156 protected SystemProperties systemProperties;
157
158
159
160
161
162
163
164
165
166 protected String jettyXml;
167
168
169
170
171
172
173
174
175 protected int stopPort;
176
177
178
179
180
181
182
183
184 protected String stopKey;
185
186
187
188
189
190
191 protected boolean dumpOnStart;
192
193
194
195
196
197
198
199 protected boolean skip;
200
201
202
203
204
205
206
207
208
209 protected String contextXml;
210
211
212
213
214
215
216
217
218 protected MavenProject project;
219
220
221
222
223
224
225
226
227 protected Set projectArtifacts;
228
229
230
231
232
233
234 protected org.apache.maven.plugin.MojoExecution execution;
235
236
237
238
239
240
241
242
243 protected List pluginArtifacts;
244
245
246
247
248
249
250
251
252 protected MavenServerConnector httpConnector;
253
254
255
256
257
258
259 protected Server server;
260
261
262
263
264
265 protected PathWatcher scanner;
266
267
268
269
270
271
272 protected Thread consoleScanner;
273
274 protected ServerSupport serverSupport;
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291 protected boolean nonblocking = false;
292
293
294 public abstract void restartWebApp(boolean reconfigureScanner) throws Exception;
295
296
297 public abstract void checkPomConfiguration() throws MojoExecutionException;
298
299
300 public abstract void configureScanner () throws MojoExecutionException;
301
302
303
304
305
306
307
308
309 public void execute() throws MojoExecutionException, MojoFailureException
310 {
311 getLog().info("Configuring Jetty for project: " + this.project.getName());
312 if (skip)
313 {
314 getLog().info("Skipping Jetty start: jetty.skip==true");
315 return;
316 }
317
318 if (isExcluded(execution.getMojoDescriptor().getGoal()))
319 {
320 getLog().info("The goal \""+execution.getMojoDescriptor().getFullGoalName()+
321 "\" has been made unavailable for this web application by an <excludedGoal> configuration.");
322 return;
323 }
324
325 configurePluginClasspath();
326 PluginLog.setLog(getLog());
327 checkPomConfiguration();
328 startJetty();
329 }
330
331
332
333
334 public void configurePluginClasspath() throws MojoExecutionException
335 {
336
337
338
339
340 if (useProvidedScope)
341 {
342 try
343 {
344 List<URL> provided = new ArrayList<URL>();
345 URL[] urls = null;
346
347 for ( Iterator<Artifact> iter = projectArtifacts.iterator(); iter.hasNext(); )
348 {
349 Artifact artifact = iter.next();
350 if (Artifact.SCOPE_PROVIDED.equals(artifact.getScope()) && !isPluginArtifact(artifact))
351 {
352 provided.add(artifact.getFile().toURI().toURL());
353 if (getLog().isDebugEnabled()) { getLog().debug("Adding provided artifact: "+artifact);}
354 }
355 }
356
357 if (!provided.isEmpty())
358 {
359 urls = new URL[provided.size()];
360 provided.toArray(urls);
361 URLClassLoader loader = new URLClassLoader(urls, getClass().getClassLoader());
362 Thread.currentThread().setContextClassLoader(loader);
363 getLog().info("Plugin classpath augmented with <scope>provided</scope> dependencies: "+Arrays.toString(urls));
364 }
365 }
366 catch (MalformedURLException e)
367 {
368 throw new MojoExecutionException("Invalid url", e);
369 }
370 }
371 }
372
373 public boolean isPluginArtifact(Artifact artifact)
374 {
375 if (pluginArtifacts == null || pluginArtifacts.isEmpty())
376 return false;
377
378 boolean isPluginArtifact = false;
379 for (Iterator<Artifact> iter = pluginArtifacts.iterator(); iter.hasNext() && !isPluginArtifact; )
380 {
381 Artifact pluginArtifact = iter.next();
382 if (getLog().isDebugEnabled()) { getLog().debug("Checking "+pluginArtifact);}
383 if (pluginArtifact.getGroupId().equals(artifact.getGroupId()) && pluginArtifact.getArtifactId().equals(artifact.getArtifactId()))
384 isPluginArtifact = true;
385 }
386
387 return isPluginArtifact;
388 }
389
390 public void finishConfigurationBeforeStart() throws Exception
391 {
392 HandlerCollection contexts = (HandlerCollection)server.getChildHandlerByClass(ContextHandlerCollection.class);
393 if (contexts==null)
394 contexts = (HandlerCollection)server.getChildHandlerByClass(HandlerCollection.class);
395
396 for (int i=0; (this.contextHandlers != null) && (i < this.contextHandlers.length); i++)
397 {
398 contexts.addHandler(this.contextHandlers[i]);
399 }
400 }
401
402 public void applyJettyXml() throws Exception
403 {
404 Server tmp = ServerSupport.applyXmlConfigurations(server, getJettyXmlFiles());
405 if (server == null)
406 server = tmp;
407
408 if (server == null)
409 server = new Server();
410 }
411
412 public void startJetty () throws MojoExecutionException
413 {
414 try
415 {
416 getLog().debug("Starting Jetty Server ...");
417
418
419 Resource.setDefaultUseCaches(false);
420
421 configureMonitor();
422
423 printSystemProperties();
424
425
426
427 applyJettyXml ();
428
429
430 if (httpConnector != null)
431 {
432
433 if (httpConnector.getPort() <= 0)
434 {
435
436 String tmp = System.getProperty(MavenServerConnector.PORT_SYSPROPERTY, System.getProperty("jetty.port", MavenServerConnector.DEFAULT_PORT_STR));
437 httpConnector.setPort(Integer.parseInt(tmp.trim()));
438 }
439 httpConnector.setServer(server);
440 }
441
442 ServerSupport.configureConnectors(server, httpConnector);
443
444
445 ServerSupport.configureHandlers(server, this.requestLog);
446 configureWebApplication();
447 ServerSupport.addWebApplication(server, webApp);
448
449
450 ServerSupport.configureLoginServices(server, loginServices);
451
452
453
454 finishConfigurationBeforeStart();
455
456
457 this.server.start();
458
459 getLog().info("Started Jetty Server");
460
461 if ( dumpOnStart )
462 {
463 getLog().info(this.server.dump());
464 }
465
466
467 if (isScanningEnabled())
468 {
469 scanner = new PathWatcher();
470 configureScanner ();
471 startScanner();
472 }
473
474
475 startConsoleScanner();
476
477
478 if (!nonblocking )
479 {
480 server.join();
481 }
482 }
483 catch (Exception e)
484 {
485 throw new MojoExecutionException("Failure", e);
486 }
487 finally
488 {
489 if (!nonblocking )
490 {
491 getLog().info("Jetty server exiting.");
492 }
493 }
494 }
495
496
497 public void configureMonitor()
498 {
499 if(stopPort>0 && stopKey!=null)
500 {
501 ShutdownMonitor monitor = ShutdownMonitor.getInstance();
502 monitor.setPort(stopPort);
503 monitor.setKey(stopKey);
504 monitor.setExitVm(!nonblocking);
505 }
506 }
507
508
509
510
511
512
513
514
515
516
517
518
519 public void configureWebApplication () throws Exception
520 {
521
522 if (webApp == null)
523 webApp = new JettyWebAppContext();
524
525
526
527
528 if (contextXml != null)
529 {
530 File file = FileUtils.getFile(contextXml);
531 XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.toURL(file));
532 getLog().info("Applying context xml file "+contextXml);
533 xmlConfiguration.configure(webApp);
534 }
535
536
537 String cp = webApp.getContextPath();
538 if (cp == null || "".equals(cp))
539 {
540 cp = "/"+project.getArtifactId();
541 webApp.setContextPath(cp);
542 }
543
544
545 if (webApp.getTempDirectory() == null)
546 {
547 File target = new File(project.getBuild().getDirectory());
548 File tmp = new File(target,"tmp");
549 if (!tmp.exists())
550 tmp.mkdirs();
551 webApp.setTempDirectory(tmp);
552 }
553
554 getLog().info("Context path = " + webApp.getContextPath());
555 getLog().info("Tmp directory = "+ (webApp.getTempDirectory()== null? " determined at runtime": webApp.getTempDirectory()));
556 getLog().info("Web defaults = "+(webApp.getDefaultsDescriptor()==null?" jetty default":webApp.getDefaultsDescriptor()));
557 getLog().info("Web overrides = "+(webApp.getOverrideDescriptor()==null?" none":webApp.getOverrideDescriptor()));
558 }
559
560
561
562
563
564
565
566
567
568
569 public void startScanner() throws Exception
570 {
571 if (!isScanningEnabled())
572 return;
573
574 scanner.setNotifyExistingOnStart(false);
575
576
577 scanner.start();
578 }
579
580
581 public boolean isScanningEnabled ()
582 {
583 if (scanIntervalSeconds <=0 || "manual".equalsIgnoreCase( reload ))
584 return false;
585 return true;
586 }
587
588 public void stopScanner() throws Exception
589 {
590 if (!isScanningEnabled())
591 return;
592
593 if (scanner != null)
594 scanner.stop();
595 }
596
597
598
599
600
601
602 protected void startConsoleScanner() throws Exception
603 {
604 if ( "manual".equalsIgnoreCase( reload ) )
605 {
606 getLog().info("Console reloading is ENABLED. Hit ENTER on the console to restart the context.");
607 consoleScanner = new ConsoleScanner(this);
608 consoleScanner.start();
609 }
610 }
611
612 protected void printSystemProperties ()
613 {
614
615 if (getLog().isDebugEnabled())
616 {
617 if (systemProperties != null)
618 {
619 Iterator itor = systemProperties.getSystemProperties().iterator();
620 while (itor.hasNext())
621 {
622 SystemProperty prop = (SystemProperty)itor.next();
623 getLog().debug("Property "+prop.getName()+"="+prop.getValue()+" was "+ (prop.isSet() ? "set" : "skipped"));
624 }
625 }
626 }
627 }
628
629
630
631
632
633
634
635 public File findJettyWebXmlFile (File webInfDir)
636 {
637 if (webInfDir == null)
638 return null;
639 if (!webInfDir.exists())
640 return null;
641
642 File f = new File (webInfDir, "jetty-web.xml");
643 if (f.exists())
644 return f;
645
646
647 f = new File (webInfDir, "web-jetty.xml");
648 if (f.exists())
649 return f;
650
651 return null;
652 }
653
654 public void setSystemPropertiesFile(File file) throws Exception
655 {
656 this.systemPropertiesFile = file;
657 Properties properties = new Properties();
658 try (InputStream propFile = new FileInputStream(systemPropertiesFile))
659 {
660 properties.load(propFile);
661 }
662 if (this.systemProperties == null )
663 this.systemProperties = new SystemProperties();
664
665 for (Enumeration<?> keys = properties.keys(); keys.hasMoreElements(); )
666 {
667 String key = (String)keys.nextElement();
668 if ( ! systemProperties.containsSystemProperty(key) )
669 {
670 SystemProperty prop = new SystemProperty();
671 prop.setKey(key);
672 prop.setValue(properties.getProperty(key));
673
674 this.systemProperties.setSystemProperty(prop);
675 }
676 }
677 }
678
679 public void setSystemProperties(SystemProperties systemProperties)
680 {
681 if (this.systemProperties == null)
682 this.systemProperties = systemProperties;
683 else
684 {
685 for (SystemProperty prop: systemProperties.getSystemProperties())
686 {
687 this.systemProperties.setSystemProperty(prop);
688 }
689 }
690 }
691
692 public List<File> getJettyXmlFiles()
693 {
694 if ( this.jettyXml == null )
695 {
696 return null;
697 }
698
699 List<File> jettyXmlFiles = new ArrayList<File>();
700
701 if ( this.jettyXml.indexOf(',') == -1 )
702 {
703 jettyXmlFiles.add( new File( this.jettyXml ) );
704 }
705 else
706 {
707 String[] files = StringUtil.csvSplit(this.jettyXml);
708
709 for ( String file : files )
710 {
711 jettyXmlFiles.add( new File(file) );
712 }
713 }
714
715 return jettyXmlFiles;
716 }
717
718 public boolean isExcluded (String goal)
719 {
720 if (excludedGoals == null || goal == null)
721 return false;
722
723 goal = goal.trim();
724 if ("".equals(goal))
725 return false;
726
727 boolean excluded = false;
728 for (int i=0; i<excludedGoals.length && !excluded; i++)
729 {
730 if (excludedGoals[i].equalsIgnoreCase(goal))
731 excluded = true;
732 }
733
734 return excluded;
735 }
736 }