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