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.MalformedURLException;
24 import java.net.URL;
25 import java.net.URLClassLoader;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.List;
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
68 public class Runner
69 {
70 private static final Logger LOG = Log.getLogger(Runner.class);
71
72 public static final String[] __plusConfigurationClasses = new String[] {
73 org.eclipse.jetty.webapp.WebInfConfiguration.class.getCanonicalName(),
74 org.eclipse.jetty.webapp.WebXmlConfiguration.class.getCanonicalName(),
75 org.eclipse.jetty.webapp.MetaInfConfiguration.class.getCanonicalName(),
76 org.eclipse.jetty.webapp.FragmentConfiguration.class.getCanonicalName(),
77 org.eclipse.jetty.plus.webapp.EnvConfiguration.class.getCanonicalName(),
78 org.eclipse.jetty.plus.webapp.PlusConfiguration.class.getCanonicalName(),
79 org.eclipse.jetty.annotations.AnnotationConfiguration.class.getCanonicalName(),
80 org.eclipse.jetty.webapp.JettyWebXmlConfiguration.class.getCanonicalName()
81 };
82 public static final String __containerIncludeJarPattern = ".*/jetty-runner-[^/]*\\.jar$";
83 public static final String __defaultContextPath = "/";
84 public static final int __defaultPort = 8080;
85
86 protected Server _server;
87 protected URLClassLoader _classLoader;
88 protected Classpath _classpath = new Classpath();
89 protected ContextHandlerCollection _contexts;
90 protected RequestLogHandler _logHandler;
91 protected String _logFile;
92 protected ArrayList<String> _configFiles;
93 protected boolean _enableStats=false;
94 protected String _statsPropFile;
95
96
97
98
99
100
101
102
103 public class Classpath
104 {
105 private List<URL> _classpath = new ArrayList<URL>();
106
107 public void addJars (Resource lib) throws MalformedURLException, IOException
108 {
109 if (lib == null || !lib.exists())
110 throw new IllegalStateException ("No such lib: "+lib);
111
112 String[] list = lib.list();
113 if (list==null)
114 return;
115
116 for (String path : list)
117 {
118 if (".".equals(path) || "..".equals(path))
119 continue;
120
121 try(Resource item = lib.addPath(path);)
122 {
123 if (item.isDirectory())
124 addJars(item);
125 else
126 {
127 if (path.toLowerCase().endsWith(".jar") ||
128 path.toLowerCase().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 if ("--port".equals(args[i]))
264 port=Integer.parseInt(args[++i]);
265 else if ("--host".equals(args[i]))
266 host=args[++i];
267 else if ("--stop-port".equals(args[i]))
268 stopPort=Integer.parseInt(args[++i]);
269 else if ("--stop-key".equals(args[i]))
270 stopKey=args[++i];
271 else if ("--log".equals(args[i]))
272 _logFile=args[++i];
273 else if ("--out".equals(args[i]))
274 {
275 String outFile=args[++i];
276 PrintStream out = new PrintStream(new RolloverFileOutputStream(outFile,true,-1));
277 LOG.info("Redirecting stderr/stdout to "+outFile);
278 System.setErr(out);
279 System.setOut(out);
280 }
281 else if ("--path".equals(args[i]))
282 {
283 contextPath=args[++i];
284 contextPathSet=true;
285 }
286 else if ("--config".equals(args[i]))
287 {
288 if (_configFiles == null)
289 _configFiles = new ArrayList<String>();
290 _configFiles.add(args[++i]);
291 }
292 else if ("--lib".equals(args[i]))
293 {
294 ++i;
295 }
296 else if ("--jar".equals(args[i]))
297 {
298 ++i;
299 }
300 else if ("--classes".equals(args[i]))
301 {
302 ++i;
303 }
304 else if ("--stats".equals( args[i]))
305 {
306 _enableStats = true;
307 _statsPropFile = args[++i];
308 _statsPropFile = ("unsecure".equalsIgnoreCase(_statsPropFile)?null:_statsPropFile);
309 }
310 else
311 {
312 if (!runnerServerInitialized)
313 {
314 if (_server == null)
315 {
316
317 _server = new Server();
318 }
319
320
321 if (_configFiles != null)
322 {
323 for (String cfg:_configFiles)
324 {
325 try (Resource resource=Resource.newResource(cfg))
326 {
327 XmlConfiguration xmlConfiguration = new XmlConfiguration(resource.getURL());
328 xmlConfiguration.configure(_server);
329 }
330 }
331 }
332
333
334 HandlerCollection handlers = (HandlerCollection) _server.getChildHandlerByClass(HandlerCollection.class);
335 if (handlers == null)
336 {
337 handlers = new HandlerCollection();
338 _server.setHandler(handlers);
339 }
340
341
342 _contexts = (ContextHandlerCollection) handlers.getChildHandlerByClass(ContextHandlerCollection.class);
343 if (_contexts == null)
344 {
345 _contexts = new ContextHandlerCollection();
346 prependHandler(_contexts, handlers);
347 }
348
349
350 if (_enableStats)
351 {
352
353 if (handlers.getChildHandlerByClass(StatisticsHandler.class) == null)
354 {
355 StatisticsHandler statsHandler = new StatisticsHandler();
356
357
358 Handler oldHandler = _server.getHandler();
359 statsHandler.setHandler(oldHandler);
360 _server.setHandler(statsHandler);
361
362
363 ServletContextHandler statsContext = new ServletContextHandler(_contexts, "/stats");
364 statsContext.addServlet(new ServletHolder(new StatisticsServlet()), "/");
365 statsContext.setSessionHandler(new SessionHandler());
366 if (_statsPropFile != null)
367 {
368 HashLoginService loginService = new HashLoginService("StatsRealm", _statsPropFile);
369 Constraint constraint = new Constraint();
370 constraint.setName("Admin Only");
371 constraint.setRoles(new String[]{"admin"});
372 constraint.setAuthenticate(true);
373
374 ConstraintMapping cm = new ConstraintMapping();
375 cm.setConstraint(constraint);
376 cm.setPathSpec("/*");
377
378 ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
379 securityHandler.setLoginService(loginService);
380 securityHandler.setConstraintMappings(Collections.singletonList(cm));
381 securityHandler.setAuthenticator(new BasicAuthenticator());
382 statsContext.setSecurityHandler(securityHandler);
383 }
384 }
385 }
386
387
388 if (handlers.getChildHandlerByClass(DefaultHandler.class) == null)
389 {
390 handlers.addHandler(new DefaultHandler());
391 }
392
393
394 _logHandler = (RequestLogHandler)handlers.getChildHandlerByClass( RequestLogHandler.class );
395 if ( _logHandler == null )
396 {
397 _logHandler = new RequestLogHandler();
398 handlers.addHandler( _logHandler );
399 }
400
401
402
403 Connector[] connectors = _server.getConnectors();
404 if (connectors == null || connectors.length == 0)
405 {
406 ServerConnector connector = new ServerConnector(_server);
407 connector.setPort(port);
408 if (host != null)
409 connector.setHost(host);
410 _server.addConnector(connector);
411 if (_enableStats)
412 connector.addBean(new ConnectorStatistics());
413 }
414 else
415 {
416 if (_enableStats)
417 {
418 for (int j=0; j<connectors.length; j++)
419 {
420 ((AbstractConnector)connectors[j]).addBean(new ConnectorStatistics());
421 }
422 }
423 }
424
425 runnerServerInitialized = true;
426 }
427
428
429 try(Resource ctx = Resource.newResource(args[i]);)
430 {
431 if (!ctx.exists())
432 usage("Context '"+ctx+"' does not exist");
433
434 if (contextPathSet && !(contextPath.startsWith("/")))
435 contextPath = "/"+contextPath;
436
437
438 if (!ctx.isDirectory() && ctx.toString().toLowerCase().endsWith(".xml"))
439 {
440
441 XmlConfiguration xmlConfiguration=new XmlConfiguration(ctx.getURL());
442 xmlConfiguration.getIdMap().put("Server",_server);
443 ContextHandler handler=(ContextHandler)xmlConfiguration.configure();
444 if (contextPathSet)
445 handler.setContextPath(contextPath);
446 _contexts.addHandler(handler);
447 handler.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", __containerIncludeJarPattern);
448 }
449 else
450 {
451
452 WebAppContext webapp = new WebAppContext(_contexts,ctx.toString(),contextPath);
453 webapp.setConfigurationClasses(__plusConfigurationClasses);
454 webapp.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
455 __containerIncludeJarPattern);
456 }
457 }
458
459 contextPathSet = false;
460 contextPath = __defaultContextPath;
461 }
462 }
463
464 if (_server==null)
465 usage("No Contexts defined");
466 _server.setStopAtShutdown(true);
467
468 switch ((stopPort > 0 ? 1 : 0) + (stopKey != null ? 2 : 0))
469 {
470 case 1:
471 usage("Must specify --stop-key when --stop-port is specified");
472 break;
473
474 case 2:
475 usage("Must specify --stop-port when --stop-key is specified");
476 break;
477
478 case 3:
479 ShutdownMonitor monitor = ShutdownMonitor.getInstance();
480 monitor.setPort(stopPort);
481 monitor.setKey(stopKey);
482 monitor.setExitVm(true);
483 break;
484 }
485
486 if (_logFile!=null)
487 {
488 NCSARequestLog requestLog = new NCSARequestLog(_logFile);
489 requestLog.setExtended(false);
490 _logHandler.setRequestLog(requestLog);
491 }
492 }
493
494
495
496
497
498
499 protected void prependHandler (Handler handler, HandlerCollection handlers)
500 {
501 if (handler == null || handlers == null)
502 return;
503
504 Handler[] existing = handlers.getChildHandlers();
505 Handler[] children = new Handler[existing.length + 1];
506 children[0] = handler;
507 System.arraycopy(existing, 0, children, 1, existing.length);
508 handlers.setHandlers(children);
509 }
510
511
512
513
514
515
516
517 public void run() throws Exception
518 {
519 _server.start();
520 _server.join();
521 }
522
523
524
525
526
527 protected void initClassLoader()
528 {
529 URL[] paths = _classpath.asArray();
530
531 if (_classLoader==null && paths !=null && paths.length > 0)
532 {
533 ClassLoader context=Thread.currentThread().getContextClassLoader();
534
535 if (context==null)
536 _classLoader=new URLClassLoader(paths);
537 else
538 _classLoader=new URLClassLoader(paths, context);
539
540 Thread.currentThread().setContextClassLoader(_classLoader);
541 }
542 }
543
544
545
546
547
548
549
550 public static void main(String[] args)
551 {
552 Runner runner = new Runner();
553
554 try
555 {
556 if (args.length>0&&args[0].equalsIgnoreCase("--help"))
557 {
558 runner.usage(null);
559 }
560 else if (args.length>0&&args[0].equalsIgnoreCase("--version"))
561 {
562 runner.version();
563 }
564 else
565 {
566 runner.configure(args);
567 runner.run();
568 }
569 }
570 catch (Exception e)
571 {
572 e.printStackTrace();
573 runner.usage(null);
574 }
575 }
576 }