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