| /* |
| * 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.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.InstanceEvent; |
| import org.apache.catalina.InstanceListener; |
| import org.apache.catalina.LifecycleException; |
| import org.apache.catalina.LifecycleState; |
| import org.apache.catalina.Wrapper; |
| import org.apache.catalina.security.SecurityUtil; |
| import org.apache.catalina.util.InstanceSupport; |
| 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 |
| */ |
| @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 support object for our instance listeners. |
| * |
| * @deprecated Will be removed in 8.5.x onwards |
| */ |
| @Deprecated |
| protected final InstanceSupport instanceSupport = new InstanceSupport(this); |
| |
| |
| /** |
| * 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; |
| |
| |
| /** |
| * 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<Servlet> 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 |
| * <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 = 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 <code>SingleThreadModel</code>. |
| */ |
| public int getCountAllocated() { |
| |
| return (this.countAllocated.get()); |
| |
| } |
| |
| |
| /** |
| * Return the InstanceSupport object for this Wrapper instance. |
| * |
| * @deprecated Will be removed in 8.5.x onwards |
| */ |
| @Deprecated |
| public InstanceSupport getInstanceSupport() { |
| |
| return (this.instanceSupport); |
| |
| } |
| |
| |
| /** |
| * 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); |
| } |
| } |
| |
| 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 |
| * <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 |
| * <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; |
| } |
| |
| |
| /** |
| * Is this servlet currently 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 (!javax.servlet.http.HttpServlet.class.isAssignableFrom( |
| servletClazz)) { |
| return DEFAULT_SERVLET_METHODS; |
| } |
| |
| HashSet<String> allow = new HashSet<>(); |
| allow.add("TRACE"); |
| allow.add("OPTIONS"); |
| |
| 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); |
| |
| } |
| |
| |
| /** |
| * Return the associated servlet instance. |
| */ |
| @Override |
| public Servlet getServlet() { |
| return instance; |
| } |
| |
| |
| /** |
| * Set the associated servlet instance. |
| */ |
| @Override |
| public void setServlet(Servlet servlet) { |
| instance = servlet; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void setServletSecurityAnnotationScanRequired(boolean b) { |
| // NO-OP |
| } |
| |
| // --------------------------------------------------------- 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() != null && (getServlet() instanceof PeriodicEventListener)) { |
| ((PeriodicEventListener) getServlet()).periodicEvent(); |
| } |
| } |
| |
| |
| /** |
| * Extract the root cause from a servlet exception. |
| * |
| * @param e 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 new listener interested in InstanceEvents. |
| * |
| * @param listener The new listener |
| * |
| * @deprecated Will be removed in 8.5.x onwards |
| */ |
| @Deprecated |
| @Override |
| public void addInstanceListener(InstanceListener listener) { |
| |
| instanceSupport.addInstanceListener(listener); |
| |
| } |
| |
| |
| /** |
| * 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. 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. |
| * |
| * @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 <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 { |
| 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 <code>null</code>. |
| * |
| * @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. |
| * <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.info("Error registering JSP monitoring with jmx " + |
| 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. |
| */ |
| public synchronized Servlet loadServlet() throws ServletException { |
| |
| if (unloading) { |
| throw new ServletException( |
| sm.getString("standardWrapper.unloading", getName())); |
| } |
| |
| // 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: |
| // http://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 |
| if ((servlet instanceof ContainerServlet) && |
| (isContainerProvidedServlet(servletClass) || |
| ((Context) getParent()).getPrivileged() )) { |
| ((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; |
| |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void servletSecurityAnnotationScan() throws ServletException { |
| // NO-OP |
| } |
| |
| |
| private synchronized void initServlet(Servlet servlet) |
| throws ServletException { |
| |
| if (instanceInitialized && !singleThreadModel) return; |
| |
| // Call the initialization method of this servlet |
| try { |
| instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT, |
| servlet); |
| |
| 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; |
| |
| instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, |
| servlet); |
| } catch (UnavailableException f) { |
| instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, |
| servlet, f); |
| unavailable(f); |
| throw f; |
| } catch (ServletException f) { |
| instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, |
| servlet, 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("StandardWrapper.Throwable", f ); |
| instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, |
| servlet, 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 listener no longer interested in InstanceEvents. |
| * |
| * @param listener The listener to remove |
| * |
| * @deprecated Will be removed in 8.5.x onwards |
| */ |
| @Deprecated |
| @Override |
| public void removeInstanceListener(InstanceListener listener) { |
| |
| instanceSupport.removeInstanceListener(listener); |
| |
| } |
| |
| |
| /** |
| * 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); |
| |
| } |
| |
| |
| /** |
| * Return a String representation of this component. |
| */ |
| @Override |
| public String toString() { |
| |
| StringBuilder sb = new StringBuilder(); |
| if (getParent() != null) { |
| sb.append(getParent().toString()); |
| sb.append("."); |
| } |
| sb.append("StandardWrapper["); |
| sb.append(getName()); |
| sb.append("]"); |
| return (sb.toString()); |
| |
| } |
| |
| |
| /** |
| * 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 |
| * <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 { |
| instanceSupport.fireInstanceEvent |
| (InstanceEvent.BEFORE_DESTROY_EVENT, instance); |
| |
| if( Globals.IS_SECURITY_ENABLED) { |
| try { |
| SecurityUtil.doAsPrivilege("destroy", |
| instance); |
| } finally { |
| SecurityUtil.remove(instance); |
| } |
| } else { |
| instance.destroy(); |
| } |
| |
| instanceSupport.fireInstanceEvent |
| (InstanceEvent.AFTER_DESTROY_EVENT, instance); |
| |
| } catch (Throwable t) { |
| t = ExceptionUtils.unwrapInvocationTargetException(t); |
| ExceptionUtils.handleThrowable(t); |
| instanceSupport.fireInstanceEvent |
| (InstanceEvent.AFTER_DESTROY_EVENT, instance, 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 <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(); |
| } |
| |
| 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 |
| |
| |
| /** |
| * Return <code>true</code> if the specified class name represents a |
| * container provided servlet class that should be loaded by the |
| * server class loader. |
| * |
| * @param classname Name of the class to be checked |
| */ |
| protected boolean isContainerProvidedServlet(String classname) { |
| |
| if (classname.startsWith("org.apache.catalina.")) { |
| return (true); |
| } |
| try { |
| Class<?> clazz = |
| this.getClass().getClassLoader().loadClass(classname); |
| return (ContainerServlet.class.isAssignableFrom(clazz)); |
| } catch (Throwable t) { |
| ExceptionUtils.handleThrowable(t); |
| return (false); |
| } |
| |
| } |
| |
| |
| 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 = ((Context) 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(); |
| } |
| |
| |
| /** |
| * JSR 77. Always return false. |
| * |
| * @deprecated The JSR-77 implementation is incomplete and will be removed |
| * in 8.5.x |
| */ |
| @Deprecated |
| public boolean isStateManageable() { |
| return false; |
| } |
| |
| |
| /* 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 |
| * @TODO use StringManager for international support! |
| * @TODO 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); |
| } |
| } |