/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.catalina.core;

import java.io.PrintStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;

import jakarta.servlet.MultipartConfigElement;
import jakarta.servlet.Servlet;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.SingleThreadModel;
import jakarta.servlet.UnavailableException;
import jakarta.servlet.annotation.MultipartConfig;

import org.apache.catalina.Container;
import org.apache.catalina.ContainerServlet;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Wrapper;
import org.apache.catalina.security.SecurityUtil;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.InstanceManager;
import org.apache.tomcat.PeriodicEventListener;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.log.SystemLogHandler;
import org.apache.tomcat.util.modeler.Registry;
import org.apache.tomcat.util.modeler.Util;

Standard implementation of the Wrapper interface that represents an individual servlet definition. No child Containers are allowed, and the parent Container must be a Context.
Author:Craig R. McClanahan, Remy Maucherat
/** * 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. * * @author Craig R. McClanahan * @author Remy Maucherat */
@SuppressWarnings("deprecation") // SingleThreadModel public class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper, NotificationEmitter { private final Log log = LogFactory.getLog(StandardWrapper.class); // must not be static protected static final String[] DEFAULT_SERVLET_METHODS = new String[] { "GET", "HEAD", "POST" }; // ----------------------------------------------------------- Constructors
Create a new StandardWrapper component with the default basic Valve.
/** * Create a new StandardWrapper component with the default basic Valve. */
public StandardWrapper() { super(); swValve=new StandardWrapperValve(); pipeline.setBasic(swValve); broadcaster = new NotificationBroadcasterSupport(); } // ----------------------------------------------------- Instance Variables
The date and time at which this servlet will become available (in milliseconds since the epoch), or zero if the servlet is available. If this value equals Long.MAX_VALUE, the unavailability of this servlet is considered permanent.
/** * The date and time at which this servlet will become available (in * milliseconds since the epoch), or zero if the servlet is available. * If this value equals Long.MAX_VALUE, the unavailability of this * servlet is considered permanent. */
protected long available = 0L;
The broadcaster that sends j2ee notifications.
/** * The broadcaster that sends j2ee notifications. */
protected final NotificationBroadcasterSupport broadcaster;
The count of allocations that are currently active (even if they are for the same instance, as will be true on a non-STM servlet).
/** * The count of allocations that are currently active (even if they * are for the same instance, as will be true on a non-STM servlet). */
protected final AtomicInteger countAllocated = new AtomicInteger(0);
The facade associated with this wrapper.
/** * The facade associated with this wrapper. */
protected final StandardWrapperFacade facade = new StandardWrapperFacade(this);
The (single) possibly uninitialized instance of this servlet.
/** * The (single) possibly uninitialized instance of this servlet. */
protected volatile Servlet instance = null;
Flag that indicates if this instance has been initialized
/** * Flag that indicates if this instance has been initialized */
protected volatile boolean instanceInitialized = false;
The load-on-startup order value (negative value means load on first call) for this servlet.
/** * The load-on-startup order value (negative value means load on * first call) for this servlet. */
protected int loadOnStartup = -1;
Mappings associated with the wrapper.
/** * Mappings associated with the wrapper. */
protected final ArrayList<String> mappings = new ArrayList<>();
The initialization parameters for this servlet, keyed by parameter name.
/** * The initialization parameters for this servlet, keyed by * parameter name. */
protected HashMap<String, String> parameters = new HashMap<>();
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.
/** * 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. */
protected HashMap<String, String> references = new HashMap<>();
The run-as identity for this servlet.
/** * The run-as identity for this servlet. */
protected String runAs = null;
The notification sequence number.
/** * The notification sequence number. */
protected long sequenceNumber = 0;
The fully qualified servlet class name for this servlet.
/** * The fully qualified servlet class name for this servlet. */
protected String servletClass = null;
Does this servlet implement the SingleThreadModel interface?
/** * Does this servlet implement the SingleThreadModel interface? */
protected volatile boolean singleThreadModel = false;
Are we unloading our servlet instance at the moment?
/** * Are we unloading our servlet instance at the moment? */
protected volatile boolean unloading = false;
Maximum number of STM instances.
/** * Maximum number of STM instances. */
protected int maxInstances = 20;
Number of instances currently loaded for a STM servlet.
/** * Number of instances currently loaded for a STM servlet. */
protected int nInstances = 0;
Stack containing the STM instances.
/** * Stack containing the STM instances. */
protected Stack<Servlet> instancePool = null;
Wait time for servlet unload in ms.
/** * Wait time for servlet unload in ms. */
protected long unloadDelay = 2000;
True if this StandardWrapper is for the JspServlet
/** * True if this StandardWrapper is for the JspServlet */
protected boolean isJspServlet;
The ObjectName of the JSP monitoring mbean
/** * The ObjectName of the JSP monitoring mbean */
protected ObjectName jspMonitorON;
Should we swallow System.out
/** * Should we swallow System.out */
protected boolean swallowOutput = false; // To support jmx attributes protected StandardWrapperValve swValve; protected long loadTime=0; protected int classLoadTime=0;
Multipart config
/** * Multipart config */
protected MultipartConfigElement multipartConfigElement = null;
Async support
/** * Async support */
protected boolean asyncSupported = false;
Enabled
/** * Enabled */
protected boolean enabled = true; private boolean overridable = false;
Static class array used when the SecurityManager is turned on and Servlet.init is invoked.
/** * Static class array used when the SecurityManager is turned on and * <code>Servlet.init</code> is invoked. */
protected static Class<?>[] classType = new Class[]{ServletConfig.class}; private final ReentrantReadWriteLock parametersLock = new ReentrantReadWriteLock(); private final ReentrantReadWriteLock mappingsLock = new ReentrantReadWriteLock(); private final ReentrantReadWriteLock referencesLock = new ReentrantReadWriteLock(); // ------------------------------------------------------------- Properties @Override public boolean isOverridable() { return overridable; } @Override public void setOverridable(boolean overridable) { this.overridable = overridable; }
Return the available date/time for this servlet, in milliseconds since the epoch. If this date/time is Long.MAX_VALUE, it is considered to mean that unavailability is permanent and any request for this servlet will return an SC_NOT_FOUND error. If this date/time is in the future, any request for this servlet will return an SC_SERVICE_UNAVAILABLE error. If it is zero, the servlet is currently available.
/** * Return the available date/time for this servlet, in milliseconds since * the epoch. If this date/time is Long.MAX_VALUE, it is considered to mean * that unavailability is permanent and any request for this servlet will return * an SC_NOT_FOUND error. If this date/time is in the future, any request for * this servlet will return an SC_SERVICE_UNAVAILABLE error. If it is zero, * the servlet is currently available. */
@Override public long getAvailable() { return this.available; }
Set the available date/time for this servlet, in milliseconds since the epoch. If this date/time is Long.MAX_VALUE, it is considered to mean that unavailability is permanent and any request for this servlet will return an SC_NOT_FOUND error. If this date/time is in the future, any request for this servlet will return an SC_SERVICE_UNAVAILABLE error.
Params:
  • available – The new available date/time
