1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.runner;
20
21 import java.io.IOException;
22 import java.io.PrintStream;
23 import java.net.URL;
24 import java.net.URLClassLoader;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Locale;
29
30 import org.eclipse.jetty.security.ConstraintMapping;
31 import org.eclipse.jetty.security.ConstraintSecurityHandler;
32 import org.eclipse.jetty.security.HashLoginService;
33 import org.eclipse.jetty.security.authentication.BasicAuthenticator;
34 import org.eclipse.jetty.server.AbstractConnector;
35 import org.eclipse.jetty.server.Connector;
36 import org.eclipse.jetty.server.ConnectorStatistics;
37 import org.eclipse.jetty.server.Handler;
38 import org.eclipse.jetty.server.NCSARequestLog;
39 import org.eclipse.jetty.server.Server;
40 import org.eclipse.jetty.server.ServerConnector;
41 import org.eclipse.jetty.server.ShutdownMonitor;
42 import org.eclipse.jetty.server.handler.ContextHandler;
43 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
44 import org.eclipse.jetty.server.handler.DefaultHandler;
45 import org.eclipse.jetty.server.handler.HandlerCollection;
46 import org.eclipse.jetty.server.handler.RequestLogHandler;
47 import org.eclipse.jetty.server.handler.StatisticsHandler;
48 import org.eclipse.jetty.server.session.SessionHandler;
49 import org.eclipse.jetty.servlet.ServletContextHandler;
50 import org.eclipse.jetty.servlet.ServletHolder;
51 import org.eclipse.jetty.servlet.StatisticsServlet;
52 import org.eclipse.jetty.util.RolloverFileOutputStream;
53 import org.eclipse.jetty.util.log.Log;
54 import org.eclipse.jetty.util.log.Logger;
55 import org.eclipse.jetty.util.resource.Resource;
56 import org.eclipse.jetty.util.security.Constraint;
57 import org.eclipse.jetty.webapp.WebAppContext;
58 import org.eclipse.jetty.xml.XmlConfiguration;
59
60
61
62
63
64
65
66
67 public class Runner
68 {
69 private static final Logger LOG = Log.getLogger(Runner.class);
70
71 public static final String[] __plusConfigurationClasses = new String[] {
72 org.eclipse.jetty.webapp.WebInfConfiguration.class.getCanonicalName(),
73 org.eclipse.jetty.webapp.WebXmlConfiguration.class.getCanonicalName(),
74 org.eclipse.jetty.webapp.MetaInfConfiguration.class.getCanonicalName(),
75 org.eclipse.jetty.webapp.FragmentConfiguration.class.getCanonicalName(),
76 org.eclipse.jetty.plus.webapp.EnvConfiguration.class.getCanonicalName(),
77 org.eclipse.jetty.plus.webapp.PlusConfiguration.class.getCanonicalName(),
78 org.eclipse.jetty.annotations.AnnotationConfiguration.class.getCanonicalName(),
79 org.eclipse.jetty.webapp.JettyWebXmlConfiguration.class.getCanonicalName()
80 };
81 public static final String __containerIncludeJarPattern = ".*/jetty-runner-[^/]*\\.jar$";
82 public static final String __defaultContextPath = "/";
83 public static final int __defaultPort = 8080;
84
85 protected Server _server;
86 protected URLClassLoader _classLoader;
87 protected Classpath _classpath = new Classpath();
88 protected ContextHandlerCollection _contexts;
89 protected RequestLogHandler _logHandler;
90 protected String _logFile;
91 protected ArrayList<String> _configFiles;
92 protected boolean _enableStats=false;
93 protected String _statsPropFile;
94
95
96
97
98
99
100
101
102 public class Classpath
103 {
104 private List<URL> _classpath = new ArrayList<>();
105
106 public void addJars (Resource lib) throws IOException
107 {
108 if (lib == null || !lib.exists())
109 throw new IllegalStateException ("No such lib: "+lib);
110
111 String[] list = lib.list();
112 if (list==null)
113 return;
114
115 for (String path : list)
116 {
117 if (".".equals(path) || "..".equals(path))
118 continue;
119
120 try(Resource item = lib.addPath(path))
121 {
122 if (item.isDirectory())
123 addJars(item);
124 else
125 {
126 String lowerCasePath = path.toLowerCase(Locale.ENGLISH);
127 if (lowerCasePath.endsWith(".jar") ||
128 lowerCasePath.endsWith(".zip"))
129 {
130 URL url = item.getURL();
131 _classpath.add(url);
132 }
133 }
134 }
135 }
136 }
137
138
139 public void addPath (Resource path)
140 {
141 if (path == null || !path.exists())
142 throw new IllegalStateException ("No such path: "+path);
143 _classpath.add(path.getURL());
144 }
145
146
147 public URL[] asArray ()
148 {
149 return _classpath.toArray(new URL[_classpath.size()]);
150 }
151 }
152
153
154
155
156
157
158
159 public Runner()
160 {
161
162 }
163
164
165
166
167
168
169
170 public void usage(String error)
171 {
172 if (error!=null)
173 System.err.println("ERROR: "+error);
174 System.err.println("Usage: java [-Djetty.home=dir] -jar jetty-runner.jar [--help|--version] [ server opts] [[ context opts] context ...] ");
175 System.err.println("Server opts:");
176 System.err.println(" --version - display version and exit");
177 System.err.println(" --log file - request log filename (with optional 'yyyy_mm_dd' wildcard");
178 System.err.println(" --out file - info/warn/debug log filename (with optional 'yyyy_mm_dd' wildcard");
179 System.err.println(" --host name|ip - interface to listen on (default is all interfaces)");
180 System.err.println(" --port n - port to listen on (default 8080)");
181 System.err.println(" --stop-port n - port to listen for stop command");
182 System.err.println(" --stop-key n - security string for stop command (required if --stop-port is present)");
183 System.err.println(" [--jar file]*n - each tuple specifies an extra jar to be added to the classloader");
184 System.err.println(" [--lib dir]*n - each tuple specifies an extra directory of jars to be added to the classloader");
185 System.err.println(" [--classes dir]*n - each tuple specifies an extra directory of classes to be added to the classloader");
186 System.err.println(" --stats [unsecure|realm.properties] - enable stats gathering servlet context");
187 System.err.println(" [--config file]*n - each tuple specifies the name of a jetty xml config file to apply (in the order defined)");
188 System.err.println("Context opts:");
189 System.err.println(" [[--path /path] context]*n - WAR file, web app dir or context xml file, optionally with a context path");
190 System.exit(1);
191 }
192
193
194
195
196
197 public void version ()
198 {
199 System.err.println("org.eclipse.jetty.runner.Runner: "+Server.getVersion());
200 System.exit(1);
201 }
202
203
204
205
206
207
208
209
210
211 public void configure(String[] args) throws Exception
212 {
213
214 for (int i=0;i<args.length;i++)
215 {
216 if ("--lib".equals(args[i]))
217 {
218 try(Resource lib = Resource.newResource(args[++i]))
219 {
220 if (!lib.exists() || !lib.isDirectory())
221 usage("No such lib directory "+lib);
222 _classpath.addJars(lib);
223 }
224 }
225 else if ("--jar".equals(args[i]))
226 {
227 try(Resource jar = Resource.newResource(args[++i]))
228 {
229 if (!jar.exists() || jar.isDirectory())
230 usage("No such jar "+jar);
231 _classpath.addPath(jar);
232 }
233 }
234 else if ("--classes".equals(args[i]))
235 {
236 try(Resource classes = Resource.newResource(args[++i]))
237 {
238 if (!classes.exists() || !classes.isDirectory())
239 usage("No such classes directory "+classes);
240 _classpath.addPath(classes);
241 }
242 }
243 else if (args[i].startsWith("--"))
244 i++;
245 }
246
247 initClassLoader();
248
249 LOG.info("Runner");
250 LOG.debug("Runner classpath {}",_classpath);
251
252 String contextPath = __defaultContextPath;
253 boolean contextPathSet = false;
254 int port = __defaultPort;
255 String host = null;
256 int stopPort = 0;
257 String stopKey = null;
258
259 boolean runnerServerInitialized = false;
260
261 for (int i=0;i<args.length;i++)
262 {
263 switch (args[i])
264 {
265 case "--port":
266 port = Integer.parseInt(args[++i]);
267 break;
268 case "--host":
269 host = args[++i];
270 break;
271 case "--stop-port":
272 stopPort = Integer.parseInt(args[++i]);
273 break;
274 case "--stop-key":
275 stopKey = args[++i];
276 break;
277 case "--log":
278 _logFile = args[++i];
279 break;
280 case "--out":
281 String outFile = args[++i];
282 PrintStream out = new PrintStream(new RolloverFileOutputStream(outFile, true, -1));
283 LOG.info("Redirecting stderr/stdout to " + outFile);
284 System.setErr(out);
285 System.setOut(out);
286 break;
287 case "--path":
288 contextPath = args[++i];
289 contextPathSet = true;
290 break;
291 case "--config":
292 if (_configFiles == null)
293 _configFiles = new ArrayList<>();
294 _configFiles.add(args[++i]);
295 break;
296 case "--lib":
297 ++i;
298
299 break;
300 case "--jar":
301 ++i;
302
303 break;
304 case "--classes":
305 ++i;
306
307 break;
308 case "--stats":
309 _enableStats = true;
310 _statsPropFile = args[++i];
311 _statsPropFile = ("unsecure".equalsIgnoreCase(_statsPropFile) ? null : _statsPropFile);
312 break;
313 default:
314
315
316 if (!runnerServerInitialized)
317 {
318 if (_server == null)
319 {
320
321 _server = new Server();
322 }
323
324
325 if (_configFiles != null)
326 {
327 for (String cfg : _configFiles)
328 {
329 try (Resource resource = Resource.newResource(cfg)) {
330 XmlConfiguration xmlConfiguration = new XmlConfiguration(resource.getURL());
331 xmlConfiguration.configure(_server);
332 }
333 }
334 }
335
336
337 HandlerCollection handlers = (HandlerCollection) _server.getChildHandlerByClass(HandlerCollection.class);
338 if (handlers == null)
339 {
340 handlers = new HandlerCollection();
341 _server.setHandler(handlers);
342 }
343
344
345 _contexts = (ContextHandlerCollection) handlers.getChildHandlerByClass(ContextHandlerCollection.class);
346 if (_contexts == null)
347 {
348 _contexts = new ContextHandlerCollection();
349 prependHandler(_contexts, handlers);
350 }
351
352
353 if (_enableStats)
354 {
355
356 if (handlers.getChildHandlerByClass(StatisticsHandler.class) == null) {
357 StatisticsHandler statsHandler = new StatisticsHandler();
358
359
360 Handler oldHandler = _server.getHandler();
361 statsHandler.setHandler(oldHandler);
362 _server.setHandler(statsHandler);
363
364
365 ServletContextHandler statsContext = new ServletContextHandler(_contexts, "/stats");
366 statsContext.addServlet(new ServletHolder(new StatisticsServlet()), "/");
367 statsContext.setSessionHandler(new SessionHandler());
368 if (_statsPropFile != null)
369 {
370 HashLoginService loginService = new HashLoginService("StatsRealm", _statsPropFile);
371 Constraint constraint = new Constraint();
372 constraint.setName("Admin Only");
373 constraint.setRoles(new String[]{"admin"});
374 constraint.setAuthenticate(true);
375
376 ConstraintMapping cm = new ConstraintMapping();
377 cm.setConstraint(constraint);
378 cm.setPathSpec("/*");
379
380 ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
381 securityHandler.setLoginService(loginService);
382 securityHandler.setConstraintMappings(Collections.singletonList(cm));
383 securityHandler.setAuthenticator(new BasicAuthenticator());
384 statsContext.setSecurityHandler(securityHandler);
385 }
386 }
387 }
388
389
390 if (handlers.getChildHandlerByClass(DefaultHandler.class) == null)
391 {
392 handlers.addHandler(new DefaultHandler());
393 }
394
395
396 _logHandler = (RequestLogHandler) handlers.getChildHandlerByClass(RequestLogHandler.class);
397 if (_logHandler == null)
398 {
399 _logHandler = new RequestLogHandler();
400 handlers.addHandler(_logHandler);
401 }
402
403
404
405 Connector[] connectors = _server.getConnectors();
406 if (connectors == null || connectors.length == 0)
407 {
408 ServerConnector connector = new ServerConnector(_server);
409 connector.setPort(port);
410 if (host != null)
411 connector.setHost(host);
412 _server.addConnector(connector);
413 if (_enableStats)
414 connector.addBean(new ConnectorStatistics());
415 }
416 else
417 {
418 if (_enableStats)
419 {
420 for (Connector connector : connectors)
421 {
422 ((AbstractConnector) connector).addBean(new ConnectorStatistics());
423 }
424 }
425 }
426
427 runnerServerInitialized = true;
428 }
429
430
431 try (Resource ctx = Resource.newResource(args[i]))
432 {
433 if (!ctx.exists())
434 usage("Context '" + ctx + "' does not exist");
435
436 if (contextPathSet && !(contextPath.startsWith("/")))
437 contextPath = "/" + contextPath;
438
439
440 if (!ctx.isDirectory() && ctx.toString().toLowerCase(Locale.ENGLISH).endsWith(".xml"))
441 {
442
443 XmlConfiguration xmlConfiguration = new XmlConfiguration(ctx.getURL());
444 xmlConfiguration.getIdMap().put("Server", _server);
445 ContextHandler handler = (ContextHandler) xmlConfiguration.configure();
446 if (contextPathSet)
447 handler.setContextPath(contextPath);
448 _contexts.addHandler(handler);
449 handler.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", __containerIncludeJarPattern);
450 }
451 else
452 {
453
454 WebAppContext webapp = new WebAppContext(_contexts, ctx.toString(), contextPath);
455 webapp.setConfigurationClasses(__plusConfigurationClasses);
456 webapp.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
457 __containerIncludeJarPattern);
458 }
459 }
460
461 contextPathSet = false;
462 contextPath = __defaultContextPath;
463 break;
464 }
465 }
466
467 if (_server==null)
468 usage("No Contexts defined");
469 _server.setStopAtShutdown(true);
470
471 switch ((stopPort > 0 ? 1 : 0) + (stopKey != null ? 2 : 0))
472 {
473 case 1:
474 usage("Must specify --stop-key when --stop-port is specified");
475 break;
476
477 case 2:
478 usage("Must specify --stop-port when --stop-key is specified");
479 break;
480
481 case 3:
482 ShutdownMonitor monitor = ShutdownMonitor.getInstance();
483 monitor.setPort(stopPort);
484 monitor.setKey(stopKey);
485 monitor.setExitVm(true);
486 break;
487 }
488
489 if (_logFile!=null)
490 {
491 NCSARequestLog requestLog = new NCSARequestLog(_logFile);
492 requestLog.setExtended(false);
493 _logHandler.setRequestLog(requestLog);
494 }
495 }
496
497
498
499
500
501
502 protected void prependHandler (Handler handler, HandlerCollection handlers)
503 {
504 if (handler == null || handlers == null)
505 return;
506
507 Handler[] existing = handlers.getChildHandlers();
508 Handler[] children = new Handler[existing.length + 1];
509 children[0] = handler;
510 System.arraycopy(existing, 0, children, 1, existing.length);
511 handlers.setHandlers(children);
512 }
513
514
515
516
517
518
519
520 public void run() throws Exception
521 {
522 _server.start();
523 _server.join();
524 }
525
526
527
528
529
530 protected void initClassLoader()
531 {
532 URL[] paths = _classpath.asArray();
533
534 if (_classLoader==null && paths !=null && paths.length > 0)
535 {
536 ClassLoader context=Thread.currentThread().getContextClassLoader();
537
538 if (context==null)
539 _classLoader=new URLClassLoader(paths);
540 else
541 _classLoader=new URLClassLoader(paths, context);
542
543 Thread.currentThread().setContextClassLoader(_classLoader);
544 }
545 }
546
547
548
549
550
551
552
553 public static void main(String[] args)
554 {
555 Runner runner = new Runner();
556
557 try
558 {
559 if (args.length>0&&args[0].equalsIgnoreCase("--help"))
560 {
561 runner.usage(null);
562 }
563 else if (args.length>0&&args[0].equalsIgnoreCase("--version"))
564 {
565 runner.version();
566 }
567 else
568 {
569 runner.configure(args);
570 runner.run();
571 }
572 }
573 catch (Exception e)
574 {
575 e.printStackTrace();
576 runner.usage(null);
577 }
578 }
579 }