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 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 public void addPath (Resource path)
139 {
140 if (path == null || !path.exists())
141 throw new IllegalStateException ("No such path: "+path);
142 _classpath.add(path.getURL());
143 }
144
145
146 public URL[] asArray ()
147 {
148 return _classpath.toArray(new URL[_classpath.size()]);
149 }
150 }
151
152
153
154
155
156
157
158 public Runner()
159 {
160
161 }
162
163
164
165
166
167
168
169 public void usage(String error)
170 {
171 if (error!=null)
172 System.err.println("ERROR: "+error);
173 System.err.println("Usage: java [-Djetty.home=dir] -jar jetty-runner.jar [--help|--version] [ server opts] [[ context opts] context ...] ");
174 System.err.println("Server opts:");
175 System.err.println(" --version - display version and exit");
176 System.err.println(" --log file - request log filename (with optional 'yyyy_mm_dd' wildcard");
177 System.err.println(" --out file - info/warn/debug log filename (with optional 'yyyy_mm_dd' wildcard");
178 System.err.println(" --host name|ip - interface to listen on (default is all interfaces)");
179 System.err.println(" --port n - port to listen on (default 8080)");
180 System.err.println(" --stop-port n - port to listen for stop command");
181 System.err.println(" --stop-key n - security string for stop command (required if --stop-port is present)");
182 System.err.println(" [--jar file]*n - each tuple specifies an extra jar to be added to the classloader");
183 System.err.println(" [--lib dir]*n - each tuple specifies an extra directory of jars to be added to the classloader");
184 System.err.println(" [--classes dir]*n - each tuple specifies an extra directory of classes to be added to the classloader");
185 System.err.println(" --stats [unsecure|realm.properties] - enable stats gathering servlet context");
186 System.err.println(" [--config file]*n - each tuple specifies the name of a jetty xml config file to apply (in the order defined)");
187 System.err.println("Context opts:");
188 System.err.println(" [[--path /path] context]*n - WAR file, web app dir or context xml file, optionally with a context path");
189 System.exit(1);
190 }
191
192
193
194
195
196 public void version ()
197 {
198 System.err.println("org.eclipse.jetty.runner.Runner: "+Server.getVersion());
199 System.exit(1);
200 }
201
202
203
204
205
206
207
208
209
210 public void configure(String[] args) throws Exception
211 {
212
213 for (int i=0;i<args.length;i++)
214 {
215 if ("--lib".equals(args[i]))
216 {
217 Resource lib = Resource.newResource(args[++i]);
218 if (!lib.exists() || !lib.isDirectory())
219 usage("No such lib directory "+lib);
220 _classpath.addJars(lib);
221 }
222 else if ("--jar".equals(args[i]))
223 {
224 Resource jar = Resource.newResource(args[++i]);
225 if (!jar.exists() || jar.isDirectory())
226 usage("No such jar "+jar);
227 _classpath.addPath(jar);
228 }
229 else if ("--classes".equals(args[i]))
230 {
231 Resource classes = Resource.newResource(args[++i]);
232 if (!classes.exists() || !classes.isDirectory())
233 usage("No such classes directory "+classes);
234 _classpath.addPath(classes);
235 }
236 else if (args[i].startsWith("--"))
237 i++;
238 }
239
240 initClassLoader();
241
242 LOG.info("Runner");
243 LOG.debug("Runner classpath {}",_classpath);
244
245 String contextPath = __defaultContextPath;
246 boolean contextPathSet = false;
247 int port = __defaultPort;
248 String host = null;
249 int stopPort = 0;
250 String stopKey = null;
251
252 boolean runnerServerInitialized = false;
253
254 for (int i=0;i<args.length;i++)
255 {
256 if ("--port".equals(args[i]))
257 port=Integer.parseInt(args[++i]);
258 else if ("--host".equals(args[i]))
259 host=args[++i];
260 else if ("--stop-port".equals(args[i]))
261 stopPort=Integer.parseInt(args[++i]);
262 else if ("--stop-key".equals(args[i]))
263 stopKey=args[++i];
264 else if ("--log".equals(args[i]))
265 _logFile=args[++i];
266 else if ("--out".equals(args[i]))
267 {
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 }
274 else if ("--path".equals(args[i]))
275 {
276 contextPath=args[++i];
277 contextPathSet=true;
278 }
279 else if ("--config".equals(args[i]))
280 {
281 if (_configFiles == null)
282 _configFiles = new ArrayList<String>();
283 _configFiles.add(args[++i]);
284 }
285 else if ("--lib".equals(args[i]))
286 {
287 ++i;
288 }
289 else if ("--jar".equals(args[i]))
290 {
291 ++i;
292 }
293 else if ("--classes".equals(args[i]))
294 {
295 ++i;
296 }
297 else if ("--stats".equals( args[i]))
298 {
299 _enableStats = true;
300 _statsPropFile = args[++i];
301 _statsPropFile = ("unsecure".equalsIgnoreCase(_statsPropFile)?null:_statsPropFile);
302 }
303 else
304 {
305 if (!runnerServerInitialized)
306 {
307 if (_server == null)
308 {
309
310 _server = new Server();
311 }
312
313
314 if (_configFiles != null)
315 {
316 for (String cfg:_configFiles)
317 {
318 XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.newResource(cfg).getURL());
319 xmlConfiguration.configure(_server);
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 {
345 StatisticsHandler statsHandler = new StatisticsHandler();
346
347
348 Handler oldHandler = _server.getHandler();
349 statsHandler.setHandler(oldHandler);
350 _server.setHandler(statsHandler);
351
352
353 ServletContextHandler statsContext = new ServletContextHandler(_contexts, "/stats");
354 statsContext.addServlet(new ServletHolder(new StatisticsServlet()), "/");
355 statsContext.setSessionHandler(new SessionHandler());
356 if (_statsPropFile != null)
357 {
358 HashLoginService loginService = new HashLoginService("StatsRealm", _statsPropFile);
359 Constraint constraint = new Constraint();
360 constraint.setName("Admin Only");
361 constraint.setRoles(new String[]{"admin"});
362 constraint.setAuthenticate(true);
363
364 ConstraintMapping cm = new ConstraintMapping();
365 cm.setConstraint(constraint);
366 cm.setPathSpec("/*");
367
368 ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
369 securityHandler.setLoginService(loginService);
370 securityHandler.setConstraintMappings(Collections.singletonList(cm));
371 securityHandler.setAuthenticator(new BasicAuthenticator());
372 statsContext.setSecurityHandler(securityHandler);
373 }
374 }
375 }
376
377
378 if (handlers.getChildHandlerByClass(DefaultHandler.class) == null)
379 {
380 handlers.addHandler(new DefaultHandler());
381 }
382
383
384 _logHandler = (RequestLogHandler)handlers.getChildHandlerByClass( RequestLogHandler.class );
385 if ( _logHandler == null )
386 {
387 _logHandler = new RequestLogHandler();
388 handlers.addHandler( _logHandler );
389 }
390
391
392
393 Connector[] connectors = _server.getConnectors();
394 if (connectors == null || connectors.length == 0)
395 {
396 ServerConnector connector = new ServerConnector(_server);
397 connector.setPort(port);
398 if (host != null)
399 connector.setHost(host);
400 _server.addConnector(connector);
401 if (_enableStats)
402 connector.addBean(new ConnectorStatistics());
403 }
404 else
405 {
406 if (_enableStats)
407 {
408 for (int j=0; j<connectors.length; j++)
409 {
410 ((AbstractConnector)connectors[j]).addBean(new ConnectorStatistics());
411 }
412 }
413 }
414
415 runnerServerInitialized = true;
416 }
417
418
419 Resource ctx = Resource.newResource(args[i]);
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 }
451 }
452
453 if (_server==null)
454 usage("No Contexts defined");
455 _server.setStopAtShutdown(true);
456
457 switch ((stopPort > 0 ? 1 : 0) + (stopKey != null ? 2 : 0))
458 {
459 case 1:
460 usage("Must specify --stop-key when --stop-port is specified");
461 break;
462
463 case 2:
464 usage("Must specify --stop-port when --stop-key is specified");
465 break;
466
467 case 3:
468 ShutdownMonitor monitor = ShutdownMonitor.getInstance();
469 monitor.setPort(stopPort);
470 monitor.setKey(stopKey);
471 monitor.setExitVm(true);
472 break;
473 }
474
475 if (_logFile!=null)
476 {
477 NCSARequestLog requestLog = new NCSARequestLog(_logFile);
478 requestLog.setExtended(false);
479 _logHandler.setRequestLog(requestLog);
480 }
481 }
482
483
484
485
486
487
488 protected void prependHandler (Handler handler, HandlerCollection handlers)
489 {
490 if (handler == null || handlers == null)
491 return;
492
493 Handler[] existing = handlers.getChildHandlers();
494 Handler[] children = new Handler[existing.length + 1];
495 children[0] = handler;
496 System.arraycopy(existing, 0, children, 1, existing.length);
497 handlers.setHandlers(children);
498 }
499
500
501
502
503
504
505
506 public void run() throws Exception
507 {
508 _server.start();
509 _server.join();
510 }
511
512
513
514
515
516 protected void initClassLoader()
517 {
518 URL[] paths = _classpath.asArray();
519
520 if (_classLoader==null && paths !=null && paths.length > 0)
521 {
522 ClassLoader context=Thread.currentThread().getContextClassLoader();
523
524 if (context==null)
525 _classLoader=new URLClassLoader(paths);
526 else
527 _classLoader=new URLClassLoader(paths, context);
528
529 Thread.currentThread().setContextClassLoader(_classLoader);
530 }
531 }
532
533
534
535
536
537
538
539 public static void main(String[] args)
540 {
541 Runner runner = new Runner();
542
543 try
544 {
545 if (args.length>0&&args[0].equalsIgnoreCase("--help"))
546 {
547 runner.usage(null);
548 }
549 else if (args.length>0&&args[0].equalsIgnoreCase("--version"))
550 {
551 runner.version();
552 }
553 else
554 {
555 runner.configure(args);
556 runner.run();
557 }
558 }
559 catch (Exception e)
560 {
561 e.printStackTrace();
562 runner.usage(null);
563 }
564 }
565 }