/*
* 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.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.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 < 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
*/
public class StandardWrapper
extends ContainerBase
implements ServletConfig, Wrapper, No
tificationEmitter {
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 .
*/
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 .
*/
protected long available = 0 L;
/**
* The broadcaster that sends j2ee notifications .
*/
protected final NotificationBroadcasterSupport broadcaster;
/**
* The count of allocations that are currently active .
*/
protected final AtomicInteger countAllocated = new AtomicInteger(0 );
/**
* The facade associated with this wrapper .
*/
protected final StandardWrapperFacade facade = new StandardWrapperFacade(this );
/**
* The ( single ) possibly uninitialized instance of this servlet .
*/
protected volatile Servlet instance = null ;
/**
* 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 .
*/
protected int loadOnStartup = -1 ;
/**
* Mappings associated with the wrapper .
*/
protected final ArrayList<String> mappings = new ArrayList<>();
/**
* 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 .
*/
protected HashMap<String,String> references = new HashMap<>();
/**
* The run - as identity for this servlet .
*/
protected String runAs = null ;
/**
* The notification sequence number .
*/
protected long sequenceNumber = 0 ;
/**
* The fully qualified servlet class name for this servlet .
*/
protected String servletClass = null ;
/**
* Are we unloading our servlet instance at the moment ?
*/
protected volatile boolean unloading = false ;
/**
* Wait time for servlet unload in ms .
*/
protected long unloadDelay = 2000 ;
/**
* True if this StandardWrapper is for the JspServlet
*/
protected boolean isJspServlet;
/**
* The ObjectName of the JSP monitoring mbean
*/
protected ObjectName jspMonitorON;
/**
* 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
*/
protected MultipartConfigElement multipartConfigElement = null ;
/**
* Async support
*/
protected boolean asyncSupported = false ;
/**
* Enabled
*/
protected boolean enabled = true ;
private boolean overridable = false ;
/**
* 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 .
*/
@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 .
*
* @ 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 = 0 L;
}
support.firePropertyChange("available" , Long .valueOf(oldAvailable), Long .valueOf(this .available));
}
/**
* @ return the number of active allocations of this servlet .
*/
public int getCountAllocated() {
return this .countAllocated.get();
}
/**
* @ return the load - on - startup order value ( negative value means load on first call ) .
*/
@Override
public int getLoadOnStartup() {
if (isJspServlet && loadOnStartup == -1 ) {
/*
* 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 ) .
*
* @ 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 .
*
* @ param value New load - on - startup value
*/
public void setLoadOnStartupString(String value) {
try {
setLoadOnStartup(Integer.parseInt(value));
} catch (NumberFormatException e) {
setLoadOnStartup(0 );
}
}
/**
* @ return the load - on - startup value that was parsed
*/
public String getLoadOnStartupString() {
return Integer.toString(getLoadOnStartup());
}
/**
* 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);
}
/**
* @ return the run - as identity for this servlet .
*/
@Override
public String getRunAs() {
return this .runAs;
}
/**
* 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);
}
/**
* @ 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 .
*
* @ 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 < 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 has been marked unavailable .
*/
@Override
public boolean isUnavailable() {
if (!isEnabled()) {
return true ;
} else if (available == 0 L) {
return false ;
} else if (available <= System.currentTimeMillis()) {
available = 0 L;
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" );
}
}
}
return allow.toArray(new String[0 ]);
}
/**
* @ return the associated servlet instance .
*/
@Override
public Servlet getServlet() {
return 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 .
*/
@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 .
*
* @ 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 .
*
* @ 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 .
*
* @ 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 .
*
* @ 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 .
*
* @ 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 < code > service ( ) < / code > method called .
*
* @ 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 ;
// Load and initialize our instance if necessary
if (instance == null || !instanceInitialized) {
synchronized (this ) {
if (instance == null ) {
try {
if (log.isDebugEnabled()) {
log.debug("Allocating instance" );
}
instance = loadServlet();
newInstance = true ;
// 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 (log.isTraceEnabled()) {
log.trace(" Returning instance" );
}
// For new instances, count will have been incremented at the
// time of creation
if (!newInstance) {
countAllocated.incrementAndGet();
}
return instance;
}
/**
* Decrement the allocation count for this servlet .
*
* @ param servlet The servlet to be returned
*
* @ exception ServletException if a deallocation error occurs
*/
@Override
public void deallocate(Servlet servlet) throws ServletException {
countAllocated.decrementAndGet();
}
/**
* 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 .
*/
@Override
public String[] findInitParameters() {
parametersLock.readLock().lock();
try {
return parameters.keySet().toArray(new String[0 ]);
} finally {
parametersLock.readLock().unlock();
}
}
/**
* 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
* < 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 .
*/
@Override
public String[] findSecurityReferences() {
referencesLock.readLock().lock();
try {
return references.keySet().toArray(new String[0 ]);
} 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 .
* < 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 an 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 (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);
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) {
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 .
*
* @ 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 .
*
* @ 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 .
*
* @ 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 .
*
* @ 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 * 1000 L));
}
}
/**
* 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 (instance == null ) {
return ;
}
unloading = true ;
// Loaf a while if the current instance is allocated
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);
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);
}
}
}
instance = null ;
instanceInitialized = false ;
}
}
// Deregister the destroyed instance
instance = null ;
if (isJspServlet && jspMonitorON != null ) {
Registry.getRegistry(null , null ).unregisterComponent(jspMonitorON);
}
unloading = false ;
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
*/
@Override
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 .
*/
@Override
public Enumeration<String> getInitParameterNames() {
parametersLock.readLock().lock();
try {
return Collections.enumeration(parameters.keySet());
} finally {
parametersLock.readLock().unlock();
}
}
/**
* @ 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();
}
}
/**
* @ 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();
}
/**
* Returns the number of requests processed by the wrapper .
*
* @ return the number of requests processed by the wrapper .
*
* @ deprecated The return type will change to long in Tomcat 11 onwards . Callers of this method should switch to
* storing the result of calls to this method in a long value rather than an int .
*/
@Deprecated
public int getRequestCount() {
return swValve.getRequestCount();
}
/**
* Returns the number of requests processed by the wrapper that resulted in an error .
*
* @ return the number of requests processed by the wrapper that resulted in an error .
*
* @ deprecated The return type will change to long in Tomcat 11 onwards . Callers of this method should switch to
* storing the result of calls to this method in a long value rather than an int .
*/
@Deprecated
public int getErrorCount() {
return swValve.getErrorCount();
}
/**
* 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
* { @ 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(0 L);
// 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
* { @ 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 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 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 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 javax . management . NotificationBroadcaster # removeNotificationListener ( javax . management . NotificationListener )
*/
@Override
public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException {
broadcaster.removeNotificationListener(listener);
}
}
Messung V0.5 in Prozent C=94 H=92 G=92
¤ Dauer der Verarbeitung: 0.28 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland