Source code: org/mortbay/jetty/Server.java
1 // ========================================================================
2 // Copyright (c) 2002 Mort Bay Consulting (Australia) Pty. Ltd.
3 // $Id: Server.java,v 1.30 2003/10/31 12:17:19 gregwilkins Exp $
4 // ========================================================================
5
6 package org.mortbay.jetty;
7
8 import java.io.IOException;
9 import java.lang.reflect.Method;
10 import java.net.URL;
11 import java.util.ArrayList;
12
13 import org.apache.commons.logging.Log;
14 import org.apache.commons.logging.LogFactory;
15 import org.mortbay.http.HttpContext;
16 import org.mortbay.http.HttpServer;
17 import org.mortbay.jetty.servlet.ServletHttpContext;
18 import org.mortbay.jetty.servlet.WebApplicationContext;
19 import org.mortbay.util.LogSupport;
20 import org.mortbay.util.Resource;
21 import org.mortbay.xml.XmlConfiguration;
22
23
24 /* ------------------------------------------------------------ */
25 /** The Jetty HttpServer.
26 *
27 * This specialization of org.mortbay.http.HttpServer adds knowledge
28 * about servlets and their specialized contexts. It also included
29 * support for initialization from xml configuration files
30 * that follow the XmlConfiguration dtd.
31 *
32 * HttpContexts created by Server are of the type
33 * org.mortbay.jetty.servlet.ServletHttpContext unless otherwise
34 * specified.
35 *
36 * This class also provides a main() method which starts a server for
37 * each config file passed on the command line. If the system
38 * property JETTY_NO_SHUTDOWN_HOOK is not set to true, then a shutdown
39 * hook is thread is registered to stop these servers.
40 *
41 * @see org.mortbay.xml.XmlConfiguration
42 * @see org.mortbay.jetty.servlet.ServletHttpContext
43 * @version $Revision: 1.30 $
44 * @author Greg Wilkins (gregw)
45 */
46 public class Server extends HttpServer
47 {
48 static Log log = LogFactory.getLog(Server.class);
49
50 private String _configuration;
51 private String _rootWebApp;
52
53 /* ------------------------------------------------------------ */
54 /** Constructor.
55 */
56 public Server()
57 {}
58
59 /* ------------------------------------------------------------ */
60 /** Constructor.
61 * @param configuration The filename or URL of the XML
62 * configuration file.
63 */
64 public Server(String configuration)
65 throws IOException
66 {
67 this(Resource.newResource(configuration).getURL());
68 }
69
70 /* ------------------------------------------------------------ */
71 /** Constructor.
72 * @param configuration The filename or URL of the XML
73 * configuration file.
74 */
75 public Server(Resource configuration)
76 throws IOException
77 {
78 this(configuration.getURL());
79 }
80
81 /* ------------------------------------------------------------ */
82 /** Constructor.
83 * @param configuration The filename or URL of the XML
84 * configuration file.
85 */
86 public Server(URL configuration)
87 throws IOException
88 {
89 _configuration=configuration.toString();
90 try
91 {
92 XmlConfiguration config=new XmlConfiguration(configuration);
93 config.configure(this);
94 }
95 catch(IOException e)
96 {
97 throw e;
98 }
99 catch(Exception e)
100 {
101 log.warn(LogSupport.EXCEPTION,e);
102 throw new IOException("Jetty configuration problem: "+e);
103 }
104 }
105
106 /* ------------------------------------------------------------ */
107 /** Get the root webapp name.
108 * @return The name of the root webapp (eg. "root" for root.war).
109 */
110 public String getRootWebApp()
111 {
112 return _rootWebApp;
113 }
114
115 /* ------------------------------------------------------------ */
116 /** Set the root webapp name.
117 * @param rootWebApp The name of the root webapp (eg. "root" for root.war).
118 */
119 public void setRootWebApp(String rootWebApp)
120 {
121 _rootWebApp = rootWebApp;
122 }
123
124 /* ------------------------------------------------------------ */
125 /** Configure the server from an XML file.
126 * @param configuration The filename or URL of the XML
127 * configuration file.
128 */
129 public void configure(String configuration)
130 throws IOException
131 {
132
133 URL url=Resource.newResource(configuration).getURL();
134 if (_configuration!=null && _configuration.equals(url.toString()))
135 return;
136 if (_configuration!=null)
137 throw new IllegalStateException("Already configured with "+_configuration);
138 try
139 {
140 XmlConfiguration config=new XmlConfiguration(url);
141 _configuration=url.toString();
142 config.configure(this);
143 }
144 catch(IOException e)
145 {
146 throw e;
147 }
148 catch(Exception e)
149 {
150 log.warn(LogSupport.EXCEPTION,e);
151 throw new IOException("Jetty configuration problem: "+e);
152 }
153 }
154
155 /* ------------------------------------------------------------ */
156 public String getConfiguration()
157 {
158 return _configuration;
159 }
160
161 /* ------------------------------------------------------------ */
162 /** Create a new ServletHttpContext.
163 * Ths method is called by HttpServer to creat new contexts. Thus
164 * calls to addContext or getContext that result in a new Context
165 * being created will return an
166 * org.mortbay.jetty.servlet.ServletHttpContext instance.
167 * @return ServletHttpContext
168 */
169 protected HttpContext newHttpContext()
170 {
171 return new ServletHttpContext();
172 }
173
174 /* ------------------------------------------------------------ */
175 /** Create a new WebApplicationContext.
176 * Ths method is called by Server to creat new contexts for web
177 * applications. Thus calls to addWebApplication that result in
178 * a new Context being created will return an correct class instance.
179 * Derived class can override this method to create instance of its
180 * own class derived from WebApplicationContext in case it needs more
181 * functionality.
182 * @param webApp The Web application directory or WAR file.
183 * @return WebApplicationContext
184 */
185 protected WebApplicationContext newWebApplicationContext(
186 String webApp
187 )
188 {
189 return new WebApplicationContext(webApp);
190 }
191
192 /* ------------------------------------------------------------ */
193 /** Add Web Application.
194 * @param contextPathSpec The context path spec. Which must be of
195 * the form / or /path/*
196 * @param webApp The Web application directory or WAR file.
197 * @return The WebApplicationContext
198 * @exception IOException
199 */
200 public WebApplicationContext addWebApplication(String contextPathSpec,
201 String webApp)
202 throws IOException
203 {
204 return addWebApplication(null,contextPathSpec,webApp);
205 }
206
207 /* ------------------------------------------------------------ */
208 /** Add Web Application.
209 * @param virtualHost Virtual host name or null
210 * @param contextPathSpec The context path spec. Which must be of
211 * the form / or /path/*
212 * @param webApp The Web application directory or WAR file.
213 * @return The WebApplicationContext
214 * @exception IOException
215 */
216 public WebApplicationContext addWebApplication(String virtualHost,
217 String contextPathSpec,
218 String webApp)
219 throws IOException
220 {
221 WebApplicationContext appContext =
222 newWebApplicationContext(webApp);
223 appContext.setContextPath(contextPathSpec);
224 addContext(virtualHost,appContext);
225 if(log.isDebugEnabled())log.debug("Web Application "+appContext+" added");
226 return appContext;
227 }
228
229
230 /* ------------------------------------------------------------ */
231 /** Add Web Applications.
232 * Add auto webapplications to the server. The name of the
233 * webapp directory or war is used as the context name. If a
234 * webapp is called "root" it is added at "/".
235 * @param webapps Directory file name or URL to look for auto webapplication.
236 * @exception IOException
237 */
238 public WebApplicationContext[] addWebApplications(String webapps)
239 throws IOException
240 {
241 return addWebApplications(null,webapps,null,false);
242 }
243
244 /* ------------------------------------------------------------ */
245 /** Add Web Applications.
246 * Add auto webapplications to the server. The name of the
247 * webapp directory or war is used as the context name. If the
248 * webapp matches the rootWebApp it is added as the "/" context.
249 * @param host Virtual host name or null
250 * @param webapps Directory file name or URL to look for auto webapplication.
251 * @exception IOException
252 */
253 public WebApplicationContext[] addWebApplications(String host,
254 String webapps)
255 throws IOException
256 {
257 return addWebApplications(host,webapps,null,false);
258 }
259
260 /* ------------------------------------------------------------ */
261 /** Add Web Applications.
262 * Add auto webapplications to the server. The name of the
263 * webapp directory or war is used as the context name. If the
264 * webapp matches the rootWebApp it is added as the "/" context.
265 * @param host Virtual host name or null
266 * @param webapps Directory file name or URL to look for auto
267 * webapplication.
268 * @param extract If true, extract war files
269 * @exception IOException
270 */
271 public WebApplicationContext[] addWebApplications(String host,
272 String webapps,
273 boolean extract)
274 throws IOException
275 {
276 return addWebApplications(host,webapps,null,extract);
277 }
278
279 /* ------------------------------------------------------------ */
280 /** Add Web Applications.
281 * Add auto webapplications to the server. The name of the
282 * webapp directory or war is used as the context name. If the
283 * webapp matches the rootWebApp it is added as the "/" context.
284 * @param host Virtual host name or null
285 * @param webapps Directory file name or URL to look for auto
286 * webapplication.
287 * @param defaults The defaults xml filename or URL which is
288 * loaded before any in the web app. Must respect the web.dtd.
289 * If null the default defaults file is used. If the empty string, then
290 * no defaults file is used.
291 * @param extract If true, extract war files
292 * @exception IOException
293 */
294 public WebApplicationContext[] addWebApplications(String host,
295 String webapps,
296 String defaults,
297 boolean extract)
298 throws IOException
299 {
300 return addWebApplications(host,webapps,null,extract,true);
301 }
302
303 /* ------------------------------------------------------------ */
304 /** Add Web Applications.
305 * Add auto webapplications to the server. The name of the
306 * webapp directory or war is used as the context name. If the
307 * webapp matches the rootWebApp it is added as the "/" context.
308 * @param host Virtual host name or null
309 * @param webapps Directory file name or URL to look for auto
310 * webapplication.
311 * @param defaults The defaults xml filename or URL which is
312 * loaded before any in the web app. Must respect the web.dtd.
313 * If null the default defaults file is used. If the empty string, then
314 * no defaults file is used.
315 * @param extract If true, extract war files
316 * @param java2CompliantClassLoader True if java2 compliance is applied to all webapplications
317 * @exception IOException
318 */
319 public WebApplicationContext[] addWebApplications(String host,
320 String webapps,
321 String defaults,
322 boolean extract,
323 boolean java2CompliantClassLoader)
324 throws IOException
325 {
326 ArrayList wacs = new ArrayList();
327 Resource r=Resource.newResource(webapps);
328 if (!r.exists())
329 throw new IllegalArgumentException("No such webapps resource "+r);
330
331 if (!r.isDirectory())
332 throw new IllegalArgumentException("Not directory webapps resource "+r);
333
334 String[] files=r.list();
335
336 for (int f=0;files!=null && f<files.length;f++)
337 {
338 String context=files[f];
339
340 if (context.equalsIgnoreCase("CVS/") ||
341 context.equalsIgnoreCase("CVS") ||
342 context.startsWith("."))
343 continue;
344
345
346 String app = r.addPath(r.encode(files[f])).toString();
347 if (context.toLowerCase().endsWith(".war") ||
348 context.toLowerCase().endsWith(".jar"))
349 {
350 context=context.substring(0,context.length()-4);
351 Resource unpacked=r.addPath(context);
352 if (unpacked!=null && unpacked.exists() && unpacked.isDirectory())
353 continue;
354 }
355
356 if (_rootWebApp!=null && (context.equals(_rootWebApp)||context.equals(_rootWebApp+"/")))
357 context="/";
358 else
359 context="/"+context;
360
361 WebApplicationContext wac= addWebApplication(host,
362 context,
363 app);
364 wac.setExtractWAR(extract);
365 wac.setClassLoaderJava2Compliant(java2CompliantClassLoader);
366 if (defaults!=null)
367 {
368 if (defaults.length()==0)
369 wac.setDefaultsDescriptor(null);
370 else
371 wac.setDefaultsDescriptor(defaults);
372 }
373 wacs.add(wac);
374 }
375
376 return (WebApplicationContext[])wacs.toArray(new WebApplicationContext[wacs.size()]);
377 }
378
379
380 /* ------------------------------------------------------------ */
381 /* ------------------------------------------------------------ */
382 /* ------------------------------------------------------------ */
383 public static void main(String[] arg)
384 {
385 String[] dftConfig={"etc/jetty.xml"};
386
387 if (arg.length==0)
388 {
389 log.info("Using default configuration: etc/jetty.xml");
390 arg=dftConfig;
391 }
392
393 final Server[] servers=new Server[arg.length];
394
395 // create and start the servers.
396 for (int i=0;i<arg.length;i++)
397 {
398 try
399 {
400 servers[i] = new Server(arg[i]);
401 servers[i].start();
402
403 }
404 catch(Exception e)
405 {
406 log.warn(LogSupport.EXCEPTION,e);
407 }
408 }
409
410 // Create and add a shutdown hook
411 if (!Boolean.getBoolean("JETTY_NO_SHUTDOWN_HOOK"))
412 {
413 try
414 {
415 Method shutdownHook=
416 java.lang.Runtime.class
417 .getMethod("addShutdownHook",new Class[] {java.lang.Thread.class});
418 Thread hook =
419 new Thread() {
420 public void run()
421 {
422 setName("Shutdown");
423 log.info("Shutdown hook executing");
424 for (int i=0;i<servers.length;i++)
425 {
426 if (servers[i]==null) continue;
427 try{servers[i].stop();}
428 catch(Exception e){log.warn(LogSupport.EXCEPTION,e);}
429 }
430
431 // Try to avoid JVM crash
432 try{Thread.sleep(1000);}
433 catch(Exception e){log.warn(LogSupport.EXCEPTION,e);}
434 }
435 };
436 shutdownHook.invoke(Runtime.getRuntime(),
437 new Object[]{hook});
438 }
439 catch(Exception e)
440 {
441 if(log.isDebugEnabled())log.debug("No shutdown hook in JVM ",e);
442 }
443 }
444
445 // create and start the servers.
446 for (int i=0;i<arg.length;i++)
447 {
448 try{servers[i].join();}
449 catch (Exception e){LogSupport.ignore(log,e);}
450 }
451 }
452 }
453
454
455
456