/*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license (CDDL), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletResponse;
import org.w3c.dom.Node;
/**
* This is the one that keeps a specific servlet instance's config, as well as
* holding the instance itself.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: ServletConfiguration.java,v 1.16 2007/04/23 02:55:35 rickknowles Exp $
*/
public class ServletConfiguration implements javax.servlet.ServletConfig,
Comparable {
static final String ELEM_NAME = "servlet-name";
static final String ELEM_DISPLAY_NAME = "display-name";
static final String ELEM_CLASS = "servlet-class";
static final String ELEM_JSP_FILE = "jsp-file";
static final String ELEM_DESCRIPTION = "description";
static final String ELEM_INIT_PARAM = "init-param";
static final String ELEM_INIT_PARAM_NAME = "param-name";
static final String ELEM_INIT_PARAM_VALUE = "param-value";
static final String ELEM_LOAD_ON_STARTUP = "load-on-startup";
static final String ELEM_RUN_AS = "run-as";
static final String ELEM_SECURITY_ROLE_REF = "security-role-ref";
static final String ELEM_ROLE_NAME = "role-name";
static final String ELEM_ROLE_LINK = "role-link";
final String JSP_FILE = "org.apache.catalina.jsp_file";
private String servletName;
private String classFile;
private Servlet instance;
private Map initParameters;
private WebAppConfiguration webAppConfig;
private int loadOnStartup;
private String jspFile;
// private String runAsRole;
private Map securityRoleRefs;
private Object servletSemaphore = new Boolean(true);
private boolean isSingleThreadModel = false;
private boolean unavailable = false;
private Throwable unavailableException = null;
protected ServletConfiguration(WebAppConfiguration webAppConfig) {
this.webAppConfig = webAppConfig;
this.initParameters = new Hashtable();
this.loadOnStartup = -1;
this.securityRoleRefs = new Hashtable();
}
public ServletConfiguration(WebAppConfiguration webAppConfig, String servletName,
String className, Map initParams, int loadOnStartup) {
this(webAppConfig);
if (initParams != null)
this.initParameters.putAll(initParams);
this.servletName = servletName;
this.classFile = className;
this.jspFile = null;
this.loadOnStartup = loadOnStartup;
}
public ServletConfiguration(WebAppConfiguration webAppConfig, Node elm) {
this(webAppConfig);
// Parse the web.xml file entry
for (int n = 0; n < elm.getChildNodes().getLength(); n++) {
Node child = elm.getChildNodes().item(n);
if (child.getNodeType() != Node.ELEMENT_NODE)
continue;
String nodeName = child.getNodeName();
// Construct the servlet instances
if (nodeName.equals(ELEM_NAME))
this.servletName = WebAppConfiguration.getTextFromNode(child);
else if (nodeName.equals(ELEM_CLASS))
this.classFile = WebAppConfiguration.getTextFromNode(child);
else if (nodeName.equals(ELEM_JSP_FILE))
this.jspFile = WebAppConfiguration.getTextFromNode(child);
else if (nodeName.equals(ELEM_LOAD_ON_STARTUP)) {
String index = child.getFirstChild() == null ? "-1" :
WebAppConfiguration.getTextFromNode(child);
this.loadOnStartup = Integer.parseInt(index);
} else if (nodeName.equals(ELEM_INIT_PARAM)) {
String paramName = "";
String paramValue = "";
for (int k = 0; k < child.getChildNodes().getLength(); k++) {
Node paramNode = child.getChildNodes().item(k);
if (paramNode.getNodeType() != Node.ELEMENT_NODE)
continue;
else if (paramNode.getNodeName().equals(ELEM_INIT_PARAM_NAME))
paramName = WebAppConfiguration.getTextFromNode(paramNode);
else if (paramNode.getNodeName().equals(ELEM_INIT_PARAM_VALUE))
paramValue = WebAppConfiguration.getTextFromNode(paramNode);
}
if (!paramName.equals("")) {
this.initParameters.put(paramName, paramValue);
}
} else if (nodeName.equals(ELEM_RUN_AS)) {
for (int m = 0; m < child.getChildNodes().getLength(); m++) {
Node roleElm = child.getChildNodes().item(m);
if ((roleElm.getNodeType() == Node.ELEMENT_NODE)
&& (roleElm.getNodeName().equals(ELEM_ROLE_NAME))) {
// this.runAsRole = WebAppConfiguration.getTextFromNode(roleElm); // not used
}
}
} else if (nodeName.equals(ELEM_SECURITY_ROLE_REF)) {
String name = "";
String link = "";
for (int k = 0; k < child.getChildNodes().getLength(); k++) {
Node roleRefNode = child.getChildNodes().item(k);
if (roleRefNode.getNodeType() != Node.ELEMENT_NODE)
continue;
else if (roleRefNode.getNodeName().equals(ELEM_ROLE_NAME))
name = WebAppConfiguration.getTextFromNode(roleRefNode);
else if (roleRefNode.getNodeName().equals(ELEM_ROLE_LINK))
link = WebAppConfiguration.getTextFromNode(roleRefNode);
}
if (!name.equals("") && !link.equals(""))
this.initParameters.put(name, link);
}
}
if ((this.jspFile != null) && (this.classFile == null)) {
this.classFile = WebAppConfiguration.JSP_SERVLET_CLASS;
WebAppConfiguration.addJspServletParams(this.initParameters);
}
Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES,
"ServletConfiguration.DeployedInstance", new String[] {
this.servletName, this.classFile });
}
public void ensureInitialization() {
if (this.instance != null) {
return; // already init'd
}
synchronized (this.servletSemaphore) {
if (this.instance != null) {
return; // already init'd
}
// Check if we were decommissioned while blocking
if (this.unavailableException != null) {
return;
}
// If no instance, class load, then call init()
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.webAppConfig.getLoader());
Servlet newInstance = null;
Throwable otherError = null;
try {
Class servletClass = Class.forName(classFile, true, this.webAppConfig.getLoader());
newInstance = (Servlet) servletClass.newInstance();
this.isSingleThreadModel = Class.forName("javax.servlet.SingleThreadModel").isInstance(newInstance);
// Initialise with the correct classloader
Logger.log(Logger.DEBUG, Launcher.RESOURCES, "ServletConfiguration.init", this.servletName);
newInstance.init(this);
this.instance = newInstance;
} catch (ClassNotFoundException err) {
Logger.log(Logger.WARNING, Launcher.RESOURCES,
"ServletConfiguration.ClassLoadError", this.classFile, err);
setUnavailable(newInstance);
this.unavailableException = err;
} catch (IllegalAccessException err) {
Logger.log(Logger.WARNING, Launcher.RESOURCES,
"ServletConfiguration.ClassLoadError", this.classFile, err);
setUnavailable(newInstance);
this.unavailableException = err;
} catch (InstantiationException err) {
Logger.log(Logger.WARNING, Launcher.RESOURCES,
"ServletConfiguration.ClassLoadError", this.classFile, err);
setUnavailable(newInstance);
this.unavailableException = err;
} catch (ServletException err) {
Logger.log(Logger.WARNING, Launcher.RESOURCES,
"ServletConfiguration.InitError", this.servletName, err);
this.instance = null; // so that we don't call the destroy method
setUnavailable(newInstance);
this.unavailableException = err;
} catch (RuntimeException err) {
otherError = err;
throw err;
} catch (Error err) {
otherError = err;
throw err;
} finally {
Thread.currentThread().setContextClassLoader(cl);
if ((otherError == null) && (this.unavailableException == null)) {
this.instance = newInstance;
}
}
}
return;
}
public void execute(ServletRequest request, ServletResponse response, String requestURI)
throws ServletException, IOException {
ensureInitialization();
// If init failed, return 500 error
if (this.unavailable) {
// ((HttpServletResponse) response).sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
// resources.getString("StaticResourceServlet.PathNotFound", requestURI));
RequestDispatcher rd = this.webAppConfig.getErrorDispatcherByClass(
this.unavailableException);
rd.forward(request, response);
return;
}
if (this.jspFile != null)
request.setAttribute(JSP_FILE, this.jspFile);
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.webAppConfig.getLoader());
try {
if (this.isSingleThreadModel) {
synchronized (this) {
this.instance.service(request, response);
}
} else
this.instance.service(request, response);
} catch (UnavailableException err) {
// catch locally and rethrow as a new ServletException, so
// we only invalidate the throwing servlet
setUnavailable(this.instance);
((HttpServletResponse) response).sendError(HttpServletResponse.SC_NOT_FOUND,
Launcher.RESOURCES.getString("StaticResourceServlet.PathNotFound", requestURI));
// throw new ServletException(resources.getString(
// "RequestDispatcher.ForwardError"), err);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
public int getLoadOnStartup() {
return this.loadOnStartup;
}
public String getInitParameter(String name) {
return (String) this.initParameters.get(name);
}
public Enumeration getInitParameterNames() {
return Collections.enumeration(this.initParameters.keySet());
}
public ServletContext getServletContext() {
return this.webAppConfig;
}
public String getServletName() {
return this.servletName;
}
public Map getSecurityRoleRefs() {
return this.securityRoleRefs;
}
/**
* This was included so that the servlet instances could be sorted on their
* loadOnStartup values. Otherwise used.
*/
public int compareTo(Object objTwo) {
Integer one = new Integer(this.loadOnStartup);
Integer two = new Integer(((ServletConfiguration) objTwo).loadOnStartup);
return one.compareTo(two);
}
/**
* Called when it's time for the container to shut this servlet down.
*/
public void destroy() {
synchronized (this.servletSemaphore) {
setUnavailable(this.instance);
}
}
protected void setUnavailable(Servlet unavailableServlet) {
this.unavailable = true;
if (unavailableServlet != null) {
Logger.log(Logger.DEBUG, Launcher.RESOURCES,
"ServletConfiguration.destroy", this.servletName);
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.webAppConfig.getLoader());
try {
unavailableServlet.destroy();
} finally {
Thread.currentThread().setContextClassLoader(cl);
this.instance = null;
}
}
// remove from webapp
this.webAppConfig.removeServletConfigurationAndMappings(this);
}
}