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
447
448 ServerSupport.configureDefaultConfigurationClasses(server);
449 configureWebApplication();
450 ServerSupport.addWebApplication(server, webApp);
451
452
453 ServerSupport.configureLoginServices(server, loginServices);
454
455
456
457 finishConfigurationBeforeStart();
458
459
460 this.server.start();
461
462 getLog().info("Started Jetty Server");
463
464 if ( dumpOnStart )
465 {
466 getLog().info(this.server.dump());
467 }
468
469
470 if (isScanningEnabled())
471 {
472 scanner = new PathWatcher();
473 configureScanner ();
474 startScanner();
475 }
476
477
478 startConsoleScanner();
479
480
481 if (!nonblocking )
482 {
483 server.join();
484 }
485 }
486 catch (Exception e)
487 {
488 throw new MojoExecutionException("Failure", e);
489 }
490 finally
491 {
492 if (!nonblocking )
493 {
494 getLog().info("Jetty server exiting.");
495 }
496 }
497 }
498
499
500 public void configureMonitor()
501 {
502 if(stopPort>0 && stopKey!=null)
503 {
504 ShutdownMonitor monitor = ShutdownMonitor.getInstance();
505 monitor.setPort(stopPort);
506 monitor.setKey(stopKey);
507 monitor.setExitVm(!nonblocking);
508 }
509 }
510
511
512
513
514
515
516
517
518
519
520
521
522 public void configureWebApplication () throws Exception
523 {
524
525 if (webApp == null)
526 webApp = new JettyWebAppContext();
527
528
529
530
531 if (contextXml != null)
532 {
533 File file = FileUtils.getFile(contextXml);
534 XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.toURL(file));
535 getLog().info("Applying context xml file "+contextXml);
536 xmlConfiguration.configure(webApp);
537 }
538
539
540 String cp = webApp.getContextPath();
541 if (cp == null || "".equals(cp))
542 {
543 cp = "/"+project.getArtifactId();
544 webApp.setContextPath(cp);
545 }
546
547
548 if (webApp.getTempDirectory() == null)
549 {
550 File target = new File(project.getBuild().getDirectory());
551 File tmp = new File(target,"tmp");
552 if (!tmp.exists())
553 tmp.mkdirs();
554 webApp.setTempDirectory(tmp);
555 }
556
557 getLog().info("Context path = " + webApp.getContextPath());
558 getLog().info("Tmp directory = "+ (webApp.getTempDirectory()== null? " determined at runtime": webApp.getTempDirectory()));
559 getLog().info("Web defaults = "+(webApp.getDefaultsDescriptor()==null?" jetty default":webApp.getDefaultsDescriptor()));
560 getLog().info("Web overrides = "+(webApp.getOverrideDescriptor()==null?" none":webApp.getOverrideDescriptor()));
561 }
562
563
564
565
566
567
568
569
570
571
572 public void startScanner() throws Exception
573 {
574 if (!isScanningEnabled())
575 return;
576
577 scanner.setNotifyExistingOnStart(false);
578
579
580 scanner.start();
581 }
582
583
584 public boolean isScanningEnabled ()
585 {
586 if (scanIntervalSeconds <=0 || "manual".equalsIgnoreCase( reload ))
587 return false;
588 return true;
589 }
590
591 public void stopScanner() throws Exception
592 {
593 if (!isScanningEnabled())
594 return;
595
596 if (scanner != null)
597 scanner.stop();
598 }
599
600
601
602
603
604
605 protected void startConsoleScanner() throws Exception
606 {
607 if ( "manual".equalsIgnoreCase( reload ) )
608 {
609 getLog().info("Console reloading is ENABLED. Hit ENTER on the console to restart the context.");
610 consoleScanner = new ConsoleScanner(this);
611 consoleScanner.start();
612 }
613 }
614
615 protected void printSystemProperties ()
616 {
617
618 if (getLog().isDebugEnabled())
619 {
620 if (systemProperties != null)
621 {
622 Iterator itor = systemProperties.getSystemProperties().iterator();
623 while (itor.hasNext())
624 {
625 SystemProperty prop = (SystemProperty)itor.next();
626 getLog().debug("Property "+prop.getName()+"="+prop.getValue()+" was "+ (prop.isSet() ? "set" : "skipped"));
627 }
628 }
629 }
630 }
631
632
633
634
635
636
637
638 public File findJettyWebXmlFile (File webInfDir)
639 {
640 if (webInfDir == null)
641 return null;
642 if (!webInfDir.exists())
643 return null;
644
645 File f = new File (webInfDir, "jetty-web.xml");
646 if (f.exists())
647 return f;
648
649
650 f = new File (webInfDir, "web-jetty.xml");
651 if (f.exists())
652 return f;
653
654 return null;
655 }
656
657 public void setSystemPropertiesFile(File file) throws Exception
658 {
659 this.systemPropertiesFile = file;
660 Properties properties = new Properties();
661 try (InputStream propFile = new FileInputStream(systemPropertiesFile))
662 {
663 properties.load(propFile);
664 }
665 if (this.systemProperties == null )
666 this.systemProperties = new SystemProperties();
667
668 for (Enumeration<?> keys = properties.keys(); keys.hasMoreElements(); )
669 {
670 String key = (String)keys.nextElement();
671 if ( ! systemProperties.containsSystemProperty(key) )
672 {
673 SystemProperty prop = new SystemProperty();
674 prop.setKey(key);
675 prop.setValue(properties.getProperty(key));
676
677 this.systemProperties.setSystemProperty(prop);
678 }
679 }
680 }
681
682 public void setSystemProperties(SystemProperties systemProperties)
683 {
684 if (this.systemProperties == null)
685 this.systemProperties = systemProperties;
686 else
687 {
688 for (SystemProperty prop: systemProperties.getSystemProperties())
689 {
690 this.systemProperties.setSystemProperty(prop);
691 }
692 }
693 }
694
695 public List<File> getJettyXmlFiles()
696 {
697 if ( this.jettyXml == null )
698 {
699 return null;
700 }
701
702 List<File> jettyXmlFiles = new ArrayList<File>();
703
704 if ( this.jettyXml.indexOf(',') == -1 )
705 {
706 jettyXmlFiles.add( new File( this.jettyXml ) );
707 }
708 else
709 {
710 String[] files = StringUtil.csvSplit(this.jettyXml);
711
712 for ( String file : files )
713 {
714 jettyXmlFiles.add( new File(file) );
715 }
716 }
717
718 return jettyXmlFiles;
719 }
720
721 public boolean isExcluded (String goal)
722 {
723 if (excludedGoals == null || goal == null)
724 return false;
725
726 goal = goal.trim();
727 if ("".equals(goal))
728 return false;
729
730 boolean excluded = false;
731 for (int i=0; i<excludedGoals.length && !excluded; i++)
732 {
733 if (excludedGoals[i].equalsIgnoreCase(goal))
734 excluded = true;
735 }
736
737 return excluded;
738 }
739 }