001: // ========================================================================
002: // Copyright 199-2004 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.servlet;
016:
017: import java.io.IOException;
018: import java.security.Principal;
019: import java.util.Enumeration;
020: import java.util.HashMap;
021: import java.util.Map;
022: import java.util.Stack;
023:
024: import javax.servlet.Servlet;
025: import javax.servlet.ServletConfig;
026: import javax.servlet.ServletContext;
027: import javax.servlet.ServletException;
028: import javax.servlet.ServletRequest;
029: import javax.servlet.ServletResponse;
030: import javax.servlet.SingleThreadModel;
031: import javax.servlet.UnavailableException;
032:
033: import org.mortbay.jetty.HttpConnection;
034: import org.mortbay.jetty.Request;
035: import org.mortbay.jetty.handler.ContextHandler;
036: import org.mortbay.jetty.security.SecurityHandler;
037: import org.mortbay.jetty.security.UserRealm;
038: import org.mortbay.log.Log;
039:
040: /* --------------------------------------------------------------------- */
041: /** Servlet Instance and Context Holder.
042: * Holds the name, params and some state of a javax.servlet.Servlet
043: * instance. It implements the ServletConfig interface.
044: * This class will organise the loading of the servlet when needed or
045: * requested.
046: *
047: * @author Greg Wilkins
048: */
049: public class ServletHolder extends Holder implements Comparable {
050: /* ---------------------------------------------------------------- */
051:
052: private int _initOrder;
053: private boolean _initOnStartup = false;
054: private Map _roleMap;
055: private String _forcedPath;
056: private String _runAs;
057: private UserRealm _realm;
058:
059: private transient Stack _servlets;
060: private transient Servlet _servlet;
061: private transient Config _config;
062: private transient long _unavailable;
063: private transient UnavailableException _unavailableEx;
064:
065: /* ---------------------------------------------------------------- */
066: /** Constructor .
067: */
068: public ServletHolder() {
069: }
070:
071: /* ---------------------------------------------------------------- */
072: /** Constructor for existing servlet.
073: */
074: public ServletHolder(Servlet servlet) {
075: setServlet(servlet);
076: }
077:
078: /* ---------------------------------------------------------------- */
079: /** Constructor for existing servlet.
080: */
081: public ServletHolder(Class servlet) {
082: super (servlet);
083: }
084:
085: /* ------------------------------------------------------------ */
086: public synchronized void setServlet(Servlet servlet) {
087: if (servlet == null || servlet instanceof SingleThreadModel)
088: throw new IllegalArgumentException();
089:
090: _extInstance = true;
091: _servlet = servlet;
092: setHeldClass(servlet.getClass());
093: if (getName() == null)
094: setName(servlet.getClass().getName() + "-"
095: + super .hashCode());
096: }
097:
098: /* ------------------------------------------------------------ */
099: public int getInitOrder() {
100: return _initOrder;
101: }
102:
103: /* ------------------------------------------------------------ */
104: /** Set the initialize order.
105: * Holders with order<0, are initialized on use. Those with
106: * order>=0 are initialized in increasing order when the handler
107: * is started.
108: */
109: public void setInitOrder(int order) {
110: _initOnStartup = true;
111: _initOrder = order;
112: }
113:
114: /* ------------------------------------------------------------ */
115: /** Comparitor by init order.
116: */
117: public int compareTo(Object o) {
118: if (o instanceof ServletHolder) {
119: ServletHolder sh = (ServletHolder) o;
120: if (sh == this )
121: return 0;
122: if (sh._initOrder < _initOrder)
123: return 1;
124: if (sh._initOrder > _initOrder)
125: return -1;
126:
127: int c = (_className != null && sh._className != null) ? _className
128: .compareTo(sh._className)
129: : 0;
130: if (c == 0)
131: c = _name.compareTo(sh._name);
132: if (c == 0)
133: c = this .hashCode() > o.hashCode() ? 1 : -1;
134: return c;
135: }
136: return 1;
137: }
138:
139: /* ------------------------------------------------------------ */
140: public boolean equals(Object o) {
141: return compareTo(o) == 0;
142: }
143:
144: /* ------------------------------------------------------------ */
145: public int hashCode() {
146: return _name.hashCode();
147: }
148:
149: /* ------------------------------------------------------------ */
150: /** Link a user role.
151: * Translate the role name used by a servlet, to the link name
152: * used by the container.
153: * @param name The role name as used by the servlet
154: * @param link The role name as used by the container.
155: */
156: public synchronized void setUserRoleLink(String name, String link) {
157: if (_roleMap == null)
158: _roleMap = new HashMap();
159: _roleMap.put(name, link);
160: }
161:
162: /* ------------------------------------------------------------ */
163: /** get a user role link.
164: * @param name The name of the role
165: * @return The name as translated by the link. If no link exists,
166: * the name is returned.
167: */
168: public String getUserRoleLink(String name) {
169: if (_roleMap == null)
170: return name;
171: String link = (String) _roleMap.get(name);
172: return (link == null) ? name : link;
173: }
174:
175: /* ------------------------------------------------------------ */
176: public Map getRoleMap() {
177: return _roleMap;
178: }
179:
180: /* ------------------------------------------------------------ */
181: /**
182: * @param role Role name that is added to UserPrincipal when this servlet
183: * is called.
184: */
185: public void setRunAs(String role) {
186: _runAs = role;
187: }
188:
189: /* ------------------------------------------------------------ */
190: public String getRunAs() {
191: return _runAs;
192: }
193:
194: /* ------------------------------------------------------------ */
195: /**
196: * @return Returns the forcedPath.
197: */
198: public String getForcedPath() {
199: return _forcedPath;
200: }
201:
202: /* ------------------------------------------------------------ */
203: /**
204: * @param forcedPath The forcedPath to set.
205: */
206: public void setForcedPath(String forcedPath) {
207: _forcedPath = forcedPath;
208: }
209:
210: /* ------------------------------------------------------------ */
211: public void doStart() throws Exception {
212: _unavailable = 0;
213: try {
214: super .doStart();
215: checkServletType();
216: } catch (UnavailableException ue) {
217: makeUnavailable(ue);
218: }
219:
220: _config = new Config();
221:
222: if (_runAs != null)
223: _realm = ((SecurityHandler) (ContextHandler
224: .getCurrentContext().getContextHandler()
225: .getChildHandlerByClass(SecurityHandler.class)))
226: .getUserRealm();
227:
228: if (javax.servlet.SingleThreadModel.class
229: .isAssignableFrom(_class))
230: _servlets = new Stack();
231:
232: if (_extInstance || _initOnStartup) {
233: if (_servlet == null)
234: _servlet = (Servlet) newInstance();
235: try {
236: initServlet(_servlet, _config);
237: } catch (Throwable e) {
238: _servlet = null;
239: _config = null;
240: if (e instanceof Exception)
241: throw (Exception) e;
242: else if (e instanceof Error)
243: throw (Error) e;
244: else
245: throw new ServletException(e);
246: }
247:
248: if (_servlets != null) {
249: _servlets.push(_servlet);
250: }
251: }
252: }
253:
254: /* ------------------------------------------------------------ */
255: public void doStop() {
256: Principal user = null;
257: try {
258: // Handle run as
259: if (_runAs != null && _realm != null)
260: user = _realm.pushRole(null, _runAs);
261:
262: if (_servlet != null) {
263: try {
264: getServletHandler().customizeServletDestroy(
265: _servlet);
266: } catch (Exception e) {
267: Log.warn(e);
268: }
269: _servlet.destroy();
270: }
271:
272: if (!_extInstance)
273: _servlet = null;
274:
275: while (_servlets != null && _servlets.size() > 0) {
276: Servlet s = (Servlet) _servlets.pop();
277: try {
278: getServletHandler().customizeServletDestroy(s);
279: } catch (Exception e) {
280: Log.warn(e);
281: }
282: s.destroy();
283: }
284: _config = null;
285: } finally {
286: super .doStop();
287: // pop run-as role
288: if (_runAs != null && _realm != null && user != null)
289: _realm.popRole(user);
290: }
291: }
292:
293: /* ------------------------------------------------------------ */
294: /** Get the servlet.
295: * @return The servlet
296: */
297: public synchronized Servlet getServlet() throws ServletException {
298: // Handle previous unavailability
299: if (_unavailable != 0) {
300: if (_unavailable < 0 || _unavailable > 0
301: && System.currentTimeMillis() < _unavailable)
302: throw _unavailableEx;
303: _unavailable = 0;
304: _unavailableEx = null;
305: }
306:
307: try {
308: if (_servlets != null) {
309: Servlet servlet = null;
310: if (_servlets.size() == 0) {
311: servlet = (Servlet) newInstance();
312: if (_config == null)
313: _config = new Config();
314: initServlet(servlet, _config);
315: } else
316: servlet = (Servlet) _servlets.pop();
317:
318: return servlet;
319: }
320:
321: if (_servlet == null) {
322: _servlet = (Servlet) newInstance();
323: if (_config == null)
324: _config = new Config();
325: initServlet(_servlet, _config);
326: }
327:
328: return _servlet;
329: } catch (UnavailableException e) {
330: _servlet = null;
331: _config = null;
332: return makeUnavailable(e);
333: } catch (ServletException e) {
334: _servlet = null;
335: _config = null;
336: throw e;
337: } catch (Throwable e) {
338: _servlet = null;
339: _config = null;
340: throw new ServletException("init", e);
341: }
342: }
343:
344: /* ------------------------------------------------------------ */
345: /**
346: * Check to ensure class of servlet is acceptable.
347: * @throws UnavailableException
348: */
349: public void checkServletType() throws UnavailableException {
350: if (!javax.servlet.Servlet.class.isAssignableFrom(_class)) {
351: throw new UnavailableException("Servlet " + _class
352: + " is not a javax.servlet.Servlet");
353: }
354: }
355:
356: /* ------------------------------------------------------------ */
357: private Servlet makeUnavailable(UnavailableException e)
358: throws UnavailableException {
359: _unavailableEx = e;
360: _unavailable = -1;
361: if (e.isPermanent())
362: _unavailable = -1;
363: else {
364: if (_unavailableEx.getUnavailableSeconds() > 0)
365: _unavailable = System.currentTimeMillis() + 1000
366: * _unavailableEx.getUnavailableSeconds();
367: else
368: _unavailable = System.currentTimeMillis() + 5000; // TODO configure
369: }
370:
371: throw _unavailableEx;
372: }
373:
374: /* ------------------------------------------------------------ */
375: private void initServlet(Servlet servlet, ServletConfig config)
376: throws ServletException {
377: Principal user = null;
378: try {
379: //handle any cusomizations of the servlet, such as @postConstruct
380: _servlet = getServletHandler().customizeServlet(servlet);
381:
382: // Handle run as
383: if (_runAs != null && _realm != null)
384: user = _realm.pushRole(null, _runAs);
385: servlet.init(config);
386: } catch (Exception e) {
387: throw new ServletException(e);
388: } finally {
389: // pop run-as role
390: if (_runAs != null && _realm != null && user != null)
391: _realm.popRole(user);
392: }
393: }
394:
395: /* ------------------------------------------------------------ */
396: /** Service a request with this servlet.
397: */
398: public void handle(ServletRequest request, ServletResponse response)
399: throws ServletException, UnavailableException, IOException {
400: if (_class == null)
401: throw new UnavailableException("Servlet Not Initialized");
402:
403: Servlet servlet = (!_initOnStartup || _servlets != null) ? getServlet()
404: : _servlet;
405: if (servlet == null)
406: throw new UnavailableException("Could not instantiate "
407: + _class);
408:
409: // Service the request
410: boolean servlet_error = true;
411: Principal user = null;
412: Request base_request = null;
413: try {
414: // Handle aliased path
415: if (_forcedPath != null)
416: // TODO complain about poor naming to the Jasper folks
417: request.setAttribute("org.apache.catalina.jsp_file",
418: _forcedPath);
419:
420: // Handle run as
421: if (_runAs != null && _realm != null) {
422: base_request = HttpConnection.getCurrentConnection()
423: .getRequest();
424: user = _realm.pushRole(base_request.getUserPrincipal(),
425: _runAs);
426: base_request.setUserPrincipal(user);
427: }
428:
429: servlet.service(request, response);
430: servlet_error = false;
431: } catch (UnavailableException e) {
432: if (_servlets != null && servlet != null)
433: try {
434: stop();
435: } catch (Exception e2) {
436: Log.ignore(e2);
437: }
438: makeUnavailable(e);
439: } finally {
440: // pop run-as role
441: if (_runAs != null && _realm != null && user != null
442: && base_request != null) {
443: user = _realm.popRole(user);
444: base_request.setUserPrincipal(user);
445: }
446:
447: // Handle error params.
448: if (servlet_error)
449: request.setAttribute(
450: "javax.servlet.error.servlet_name", getName());
451:
452: // Return to singleThreaded pool
453: synchronized (this ) {
454: if (_servlets != null && servlet != null)
455: _servlets.push(servlet);
456: }
457: }
458: }
459:
460: /* ------------------------------------------------------------ */
461: /* ------------------------------------------------------------ */
462: /* ------------------------------------------------------------ */
463: class Config implements ServletConfig {
464: /* -------------------------------------------------------- */
465: public String getServletName() {
466: return getName();
467: }
468:
469: /* -------------------------------------------------------- */
470: public ServletContext getServletContext() {
471: return _servletHandler.getServletContext();
472: }
473:
474: /* -------------------------------------------------------- */
475: public String getInitParameter(String param) {
476: return ServletHolder.this .getInitParameter(param);
477: }
478:
479: /* -------------------------------------------------------- */
480: public Enumeration getInitParameterNames() {
481: return ServletHolder.this.getInitParameterNames();
482: }
483: }
484:
485: }
|