/** * Set the available date/time for this servlet, in milliseconds since the * epoch. If this date/time is Long.MAX_VALUE, it is considered to mean * that unavailability is permanent and any request for this servlet will return * an SC_NOT_FOUND error. If this date/time is in the future, any request for * this servlet will return an SC_SERVICE_UNAVAILABLE error. * * @param available The new available date/time */
@Override public void setAvailable(long available) { long oldAvailable = this.available; if (available > System.currentTimeMillis()) this.available = available; else this.available = 0L; support.firePropertyChange("available", Long.valueOf(oldAvailable), Long.valueOf(this.available)); }
Returns:the number of active allocations of this servlet, even if they are all for the same instance (as will be true for servlets that do not implement SingleThreadModel.
/** * @return the number of active allocations of this servlet, even if they * are all for the same instance (as will be true for servlets that do * not implement <code>SingleThreadModel</code>. */
public int getCountAllocated() { return this.countAllocated.get(); }
Returns:the load-on-startup order value (negative value means load on first call).
/** * @return the load-on-startup order value (negative value means * load on first call). */
@Override public int getLoadOnStartup() { if (isJspServlet && loadOnStartup < 0) { /* * JspServlet must always be preloaded, because its instance is * used during registerJMX (when registering the JSP * monitoring mbean) */ return Integer.MAX_VALUE; } else { return this.loadOnStartup; } }
Set the load-on-startup order value (negative value means load on first call).
Params:
  • value – New load-on-startup value
/** * Set the load-on-startup order value (negative value means * load on first call). * * @param value New load-on-startup value */
@Override public void setLoadOnStartup(int value) { int oldLoadOnStartup = this.loadOnStartup; this.loadOnStartup = value; support.firePropertyChange("loadOnStartup", Integer.valueOf(oldLoadOnStartup), Integer.valueOf(this.loadOnStartup)); }
Set the load-on-startup order value from a (possibly null) string. Per the specification, any missing or non-numeric value is converted to a zero, so that this servlet will still be loaded at startup time, but in an arbitrary order.
Params:
  • value – New load-on-startup value
/** * Set the load-on-startup order value from a (possibly null) string. * Per the specification, any missing or non-numeric value is converted * to a zero, so that this servlet will still be loaded at startup * time, but in an arbitrary order. * * @param value New load-on-startup value */
public void setLoadOnStartupString(String value) { try { setLoadOnStartup(Integer.parseInt(value)); } catch (NumberFormatException e) { setLoadOnStartup(0); } }
Returns:the load-on-startup value that was parsed
/** * @return the load-on-startup value that was parsed */
public String getLoadOnStartupString() { return Integer.toString( getLoadOnStartup()); }
Returns:maximum number of instances that will be allocated when a single thread model servlet is used.
/** * @return maximum number of instances that will be allocated when a single * thread model servlet is used. */
public int getMaxInstances() { return this.maxInstances; }
Set the maximum number of instances that will be allocated when a single thread model servlet is used.
Params:
  • maxInstances – New value of maxInstances
/** * Set the maximum number of instances that will be allocated when a single * thread model servlet is used. * * @param maxInstances New value of maxInstances */
public void setMaxInstances(int maxInstances) { int oldMaxInstances = this.maxInstances; this.maxInstances = maxInstances; support.firePropertyChange("maxInstances", oldMaxInstances, this.maxInstances); }
Set the parent Container of this Wrapper, but only if it is a Context.
Params:
  • container – Proposed parent Container
/** * Set the parent Container of this Wrapper, but only if it is a Context. * * @param container Proposed parent Container */
@Override public void setParent(Container container) { if ((container != null) && !(container instanceof Context)) throw new IllegalArgumentException (sm.getString("standardWrapper.notContext")); if (container instanceof StandardContext) { swallowOutput = ((StandardContext)container).getSwallowOutput(); unloadDelay = ((StandardContext)container).getUnloadDelay(); } super.setParent(container); }
Returns:the run-as identity for this servlet.
/** * @return the run-as identity for this servlet. */
@Override public String getRunAs() { return this.runAs; }
Set the run-as identity for this servlet.
Params:
  • runAs – New run-as identity value
/** * Set the run-as identity for this servlet. * * @param runAs New run-as identity value */
@Override public void setRunAs(String runAs) { String oldRunAs = this.runAs; this.runAs = runAs; support.firePropertyChange("runAs", oldRunAs, this.runAs); }
Returns:the fully qualified servlet class name for this servlet.
/** * @return the fully qualified servlet class name for this servlet. */
@Override public String getServletClass() { return this.servletClass; }
Set the fully qualified servlet class name for this servlet.
Params:
  • servletClass – Servlet class name
/** * Set the fully qualified servlet class name for this servlet. * * @param servletClass Servlet class name */
@Override public void setServletClass(String servletClass) { String oldServletClass = this.servletClass; this.servletClass = servletClass; support.firePropertyChange("servletClass", oldServletClass, this.servletClass); if (Constants.JSP_SERVLET_CLASS.equals(servletClass)) { isJspServlet = true; } }
Set the name of this servlet. This is an alias for the normal Container.setName() method, and complements the getServletName() method required by the ServletConfig interface.
Params:
  • name – The new name of this servlet
/** * 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); }
Does the servlet class represented by this component implement the SingleThreadModel interface? This can only be determined once the class is loaded. Calling this method will not trigger loading the class since that may cause the application to behave unexpectedly.
Returns:null if the class has not been loaded, otherwise true if the servlet does implement SingleThreadModel and false if it does not.
/** * Does the servlet class represented by this component implement the * <code>SingleThreadModel</code> interface? This can only be determined * once the class is loaded. Calling this method will not trigger loading * the class since that may cause the application to behave unexpectedly. * * @return {@code null} if the class has not been loaded, otherwise {@code * true} if the servlet does implement {@code SingleThreadModel} and * {@code false} if it does not. */
public Boolean isSingleThreadModel() { // If the servlet has been loaded either singleThreadModel will be true // or instance will be non-null if (singleThreadModel || instance != null) { return Boolean.valueOf(singleThreadModel); } return null; }
Returns:true if the Servlet has been marked unavailable.
/** * @return <code>true</code> if the Servlet has been marked unavailable. */
@Override public boolean isUnavailable() { if (!isEnabled()) return true; else if (available == 0L) return false; else if (available <= System.currentTimeMillis()) { available = 0L; return false; } else return true; } @Override public String[] getServletMethods() throws ServletException { instance = loadServlet(); Class<? extends Servlet> servletClazz = instance.getClass(); if (!jakarta.servlet.http.HttpServlet.class.isAssignableFrom( servletClazz)) { return DEFAULT_SERVLET_METHODS; } Set<String> allow = new HashSet<>(); allow.add("OPTIONS"); if (isJspServlet) { allow.add("GET"); allow.add("HEAD"); allow.add("POST"); } else { allow.add("TRACE"); Method[] methods = getAllDeclaredMethods(servletClazz); for (int i=0; methods != null && i<methods.length; i++) { Method m = methods[i]; if (m.getName().equals("doGet")) { allow.add("GET"); allow.add("HEAD"); } else if (m.getName().equals("doPost")) { allow.add("POST"); } else if (m.getName().equals("doPut")) { allow.add("PUT"); } else if (m.getName().equals("doDelete")) { allow.add("DELETE"); } } } String[] methodNames = new String[allow.size()]; return allow.toArray(methodNames); }
Returns:the associated servlet instance.
/** * @return the associated servlet instance. */
@Override public Servlet getServlet() { return instance; }
Set the associated servlet instance.
/** * Set the associated servlet instance. */
@Override public void setServlet(Servlet servlet) { instance = servlet; } // --------------------------------------------------------- Public Methods
Execute a periodic task, such as reloading, etc. This method will be invoked inside the classloading context of this container. Unexpected throwables will be caught and logged.
/** * Execute a periodic task, such as reloading, etc. This method will be * invoked inside the classloading context of this container. Unexpected * throwables will be caught and logged. */
@Override public void backgroundProcess() { super.backgroundProcess(); if (!getState().isAvailable()) return; if (getServlet() instanceof PeriodicEventListener) { ((PeriodicEventListener) getServlet()).periodicEvent(); } }
Extract the root cause from a servlet exception.
Params:
  • e – The servlet exception
Returns:the root cause of the Servlet exception
/** * Extract the root cause from a servlet exception. * * @param e The servlet exception * @return the root cause of the Servlet exception */
public static Throwable getRootCause(ServletException e) { Throwable rootCause = e; Throwable rootCauseCheck = null; // Extra aggressive rootCause finding int loops = 0; do { loops++; rootCauseCheck = rootCause.getCause(); if (rootCauseCheck != null) rootCause = rootCauseCheck; } while (rootCauseCheck != null && (loops < 20)); return rootCause; }
Refuse to add a child Container, because Wrappers are the lowest level of the Container hierarchy.
Params:
  • child – Child container to be added
/** * Refuse to add a child Container, because Wrappers are the lowest level * of the Container hierarchy. * * @param child Child container to be added */
@Override public void addChild(Container child) { throw new IllegalStateException (sm.getString("standardWrapper.notChild")); }
Add a new servlet initialization parameter for this servlet.
Params:
  • name – Name of this initialization parameter to add
  • value – Value of this initialization parameter to add
/** * 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 */
@Override public void addInitParameter(String name, String value) { parametersLock.writeLock().lock(); try { parameters.put(name, value); } finally { parametersLock.writeLock().unlock(); } fireContainerEvent("addInitParameter", name); }
Add a mapping associated with the Wrapper.
Params:
  • mapping – The new wrapper mapping
/** * Add a mapping associated with the Wrapper. * * @param mapping The new wrapper mapping */
@Override public void addMapping(String mapping) { mappingsLock.writeLock().lock(); try { mappings.add(mapping); } finally { mappingsLock.writeLock().unlock(); } if(parent.getState().equals(LifecycleState.STARTED)) fireContainerEvent(ADD_MAPPING_EVENT, mapping); }
Add a new security role reference record to the set of records for this servlet.
Params:
  • name – Role name used within this servlet
  • link – Role name used within the web application
/** * 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 */
@Override public void addSecurityReference(String name, String link) { referencesLock.writeLock().lock(); try { references.put(name, link); } finally { referencesLock.writeLock().unlock(); } fireContainerEvent("addSecurityReference", name); }
Allocate an initialized instance of this Servlet that is ready to have its service() method called. If the servlet class does not implement SingleThreadModel, the (only) initialized instance may be returned immediately. If the servlet class implements SingleThreadModel, the Wrapper implementation must ensure that this instance is not allocated again until it is deallocated by a call to deallocate().
Throws:
  • ServletException – if the servlet init() method threw an exception
  • ServletException – if a loading error occurs
/** * 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>. * * @exception ServletException if the servlet init() method threw * an exception * @exception ServletException if a loading error occurs */
@Override public Servlet allocate() throws ServletException { // If we are currently unloading this servlet, throw an exception if (unloading) { throw new ServletException(sm.getString("standardWrapper.unloading", getName())); } boolean newInstance = false; // If not SingleThreadedModel, return the same instance every time if (!singleThreadModel) { // Load and initialize our instance if necessary if (instance == null || !instanceInitialized) { synchronized (this) { if (instance == null) { try { if (log.isDebugEnabled()) { log.debug("Allocating non-STM instance"); } // Note: We don't know if the Servlet implements // SingleThreadModel until we have loaded it. instance = loadServlet(); newInstance = true; if (!singleThreadModel) { // For non-STM, increment here to prevent a race // condition with unload. Bug 43683, test case // #3 countAllocated.incrementAndGet(); } } catch (ServletException e) { throw e; } catch (Throwable e) { ExceptionUtils.handleThrowable(e); throw new ServletException(sm.getString("standardWrapper.allocate"), e); } } if (!instanceInitialized) { initServlet(instance); } } } if (singleThreadModel) { if (newInstance) { // Have to do this outside of the sync above to prevent a // possible deadlock synchronized (instancePool) { instancePool.push(instance); nInstances++; } } } else { if (log.isTraceEnabled()) { log.trace(" Returning non-STM instance"); } // For new instances, count will have been incremented at the // time of creation if (!newInstance) { countAllocated.incrementAndGet(); } return instance; } } synchronized (instancePool) { while (countAllocated.get() >= nInstances) { // Allocate a new instance if possible, or else wait if (nInstances < maxInstances) { try { instancePool.push(loadServlet()); nInstances++; } catch (ServletException e) { throw e; } catch (Throwable e) { ExceptionUtils.handleThrowable(e); throw new ServletException(sm.getString("standardWrapper.allocate"), e); } } else { try { instancePool.wait(); } catch (InterruptedException e) { // Ignore } } } if (log.isTraceEnabled()) { log.trace(" Returning allocated STM instance"); } countAllocated.incrementAndGet(); return instancePool.pop(); } }
Return this previously allocated servlet to the pool of available instances. If this servlet class does not implement SingleThreadModel, no action is actually required.
Params:
  • servlet – The servlet to be returned
Throws:
/** * 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 * * @exception ServletException if a deallocation error occurs */
@Override public void deallocate(Servlet servlet) throws ServletException { // If not SingleThreadModel, no action is required if (!singleThreadModel) { countAllocated.decrementAndGet(); return; } // Unlock and free this instance synchronized (instancePool) { countAllocated.decrementAndGet(); instancePool.push(servlet); instancePool.notify(); } }
Return the value for the specified initialization parameter name, if any; otherwise return null.
Params:
  • name – Name of the requested initialization parameter
/** * Return the value for the specified initialization parameter name, * if any; otherwise return <code>null</code>. * * @param name Name of the requested initialization parameter */
@Override public String findInitParameter(String name) { parametersLock.readLock().lock(); try { return parameters.get(name); } finally { parametersLock.readLock().unlock(); } }
Return the names of all defined initialization parameters for this servlet.
/** * Return the names of all defined initialization parameters for this * servlet. */
@Override public String[] findInitParameters() { parametersLock.readLock().lock(); try { String results[] = new String[parameters.size()]; return parameters.keySet().toArray(results); } finally { parametersLock.readLock().unlock(); } }
Return the mappings associated with this wrapper.
/** * Return the mappings associated with this wrapper. */
@Override public String[] findMappings() { mappingsLock.readLock().lock(); try { return mappings.toArray(new String[0]); } finally { mappingsLock.readLock().unlock(); } }
Return the security role link for the specified security role reference name, if any; otherwise return null.
Params:
  • name – Security role reference used within this servlet
/** * 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 */
@Override public String findSecurityReference(String name) { String reference = null; referencesLock.readLock().lock(); try { reference = references.get(name); } finally { referencesLock.readLock().unlock(); } // If not specified on the Wrapper, check the Context if (getParent() instanceof Context) { Context context = (Context) getParent(); if (reference != null) { reference = context.findRoleMapping(reference); } else { reference = context.findRoleMapping(name); } } return reference; }
Return the set of security role reference names associated with this servlet, if any; otherwise return a zero-length array.
/** * Return the set of security role reference names associated with * this servlet, if any; otherwise return a zero-length array. */
@Override public String[] findSecurityReferences() { referencesLock.readLock().lock(); try { String results[] = new String[references.size()]; return references.keySet().toArray(results); } finally { referencesLock.readLock().unlock(); } }
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.

IMPLEMENTATION NOTE: Servlets whose classnames begin with org.apache.catalina. (so-called "container" servlets) are loaded by the same classloader that loaded this class, rather than the classloader for the current web application. This gives such classes access to Catalina internals, which are prevented for classes loaded for web applications.

Throws:
  • ServletException – if the servlet init() method threw an exception
  • ServletException – if some other loading problem occurs
/** * 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. * <p> * <b>IMPLEMENTATION NOTE</b>: Servlets whose classnames begin with * <code>org.apache.catalina.</code> (so-called "container" servlets) * are loaded by the same classloader that loaded this class, rather than * the classloader for the current web application. * This gives such classes access to Catalina internals, which are * prevented for classes loaded for web applications. * * @exception ServletException if the servlet init() method threw * an exception * @exception ServletException if some other loading problem occurs */
@Override public synchronized void load() throws ServletException { instance = loadServlet(); if (!instanceInitialized) { initServlet(instance); } if (isJspServlet) { StringBuilder oname = new StringBuilder(getDomain()); oname.append(":type=JspMonitor"); oname.append(getWebModuleKeyProperties()); oname.append(",name="); oname.append(getName()); oname.append(getJ2EEKeyProperties()); try { jspMonitorON = new ObjectName(oname.toString()); Registry.getRegistry(null, null).registerComponent(instance, jspMonitorON, null); } catch (Exception ex) { log.warn(sm.getString("standardWrapper.jspMonitorError", instance)); } } }
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.
Throws:
Returns:the loaded Servlet instance
/** * 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. * @return the loaded Servlet instance * @throws ServletException for a Servlet load error */
public synchronized Servlet loadServlet() throws ServletException { // Nothing to do if we already have an instance or an instance pool if (!singleThreadModel && (instance != null)) return instance; PrintStream out = System.out; if (swallowOutput) { SystemLogHandler.startCapture(); } Servlet servlet; try { long t1=System.currentTimeMillis(); // Complain if no servlet class has been specified if (servletClass == null) { unavailable(null); throw new ServletException (sm.getString("standardWrapper.notClass", getName())); } InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager(); try { servlet = (Servlet) instanceManager.newInstance(servletClass); } catch (ClassCastException e) { unavailable(null); // Restore the context ClassLoader throw new ServletException (sm.getString("standardWrapper.notServlet", servletClass), e); } catch (Throwable e) { e = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(e); unavailable(null); // Added extra log statement for Bugzilla 36630: // https://bz.apache.org/bugzilla/show_bug.cgi?id=36630 if(log.isDebugEnabled()) { log.debug(sm.getString("standardWrapper.instantiate", servletClass), e); } // Restore the context ClassLoader throw new ServletException (sm.getString("standardWrapper.instantiate", servletClass), e); } if (multipartConfigElement == null) { MultipartConfig annotation = servlet.getClass().getAnnotation(MultipartConfig.class); if (annotation != null) { multipartConfigElement = new MultipartConfigElement(annotation); } } // Special handling for ContainerServlet instances // Note: The InstanceManager checks if the application is permitted // to load ContainerServlets if (servlet instanceof ContainerServlet) { ((ContainerServlet) servlet).setWrapper(this); } classLoadTime=(int) (System.currentTimeMillis() -t1); if (servlet instanceof SingleThreadModel) { if (instancePool == null) { instancePool = new Stack<>(); } singleThreadModel = true; } initServlet(servlet); fireContainerEvent("load", this); loadTime=System.currentTimeMillis() -t1; } finally { if (swallowOutput) { String log = SystemLogHandler.stopCapture(); if (log != null && log.length() > 0) { if (getServletContext() != null) { getServletContext().log(log); } else { out.println(log); } } } } return servlet; } private synchronized void initServlet(Servlet servlet) throws ServletException { if (instanceInitialized && !singleThreadModel) return; // Call the initialization method of this servlet try { if( Globals.IS_SECURITY_ENABLED) { boolean success = false; try { Object[] args = new Object[] { facade }; SecurityUtil.doAsPrivilege("init", servlet, classType, args); success = true; } finally { if (!success) { // destroy() will not be called, thus clear the reference now SecurityUtil.remove(servlet); } } } else { servlet.init(facade); } instanceInitialized = true; } catch (UnavailableException f) { unavailable(f); throw f; } catch (ServletException f) { // If the servlet wanted to be unavailable it would have // said so, so do not call unavailable(null). throw f; } catch (Throwable f) { ExceptionUtils.handleThrowable(f); getServletContext().log(sm.getString("standardWrapper.initException", getName()), f); // If the servlet wanted to be unavailable it would have // said so, so do not call unavailable(null). throw new ServletException (sm.getString("standardWrapper.initException", getName()), f); } }
Remove the specified initialization parameter from this servlet.
Params:
  • name – Name of the initialization parameter to remove
/** * Remove the specified initialization parameter from this servlet. * * @param name Name of the initialization parameter to remove */
@Override public void removeInitParameter(String name) { parametersLock.writeLock().lock(); try { parameters.remove(name); } finally { parametersLock.writeLock().unlock(); } fireContainerEvent("removeInitParameter", name); }
Remove a mapping associated with the wrapper.
Params:
  • mapping – The pattern to remove
/** * Remove a mapping associated with the wrapper. * * @param mapping The pattern to remove */
@Override public void removeMapping(String mapping) { mappingsLock.writeLock().lock(); try { mappings.remove(mapping); } finally { mappingsLock.writeLock().unlock(); } if(parent.getState().equals(LifecycleState.STARTED)) fireContainerEvent(REMOVE_MAPPING_EVENT, mapping); }
Remove any security role reference for the specified role name.
Params:
  • name – Security role used within this servlet to be removed
/** * Remove any security role reference for the specified role name. * * @param name Security role used within this servlet to be removed */
@Override public void removeSecurityReference(String name) { referencesLock.writeLock().lock(); try { references.remove(name); } finally { referencesLock.writeLock().unlock(); } fireContainerEvent("removeSecurityReference", name); }
Process an UnavailableException, marking this servlet as unavailable for the specified amount of time.
Params:
  • unavailable – The exception that occurred, or null to mark this servlet as permanently unavailable
/** * Process an UnavailableException, marking this servlet as unavailable * for the specified amount of time. * * @param unavailable The exception that occurred, or <code>null</code> * to mark this servlet as permanently unavailable */
@Override public void unavailable(UnavailableException unavailable) { getServletContext().log(sm.getString("standardWrapper.unavailable", getName())); if (unavailable == null) setAvailable(Long.MAX_VALUE); else if (unavailable.isPermanent()) setAvailable(Long.MAX_VALUE); else { int unavailableSeconds = unavailable.getUnavailableSeconds(); if (unavailableSeconds <= 0) unavailableSeconds = 60; // Arbitrary default setAvailable(System.currentTimeMillis() + (unavailableSeconds * 1000L)); } }
Unload all initialized instances of this servlet, after calling the destroy() 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.
Throws:
  • ServletException – if an exception is thrown by the destroy() method
/** * 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. * * @exception ServletException if an exception is thrown by the * destroy() method */
@Override public synchronized void unload() throws ServletException { // Nothing to do if we have never loaded the instance if (!singleThreadModel && (instance == null)) return; unloading = true; // Loaf a while if the current instance is allocated // (possibly more than once if non-STM) if (countAllocated.get() > 0) { int nRetries = 0; long delay = unloadDelay / 20; while ((nRetries < 21) && (countAllocated.get() > 0)) { if ((nRetries % 10) == 0) { log.info(sm.getString("standardWrapper.waiting", countAllocated.toString(), getName())); } try { Thread.sleep(delay); } catch (InterruptedException e) { // Ignore } nRetries++; } } if (instanceInitialized) { PrintStream out = System.out; if (swallowOutput) { SystemLogHandler.startCapture(); } // Call the servlet destroy() method try { if( Globals.IS_SECURITY_ENABLED) { try { SecurityUtil.doAsPrivilege("destroy", instance); } finally { SecurityUtil.remove(instance); } } else { instance.destroy(); } } catch (Throwable t) { t = ExceptionUtils.unwrapInvocationTargetException(t); ExceptionUtils.handleThrowable(t); instance = null; instancePool = null; nInstances = 0; fireContainerEvent("unload", this); unloading = false; throw new ServletException (sm.getString("standardWrapper.destroyException", getName()), t); } finally { // Annotation processing if (!((Context) getParent()).getIgnoreAnnotations()) { try { ((Context)getParent()).getInstanceManager().destroyInstance(instance); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); log.error(sm.getString("standardWrapper.destroyInstance", getName()), t); } } // Write captured output if (swallowOutput) { String log = SystemLogHandler.stopCapture(); if (log != null && log.length() > 0) { if (getServletContext() != null) { getServletContext().log(log); } else { out.println(log); } } } } } // Deregister the destroyed instance instance = null; instanceInitialized = false; if (isJspServlet && jspMonitorON != null ) { Registry.getRegistry(null, null).unregisterComponent(jspMonitorON); } if (singleThreadModel && (instancePool != null)) { try { while (!instancePool.isEmpty()) { Servlet s = instancePool.pop(); if (Globals.IS_SECURITY_ENABLED) { try { SecurityUtil.doAsPrivilege("destroy", s); } finally { SecurityUtil.remove(s); } } else { s.destroy(); } // Annotation processing if (!((Context) getParent()).getIgnoreAnnotations()) { ((StandardContext)getParent()).getInstanceManager().destroyInstance(s); } } } catch (Throwable t) { t = ExceptionUtils.unwrapInvocationTargetException(t); ExceptionUtils.handleThrowable(t); instancePool = null; nInstances = 0; unloading = false; fireContainerEvent("unload", this); throw new ServletException (sm.getString("standardWrapper.destroyException", getName()), t); } instancePool = null; nInstances = 0; } singleThreadModel = false; unloading = false; fireContainerEvent("unload", this); } // -------------------------------------------------- ServletConfig Methods
Params:
  • name – Name of the initialization parameter to retrieve
Returns:the initialization parameter value for the specified name, if any; otherwise return null.
/** * @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 */
@Override public String getInitParameter(String name) { return findInitParameter(name); }
Returns:the set of initialization parameter names defined for this servlet. If none are defined, an empty Enumeration is returned.
/** * @return the set of initialization parameter names defined for this * servlet. If none are defined, an empty Enumeration is returned. */
@Override public Enumeration<String> getInitParameterNames() { parametersLock.readLock().lock(); try { return Collections.enumeration(parameters.keySet()); } finally { parametersLock.readLock().unlock(); } }
Returns:the servlet context with which this servlet is associated.
/** * @return the servlet context with which this servlet is associated. */
@Override public ServletContext getServletContext() { if (parent == null) return null; else if (!(parent instanceof Context)) return null; else return ((Context) parent).getServletContext(); }
Returns:the name of this servlet.
/** * @return the name of this servlet. */
@Override public String getServletName() { return getName(); } public long getProcessingTime() { return swValve.getProcessingTime(); } public long getMaxTime() { return swValve.getMaxTime(); } public long getMinTime() { return swValve.getMinTime(); } public int getRequestCount() { return swValve.getRequestCount(); } public int getErrorCount() { return swValve.getErrorCount(); }
Increment the error count used for monitoring.
/** * Increment the error count used for monitoring. */
@Override public void incrementErrorCount(){ swValve.incrementErrorCount(); } public long getLoadTime() { return loadTime; } public int getClassLoadTime() { return classLoadTime; } @Override public MultipartConfigElement getMultipartConfigElement() { return multipartConfigElement; } @Override public void setMultipartConfigElement( MultipartConfigElement multipartConfigElement) { this.multipartConfigElement = multipartConfigElement; } @Override public boolean isAsyncSupported() { return asyncSupported; } @Override public void setAsyncSupported(boolean asyncSupported) { this.asyncSupported = asyncSupported; } @Override public boolean isEnabled() { return enabled; } @Override public void setEnabled(boolean enabled) { this.enabled = enabled; } // -------------------------------------------------------- Package Methods // -------------------------------------------------------- protected Methods protected Method[] getAllDeclaredMethods(Class<?> c) { if (c.equals(jakarta.servlet.http.HttpServlet.class)) { return null; } Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass()); Method[] thisMethods = c.getDeclaredMethods(); if (thisMethods.length == 0) { return parentMethods; } if ((parentMethods != null) && (parentMethods.length > 0)) { Method[] allMethods = new Method[parentMethods.length + thisMethods.length]; System.arraycopy(parentMethods, 0, allMethods, 0, parentMethods.length); System.arraycopy(thisMethods, 0, allMethods, parentMethods.length, thisMethods.length); thisMethods = allMethods; } return thisMethods; } // ------------------------------------------------------ Lifecycle Methods
Start this component and implement the requirements of LifecycleBase.startInternal().
Throws:
  • LifecycleException – if this component detects a fatal error that prevents this component from being used
/** * Start this component and implement the requirements * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. * * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */
@Override protected synchronized void startInternal() throws LifecycleException { // Send j2ee.state.starting notification if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.state.starting", this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); } // Start up this component super.startInternal(); setAvailable(0L); // Send j2ee.state.running notification if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.state.running", this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); } }
Stop this component and implement the requirements of LifecycleBase.stopInternal().
Throws:
  • LifecycleException – if this component detects a fatal error that prevents this component from being used
/** * Stop this component and implement the requirements * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}. * * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */
@Override protected synchronized void stopInternal() throws LifecycleException { setAvailable(Long.MAX_VALUE); // Send j2ee.state.stopping notification if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.state.stopping", this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); } // Shut down our servlet instance (if it has been initialized) try { unload(); } catch (ServletException e) { getServletContext().log(sm.getString ("standardWrapper.unloadException", getName()), e); } // Shut down this component super.stopInternal(); // Send j2ee.state.stopped notification if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.state.stopped", this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); } // Send j2ee.object.deleted notification Notification notification = new Notification("j2ee.object.deleted", this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); } @Override protected String getObjectNameKeyProperties() { StringBuilder keyProperties = new StringBuilder("j2eeType=Servlet"); keyProperties.append(getWebModuleKeyProperties()); keyProperties.append(",name="); String name = getName(); if (Util.objectNameValueNeedsQuote(name)) { name = ObjectName.quote(name); } keyProperties.append(name); keyProperties.append(getJ2EEKeyProperties()); return keyProperties.toString(); } private String getWebModuleKeyProperties() { StringBuilder keyProperties = new StringBuilder(",WebModule=//"); String hostName = getParent().getParent().getName(); if (hostName == null) { keyProperties.append("DEFAULT"); } else { keyProperties.append(hostName); } String contextName = getParent().getName(); if (!contextName.startsWith("/")) { keyProperties.append('/'); } keyProperties.append(contextName); return keyProperties.toString(); } private String getJ2EEKeyProperties() { StringBuilder keyProperties = new StringBuilder(",J2EEApplication="); StandardContext ctx = null; if (parent instanceof StandardContext) { ctx = (StandardContext) getParent(); } if (ctx == null) { keyProperties.append("none"); } else { keyProperties.append(ctx.getJ2EEApplication()); } keyProperties.append(",J2EEServer="); if (ctx == null) { keyProperties.append("none"); } else { keyProperties.append(ctx.getJ2EEServer()); } return keyProperties.toString(); }
Remove a JMX notificationListener
See Also:
  • removeNotificationListener.removeNotificationListener(NotificationListener, NotificationFilter, Object)
/** * Remove a JMX notificationListener * @see javax.management.NotificationEmitter#removeNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object) */
@Override public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object object) throws ListenerNotFoundException { broadcaster.removeNotificationListener(listener,filter,object); } protected MBeanNotificationInfo[] notificationInfo;
Get JMX Broadcaster Info
See Also:
  • getNotificationInfo.getNotificationInfo()
/** * Get JMX Broadcaster Info * @see javax.management.NotificationBroadcaster#getNotificationInfo() */
@Override public MBeanNotificationInfo[] getNotificationInfo() { // FIXME: we not send j2ee.state.failed // FIXME: we not send j2ee.attribute.changed if (notificationInfo == null) { notificationInfo = new MBeanNotificationInfo[] { new MBeanNotificationInfo( new String[] { "j2ee.object.created" }, Notification.class.getName(), "servlet is created"), new MBeanNotificationInfo( new String[] { "j2ee.state.starting" }, Notification.class.getName(), "servlet is starting"), new MBeanNotificationInfo( new String[] { "j2ee.state.running" }, Notification.class.getName(), "servlet is running"), new MBeanNotificationInfo( new String[] { "j2ee.state.stopped" }, Notification.class.getName(), "servlet start to stopped"), new MBeanNotificationInfo( new String[] { "j2ee.object.stopped" }, Notification.class.getName(), "servlet is stopped"), new MBeanNotificationInfo( new String[] { "j2ee.object.deleted" }, Notification.class.getName(), "servlet is deleted") }; } return notificationInfo; }
Add a JMX-NotificationListener
See Also:
  • addNotificationListener.addNotificationListener(NotificationListener, NotificationFilter, Object)
/** * Add a JMX-NotificationListener * @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object) */
@Override public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object object) throws IllegalArgumentException { broadcaster.addNotificationListener(listener,filter,object); }
Remove a JMX-NotificationListener
See Also:
  • removeNotificationListener.removeNotificationListener(NotificationListener)
/** * Remove a JMX-NotificationListener * @see javax.management.NotificationBroadcaster#removeNotificationListener(javax.management.NotificationListener) */
@Override public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { broadcaster.removeNotificationListener(listener); } }