All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.catalina.core.StandardWrapper Maven / Gradle / Ivy

There is a newer version: 11.0.0-M20
Show newest version
/*
 * 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 javax.servlet.MultipartConfigElement;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.SingleThreadModel;
import javax.servlet.UnavailableException;
import javax.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
 * @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.
     */
    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 = 0L;

    /**
     * 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).
     */
    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 mappings = new ArrayList<>();


    /**
     * The initialization parameters for this servlet, keyed by
     * parameter name.
     */
    protected HashMap 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 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;


    /**
     * Does this servlet implement the SingleThreadModel interface?
     */
    protected volatile boolean singleThreadModel = false;


    /**
     * Are we unloading our servlet instance at the moment?
     */
    protected volatile boolean unloading = false;


    /**
     * Maximum number of STM instances.
     */
    protected int maxInstances = 20;


    /**
     * Number of instances currently loaded for a STM servlet.
     */
    protected int nInstances = 0;


    /**
     * Stack containing the STM instances.
     */
    protected Stack instancePool = null;


    /**
     * 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
     * Servlet.init 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 = 0L;
        support.firePropertyChange("available", Long.valueOf(oldAvailable),
                                   Long.valueOf(this.available));
    }


    /**
     * @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 SingleThreadModel.
     */
    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 < 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).
     *
     * @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());
    }


    /**
     * @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.
     *
     * @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.
     *
     * @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
     * Container.setName() method, and complements the
     * getServletName() method required by the
     * ServletConfig 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.
     *
     * @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;
    }


    /**
     * @return true 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 servletClazz = instance.getClass();
        if (!javax.servlet.http.HttpServlet.class.isAssignableFrom(
                                                        servletClazz)) {
            return DEFAULT_SERVLET_METHODS;
        }

        Set 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 && iservice() 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().
     *
     * @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.
     *
     * @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.
     *
     * @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 {
            String results[] = new String[parameters.size()];
            return parameters.keySet().toArray(results);
        } finally {
            parametersLock.readLock().unlock();
        }

    }


    /**
     * Return the mappings associated with this wrapper.
     */
    @Override
    public String[] findMappings() {

        mappingsLock.readLock().lock();
        try {
            return mappings.toArray(new String[mappings.size()]);
        } finally {
            mappingsLock.readLock().unlock();
        }

    }


    /**
     * Return the security role link for the specified security role
     * reference name, if any; otherwise return null.
     *
     * @param name Security role reference used within this servlet
     */
    @Override
    public String findSecurityReference(String name) {

        referencesLock.readLock().lock();
        try {
            return references.get(name);
        } finally {
            referencesLock.readLock().unlock();
        }

    }


    /**
     * 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. * * @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. * @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. * * @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 null * 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. * * @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 /** * @return the initialization parameter value for the specified name, * if any; otherwise return null. * * @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 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(); } public int getRequestCount() { return swValve.getRequestCount(); } 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(javax.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(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 {@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 * FIXME: This two events we not send j2ee.state.failed and j2ee.attribute.changed! * @see javax.management.NotificationBroadcaster#getNotificationInfo() */ @Override public MBeanNotificationInfo[] getNotificationInfo() { 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); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy