001: // ========================================================================
002: // Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
003: // ------------------------------------------------------------------------
004: // Licensed under the Apache License, Version 2.0 (the "License");
005: // you may not use this file except in compliance with the License.
006: // You may obtain a copy of the License at
007: // http://www.apache.org/licenses/LICENSE-2.0
008: // Unless required by applicable law or agreed to in writing, software
009: // distributed under the License is distributed on an "AS IS" BASIS,
010: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011: // See the License for the specific language governing permissions and
012: // limitations under the License.
013: // ========================================================================
014:
015: package org.mortbay.jetty;
016:
017: import java.io.IOException;
018: import java.lang.reflect.Method;
019: import java.util.ArrayList;
020: import java.util.Collection;
021: import java.util.Enumeration;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.ListIterator;
025:
026: import javax.servlet.ServletException;
027:
028: import org.mortbay.component.Container;
029: import org.mortbay.component.LifeCycle;
030: import org.mortbay.component.Container.Relationship;
031: import org.mortbay.jetty.bio.SocketConnector;
032: import org.mortbay.jetty.handler.HandlerCollection;
033: import org.mortbay.jetty.handler.HandlerWrapper;
034: import org.mortbay.jetty.security.UserRealm;
035: import org.mortbay.log.Log;
036: import org.mortbay.thread.BoundedThreadPool;
037: import org.mortbay.thread.ThreadPool;
038: import org.mortbay.util.Attributes;
039: import org.mortbay.util.AttributesMap;
040: import org.mortbay.util.LazyList;
041: import org.mortbay.util.MultiException;
042:
043: /* ------------------------------------------------------------ */
044: /** Jetty HTTP Servlet Server.
045: * This class is the main class for the Jetty HTTP Servlet server.
046: * It aggregates Connectors (HTTP request receivers) and request Handlers.
047: * The server is itself a handler and a ThreadPool. Connectors use the ThreadPool methods
048: * to run jobs that will eventually call the handle method.
049: *
050: * @org.apache.xbean.XBean description="Creates an embedded Jetty web server"
051: */
052: public class Server extends HandlerWrapper implements Attributes {
053: private static ShutdownHookThread hookThread = new ShutdownHookThread();
054:
055: private ThreadPool _threadPool;
056: private Connector[] _connectors;
057: private UserRealm[] _realms;
058: private Container _container = new Container();
059: private SessionIdManager _sessionIdManager;
060: private boolean _sendServerVersion = true; //send Server: header by default
061: private AttributesMap _attributes = new AttributesMap();
062: private String _version = "6.1.x";
063: private List _dependentLifeCycles = new ArrayList();
064:
065: /* ------------------------------------------------------------ */
066: public Server() {
067: setServer(this );
068: }
069:
070: /* ------------------------------------------------------------ */
071: /** Convenience constructor
072: * Creates server and a {@link SocketConnector} at the passed port.
073: */
074: public Server(int port) {
075: setServer(this );
076:
077: Connector connector = new SocketConnector();
078: connector.setPort(port);
079: setConnectors(new Connector[] { connector });
080: }
081:
082: /* ------------------------------------------------------------ */
083: /**
084: * @return Returns the container.
085: */
086: public Container getContainer() {
087: return _container;
088: }
089:
090: /* ------------------------------------------------------------ */
091: public boolean getStopAtShutdown() {
092: return hookThread.contains(this );
093: }
094:
095: /* ------------------------------------------------------------ */
096: public void setStopAtShutdown(boolean stop) {
097: if (stop)
098: hookThread.add(this );
099: else
100: hookThread.remove(this );
101: }
102:
103: /* ------------------------------------------------------------ */
104: /**
105: * @return Returns the connectors.
106: */
107: public Connector[] getConnectors() {
108: return _connectors;
109: }
110:
111: /* ------------------------------------------------------------ */
112: public void addConnector(Connector connector) {
113: setConnectors((Connector[]) LazyList.addToArray(
114: getConnectors(), connector, Connector.class));
115: }
116:
117: /* ------------------------------------------------------------ */
118: /**
119: * Conveniance method which calls {@link #getConnectors()} and {@link #setConnectors(Connector[])} to
120: * remove a connector.
121: * @param connector The connector to remove.
122: */
123: public void removeConnector(Connector connector) {
124: setConnectors((Connector[]) LazyList.removeFromArray(
125: getConnectors(), connector));
126: }
127:
128: /* ------------------------------------------------------------ */
129: /** Set the connectors for this server.
130: * Each connector has this server set as it's ThreadPool and its Handler.
131: * @param connectors The connectors to set.
132: */
133: public void setConnectors(Connector[] connectors) {
134: if (connectors != null) {
135: for (int i = 0; i < connectors.length; i++)
136: connectors[i].setServer(this );
137: }
138:
139: _container.update(this , _connectors, connectors, "connector");
140: _connectors = connectors;
141: }
142:
143: /* ------------------------------------------------------------ */
144: /**
145: * @return Returns the threadPool.
146: */
147: public ThreadPool getThreadPool() {
148: return _threadPool;
149: }
150:
151: /* ------------------------------------------------------------ */
152: /**
153: * @param threadPool The threadPool to set.
154: */
155: public void setThreadPool(ThreadPool threadPool) {
156: _container.update(this , _threadPool, threadPool, "threadpool",
157: true);
158: _threadPool = threadPool;
159: }
160:
161: /* ------------------------------------------------------------ */
162: protected void doStart() throws Exception {
163: if (this .getClass().getPackage().getImplementationVersion() != null)
164: _version = this .getClass().getPackage()
165: .getImplementationVersion();
166: Log.info("jetty-" + _version);
167: HttpGenerator.setServerVersion(_version);
168: MultiException mex = new MultiException();
169:
170: Iterator itor = _dependentLifeCycles.iterator();
171: while (itor.hasNext()) {
172: try {
173: ((LifeCycle) itor.next()).start();
174: } catch (Throwable e) {
175: mex.add(e);
176: }
177: }
178:
179: if (_threadPool == null) {
180: BoundedThreadPool btp = new BoundedThreadPool();
181: setThreadPool(btp);
182: }
183:
184: if (_sessionIdManager != null)
185: _sessionIdManager.start();
186:
187: try {
188: if (_threadPool instanceof LifeCycle)
189: ((LifeCycle) _threadPool).start();
190: } catch (Throwable e) {
191: mex.add(e);
192: }
193:
194: try {
195: super .doStart();
196: } catch (Throwable e) {
197: Log.warn("Error starting handlers", e);
198: }
199:
200: if (_connectors != null) {
201: for (int i = 0; i < _connectors.length; i++) {
202: try {
203: _connectors[i].start();
204: } catch (Throwable e) {
205: mex.add(e);
206: }
207: }
208: }
209: mex.ifExceptionThrow();
210: }
211:
212: /* ------------------------------------------------------------ */
213: protected void doStop() throws Exception {
214: MultiException mex = new MultiException();
215:
216: if (_connectors != null) {
217: for (int i = _connectors.length; i-- > 0;)
218: try {
219: _connectors[i].stop();
220: } catch (Throwable e) {
221: mex.add(e);
222: }
223: }
224:
225: try {
226: super .doStop();
227: } catch (Throwable e) {
228: mex.add(e);
229: }
230:
231: if (_sessionIdManager != null)
232: _sessionIdManager.stop();
233:
234: try {
235: if (_threadPool instanceof LifeCycle)
236: ((LifeCycle) _threadPool).stop();
237: } catch (Throwable e) {
238: mex.add(e);
239: }
240:
241: if (!_dependentLifeCycles.isEmpty()) {
242: ListIterator itor = _dependentLifeCycles
243: .listIterator(_dependentLifeCycles.size() - 1);
244: while (itor.hasPrevious()) {
245: try {
246: ((LifeCycle) itor.previous()).stop();
247: } catch (Throwable e) {
248: mex.add(e);
249: }
250: }
251: }
252:
253: mex.ifExceptionThrow();
254: }
255:
256: /* ------------------------------------------------------------ */
257: /* Handle a request from a connection.
258: * Called to handle a request on the connection when either the header has been received,
259: * or after the entire request has been received (for short requests of known length).
260: */
261: public void handle(HttpConnection connection) throws IOException,
262: ServletException {
263: String target = connection.getRequest().getPathInfo();
264: if (Log.isDebugEnabled()) {
265: Log.debug("REQUEST " + target + " on " + connection);
266: handle(target, connection.getRequest(), connection
267: .getResponse(), Handler.REQUEST);
268: Log.debug("RESPONSE " + target + " "
269: + connection.getResponse().getStatus());
270: } else
271: handle(target, connection.getRequest(), connection
272: .getResponse(), Handler.REQUEST);
273: }
274:
275: /* ------------------------------------------------------------ */
276: public void join() throws InterruptedException {
277: getThreadPool().join();
278: }
279:
280: /* ------------------------------------------------------------ */
281: /**
282: * @return Map of realm name to UserRealm instances.
283: */
284: public UserRealm[] getUserRealms() {
285: return _realms;
286: }
287:
288: /* ------------------------------------------------------------ */
289: /**
290: * @param realms Map of realm name to UserRealm instances.
291: */
292: public void setUserRealms(UserRealm[] realms) {
293: _container.update(this , _realms, realms, "realm", true);
294: _realms = realms;
295: }
296:
297: /* ------------------------------------------------------------ */
298: public void addUserRealm(UserRealm realm) {
299: setUserRealms((UserRealm[]) LazyList.addToArray(
300: getUserRealms(), realm, UserRealm.class));
301: }
302:
303: /* ------------------------------------------------------------ */
304: public void removeUserRealm(UserRealm realm) {
305: setUserRealms((UserRealm[]) LazyList.removeFromArray(
306: getUserRealms(), realm));
307: }
308:
309: /* ------------------------------------------------------------ */
310: /**
311: * @return Returns the sessionIdManager.
312: */
313: public SessionIdManager getSessionIdManager() {
314: return _sessionIdManager;
315: }
316:
317: /* ------------------------------------------------------------ */
318: /**
319: * @param sessionIdManager The sessionIdManager to set.
320: */
321: public void setSessionIdManager(SessionIdManager sessionIdManager) {
322: _container.update(this , _sessionIdManager, sessionIdManager,
323: "sessionIdManager", true);
324: _sessionIdManager = sessionIdManager;
325: }
326:
327: /* ------------------------------------------------------------ */
328: public void setSendServerVersion(boolean sendServerVersion) {
329: _sendServerVersion = sendServerVersion;
330: }
331:
332: /* ------------------------------------------------------------ */
333: public boolean getSendServerVersion() {
334: return _sendServerVersion;
335: }
336:
337: /* ------------------------------------------------------------ */
338: public String getVersion() {
339: return _version;
340: }
341:
342: /**
343: * Add a LifeCycle object to be started/stopped
344: * along with the Server.
345: * @param c
346: */
347: public void addLifeCycle(LifeCycle c) {
348: if (c == null)
349: return;
350: if (!_dependentLifeCycles.contains(c)) {
351: _dependentLifeCycles.add(c);
352: _container.addBean(c);
353: }
354: try {
355: if (isStarted())
356: ((LifeCycle) c).start();
357: } catch (Exception e) {
358: throw new RuntimeException(e);
359: }
360: }
361:
362: /**
363: * Remove a LifeCycle object to be started/stopped
364: * along with the Server
365: * @param c
366: */
367: public void removeLifeCycle(LifeCycle c) {
368: if (c == null)
369: return;
370: _dependentLifeCycles.remove(c);
371: _container.removeBean(c);
372: }
373:
374: /* ------------------------------------------------------------ */
375: /* ------------------------------------------------------------ */
376: /* ------------------------------------------------------------ */
377: /**
378: * ShutdownHook thread for stopping all servers.
379: *
380: * Thread is hooked first time list of servers is changed.
381: */
382: private static class ShutdownHookThread extends Thread {
383: private boolean hooked = false;
384: private ArrayList servers = new ArrayList();
385:
386: /**
387: * Hooks this thread for shutdown.
388: *
389: * @see java.lang.Runtime#addShutdownHook(java.lang.Thread)
390: */
391: private void createShutdownHook() {
392: if (!Boolean.getBoolean("JETTY_NO_SHUTDOWN_HOOK")
393: && !hooked) {
394: try {
395: Method shutdownHook = java.lang.Runtime.class
396: .getMethod(
397: "addShutdownHook",
398: new Class[] { java.lang.Thread.class });
399: shutdownHook.invoke(Runtime.getRuntime(),
400: new Object[] { this });
401: this .hooked = true;
402: } catch (Exception e) {
403: if (Log.isDebugEnabled())
404: Log.debug("No shutdown hook in JVM ", e);
405: }
406: }
407: }
408:
409: /**
410: * Add Server to servers list.
411: */
412: public boolean add(Server server) {
413: createShutdownHook();
414: return this .servers.add(server);
415: }
416:
417: /**
418: * Contains Server in servers list?
419: */
420: public boolean contains(Server server) {
421: return this .servers.contains(server);
422: }
423:
424: /**
425: * Append all Servers from Collection
426: */
427: public boolean addAll(Collection c) {
428: createShutdownHook();
429: return this .servers.addAll(c);
430: }
431:
432: /**
433: * Clear list of Servers.
434: */
435: public void clear() {
436: createShutdownHook();
437: this .servers.clear();
438: }
439:
440: /**
441: * Remove Server from list.
442: */
443: public boolean remove(Server server) {
444: createShutdownHook();
445: return this .servers.remove(server);
446: }
447:
448: /**
449: * Remove all Servers in Collection from list.
450: */
451: public boolean removeAll(Collection c) {
452: createShutdownHook();
453: return this .servers.removeAll(c);
454: }
455:
456: /**
457: * Stop all Servers in list.
458: */
459: public void run() {
460: setName("Shutdown");
461: Log.info("Shutdown hook executing");
462: Iterator it = servers.iterator();
463: while (it.hasNext()) {
464: Server svr = (Server) it.next();
465: if (svr == null)
466: continue;
467: try {
468: svr.stop();
469: } catch (Exception e) {
470: Log.warn(e);
471: }
472: Log.info("Shutdown hook complete");
473:
474: // Try to avoid JVM crash
475: try {
476: Thread.sleep(1000);
477: } catch (Exception e) {
478: Log.warn(e);
479: }
480: }
481: }
482: }
483:
484: /* ------------------------------------------------------------ */
485: /**
486: */
487: public void addHandler(Handler handler) {
488: if (getHandler() == null)
489: setHandler(handler);
490: else if (getHandler() instanceof HandlerCollection)
491: ((HandlerCollection) getHandler()).addHandler(handler);
492: else {
493: HandlerCollection collection = new HandlerCollection();
494: collection.setHandlers(new Handler[] { getHandler(),
495: handler });
496: setHandler(collection);
497: }
498: }
499:
500: /* ------------------------------------------------------------ */
501: /**
502: */
503: public void removeHandler(Handler handler) {
504: if (getHandler() instanceof HandlerCollection)
505: ((HandlerCollection) getHandler()).removeHandler(handler);
506: }
507:
508: /* ------------------------------------------------------------ */
509: /**
510: */
511: public Handler[] getHandlers() {
512: if (getHandler() instanceof HandlerCollection)
513: return ((HandlerCollection) getHandler()).getHandlers();
514:
515: return null;
516: }
517:
518: /* ------------------------------------------------------------ */
519: /**
520: */
521: public void setHandlers(Handler[] handlers) {
522: HandlerCollection collection;
523: if (getHandler() instanceof HandlerCollection)
524: collection = (HandlerCollection) getHandler();
525: else {
526: collection = new HandlerCollection();
527: setHandler(collection);
528: }
529:
530: collection.setHandlers(handlers);
531: }
532:
533: /* ------------------------------------------------------------ */
534: /*
535: * @see org.mortbay.util.AttributesMap#clearAttributes()
536: */
537: public void clearAttributes() {
538: _attributes.clearAttributes();
539: }
540:
541: /* ------------------------------------------------------------ */
542: /*
543: * @see org.mortbay.util.AttributesMap#getAttribute(java.lang.String)
544: */
545: public Object getAttribute(String name) {
546: return _attributes.getAttribute(name);
547: }
548:
549: /* ------------------------------------------------------------ */
550: /*
551: * @see org.mortbay.util.AttributesMap#getAttributeNames()
552: */
553: public Enumeration getAttributeNames() {
554: return _attributes.getAttributeNames();
555: }
556:
557: /* ------------------------------------------------------------ */
558: /*
559: * @see org.mortbay.util.AttributesMap#removeAttribute(java.lang.String)
560: */
561: public void removeAttribute(String name) {
562: _attributes.removeAttribute(name);
563: }
564:
565: /* ------------------------------------------------------------ */
566: /*
567: * @see org.mortbay.util.AttributesMap#setAttribute(java.lang.String, java.lang.Object)
568: */
569: public void setAttribute(String name, Object attribute) {
570: _attributes.setAttribute(name, attribute);
571: }
572:
573: }
|