001: /*
002: * Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: */
007: package winstone;
008:
009: import java.io.IOException;
010: import java.util.Collections;
011: import java.util.Enumeration;
012: import java.util.Hashtable;
013: import java.util.Map;
014:
015: import javax.servlet.Servlet;
016: import javax.servlet.ServletContext;
017: import javax.servlet.ServletException;
018: import javax.servlet.ServletRequest;
019: import javax.servlet.ServletResponse;
020: import javax.servlet.UnavailableException;
021: import javax.servlet.http.HttpServletResponse;
022:
023: import org.w3c.dom.Node;
024:
025: /**
026: * This is the one that keeps a specific servlet instance's config, as well as
027: * holding the instance itself.
028: *
029: * @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
030: * @version $Id: ServletConfiguration.java,v 1.16 2007/04/23 02:55:35 rickknowles Exp $
031: */
032: public class ServletConfiguration implements
033: javax.servlet.ServletConfig, Comparable {
034:
035: static final String ELEM_NAME = "servlet-name";
036: static final String ELEM_DISPLAY_NAME = "display-name";
037: static final String ELEM_CLASS = "servlet-class";
038: static final String ELEM_JSP_FILE = "jsp-file";
039: static final String ELEM_DESCRIPTION = "description";
040: static final String ELEM_INIT_PARAM = "init-param";
041: static final String ELEM_INIT_PARAM_NAME = "param-name";
042: static final String ELEM_INIT_PARAM_VALUE = "param-value";
043: static final String ELEM_LOAD_ON_STARTUP = "load-on-startup";
044: static final String ELEM_RUN_AS = "run-as";
045: static final String ELEM_SECURITY_ROLE_REF = "security-role-ref";
046: static final String ELEM_ROLE_NAME = "role-name";
047: static final String ELEM_ROLE_LINK = "role-link";
048:
049: final String JSP_FILE = "org.apache.catalina.jsp_file";
050:
051: private String servletName;
052: private String classFile;
053: private Servlet instance;
054: private Map initParameters;
055: private WebAppConfiguration webAppConfig;
056: private int loadOnStartup;
057: private String jspFile;
058: // private String runAsRole;
059: private Map securityRoleRefs;
060: private Object servletSemaphore = new Boolean(true);
061: private boolean isSingleThreadModel = false;
062: private boolean unavailable = false;
063: private Throwable unavailableException = null;
064:
065: protected ServletConfiguration(WebAppConfiguration webAppConfig) {
066: this .webAppConfig = webAppConfig;
067: this .initParameters = new Hashtable();
068: this .loadOnStartup = -1;
069: this .securityRoleRefs = new Hashtable();
070: }
071:
072: public ServletConfiguration(WebAppConfiguration webAppConfig,
073: String servletName, String className, Map initParams,
074: int loadOnStartup) {
075: this (webAppConfig);
076: if (initParams != null)
077: this .initParameters.putAll(initParams);
078: this .servletName = servletName;
079: this .classFile = className;
080: this .jspFile = null;
081: this .loadOnStartup = loadOnStartup;
082: }
083:
084: public ServletConfiguration(WebAppConfiguration webAppConfig,
085: Node elm) {
086: this (webAppConfig);
087:
088: // Parse the web.xml file entry
089: for (int n = 0; n < elm.getChildNodes().getLength(); n++) {
090: Node child = elm.getChildNodes().item(n);
091: if (child.getNodeType() != Node.ELEMENT_NODE)
092: continue;
093: String nodeName = child.getNodeName();
094:
095: // Construct the servlet instances
096: if (nodeName.equals(ELEM_NAME))
097: this .servletName = WebAppConfiguration
098: .getTextFromNode(child);
099: else if (nodeName.equals(ELEM_CLASS))
100: this .classFile = WebAppConfiguration
101: .getTextFromNode(child);
102: else if (nodeName.equals(ELEM_JSP_FILE))
103: this .jspFile = WebAppConfiguration
104: .getTextFromNode(child);
105: else if (nodeName.equals(ELEM_LOAD_ON_STARTUP)) {
106: String index = child.getFirstChild() == null ? "-1"
107: : WebAppConfiguration.getTextFromNode(child);
108: this .loadOnStartup = Integer.parseInt(index);
109: } else if (nodeName.equals(ELEM_INIT_PARAM)) {
110: String paramName = "";
111: String paramValue = "";
112: for (int k = 0; k < child.getChildNodes().getLength(); k++) {
113: Node paramNode = child.getChildNodes().item(k);
114: if (paramNode.getNodeType() != Node.ELEMENT_NODE)
115: continue;
116: else if (paramNode.getNodeName().equals(
117: ELEM_INIT_PARAM_NAME))
118: paramName = WebAppConfiguration
119: .getTextFromNode(paramNode);
120: else if (paramNode.getNodeName().equals(
121: ELEM_INIT_PARAM_VALUE))
122: paramValue = WebAppConfiguration
123: .getTextFromNode(paramNode);
124: }
125: if (!paramName.equals("")) {
126: this .initParameters.put(paramName, paramValue);
127: }
128: } else if (nodeName.equals(ELEM_RUN_AS)) {
129: for (int m = 0; m < child.getChildNodes().getLength(); m++) {
130: Node roleElm = child.getChildNodes().item(m);
131: if ((roleElm.getNodeType() == Node.ELEMENT_NODE)
132: && (roleElm.getNodeName()
133: .equals(ELEM_ROLE_NAME))) {
134: // this.runAsRole = WebAppConfiguration.getTextFromNode(roleElm); // not used
135: }
136: }
137: } else if (nodeName.equals(ELEM_SECURITY_ROLE_REF)) {
138: String name = "";
139: String link = "";
140: for (int k = 0; k < child.getChildNodes().getLength(); k++) {
141: Node roleRefNode = child.getChildNodes().item(k);
142: if (roleRefNode.getNodeType() != Node.ELEMENT_NODE)
143: continue;
144: else if (roleRefNode.getNodeName().equals(
145: ELEM_ROLE_NAME))
146: name = WebAppConfiguration
147: .getTextFromNode(roleRefNode);
148: else if (roleRefNode.getNodeName().equals(
149: ELEM_ROLE_LINK))
150: link = WebAppConfiguration
151: .getTextFromNode(roleRefNode);
152: }
153: if (!name.equals("") && !link.equals(""))
154: this .initParameters.put(name, link);
155: }
156: }
157:
158: if ((this .jspFile != null) && (this .classFile == null)) {
159: this .classFile = WebAppConfiguration.JSP_SERVLET_CLASS;
160: WebAppConfiguration
161: .addJspServletParams(this .initParameters);
162: }
163: Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES,
164: "ServletConfiguration.DeployedInstance", new String[] {
165: this .servletName, this .classFile });
166: }
167:
168: public void ensureInitialization() {
169:
170: if (this .instance != null) {
171: return; // already init'd
172: }
173:
174: synchronized (this .servletSemaphore) {
175:
176: if (this .instance != null) {
177: return; // already init'd
178: }
179:
180: // Check if we were decommissioned while blocking
181: if (this .unavailableException != null) {
182: return;
183: }
184:
185: // If no instance, class load, then call init()
186: ClassLoader cl = Thread.currentThread()
187: .getContextClassLoader();
188: Thread.currentThread().setContextClassLoader(
189: this .webAppConfig.getLoader());
190:
191: Servlet newInstance = null;
192: Throwable otherError = null;
193: try {
194: Class servletClass = Class.forName(classFile, true,
195: this .webAppConfig.getLoader());
196: newInstance = (Servlet) servletClass.newInstance();
197: this .isSingleThreadModel = Class.forName(
198: "javax.servlet.SingleThreadModel").isInstance(
199: newInstance);
200:
201: // Initialise with the correct classloader
202: Logger.log(Logger.DEBUG, Launcher.RESOURCES,
203: "ServletConfiguration.init", this .servletName);
204: newInstance.init(this );
205: this .instance = newInstance;
206: } catch (ClassNotFoundException err) {
207: Logger.log(Logger.WARNING, Launcher.RESOURCES,
208: "ServletConfiguration.ClassLoadError",
209: this .classFile, err);
210: setUnavailable(newInstance);
211: this .unavailableException = err;
212: } catch (IllegalAccessException err) {
213: Logger.log(Logger.WARNING, Launcher.RESOURCES,
214: "ServletConfiguration.ClassLoadError",
215: this .classFile, err);
216: setUnavailable(newInstance);
217: this .unavailableException = err;
218: } catch (InstantiationException err) {
219: Logger.log(Logger.WARNING, Launcher.RESOURCES,
220: "ServletConfiguration.ClassLoadError",
221: this .classFile, err);
222: setUnavailable(newInstance);
223: this .unavailableException = err;
224: } catch (ServletException err) {
225: Logger.log(Logger.WARNING, Launcher.RESOURCES,
226: "ServletConfiguration.InitError",
227: this .servletName, err);
228: this .instance = null; // so that we don't call the destroy method
229: setUnavailable(newInstance);
230: this .unavailableException = err;
231: } catch (RuntimeException err) {
232: otherError = err;
233: throw err;
234: } catch (Error err) {
235: otherError = err;
236: throw err;
237: } finally {
238: Thread.currentThread().setContextClassLoader(cl);
239: if ((otherError == null)
240: && (this .unavailableException == null)) {
241: this .instance = newInstance;
242: }
243: }
244: }
245: return;
246: }
247:
248: public void execute(ServletRequest request,
249: ServletResponse response, String requestURI)
250: throws ServletException, IOException {
251:
252: ensureInitialization();
253:
254: // If init failed, return 500 error
255: if (this .unavailable) {
256: // ((HttpServletResponse) response).sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
257: // resources.getString("StaticResourceServlet.PathNotFound", requestURI));
258: RequestDispatcher rd = this .webAppConfig
259: .getErrorDispatcherByClass(this .unavailableException);
260: rd.forward(request, response);
261: return;
262: }
263:
264: if (this .jspFile != null)
265: request.setAttribute(JSP_FILE, this .jspFile);
266:
267: ClassLoader cl = Thread.currentThread().getContextClassLoader();
268: Thread.currentThread().setContextClassLoader(
269: this .webAppConfig.getLoader());
270:
271: try {
272: if (this .isSingleThreadModel) {
273: synchronized (this ) {
274: this .instance.service(request, response);
275: }
276: } else
277: this .instance.service(request, response);
278: } catch (UnavailableException err) {
279: // catch locally and rethrow as a new ServletException, so
280: // we only invalidate the throwing servlet
281: setUnavailable(this .instance);
282: ((HttpServletResponse) response).sendError(
283: HttpServletResponse.SC_NOT_FOUND,
284: Launcher.RESOURCES.getString(
285: "StaticResourceServlet.PathNotFound",
286: requestURI));
287: // throw new ServletException(resources.getString(
288: // "RequestDispatcher.ForwardError"), err);
289: } finally {
290: Thread.currentThread().setContextClassLoader(cl);
291: }
292: }
293:
294: public int getLoadOnStartup() {
295: return this .loadOnStartup;
296: }
297:
298: public String getInitParameter(String name) {
299: return (String) this .initParameters.get(name);
300: }
301:
302: public Enumeration getInitParameterNames() {
303: return Collections.enumeration(this .initParameters.keySet());
304: }
305:
306: public ServletContext getServletContext() {
307: return this .webAppConfig;
308: }
309:
310: public String getServletName() {
311: return this .servletName;
312: }
313:
314: public Map getSecurityRoleRefs() {
315: return this .securityRoleRefs;
316: }
317:
318: /**
319: * This was included so that the servlet instances could be sorted on their
320: * loadOnStartup values. Otherwise used.
321: */
322: public int compareTo(Object objTwo) {
323: Integer one = new Integer(this .loadOnStartup);
324: Integer two = new Integer(
325: ((ServletConfiguration) objTwo).loadOnStartup);
326: return one.compareTo(two);
327: }
328:
329: /**
330: * Called when it's time for the container to shut this servlet down.
331: */
332: public void destroy() {
333: synchronized (this .servletSemaphore) {
334: setUnavailable(this .instance);
335: }
336: }
337:
338: protected void setUnavailable(Servlet unavailableServlet) {
339:
340: this .unavailable = true;
341: if (unavailableServlet != null) {
342: Logger.log(Logger.DEBUG, Launcher.RESOURCES,
343: "ServletConfiguration.destroy", this .servletName);
344: ClassLoader cl = Thread.currentThread()
345: .getContextClassLoader();
346: Thread.currentThread().setContextClassLoader(
347: this .webAppConfig.getLoader());
348: try {
349: unavailableServlet.destroy();
350: } finally {
351: Thread.currentThread().setContextClassLoader(cl);
352: this .instance = null;
353: }
354: }
355:
356: // remove from webapp
357: this.webAppConfig.removeServletConfigurationAndMappings(this);
358: }
359: }
|