/*
* $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/core/StandardWrapper.java,v 1.4 2000/02/08 05:43:36 craigmcc Exp $
* $Revision: 1.4 $
* $Date: 2000/02/08 05:43:36 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.core;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.SingleThreadModel;
import javax.servlet.http.HttpServletResponse;
import org.apache.tomcat.Container;
import org.apache.tomcat.Context;
import org.apache.tomcat.Lifecycle;
import org.apache.tomcat.LifecycleException;
import org.apache.tomcat.Loader;
import org.apache.tomcat.Request;
import org.apache.tomcat.Response;
import org.apache.tomcat.Wrapper;
/**
* Standard implementation of the <b>Wrapper</b> interface that represents
* an individual servlet definition. No child Containers are allowed, and
* the parent Container must be a Context.
* <p>
* <b>IMPLEMENTATION NOTE</b>: This implementation does <i>not</i> support
* a pool of instances for servlets that implement SingleThreadModel. IMHO
* developers should not be encouraged to use this technique, so efforts to
* make them efficient are counter-productive.
*
* @author Craig R. McClanahan
* @version $Revision: 1.4 $ $Date: 2000/02/08 05:43:36 $
*/
public final class StandardWrapper
extends ContainerBase
implements ServletConfig, Wrapper {
// ----------------------------------------------------------- Constructors
/**
* Create a new StandardWrapper component with the default basic Valve.
*/
public StandardWrapper() {
super();
setBasic(new StandardWrapperValve());
}
// ----------------------------------------------------- Instance Variables
/**
* Has our SingleThreadModel servlet instance been allocated already?
*/
private boolean allocated = false;
/**
* The descriptive information string for this implementation.
*/
private static final String info =
"org.apache.tomcat.core.StandardWrapper/1.0";
/**
* The (single) initialized instance of this servlet.
*/
private Servlet instance = null;
/**
* The context-relative URI of the JSP file for this servlet.
*/
private String jspFile = null;
/**
* The load-on-startup order value (negative value means load on
* first call) for this servlet.
*/
private int loadOnStartup = -1;
/**
* The initialization parameters for this servlet, keyed by
* parameter name.
*/
private Hashtable parameters = new Hashtable();
/**
* The security role references for this servlet, keyed by role name
* used in the servlet. The corresponding value is the role name of
* the web application itself.
*/
private Hashtable references = new Hashtable();
/**
* The fully qualified servlet class name for this servlet.
*/
private String servletClass = null;
/**
* Does this servlet implement the SingleThreadModel interface?
*/
private boolean singleThreadModel = false;
// ------------------------------------------------------------- Properties
/**
* Return descriptive information about this Container implementation and
* the corresponding version number, in the format
* <code><description>/<version></code>.
*/
public String getInfo() {
return (info);
}
/**
* Return the context-relative URI of the JSP file for this servlet.
*/
public String getJspFile() {
return (this.jspFile);
}
/**
* Set the context-relative URI of the JSP file for this servlet.
*
* @param jspFile JSP file URI
*/
public void setJspFile(String jspFile) {
String oldJspFile = this.jspFile;
this.jspFile = jspFile;
support.firePropertyChange("jspFile", oldJspFile, this.jspFile);
}
/**
* Return the load-on-startup order value (negative value means
* load on first call).
*/
public int getLoadOnStartup() {
return (this.loadOnStartup);
}
/**
* Set the load-on-startup order value (negative value means
* load on first call).
*
* @param value New load-on-startup value
*/
public void setLoadOnStartup(int value) {
int oldLoadOnStartup = this.loadOnStartup;
this.loadOnStartup = value;
support.firePropertyChange("loadOnStartup",
new Integer(oldLoadOnStartup),
new Integer(this.loadOnStartup));
}
/**
* Set the parent Container of this Wrapper, but only if it is a Context.
*
* @param container Proposed parent Container
*/
public void setParent(Container container) {
if (!(container instanceof Context))
throw new IllegalArgumentException
(sm.getString("standardWrapper.notContext"));
super.setParent(container);
}
/**
* Return the fully qualified servlet class name for this servlet.
*/
public String getServletClass() {
return (this.servletClass);
}
/**
* Set the fully qualified servlet class name for this servlet.
*
* @param servletClass Servlet class name
*/
public void setServletClass(String servletClass) {
String oldServletClass = this.servletClass;
this.servletClass = servletClass;
support.firePropertyChange("servletClass", oldServletClass,
this.servletClass);
}
/**
* Set the name of this servlet. This is an alias for the normal
* <code>Container.setName()</code> method, and complements the
* <code>getServletName()</code> method required by the
* <code>ServletConfig</code> interface.
*
* @param name The new name of this servlet
*/
public void setServletName(String name) {
setName(name);
}
/**
* Return <code>true</code> if the servlet class represented by this
* component implements the <code>SingleThreadModel</code> interface.
*/
public boolean isSingleThreadModel() {
try {
load();
} catch (ServletException e) {
;
}
return (singleThreadModel);
}
// --------------------------------------------------------- Public Methods
/**
* Refuse to add a child Container, because Wrappers are the lowest level
* of the Container hierarchy.
*
* @param child Child container to be added
*/
public void addChild(Container child) {
throw new IllegalStateException
(sm.getString("standardWrapper.noChild"));
}
/**
* Add a new servlet initialization parameter for this servlet.
*
* @param name Name of this initialization parameter to add
* @param value Value of this initialization parameter to add
*/
public void addInitParameter(String name, String value) {
parameters.put(name, value);
fireContainerEvent("addInitParameter", name);
}
/**
* Add a new security role reference record to the set of records for
* this servlet.
*
* @param name Role name used within this servlet
* @param link Role name used within the web application
*/
public void addSecurityReference(String name, String link) {
references.put(name, link);
fireContainerEvent("addSecurityReference", name);
}
/**
* Allocate an initialized instance of this Servlet that is ready to have
* its <code>service()</code> method called. If the servlet class does
* not implement <code>SingleThreadModel</code>, the (only) initialized
* instance may be returned immediately. If the servlet class implements
* <code>SingleThreadModel</code>, the Wrapper implementation must ensure
* that this instance is not allocated again until it is deallocated by a
* call to <code>deallocate()</code>.
* <p>
* <b>FIXME: Provide a way to avoid waiting forever.</b>
*/
public Servlet allocate() {
// Load and initialize our instance if necessary
if (instance == null) {
try {
load();
} catch (Exception e) {
return (null);
}
}
// If not SingleThreadedModel, return the same instance every time
if (!singleThreadModel)
return (instance);
// Lock and return this instance
synchronized (instance) {
if (allocated) {
try {
instance.wait();
} catch (InterruptedException e) {
;
}
}
allocated = true;
return (instance);
}
}
/**
* Return this previously allocated servlet to the pool of available
* instances. If this servlet class does not implement SingleThreadModel,
* no action is actually required.
*
* @param servlet The servlet to be returned
*/
public void deallocate(Servlet servlet) {
// If not SingleThreadModel, no action is required
if (!singleThreadModel)
return;
// Unlock and free this instance
synchronized (instance) {
allocated = false;
instance.notify();
}
}
/**
* Return the value for the specified initialization parameter name,
* if any; otherwise return <code>null</code>.
*
* @param name Name of the requested initialization parameter
*/
public String findInitParameter(String name) {
return ((String) parameters.get(name));
}
/**
* Return the names of all defined initialization parameters for this
* servlet.
*/
public String[] findInitParameters() {
synchronized (parameters) {
String results[] = new String[parameters.size()];
int n = 0;
Enumeration names = parameters.keys();
while (names.hasMoreElements()) {
results[n++] = (String) names.nextElement();
}
return (results);
}
}
/**
* Return the security role link for the specified security role
* reference name, if any; otherwise return <code>null</code>.
*
* @param name Security role reference used within this servlet
*/
public String findSecurityReference(String name) {
return ((String) references.get(name));
}
/**
* Return the set of security role reference names associated with
* this servlet, if any; otherwise return a zero-length array.
*/
public String[] findSecurityReferences() {
synchronized (references) {
String results[] = new String[references.size()];
int n = 0;
Enumeration names = references.keys();
while (names.hasMoreElements()) {
results[n++] = (String) names.nextElement();
}
return (results);
}
}
/**
* Load and initialize an instance of this servlet, if there is not already
* at least one initialized instance. This can be used, for example, to
* load servlets that are marked in the deployment descriptor to be loaded
* at server startup time.
*
* @exception ServletException if thrown by the init() method of the
* loaded servlet
*/
public synchronized void load() throws ServletException {
if (instance != null)
return;
// Complain if no servlet class has been specified
// FIXME - support for JSP file servlets!!!
if (servletClass == null)
throw new ServletException
(sm.getString("standardWrapper.noServlet"));
// Acquire an instance of the class loader to be used
Loader loader = ((Context) getParent()).getLoader();
if (loader == null)
throw new ServletException
(sm.getString("standardWrapper.noLoader"));
ClassLoader classLoader = loader.getClassLoader();
// Load and initialize an instance of the specified servlet class
try {
Class classClass = classLoader.loadClass(servletClass);
Servlet servlet = (Servlet) classClass.newInstance();
servlet.init((ServletConfig) this);
instance = servlet;
} catch (Exception e) {
throw new ServletException("load: " + e);
}
singleThreadModel = instance instanceof SingleThreadModel;
fireContainerEvent("load", this);
}
/**
* Remove the specified initialization parameter from this servlet.
*
* @param name Name of the initialization parameter to remove
*/
public void removeInitParameter(String name) {
if (parameters.get(name) != null) {
parameters.remove(name);
fireContainerEvent("removeInitParameter", name);
}
}
/**
* Remove any security role reference for the specified role name.
*
* @param name Security role used within this servlet to be removed
*/
public void removeSecurityReference(String name) {
if (references.get(name) != null) {
references.remove(name);
fireContainerEvent("removeSecurityReference", name);
}
}
/**
* Unload all initialized instances of this servlet, after calling the
* <code>destroy()</code> method for each instance. This can be used,
* for example, prior to shutting down the entire servlet engine, or
* prior to reloading all of the classes from the Loader associated with
* our Loader's repository.
*/
public synchronized void unload() {
if (instance == null)
return;
instance.destroy();
instance = null;
fireContainerEvent("unload", this);
}
// -------------------------------------------------- ServletConfig Methods
/**
* Return the initialization parameter value for the specified name,
* if any; otherwise return <code>null</code>.
*
* @param name Name of the initialization parameter to retrieve
*/
public String getInitParameter(String name) {
return (findInitParameter(name));
}
/**
* Return the set of initialization parameter names defined for this
* servlet. If none are defined, an empty Enumeration is returned.
*/
public Enumeration getInitParameterNames() {
Vector results = new Vector();
String names[] = findInitParameters();
for (int i = 0; i < names.length; i++)
results.addElement(names[i]);
return (results.elements());
}
/**
* Return the servlet context with which this servlet is associated.
*/
public ServletContext getServletContext() {
if (parent == null)
return (null);
else if (!(parent instanceof Context))
return (null);
else
return (((Context) parent).getServletContext());
}
/**
* Return the name of this servlet.
*/
public String getServletName() {
return (getName());
}
// -------------------------------------------------------- Private Methods
// ------------------------------------------------------ Lifecycle Methods
/**
* Start this component, pre-loading the servlet if the load-on-startup
* value is set appropriately.
*
* @exception LifecycleException if a fatal error occurs during startup
*/
public void start() throws LifecycleException {
// Start up this component
super.start();
// Load and initialize an instance of this servlet if requested
if (loadOnStartup < 0)
return;
try {
load();
} catch (ServletException e) {
throw new LifecycleException("start: " + e);
}
}
/**
* Stop this component, gracefully shutting down the servlet if it has
* been initialized.
*
* @exception LifecycleException if a fatal error occurs during shutdown
*/
public void stop() throws LifecycleException {
// Shut down our servlet instance (if it has been initialized)
unload();
// Shut down this component
super.stop();
}
}