/* * 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;
/** * 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
*/ publicclass StandardWrapper extends ContainerBase implements ServletConfig, Wrapper, NotificationEmitter {
privatefinal Log log = LogFactory.getLog(StandardWrapper.class); // must not be static
/** * 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.
*/ protectedlong available = 0L;
/** * The broadcaster that sends j2ee notifications.
*/ protectedfinal NotificationBroadcasterSupport broadcaster;
/** * The count of allocations that are currently active.
*/ protectedfinal AtomicInteger countAllocated = new AtomicInteger(0);
/** * The facade associated with this wrapper.
*/ protectedfinal StandardWrapperFacade facade = new StandardWrapperFacade(this);
/** * The (single) possibly uninitialized instance of this servlet.
*/ protectedvolatile Servlet instance = null;
/** * Flag that indicates if this instance has been initialized
*/ protectedvolatileboolean instanceInitialized = false;
/** * The load-on-startup order value (negative value means load on first call) for this servlet.
*/ protectedint loadOnStartup = -1;
/** * Mappings associated with the wrapper.
*/ protectedfinal 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;
/** * Async support
*/ protectedboolean asyncSupported = false;
/** * Enabled
*/ protectedboolean enabled = true;
privateboolean overridable = false;
/** * Static class array used when the SecurityManager is turned on and <code>Servlet.init</code> is invoked.
*/ protectedstaticClass<?>[] classType = newClass[] { ServletConfig.class };
privatefinal ReentrantReadWriteLock parametersLock = new ReentrantReadWriteLock();
privatefinal ReentrantReadWriteLock mappingsLock = new ReentrantReadWriteLock();
privatefinal ReentrantReadWriteLock referencesLock = new ReentrantReadWriteLock();
/** * 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 publiclong getAvailable() { returnthis.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 publicvoid 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.
*/ publicint getCountAllocated() { returnthis.countAllocated.get();
}
/** * @return the load-on-startup order value (negative value means load on first call).
*/
@Override publicint getLoadOnStartup() {
if (isJspServlet && loadOnStartup == -1) { /* * JspServlet must always be preloaded, because its instance is used during registerJMX (when registering * the JSP monitoring mbean)
*/ return Integer.MAX_VALUE;
} else { returnthis.loadOnStartup;
}
}
/** * Set the load-on-startup order value (negative value means load on first call). * * @param value New load-on-startup value
*/
@Override publicvoid 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
*/ publicvoid setLoadOnStartupString(String value) {
/** * @return the load-on-startup value that was parsed
*/ public String getLoadOnStartupString() { return Integer.toString(getLoadOnStartup());
}
/** * Set the parent Container of this Wrapper, but only if it is a Context. * * @param container Proposed parent Container
*/
@Override publicvoid setParent(Container container) {
/** * @return the fully qualified servlet class name for this servlet.
*/
@Override public String getServletClass() { returnthis.servletClass;
}
/** * Set the fully qualified servlet class name for this servlet. * * @param servletClass Servlet class name
*/
@Override publicvoid setServletClass(String servletClass) {
/** * Set the name of this servlet. This is an alias for the normal <code>Container.setName()</code> method, and * complements the <code>getServletName()</code> method required by the <code>ServletConfig</code> interface. * * @param name The new name of this servlet
*/ publicvoid setServletName(String name) {
setName(name);
}
/** * @return <code>true</code> if the Servlet has been marked unavailable.
*/
@Override publicboolean isUnavailable() {
/** * @return the associated servlet instance.
*/
@Override public Servlet getServlet() { return instance;
}
/** * Set the associated servlet instance.
*/
@Override publicvoid setServlet(Servlet servlet) {
instance = servlet;
}
// --------------------------------------------------------- Public Methods
/** * Execute a periodic task, such as reloading, etc. This method will be invoked inside the classloading context of * this container. Unexpected throwables will be caught and logged.
*/
@Override publicvoid backgroundProcess() { super.backgroundProcess();
if (!getState().isAvailable()) { return;
}
if (getServlet() instanceof PeriodicEventListener) {
((PeriodicEventListener) getServlet()).periodicEvent();
}
}
/** * Extract the root cause from a servlet exception. * * @param e The servlet exception * * @return the root cause of the Servlet exception
*/ publicstatic 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 publicvoid addChild(Container child) {
/** * 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 publicvoid addInitParameter(String name, String value) {
/** * 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 publicvoid addSecurityReference(String name, String link) {
/** * Allocate an initialized instance of this Servlet that is ready to have its <code>service()</code> method called. * * @exception ServletException if the servlet init() method threw an exception * @exception ServletException if a loading error occurs
*/
@Override public Servlet allocate() throws ServletException {
// If we are currently unloading this servlet, throw an exception if (unloading) { thrownew ServletException(sm.getString("standardWrapper.unloading", getName()));
}
boolean newInstance = false;
// Load and initialize our instance if necessary if (instance == null || !instanceInitialized) { synchronized (this) { if (instance == null) { try { if (log.isDebugEnabled()) {
log.debug("Allocating instance");
}
instance = loadServlet();
newInstance = true; // Increment here to prevent a race condition // with unload. Bug 43683, test case #3
countAllocated.incrementAndGet();
} catch (ServletException e) { throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e); thrownew ServletException(sm.getString("standardWrapper.allocate"), e);
}
} if (!instanceInitialized) {
initServlet(instance);
}
}
}
if (log.isTraceEnabled()) {
log.trace(" Returning instance");
} // For new instances, count will have been incremented at the // time of creation if (!newInstance) {
countAllocated.incrementAndGet();
} return instance;
}
/** * Decrement the allocation count for this servlet. * * @param servlet The servlet to be returned * * @exception ServletException if a deallocation error occurs
*/
@Override publicvoid deallocate(Servlet servlet) throws ServletException {
countAllocated.decrementAndGet();
}
/** * Return the value for the specified initialization parameter name, if any; otherwise return <code>null</code>. * * @param name Name of the requested initialization parameter
*/
@Override public String findInitParameter(String name) {
/** * Return the security role link for the specified security role reference name, if any; otherwise return * <code>null</code>. * * @param name Security role reference used within this servlet
*/
@Override public String findSecurityReference(String name) {
String reference = null;
// If not specified on the Wrapper, check the Context if (getParent() instanceof Context) {
Context context = (Context) getParent(); if (reference != null) {
reference = context.findRoleMapping(reference);
} else {
reference = context.findRoleMapping(name);
}
}
return reference;
}
/** * Return the set of security role reference names associated with this servlet, if any; otherwise return a * zero-length array.
*/
@Override public String[] findSecurityReferences() {
/** * 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 publicsynchronizedvoid load() throws ServletException {
instance = loadServlet();
if (!instanceInitialized) {
initServlet(instance);
}
if (isJspServlet) {
StringBuilder oname = new StringBuilder(getDomain());
/** * Load and initialize an instance of this servlet, if there is not already an initialized instance. This can be * used, for example, to load servlets that are marked in the deployment descriptor to be loaded at server startup * time. * * @return the loaded Servlet instance * * @throws ServletException for a Servlet load error
*/ publicsynchronized Servlet loadServlet() throws ServletException {
// Nothing to do if we already have an instance or an instance pool if (instance != null) { return instance;
}
PrintStream out = System.out; if (swallowOutput) {
SystemLogHandler.startCapture();
}
Servlet servlet; try { long t1 = System.currentTimeMillis(); // Complain if no servlet class has been specified if (servletClass == null) {
unavailable(null); thrownew ServletException(sm.getString("standardWrapper.notClass", getName()));
}
// Restore the context ClassLoader thrownew 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);
}
// 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). thrownew 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 publicvoid removeInitParameter(String name) {
/** * Remove any security role reference for the specified role name. * * @param name Security role used within this servlet to be removed
*/
@Override publicvoid removeSecurityReference(String name) {
/** * Process an UnavailableException, marking this servlet as unavailable for the specified amount of time. * * @param unavailable The exception that occurred, or <code>null</code> to mark this servlet as permanently * unavailable
*/
@Override publicvoid unavailable(UnavailableException unavailable) {
getServletContext().log(sm.getString("standardWrapper.unavailable", getName())); if (unavailable == null) {
setAvailable(Long.MAX_VALUE);
} elseif (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 publicsynchronizedvoid unload() throws ServletException {
// Nothing to do if we have never loaded the instance if (instance == null) { return;
}
unloading = true;
// Loaf a while if the current instance is allocated if (countAllocated.get() > 0) { int nRetries = 0; long delay = unloadDelay / 20; while ((nRetries < 21) && (countAllocated.get() > 0)) { if ((nRetries % 10) == 0) {
log.info(sm.getString("standardWrapper.waiting", countAllocated.toString(), getName()));
} try { Thread.sleep(delay);
} catch (InterruptedException e) { // Ignore
}
nRetries++;
}
}
if (instanceInitialized) {
PrintStream out = System.out; if (swallowOutput) {
SystemLogHandler.startCapture();
}
/** * @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() {
/** * Returns the number of requests processed by the wrapper. * * @return the number of requests processed by the wrapper. * * @deprecated The return type will change to long in Tomcat 11 onwards. Callers of this method should switch to * storing the result of calls to this method in a long value rather than an int.
*/
@Deprecated publicint getRequestCount() { return swValve.getRequestCount();
}
/** * Returns the number of requests processed by the wrapper that resulted in an error. * * @return the number of requests processed by the wrapper that resulted in an error. * * @deprecated The return type will change to long in Tomcat 11 onwards. Callers of this method should switch to * storing the result of calls to this method in a long value rather than an int.
*/
@Deprecated publicint getErrorCount() { return swValve.getErrorCount();
}
/** * Increment the error count used for monitoring.
*/
@Override publicvoid incrementErrorCount() {
swValve.incrementErrorCount();
}
/** * 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 protectedsynchronizedvoid 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 protectedsynchronizedvoid 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);
}
/** * Get JMX Broadcaster Info * * @see javax.management.NotificationBroadcaster#getNotificationInfo()
*/
@Override public MBeanNotificationInfo[] getNotificationInfo() { // FIXME: we not send j2ee.state.failed // FIXME: we not send j2ee.attribute.changed if (notificationInfo == null) {
notificationInfo = new MBeanNotificationInfo[] { new MBeanNotificationInfo(new String[] { "j2ee.object.created" }, Notification.class.getName(), "servlet is created"), new MBeanNotificationInfo(new String[] { "j2ee.state.starting" }, Notification.class.getName(), "servlet is starting"), new MBeanNotificationInfo(new String[] { "j2ee.state.running" }, Notification.class.getName(), "servlet is running"), new MBeanNotificationInfo(new String[] { "j2ee.state.stopped" }, Notification.class.getName(), "servlet start to stopped"), new MBeanNotificationInfo(new String[] { "j2ee.object.stopped" }, Notification.class.getName(), "servlet is stopped"), new MBeanNotificationInfo(new String[] { "j2ee.object.deleted" }, Notification.class.getName(), "servlet is deleted") };
} return notificationInfo;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.