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