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