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