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

org.eclipse.jetty.ee10.servlet.ServletContextHandler Maven / Gradle / Ivy

There is a newer version: 2.0.31
Show newest version
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.ee10.servlet;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterRegistration;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.Servlet;
import jakarta.servlet.ServletContainerInitializer;
import jakarta.servlet.ServletContextAttributeEvent;
import jakarta.servlet.ServletContextAttributeListener;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRegistration;
import jakarta.servlet.ServletRequestAttributeListener;
import jakarta.servlet.ServletRequestEvent;
import jakarta.servlet.ServletRequestListener;
import jakarta.servlet.ServletSecurityElement;
import jakarta.servlet.SessionCookieConfig;
import jakarta.servlet.SessionTrackingMode;
import jakarta.servlet.descriptor.JspConfigDescriptor;
import jakarta.servlet.descriptor.JspPropertyGroupDescriptor;
import jakarta.servlet.descriptor.TaglibDescriptor;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSessionActivationListener;
import jakarta.servlet.http.HttpSessionAttributeListener;
import jakarta.servlet.http.HttpSessionBindingListener;
import jakarta.servlet.http.HttpSessionIdListener;
import jakarta.servlet.http.HttpSessionListener;
import org.eclipse.jetty.ee10.servlet.ServletContextResponse.EncodingFrom;
import org.eclipse.jetty.ee10.servlet.ServletContextResponse.OutputType;
import org.eclipse.jetty.ee10.servlet.security.ConstraintAware;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.pathmap.MatchedResource;
import org.eclipse.jetty.io.IOResources;
import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.server.Context;
import org.eclipse.jetty.server.FormFields;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.ContextRequest;
import org.eclipse.jetty.server.handler.ContextResponse;
import org.eclipse.jetty.session.AbstractSessionManager;
import org.eclipse.jetty.session.ManagedSession;
import org.eclipse.jetty.session.SessionManager;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.DecoratedObjectFactory;
import org.eclipse.jetty.util.DeprecationWarning;
import org.eclipse.jetty.util.ExceptionUtil;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.ClassLoaderDump;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.component.DumpableCollection;
import org.eclipse.jetty.util.component.Environment;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.eclipse.jetty.util.resource.Resources;
import org.eclipse.jetty.util.security.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static jakarta.servlet.ServletContext.TEMPDIR;

/**
 * Servlet Context.
 * 

* This extension to the ContextHandler allows for * simple construction of a context with ServletHandler and optionally * session and security handlers, et. *

 *   new ServletContext("/context",Context.SESSIONS|Context.NO_SECURITY);
 * 
*

* This class should have been called ServletContext, but this would have * cause confusion with {@link jakarta.servlet.ServletContext}. */ @ManagedObject("Servlet Context Handler") public class ServletContextHandler extends ContextHandler { private static final Logger LOG = LoggerFactory.getLogger(ServletContextHandler.class); public static final Environment ENVIRONMENT = Environment.ensure("ee10"); /** * @deprecated Use {@link ServletContextHandler#ENVIRONMENT} instead. */ @Deprecated(since = "12.0.9", forRemoval = true) public static final Environment __environment = ENVIRONMENT; public static final Class[] SERVLET_LISTENER_TYPES = { ServletContextListener.class, ServletContextAttributeListener.class, ServletRequestListener.class, ServletRequestAttributeListener.class, HttpSessionIdListener.class, HttpSessionListener.class, HttpSessionAttributeListener.class }; public static final int DEFAULT_LISTENER_TYPE_INDEX = 1; public static final int EXTENDED_LISTENER_TYPE_INDEX = 0; public static final String MAX_FORM_KEYS_KEY = FormFields.MAX_FIELDS_ATTRIBUTE; public static final String MAX_FORM_CONTENT_SIZE_KEY = FormFields.MAX_LENGTH_ATTRIBUTE; public static final int DEFAULT_MAX_FORM_KEYS = FormFields.MAX_FIELDS_DEFAULT; public static final int DEFAULT_MAX_FORM_CONTENT_SIZE = FormFields.MAX_LENGTH_DEFAULT; public static final int SESSIONS = 1; public static final int SECURITY = 2; public static final int NO_SESSIONS = 0; public static final int NO_SECURITY = 0; public enum ContextStatus { NOTSET, INITIALIZED, DESTROYED } public static ServletContextHandler getServletContextHandler(jakarta.servlet.ServletContext servletContext, String purpose) { if (servletContext instanceof ServletContextApi servletContextApi) return servletContextApi.getContext().getServletContextHandler(); ServletContextHandler sch = getCurrentServletContextHandler(); if (sch != null) return sch; throw new IllegalStateException("No Jetty ServletContextHandler, " + purpose + " unavailable"); } public static ServletContextHandler getServletContextHandler(jakarta.servlet.ServletContext servletContext) { if (servletContext instanceof ServletContextApi) return ((ServletContextApi)servletContext).getContext().getServletContextHandler(); return getCurrentServletContextHandler(); } public static jakarta.servlet.ServletContext getCurrentServletContext() { return getServletContext(ContextHandler.getCurrentContext()); } public static jakarta.servlet.ServletContext getServletContext(Context context) { if (context instanceof ServletScopedContext) return ((ServletScopedContext)context).getServletContext(); return null; } public static ServletContextHandler getCurrentServletContextHandler() { Context context = ContextHandler.getCurrentContext(); if (context instanceof ServletScopedContext) return ((ServletScopedContext)context).getServletContextHandler(); return null; } public interface ServletContainerInitializerCaller extends LifeCycle {} private Class _defaultSecurityHandlerClass = ConstraintSecurityHandler.class; private final ServletContextApi _servletContext; protected ContextStatus _contextStatus = ContextStatus.NOTSET; private final Map _initParams = new HashMap<>(); private String _defaultRequestCharacterEncoding; private String _defaultResponseCharacterEncoding; private String _contextPathEncoded = "/"; private Map _localeEncodingMap; private String[] _welcomeFiles; private Logger _logger; private int _maxFormKeys = Integer.getInteger(MAX_FORM_KEYS_KEY, DEFAULT_MAX_FORM_KEYS); private int _maxFormContentSize = Integer.getInteger(MAX_FORM_CONTENT_SIZE_KEY, DEFAULT_MAX_FORM_CONTENT_SIZE); private boolean _usingSecurityManager = getSecurityManager() != null; private final List _programmaticListeners = new CopyOnWriteArrayList<>(); private final List _servletContextListeners = new CopyOnWriteArrayList<>(); private final List _destroyServletContextListeners = new ArrayList<>(); private final List _servletContextAttributeListeners = new CopyOnWriteArrayList<>(); private final List _servletRequestListeners = new CopyOnWriteArrayList<>(); private final List _servletRequestAttributeListeners = new CopyOnWriteArrayList<>(); private final List _contextListeners = new CopyOnWriteArrayList<>(); private final Set _durableListeners = new HashSet<>(); protected final DecoratedObjectFactory _objFactory; // protected Class _defaultSecurityHandlerClass = org.eclipse.jetty.security.ConstraintSecurityHandler.class; protected SessionHandler _sessionHandler; protected SecurityHandler _securityHandler; protected ServletHandler _servletHandler; protected int _options; protected JspConfigDescriptor _jspConfig; private boolean _startListeners; public ServletContextHandler() { this(null, null, null, null, null); } public ServletContextHandler(String contextPath) { this(contextPath, null, null, null, null); } public ServletContextHandler(int options) { this(null, options); } public ServletContextHandler(String contextPath, int options) { this(contextPath, null, null, null, null, options); } public ServletContextHandler(String contextPath, boolean sessions, boolean security) { this(contextPath, (sessions ? SESSIONS : 0) | (security ? SECURITY : 0)); } public ServletContextHandler(SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler) { this(null, sessionHandler, securityHandler, servletHandler, errorHandler); } public ServletContextHandler(String contextPath, SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler) { this(contextPath, sessionHandler, securityHandler, servletHandler, errorHandler, 0); } public ServletContextHandler(String contextPath, SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler, int options) { _servletContext = newServletContextApi(); if (contextPath != null) setContextPath(contextPath); _options = options; _sessionHandler = sessionHandler; _securityHandler = securityHandler; _servletHandler = servletHandler; setErrorHandler(errorHandler); _objFactory = new DecoratedObjectFactory(); installBean(_objFactory, true); // Link the handlers relinkHandlers(); } @Override public void setTempDirectory(File tempDirectory) { super.setTempDirectory(tempDirectory); setAttribute(TEMPDIR, getTempDirectory()); } public ServletContextApi newServletContextApi() { return new ServletContextApi(); } @Override public InvocationType getInvocationType() { return InvocationType.BLOCKING; } @Override public void dump(Appendable out, String indent) throws IOException { dumpObjects(out, indent, new ClassLoaderDump(getClassLoader()), Dumpable.named("context " + this, getContext()), Dumpable.named("handler attributes " + this, getContext().getPersistentAttributes()), new DumpableCollection("initparams " + this, getInitParams().entrySet())); } public boolean isUsingSecurityManager() { return _usingSecurityManager; } public void setUsingSecurityManager(boolean usingSecurityManager) { if (usingSecurityManager && getSecurityManager() == null) throw new IllegalStateException("No security manager"); _usingSecurityManager = usingSecurityManager; } /** * @return Returns the encoded contextPath. */ public String getContextPathEncoded() { return _contextPathEncoded; } /** * Get the context path in a form suitable to be returned from {@link HttpServletRequest#getContextPath()} * or {@link jakarta.servlet.ServletContext#getContextPath()}. * * @return Returns the encoded contextPath, or empty string for root context */ public String getRequestContextPath() { String contextPathEncoded = getContextPathEncoded(); return "/".equals(contextPathEncoded) ? "" : contextPathEncoded; } /* * @see jakarta.servlet.ServletContext#getInitParameter(java.lang.String) */ public String getInitParameter(String name) { return _initParams.get(name); } public String setInitParameter(String name, String value) { return _initParams.put(name, value); } /* * @see jakarta.servlet.ServletContext#getInitParameterNames() */ public Enumeration getInitParameterNames() { return Collections.enumeration(_initParams.keySet()); } /** * @return Returns the initParams. */ @ManagedAttribute("Initial Parameter map for the context") public Map getInitParams() { return _initParams; } @Override public boolean removeEventListener(EventListener listener) { if (super.removeEventListener(listener)) { if (listener instanceof ServletContextScopeListener) _contextListeners.remove(listener); if (listener instanceof ServletContextListener) { _servletContextListeners.remove(listener); _destroyServletContextListeners.remove(listener); } if (listener instanceof ServletContextAttributeListener) _servletContextAttributeListeners.remove(listener); if (listener instanceof ServletRequestListener) _servletRequestListeners.remove(listener); if (listener instanceof ServletRequestAttributeListener) _servletRequestAttributeListeners.remove(listener); return true; } return false; } /** * Apply any necessary restrictions on a programmatic added listener. * * @param listener the programmatic listener to add */ protected void addProgrammaticListener(EventListener listener) { _programmaticListeners.add(listener); } public boolean isProgrammaticListener(EventListener listener) { return _programmaticListeners.contains(listener); } public boolean isDurableListener(EventListener listener) { // The durable listeners are those set when the context is started if (isStarted()) return _durableListeners.contains(listener); // If we are not yet started then all set listeners are durable return getEventListeners().contains(listener); } public Logger getLogger() { return _logger; } public void setLogger(Logger logger) { _logger = logger; } private String getLogNameSuffix() { // Use display name first String logName = getDisplayName(); if (StringUtil.isBlank(logName)) { // try context path logName = getContextPath(); if (logName != null) { // Strip prefix slash if (logName.startsWith("/")) { logName = logName.substring(1); } } if (StringUtil.isBlank(logName)) { // an empty context path is the ROOT context logName = "ROOT"; } } // Replace bad characters. return '.' + logName.replaceAll("\\W", "_"); } /** * Call the ServletContextListeners contextInitialized methods. * This can be called from a ServletHandler during the proper sequence * of initializing filters, servlets and listeners. However, if there is * no ServletHandler, the ContextHandler will call this method during * doStart(). */ public void contextInitialized() throws Exception { // Call context listeners if (_contextStatus == ContextStatus.NOTSET) { _contextStatus = ContextStatus.INITIALIZED; _destroyServletContextListeners.clear(); if (!_servletContextListeners.isEmpty()) { ServletContextEvent event = new ServletContextEvent(getServletContext()); for (ServletContextListener listener : _servletContextListeners) { callContextInitialized(listener, event); _destroyServletContextListeners.add(listener); } } } } /** * Call the ServletContextListeners with contextDestroyed. * This method can be called from a ServletHandler in the * proper sequence of destroying filters, servlets and listeners. * If there is no ServletHandler, the ContextHandler must ensure * these listeners are called instead. */ public void contextDestroyed() throws Exception { switch (_contextStatus) { case INITIALIZED: { try { //Call context listeners Throwable multiException = null; ServletContextEvent event = new ServletContextEvent(getServletContext()); Collections.reverse(_destroyServletContextListeners); for (ServletContextListener listener : _destroyServletContextListeners) { try { callContextDestroyed(listener, event); } catch (Exception x) { multiException = ExceptionUtil.combine(multiException, x); } } ExceptionUtil.ifExceptionThrow(multiException); } finally { _contextStatus = ContextStatus.DESTROYED; } break; } default: break; } } protected void requestInitialized(Request baseRequest, HttpServletRequest request) { ServletContextRequest scopedRequest = Request.as(baseRequest, ServletContextRequest.class); // Handle the REALLY SILLY request events! if (!_servletRequestAttributeListeners.isEmpty()) { for (ServletRequestAttributeListener l : _servletRequestAttributeListeners) { scopedRequest.addEventListener(l); } } if (!_servletRequestListeners.isEmpty()) { final ServletRequestEvent sre = new ServletRequestEvent(getServletContext(), request); for (ServletRequestListener l : _servletRequestListeners) { l.requestInitialized(sre); } } } protected void requestDestroyed(Request baseRequest, HttpServletRequest request) { ServletContextRequest scopedRequest = Request.as(baseRequest, ServletContextRequest.class); // Handle more REALLY SILLY request events! if (!_servletRequestListeners.isEmpty()) { final ServletRequestEvent sre = new ServletRequestEvent(getServletContext(), request); for (int i = _servletRequestListeners.size(); i-- > 0; ) { _servletRequestListeners.get(i).requestDestroyed(sre); } } if (!_servletRequestAttributeListeners.isEmpty()) { for (int i = _servletRequestAttributeListeners.size(); i-- > 0; ) { scopedRequest.removeEventListener(_servletRequestAttributeListeners.get(i)); } } } public void setDefaultRequestCharacterEncoding(String encoding) { _defaultRequestCharacterEncoding = encoding; } public String getDefaultRequestCharacterEncoding() { return _defaultRequestCharacterEncoding; } public void setDefaultResponseCharacterEncoding(String encoding) { _defaultResponseCharacterEncoding = encoding; } public String getDefaultResponseCharacterEncoding() { return _defaultResponseCharacterEncoding; } /** * @param contextPath The _contextPath to set. */ @Override public void setContextPath(String contextPath) { if (contextPath == null) throw new IllegalArgumentException("null contextPath"); if (contextPath.endsWith("/*")) { LOG.warn("{} contextPath ends with /*", this); contextPath = contextPath.substring(0, contextPath.length() - 2); } else if (contextPath.length() > 1 && contextPath.endsWith("/")) { LOG.warn("{} contextPath ends with /", this); contextPath = contextPath.substring(0, contextPath.length() - 1); } if (contextPath.length() == 0) { LOG.warn("Empty contextPath"); contextPath = "/"; } super.setContextPath(contextPath); _contextPathEncoded = URIUtil.canonicalPath(contextPath); if (getServer() != null && (getServer().isStarting() || getServer().isStarted())) { Class handlerClass = ContextHandlerCollection.class; List contextCollections = getServer().getDescendants(handlerClass); if (contextCollections != null) { for (Handler contextCollection : contextCollections) { handlerClass.cast(contextCollection).mapContexts(); } } } } public void setWelcomeFiles(String[] files) { _welcomeFiles = files; } /** * @return The names of the files which the server should consider to be welcome files in this context. * @see The Servlet Specification * @see #setWelcomeFiles */ @ManagedAttribute(value = "Partial URIs of directory welcome files", readonly = true) public String[] getWelcomeFiles() { return _welcomeFiles; } @ManagedAttribute("The maximum content size") public int getMaxFormContentSize() { return _maxFormContentSize; } /** * Set the maximum size of a form post, to protect against DOS attacks from large forms. * * @param maxSize the maximum size of the form content (in bytes) */ public void setMaxFormContentSize(int maxSize) { _maxFormContentSize = maxSize; } public int getMaxFormKeys() { return _maxFormKeys; } /** * Set the maximum number of form Keys to protect against DOS attack from crafted hash keys. * * @param max the maximum number of form keys */ public void setMaxFormKeys(int max) { _maxFormKeys = max; } public Class loadClass(String className) throws ClassNotFoundException { if (className == null) return null; ClassLoader loader = getClassLoader(); if (loader == null) return Loader.loadClass(className); return loader.loadClass(className); } public void addLocaleEncoding(String locale, String encoding) { if (_localeEncodingMap == null) _localeEncodingMap = new HashMap<>(); _localeEncodingMap.put(locale, encoding); } public String getLocaleEncoding(String locale) { if (_localeEncodingMap == null) return null; return _localeEncodingMap.get(locale); } /** * Get the character encoding for a locale. The full locale name is first looked up in the map of encodings. If no encoding is found, then the locale * language is looked up. * * @param locale a Locale value * @return a String representing the character encoding for the locale or null if none found. */ public String getLocaleEncoding(Locale locale) { if (_localeEncodingMap == null) return null; String encoding = _localeEncodingMap.get(locale.toString()); if (encoding == null) encoding = _localeEncodingMap.get(locale.getLanguage()); return encoding; } /** * Get all of the locale encodings * * @return a map of all the locale encodings: key is name of the locale and value is the char encoding */ public Map getLocaleEncodings() { if (_localeEncodingMap == null) return null; return Collections.unmodifiableMap(_localeEncodingMap); } /** * Attempt to get a Resource from the Context. * * @param pathInContext the path within the base resource to attempt to get * @return the resource, or null if not available. * @throws MalformedURLException if unable to form a Resource from the provided path */ public Resource getResource(String pathInContext) throws MalformedURLException { if (pathInContext == null || !pathInContext.startsWith("/")) throw new MalformedURLException(pathInContext); Resource baseResource = getBaseResource(); if (baseResource == null) return null; return baseResource.resolve(pathInContext); } /** * Convert URL to Resource wrapper for {@link ResourceFactory#newResource(URL)} enables extensions to provide alternate resource implementations. * * @param url the url to convert to a Resource * @return the Resource for that url * @throws IOException if unable to create a Resource from the URL */ public Resource newResource(URL url) throws IOException { return ResourceFactory.of(this).newResource(url); } /** * Convert URL to Resource wrapper for {@link ResourceFactory#newResource(URL)} enables extensions to provide alternate resource implementations. * * @param uri the URI to convert to a Resource * @return the Resource for that URI */ public Resource newResource(URI uri) { return ResourceFactory.of(this).newResource(uri); } /** * Convert a URL or path to a Resource. The default implementation is a wrapper for {@link ResourceFactory#newResource(String)}. * * @param urlOrPath The URL or path to convert * @return The Resource for the URL/path */ public Resource newResource(String urlOrPath) { return ResourceFactory.of(this).newResource(urlOrPath); } public Set getResourcePaths(String path) { try { Resource resource = getResource(path); if (!path.endsWith("/")) path = path + '/'; HashSet set = new HashSet<>(); for (Resource item: resource.list()) { String entry = path + item.getFileName(); if (item.isDirectory()) entry = entry + '/'; set.add(entry); } return set; } catch (Exception e) { LOG.trace("IGNORED", e); } return Collections.emptySet(); } /** * Listener for all threads entering context scope, including async IO callbacks */ public interface ServletContextScopeListener extends EventListener { /** * @param context The context being entered * @param request A request that is applicable to the scope, or null */ void enterScope(ServletScopedContext context, ServletContextRequest request); /** * @param context The context being exited * @param request A request that is applicable to the scope, or null */ void exitScope(ServletScopedContext context, ServletContextRequest request); } public jakarta.servlet.ServletContext getServletContext() { return getContext().getServletContext(); } @Override protected ScopedContext newContext() { return new ServletScopedContext(); } @Override public ServletScopedContext getContext() { return (ServletScopedContext)super.getContext(); } /** * Add a context event listeners. * * @param listener the event listener to add * @return true if the listener was added * @see HttpSessionAttributeListener * @see HttpSessionActivationListener * @see HttpSessionBindingListener * @see HttpSessionListener * @see HttpSessionIdListener * @see ServletContextScopeListener * @see ServletContextListener * @see ServletContextAttributeListener * @see ServletRequestListener * @see ServletRequestAttributeListener * @see ContextHandler#addEventListener(EventListener) */ @Override public boolean addEventListener(EventListener listener) { if (super.addEventListener(listener)) { if ((listener instanceof HttpSessionActivationListener) || (listener instanceof HttpSessionAttributeListener) || (listener instanceof HttpSessionBindingListener) || (listener instanceof HttpSessionListener) || (listener instanceof HttpSessionIdListener)) { if (_sessionHandler != null) _sessionHandler.addEventListener(listener); } if (listener instanceof ServletContextScopeListener) { Context currentContext = ContextHandler.getCurrentContext(); _contextListeners.add((ServletContextScopeListener)listener); if (currentContext != null) ((ServletContextScopeListener)listener).enterScope(getContext(), null); } if (listener instanceof ServletContextListener) { if (_contextStatus == ContextStatus.INITIALIZED) { ServletContextListener scl = (ServletContextListener)listener; _destroyServletContextListeners.add(scl); if (isStarting()) { LOG.warn("ContextListener {} added whilst starting {}", scl, this); callContextInitialized(scl, new ServletContextEvent(getServletContext())); } else { LOG.warn("ContextListener {} added after starting {}", scl, this); } } _servletContextListeners.add((ServletContextListener)listener); } if (listener instanceof ServletContextAttributeListener) _servletContextAttributeListeners.add((ServletContextAttributeListener)listener); if (listener instanceof ServletRequestListener) _servletRequestListeners.add((ServletRequestListener)listener); if (listener instanceof ServletRequestAttributeListener) _servletRequestAttributeListeners.add((ServletRequestAttributeListener)listener); return true; } return false; } @Override public void setHandler(Handler handler) { if (handler instanceof SessionHandler) setSessionHandler((SessionHandler)handler); else if (handler instanceof SecurityHandler) setSecurityHandler((SecurityHandler)handler); else if (handler instanceof ServletHandler) setServletHandler((ServletHandler)handler); else { if (handler != null) LOG.warn("ServletContextHandler.setHandler should not be called directly. Use insertHandler or setSessionHandler etc."); super.setHandler(handler); relinkHandlers(); } } private void doSetHandler(Singleton wrapper, Handler handler) { if (wrapper == this) super.setHandler(handler); else wrapper.setHandler(handler); } private void relinkHandlers() { Singleton handler = this; // link session handler if (getSessionHandler() != null) { while (!(handler.getHandler() instanceof SessionHandler) && !(handler.getHandler() instanceof SecurityHandler) && !(handler.getHandler() instanceof ServletHandler) && handler.getHandler() instanceof Singleton wrapped) { handler = wrapped; } if (handler.getHandler() != _sessionHandler) doSetHandler(handler, _sessionHandler); handler = _sessionHandler; } // link security handler if (getSecurityHandler() != null) { while (!(handler.getHandler() instanceof SecurityHandler) && !(handler.getHandler() instanceof ServletHandler) && handler.getHandler() instanceof Singleton wrapped) { handler = wrapped; } if (handler.getHandler() != _securityHandler) doSetHandler(handler, _securityHandler); handler = _securityHandler; } // link servlet handler if (getServletHandler() != null) { while (!(handler.getHandler() instanceof ServletHandler) && handler.getHandler() instanceof Singleton wrapped) { handler = wrapped; } if (handler.getHandler() != _servletHandler) doSetHandler(handler, _servletHandler); } } @Override protected void doStart() throws Exception { _objFactory.addDecorator(new DeprecationWarning()); getServletContext().setAttribute(DecoratedObjectFactory.ATTR, _objFactory); if (getContextPath() == null) throw new IllegalStateException("Null contextPath"); Resource baseResource = getBaseResource(); if (baseResource != null && baseResource.isAlias()) LOG.warn("BaseResource {} is aliased to {} in {}. May not be supported in future releases.", baseResource, baseResource.getRealURI(), this); if (_logger == null) _logger = LoggerFactory.getLogger(ContextHandler.class.getName() + getLogNameSuffix()); if (getServer() != null) _servletContext.setAttribute("org.eclipse.jetty.server.Executor", getServer().getThreadPool()); _durableListeners.addAll(getEventListeners()); getContext().call(() -> { // defers the calling of super.doStart() startContext(); contextInitialized(); }, null); LOG.info("Started {}", this); } @Override protected void doStop() throws Exception { // Should we attempt a graceful shutdown? Throwable multiException = null; ClassLoader oldClassloader = null; ClassLoader oldWebapploader = null; Thread currentThread = null; Context lastContext = ContextHandler.getCurrentContext(); ClassLoader lastLoader = enterScope(null); ServletScopedContext context = getContext(); try { // Set the classloader ClassLoader loader = getClassLoader(); if (loader != null) { oldWebapploader = loader; currentThread = Thread.currentThread(); oldClassloader = currentThread.getContextClassLoader(); currentThread.setContextClassLoader(loader); } stopContext(); contextDestroyed(); // retain only durable listeners setEventListeners(_durableListeners); _durableListeners.clear(); for (EventListener l : _programmaticListeners) { removeEventListener(l); if (l instanceof ContextScopeListener) { try { ((ContextScopeListener)l).exitScope(context, null); } catch (Throwable e) { LOG.warn("Unable to exit scope", e); } } } _programmaticListeners.clear(); } catch (Throwable x) { multiException = ExceptionUtil.combine(multiException, x); } finally { _contextStatus = ContextStatus.NOTSET; exitScope(null, lastContext, lastLoader); LOG.info("Stopped {}", this); // reset the classloader if ((oldClassloader == null || (oldClassloader != oldWebapploader)) && currentThread != null) currentThread.setContextClassLoader(oldClassloader); context.clearAttributes(); } ExceptionUtil.ifExceptionThrow(multiException); _objFactory.clear(); getServletContext().removeAttribute(DecoratedObjectFactory.ATTR); } protected ServletContextRequest newServletContextRequest(ServletChannel servletChannel, Request request, Response response, String decodedPathInContext, MatchedResource matchedResource) { return new ServletContextRequest(_servletContext, servletChannel, request, response, decodedPathInContext, matchedResource, getSessionHandler()); } @Override protected ContextRequest wrapRequest(Request request, Response response) { String decodedPathInContext; MatchedResource matchedResource; // Need to ask directly to the Context for the pathInContext, rather than using // Request.getPathInContext(), as the request is not yet wrapped in this Context. decodedPathInContext = URIUtil.decodePath(getContext().getPathInContext(request.getHttpURI().getCanonicalPath())); matchedResource = _servletHandler.getMatchedServlet(decodedPathInContext); if (matchedResource == null) return wrapNoServlet(request, response); ServletHandler.MappedServlet mappedServlet = matchedResource.getResource(); if (mappedServlet == null) return wrapNoServlet(request, response); // Get a servlet request, possibly from a cached version in the channel attributes. Attributes cache = request.getComponents().getCache(); Object cachedChannel = cache.getAttribute(ServletChannel.class.getName()); ServletChannel servletChannel; if (cachedChannel instanceof ServletChannel sc && sc.getContext() == getContext() && !sc.isAborted()) { servletChannel = sc; } else { servletChannel = new ServletChannel(this, request); cache.setAttribute(ServletChannel.class.getName(), servletChannel); } ServletContextRequest servletContextRequest = newServletContextRequest(servletChannel, request, response, decodedPathInContext, matchedResource); servletChannel.associate(servletContextRequest); Request.addCompletionListener(request, servletChannel::recycle); return servletContextRequest; } private ContextRequest wrapNoServlet(Request request, Response response) { Handler next = getServletHandler().getHandler(); if (next == null) return null; return super.wrapRequest(request, response); } @Override protected ContextResponse wrapResponse(ContextRequest request, Response response) { if (request instanceof ServletContextRequest servletContextRequest) return servletContextRequest.getServletContextResponse(); return super.wrapResponse(request, response); } @Override protected boolean handleByContextHandler(String pathInContext, ContextRequest request, Response response, Callback callback) { boolean initialDispatch = request instanceof ServletContextRequest; if (!initialDispatch) return false; return super.handleByContextHandler(pathInContext, request, response, callback); } @Override protected void notifyEnterScope(Request request) { super.notifyEnterScope(request); ServletContextRequest scopedRequest = Request.as(request, ServletContextRequest.class); if (!_contextListeners.isEmpty()) { for (ServletContextScopeListener listener : _contextListeners) { try { listener.enterScope(getContext(), scopedRequest); } catch (Throwable e) { LOG.warn("Unable to enter scope", e); } } } } @Override protected void notifyExitScope(Request request) { ServletContextRequest scopedRequest = Request.as(request, ServletContextRequest.class); if (!_contextListeners.isEmpty()) { for (int i = _contextListeners.size(); i-- > 0; ) { try { _contextListeners.get(i).exitScope(getContext(), scopedRequest); } catch (Throwable e) { LOG.warn("Unable to exit scope", e); } } } super.notifyExitScope(request); } /** * Get the defaultSecurityHandlerClass. * * @return the defaultSecurityHandlerClass */ public Class getDefaultSecurityHandlerClass() { return _defaultSecurityHandlerClass; } /** * Set the defaultSecurityHandlerClass. * * @param defaultSecurityHandlerClass the defaultSecurityHandlerClass to set */ public void setDefaultSecurityHandlerClass(Class defaultSecurityHandlerClass) { _defaultSecurityHandlerClass = defaultSecurityHandlerClass; } protected SessionHandler newSessionHandler() { return new SessionHandler(); } protected SecurityHandler newSecurityHandler() { try { return getDefaultSecurityHandlerClass().getDeclaredConstructor().newInstance(); } catch (Exception e) { throw new IllegalStateException(e); } } protected ServletHandler newServletHandler() { return new ServletHandler(); } /** * Finish constructing handlers and link them together. */ protected void startContext() throws Exception { for (ServletContainerInitializerCaller sci : getBeans(ServletContainerInitializerCaller.class)) { if (sci.isStopped()) { sci.start(); if (isAuto(sci)) manage(sci); } } if (_servletHandler != null) { //Ensure listener instances are created, added to ContextHandler if (_servletHandler.getListeners() != null) { for (ListenerHolder holder : _servletHandler.getListeners()) { holder.start(); } } } _startListeners = true; String managedAttributes = _initParams.get(MANAGED_ATTRIBUTES); if (managedAttributes != null) addEventListener(new ManagedAttributeListener(this, StringUtil.csvSplit(managedAttributes))); super.doStart(); // OK to Initialize servlet handler now that all relevant object trees have been started if (_servletHandler != null) _servletHandler.initialize(); } protected void stopContext() throws Exception { _startListeners = false; // stop all the handler hierarchy super.doStop(); } /** * @return Returns the securityHandler. */ @ManagedAttribute(value = "context security handler", readonly = true) public SecurityHandler getSecurityHandler() { if (_securityHandler == null && (_options & SECURITY) != 0 && !isStarted()) _securityHandler = newSecurityHandler(); return _securityHandler; } /** * @return Returns the servletHandler. */ @ManagedAttribute(value = "context servlet handler", readonly = true) public ServletHandler getServletHandler() { if (_servletHandler == null && !isStarted()) _servletHandler = newServletHandler(); return _servletHandler; } /** * @return Returns the sessionHandler. */ @ManagedAttribute(value = "context session handler", readonly = true) public SessionHandler getSessionHandler() { if (_sessionHandler == null && (_options & SESSIONS) != 0 && !isStarted()) _sessionHandler = newSessionHandler(); return _sessionHandler; } /** * Convenience method to add a servlet. * * @param className the servlet class name * @param pathSpec the path spec to map servlet to * @return the ServletHolder for the added servlet */ public ServletHolder addServlet(String className, String pathSpec) { return getServletHandler().addServletWithMapping(className, pathSpec); } /** * Convenience method to add a servlet. * * @param servlet the servlet class * @param pathSpec the path spec to map servlet to * @return the ServletHolder for the added servlet */ public ServletHolder addServlet(Class servlet, String pathSpec) { return getServletHandler().addServletWithMapping(servlet, pathSpec); } /** * Convenience method to add a servlet. * * @param servlet the servlet holder * @param pathSpec the path spec */ public void addServlet(ServletHolder servlet, String pathSpec) { getServletHandler().addServletWithMapping(servlet, pathSpec); } /** * Convenience method to add a servlet. * * @param servlet the servlet instance * @param pathSpec the path spec * @return the ServletHolder for the added servlet */ public ServletHolder addServlet(HttpServlet servlet, String pathSpec) { ServletHolder servletHolder = new ServletHolder(servlet); getServletHandler().addServletWithMapping(servletHolder, pathSpec); return servletHolder; } /** * Convenience method to add a filter * * @param holder the filter holder * @param pathSpec the path spec * @param dispatches the dispatcher types for this filter */ public void addFilter(FilterHolder holder, String pathSpec, EnumSet dispatches) { getServletHandler().addFilterWithMapping(holder, pathSpec, dispatches); } /** * Convenience method to add a filter * * @param filterClass the filter class * @param pathSpec the path spec * @param dispatches the dispatcher types for this filter * @return the FilterHolder that was created */ public FilterHolder addFilter(Class filterClass, String pathSpec, EnumSet dispatches) { return getServletHandler().addFilterWithMapping(filterClass, pathSpec, dispatches); } /** * Convenience method to add a filter * * @param filterClass the filter class name * @param pathSpec the path spec * @param dispatches the dispatcher types for this filter * @return the FilterHolder that was created */ public FilterHolder addFilter(String filterClass, String pathSpec, EnumSet dispatches) { return getServletHandler().addFilterWithMapping(filterClass, pathSpec, dispatches); } /** * Convenience method to add a servlet. * * @param filter the filter instance * @param pathSpec the path spec * @param dispatches the dispatcher types for this filter * @return the FilterHolder that was created */ public FilterHolder addFilter(Filter filter, String pathSpec, EnumSet dispatches) { FilterHolder filterHolder = new FilterHolder(filter); getServletHandler().addFilterWithMapping(filterHolder, pathSpec, dispatches); return filterHolder; } /** * Convenience method to programmatically add a {@link ServletContainerInitializer}. * @param sci the ServletContainerInitializer to register. * @return the ServletContainerInitializerHolder that was created */ public ServletContainerInitializerHolder addServletContainerInitializer(ServletContainerInitializer sci) { if (!isStopped()) throw new IllegalStateException("ServletContainerInitializers should be added before starting"); ServletContainerInitializerHolder holder = new ServletContainerInitializerHolder(sci); addServletContainerInitializer(holder); return holder; } /** * Convenience method to programmatically add a {@link ServletContainerInitializer}. * @param sci the ServletContainerInitializer to register. * @param classes the Set of application classes. * @return the ServletContainerInitializerHolder that was created */ public ServletContainerInitializerHolder addServletContainerInitializer(ServletContainerInitializer sci, Class... classes) { if (!isStopped()) throw new IllegalStateException("ServletContainerInitializers should be added before starting"); ServletContainerInitializerHolder holder = new ServletContainerInitializerHolder(sci, classes); addServletContainerInitializer(holder); return holder; } /** * Convenience method to programmatically add a list of {@link ServletContainerInitializer}. * The initializers are guaranteed to be called in the order they are passed into this method. * @param sciHolders the ServletContainerInitializerHolders */ public void addServletContainerInitializer(ServletContainerInitializerHolder... sciHolders) { ServletContainerInitializerStarter starter = getBean(ServletContainerInitializerStarter.class); if (starter == null) { //add the starter as bean which will start when the context is started //NOTE: do not use addManaged(starter) because this will start the //starter immediately, which may not be before we have parsed web.xml starter = new ServletContainerInitializerStarter(); addBean(starter, true); } starter.addServletContainerInitializerHolders(sciHolders); } /** * notification that a ServletRegistration has been created so we can track the annotations * * @param holder new holder created through the api. * @return the ServletRegistration.Dynamic */ protected ServletRegistration.Dynamic dynamicHolderAdded(ServletHolder holder) { return holder.getRegistration(); } /** * delegate for ServletContext.declareRole method * * @param roleNames role names to add */ protected void addRoles(String... roleNames) { //Get a reference to the SecurityHandler, which must be ConstraintAware if (_securityHandler != null && _securityHandler instanceof ConstraintAware) { HashSet union = new HashSet<>(); Set existing = ((ConstraintAware)_securityHandler).getKnownRoles(); if (existing != null) union.addAll(existing); union.addAll(Arrays.asList(roleNames)); ((ConstraintSecurityHandler)_securityHandler).setRoles(union); } } /** * Delegate for ServletRegistration.Dynamic.setServletSecurity method * * @param registration ServletRegistration.Dynamic instance that setServletSecurity was called on * @param servletSecurityElement new security info * @return the set of exact URL mappings currently associated with the registration that are also present in the web.xml * security constraints and thus will be unaffected by this call. */ public Set setServletSecurity(ServletRegistration.Dynamic registration, ServletSecurityElement servletSecurityElement) { //Default implementation is to just accept them all. If using a webapp, then this behaviour is overridden in WebAppContext.setServletSecurity java.util.Collection pathSpecs = registration.getMappings(); if (pathSpecs != null) { for (String pathSpec : pathSpecs) { List mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement); for (ConstraintMapping m : mappings) { ((ConstraintAware)getSecurityHandler()).addConstraintMapping(m); } } } return Collections.emptySet(); } public void callContextInitialized(ServletContextListener l, ServletContextEvent e) { try { //toggle state of the dynamic API so that the listener cannot use it if (isProgrammaticListener(l)) getContext().getServletContext().setEnabled(false); if (getServer().isDryRun()) return; if (LOG.isDebugEnabled()) LOG.debug("contextInitialized: {}->{}", e, l); l.contextInitialized(e); } finally { //untoggle the state of the dynamic API getContext().getServletContext().setEnabled(true); } } public void callContextDestroyed(ServletContextListener l, ServletContextEvent e) { if (getServer().isDryRun()) return; if (LOG.isDebugEnabled()) LOG.debug("contextDestroyed: {}->{}", e, l); l.contextDestroyed(e); } private void replaceHandler(Singleton handler, Singleton replacement) { if (isStarted()) throw new IllegalStateException("STARTED"); Handler next = null; if (handler != null) { next = handler.getHandler(); handler.setHandler((Handler)null); Singleton wrapper = this; while (wrapper != null) { if (wrapper.getHandler() == handler) { doSetHandler(wrapper, replacement); break; } wrapper = (wrapper.getHandler() instanceof Singleton wrapped) ? wrapped : null; } } if (next != null && replacement.getHandler() == null) replacement.setHandler(next); } /** * @param sessionHandler The sessionHandler to set. */ public void setSessionHandler(SessionHandler sessionHandler) { replaceHandler(_sessionHandler, sessionHandler); _sessionHandler = sessionHandler; relinkHandlers(); } /** * @param securityHandler The {@link SecurityHandler} to set on this context. */ public void setSecurityHandler(SecurityHandler securityHandler) { replaceHandler(_securityHandler, securityHandler); _securityHandler = securityHandler; relinkHandlers(); } /** * @param servletHandler The servletHandler to set. */ public void setServletHandler(ServletHandler servletHandler) { replaceHandler(_servletHandler, servletHandler); _servletHandler = servletHandler; relinkHandlers(); } /** * Insert a HandlerWrapper before the first Session, Security or ServletHandler * but after any other HandlerWrappers. */ @Override public void insertHandler(Singleton handler) { if (handler instanceof SessionHandler) setSessionHandler((SessionHandler)handler); else if (handler instanceof SecurityHandler) setSecurityHandler((SecurityHandler)handler); else if (handler instanceof ServletHandler) setServletHandler((ServletHandler)handler); else { // We cannot call super.insertHandler here, because it uses this.setHandler // which sets the servletHandlers next handler. // This is the same insert code, but uses super.setHandler, which sets this // handler's next handler. Singleton tail = handler.getTail(); if (tail.getHandler() != null) throw new IllegalArgumentException("bad tail of inserted wrapper chain"); tail.setHandler(getHandler()); super.setHandler(handler); relinkHandlers(); } } /** * The DecoratedObjectFactory for use by IoC containers (weld / spring / etc) * * @return The DecoratedObjectFactory */ public DecoratedObjectFactory getObjectFactory() { return _objFactory; } void destroyServlet(Servlet servlet) { getContext().destroy(servlet); } void destroyFilter(Filter filter) { getContext().destroy(filter); } void destroyListener(EventListener listener) { getContext().destroy(listener); } private static Object getSecurityManager() { return SecurityUtils.getSecurityManager(); } public static class JspPropertyGroup implements JspPropertyGroupDescriptor { private final List _urlPatterns = new ArrayList<>(); private String _elIgnored; private String _pageEncoding; private String _scriptingInvalid; private String _isXml; private final List _includePreludes = new ArrayList<>(); private final List _includeCodas = new ArrayList<>(); private String _deferredSyntaxAllowedAsLiteral; private String _trimDirectiveWhitespaces; private String _defaultContentType; private String _buffer; private String _errorOnUndeclaredNamespace; private String _errorOnELNotFound; @Override public java.util.Collection getUrlPatterns() { return new ArrayList<>(_urlPatterns); // spec says must be a copy } public void addUrlPattern(String s) { if (!_urlPatterns.contains(s)) _urlPatterns.add(s); } @Override public String getElIgnored() { return _elIgnored; } @Override public String getErrorOnELNotFound() { return _errorOnELNotFound; } public void setElIgnored(String s) { _elIgnored = s; } @Override public String getPageEncoding() { return _pageEncoding; } public void setPageEncoding(String pageEncoding) { _pageEncoding = pageEncoding; } public void setScriptingInvalid(String scriptingInvalid) { _scriptingInvalid = scriptingInvalid; } public void setIsXml(String isXml) { _isXml = isXml; } public void setDeferredSyntaxAllowedAsLiteral(String deferredSyntaxAllowedAsLiteral) { _deferredSyntaxAllowedAsLiteral = deferredSyntaxAllowedAsLiteral; } public void setTrimDirectiveWhitespaces(String trimDirectiveWhitespaces) { _trimDirectiveWhitespaces = trimDirectiveWhitespaces; } public void setDefaultContentType(String defaultContentType) { _defaultContentType = defaultContentType; } public void setBuffer(String buffer) { _buffer = buffer; } public void setErrorOnUndeclaredNamespace(String errorOnUndeclaredNamespace) { _errorOnUndeclaredNamespace = errorOnUndeclaredNamespace; } public void setErrorOnELNotFound(String errorOnELNotFound) { _errorOnELNotFound = errorOnELNotFound; } @Override public String getScriptingInvalid() { return _scriptingInvalid; } @Override public String getIsXml() { return _isXml; } @Override public java.util.Collection getIncludePreludes() { return new ArrayList<>(_includePreludes); //must be a copy } public void addIncludePrelude(String prelude) { if (!_includePreludes.contains(prelude)) _includePreludes.add(prelude); } @Override public java.util.Collection getIncludeCodas() { return new ArrayList<>(_includeCodas); //must be a copy } public void addIncludeCoda(String coda) { if (!_includeCodas.contains(coda)) _includeCodas.add(coda); } @Override public String getDeferredSyntaxAllowedAsLiteral() { return _deferredSyntaxAllowedAsLiteral; } @Override public String getTrimDirectiveWhitespaces() { return _trimDirectiveWhitespaces; } @Override public String getDefaultContentType() { return _defaultContentType; } @Override public String getBuffer() { return _buffer; } @Override public String getErrorOnUndeclaredNamespace() { return _errorOnUndeclaredNamespace; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("JspPropertyGroupDescriptor:"); sb.append(" error-on-el-not-found=").append(_errorOnELNotFound); sb.append(" el-ignored=").append(_elIgnored); sb.append(" is-xml=").append(_isXml); sb.append(" page-encoding=").append(_pageEncoding); sb.append(" scripting-invalid=").append(_scriptingInvalid); sb.append(" deferred-syntax-allowed-as-literal=").append(_deferredSyntaxAllowedAsLiteral); sb.append(" trim-directive-whitespaces").append(_trimDirectiveWhitespaces); sb.append(" default-content-type=").append(_defaultContentType); sb.append(" buffer=").append(_buffer); sb.append(" error-on-undeclared-namespace=").append(_errorOnUndeclaredNamespace); for (String prelude : _includePreludes) { sb.append(" include-prelude=").append(prelude); } for (String coda : _includeCodas) { sb.append(" include-coda=").append(coda); } return sb.toString(); } } public static class TagLib implements TaglibDescriptor { private String _uri; private String _location; @Override public String getTaglibURI() { return _uri; } public void setTaglibURI(String uri) { _uri = uri; } @Override public String getTaglibLocation() { return _location; } public void setTaglibLocation(String location) { _location = location; } @Override public String toString() { return ("TagLibDescriptor: taglib-uri=" + _uri + " location=" + _location); } } public static class JspConfig implements JspConfigDescriptor { private final List _taglibs = new ArrayList<>(); private final List _jspPropertyGroups = new ArrayList<>(); public JspConfig() { } @Override public java.util.Collection getTaglibs() { return new ArrayList<>(_taglibs); } public void addTaglibDescriptor(TaglibDescriptor d) { _taglibs.add(d); } @Override public java.util.Collection getJspPropertyGroups() { return new ArrayList<>(_jspPropertyGroups); } public void addJspPropertyGroup(JspPropertyGroupDescriptor g) { _jspPropertyGroups.add(g); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("JspConfigDescriptor: \n"); for (TaglibDescriptor taglib : _taglibs) { sb.append(taglib).append("\n"); } for (JspPropertyGroupDescriptor jpg : _jspPropertyGroups) { sb.append(jpg).append("\n"); } return sb.toString(); } } public class ServletScopedContext extends ScopedContext { public ServletContextApi getServletContext() { return _servletContext; } public ServletContextHandler getServletContextHandler() { return ServletContextHandler.this; } @Override protected DecoratedObjectFactory getDecoratedObjectFactory() { return _objFactory; } public T createInstance(BaseHolder holder) throws ServletException { try { //set a thread local DecoratedObjectFactory.associateInfo(holder); try { T t = holder.getHeldClass().getDeclaredConstructor().newInstance(); return decorate(t); } catch (Exception e) { throw new ServletException(e); } } finally { //unset the thread local DecoratedObjectFactory.disassociateInfo(); } } public void destroyFilter(T f) { _objFactory.destroy(f); } public void destroyServlet(T s) { _objFactory.destroy(s); } public void setExtendedListenerTypes(boolean b) { _servletContext.setExtendedListenerTypes(b); } } public class ServletContextApi implements jakarta.servlet.ServletContext { public static final int SERVLET_MAJOR_VERSION = 6; public static final int SERVLET_MINOR_VERSION = 0; private int _effectiveMajorVersion = SERVLET_MAJOR_VERSION; private int _effectiveMinorVersion = SERVLET_MINOR_VERSION; protected boolean _enabled = true; // whether or not the dynamic API is enabled for callers protected boolean _extendedListenerTypes = false; public ServletContextApi() { } @Override public String getServerInfo() { return getServer().getServerInfo(); } @Override public int getMajorVersion() { return SERVLET_MAJOR_VERSION; } @Override public int getMinorVersion() { return SERVLET_MINOR_VERSION; } @Override public int getEffectiveMajorVersion() { return _effectiveMajorVersion; } @Override public int getEffectiveMinorVersion() { return _effectiveMinorVersion; } public void setEffectiveMajorVersion(int v) { _effectiveMajorVersion = v; } public void setEffectiveMinorVersion(int v) { _effectiveMinorVersion = v; } public ServletScopedContext getContext() { return ServletContextHandler.this.getContext(); } @Override public RequestDispatcher getNamedDispatcher(String name) { ServletContextHandler context = ServletContextHandler.this; if (_servletHandler == null) return null; ServletHolder holder = _servletHandler.getServlet(name); if (holder == null || !holder.isEnabled()) return null; return new Dispatcher(context, name); } private void checkDynamic(String name) { if (isStarted()) throw new IllegalStateException(); if (ServletContextHandler.this.getServletHandler().isInitialized()) throw new IllegalStateException(); if (StringUtil.isBlank(name)) throw new IllegalArgumentException("Missing name"); if (!_enabled) throw new UnsupportedOperationException(); } /** * @since servlet-api-3.0 */ @Override public FilterRegistration.Dynamic addFilter(String filterName, Class filterClass) { checkDynamic(filterName); final ServletHandler handler = ServletContextHandler.this.getServletHandler(); FilterHolder holder = handler.getFilter(filterName); if (holder == null) { //new filter holder = handler.newFilterHolder(Source.JAKARTA_API); holder.setName(filterName); holder.setHeldClass(filterClass); handler.addFilter(holder); return holder.getRegistration(); } if (holder.getClassName() == null && holder.getHeldClass() == null) { //preliminary filter registration completion holder.setHeldClass(filterClass); return holder.getRegistration(); } else return null; //existing filter } /** * @since servlet-api-3.0 */ @Override public FilterRegistration.Dynamic addFilter(String filterName, String className) { checkDynamic(filterName); final ServletHandler handler = ServletContextHandler.this.getServletHandler(); FilterHolder holder = handler.getFilter(filterName); if (holder == null) { //new filter holder = handler.newFilterHolder(Source.JAKARTA_API); holder.setName(filterName); holder.setClassName(className); handler.addFilter(holder); return holder.getRegistration(); } if (holder.getClassName() == null && holder.getHeldClass() == null) { //preliminary filter registration completion holder.setClassName(className); return holder.getRegistration(); } else return null; //existing filter } /** * @since servlet-api-3.0 */ @Override public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) { checkDynamic(filterName); final ServletHandler handler = ServletContextHandler.this.getServletHandler(); FilterHolder holder = handler.getFilter(filterName); if (holder == null) { //new filter holder = handler.newFilterHolder(Source.JAKARTA_API); holder.setName(filterName); holder.setFilter(filter); handler.addFilter(holder); return holder.getRegistration(); } if (holder.getClassName() == null && holder.getHeldClass() == null) { //preliminary filter registration completion holder.setFilter(filter); return holder.getRegistration(); } else return null; //existing filter } /** * @since servlet-api-3.0 */ @Override public ServletRegistration.Dynamic addServlet(String servletName, Class servletClass) { checkDynamic(servletName); final ServletHandler handler = ServletContextHandler.this.getServletHandler(); ServletHolder holder = handler.getServlet(servletName); if (holder == null) { //new servlet holder = handler.newServletHolder(Source.JAKARTA_API); holder.setName(servletName); holder.setHeldClass(servletClass); handler.addServlet(holder); return dynamicHolderAdded(holder); } //complete a partial registration if (holder.getClassName() == null && holder.getHeldClass() == null) { holder.setHeldClass(servletClass); return holder.getRegistration(); } else return null; //existing completed registration for servlet name } /** * @since servlet-api-3.0 */ @Override public ServletRegistration.Dynamic addServlet(String servletName, String className) { checkDynamic(servletName); final ServletHandler handler = ServletContextHandler.this.getServletHandler(); ServletHolder holder = handler.getServlet(servletName); if (holder == null) { //new servlet holder = handler.newServletHolder(Source.JAKARTA_API); holder.setName(servletName); holder.setClassName(className); handler.addServlet(holder); return dynamicHolderAdded(holder); } //complete a partial registration if (holder.getClassName() == null && holder.getHeldClass() == null) { holder.setClassName(className); return holder.getRegistration(); } else return null; //existing completed registration for servlet name } /** * @since servlet-api-3.0 */ @Override public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) { checkDynamic(servletName); final ServletHandler handler = ServletContextHandler.this.getServletHandler(); ServletHolder holder = handler.getServlet(servletName); if (holder == null) { holder = handler.newServletHolder(Source.JAKARTA_API); holder.setName(servletName); holder.setServlet(servlet); handler.addServlet(holder); return dynamicHolderAdded(holder); } //complete a partial registration if (holder.getClassName() == null && holder.getHeldClass() == null) { holder.setServlet(servlet); return holder.getRegistration(); } else return null; //existing completed registration for servlet name } @Override public ServletRegistration.Dynamic addJspFile(String servletName, String jspFile) { checkDynamic(servletName); final ServletHandler handler = ServletContextHandler.this.getServletHandler(); ServletHolder holder = handler.getServlet(servletName); if (holder == null) { //new servlet holder = handler.newServletHolder(Source.JAKARTA_API); holder.setName(servletName); holder.setForcedPath(jspFile); handler.addServlet(holder); return dynamicHolderAdded(holder); } //complete a partial registration if (holder.getClassName() == null && holder.getHeldClass() == null && holder.getForcedPath() == null) { holder.setForcedPath(jspFile); return holder.getRegistration(); } else return null; //existing completed registration for servlet name } public String getInitParameter(String name) { //since servlet spec 4.0 Objects.requireNonNull(name); return ServletContextHandler.this.getInitParameter(name); } public boolean setInitParameter(String name, String value) { //since servlet spec 4.0 Objects.requireNonNull(name); if (!isStarting()) throw new IllegalStateException(); if (!_enabled) throw new UnsupportedOperationException(); if (ServletContextHandler.this.getInitParameter(name) != null) return false; ServletContextHandler.this.getInitParams().put(name, value); return true; } public T createInstance(Class clazz) throws ServletException { try { return getContext().decorate(clazz.getDeclaredConstructor().newInstance()); } catch (Exception e) { throw new ServletException(e); } } @Override public Set getDefaultSessionTrackingModes() { if (_sessionHandler != null) return _sessionHandler.getDefaultSessionTrackingModes(); return null; } @Override public Set getEffectiveSessionTrackingModes() { if (_sessionHandler != null) return _sessionHandler.getEffectiveSessionTrackingModes(); return null; } @Override public FilterRegistration getFilterRegistration(String filterName) { if (!_enabled) throw new UnsupportedOperationException(); final FilterHolder holder = ServletContextHandler.this.getServletHandler().getFilter(filterName); return (holder == null) ? null : holder.getRegistration(); } @Override public Map getFilterRegistrations() { if (!_enabled) throw new UnsupportedOperationException(); HashMap registrations = new HashMap<>(); ServletHandler handler = ServletContextHandler.this.getServletHandler(); FilterHolder[] holders = handler.getFilters(); if (holders != null) { for (FilterHolder holder : holders) { registrations.put(holder.getName(), holder.getRegistration()); } } return registrations; } @Override public ServletRegistration getServletRegistration(String servletName) { if (!_enabled) throw new UnsupportedOperationException(); final ServletHolder holder = ServletContextHandler.this.getServletHandler().getServlet(servletName); return (holder == null) ? null : holder.getRegistration(); } @Override public Map getServletRegistrations() { if (!_enabled) throw new UnsupportedOperationException(); HashMap registrations = new HashMap<>(); ServletHandler handler = ServletContextHandler.this.getServletHandler(); ServletHolder[] holders = handler.getServlets(); if (holders != null) { for (ServletHolder holder : holders) { registrations.put(holder.getName(), holder.getRegistration()); } } return registrations; } @Override public SessionCookieConfig getSessionCookieConfig() { if (!_enabled) throw new UnsupportedOperationException(); if (_sessionHandler != null) return _sessionHandler.getSessionCookieConfig(); return null; } @Override public void setSessionTrackingModes(Set sessionTrackingModes) { if (!isStarting()) throw new IllegalStateException(); if (!_enabled) throw new UnsupportedOperationException(); if (_sessionHandler != null) _sessionHandler.setSessionTrackingModes(sessionTrackingModes); } @Override public int getSessionTimeout() { int timeout = -1; if (_sessionHandler != null) { timeout = _sessionHandler.getMaxInactiveInterval(); } return (int)TimeUnit.SECONDS.toMinutes(timeout); } @Override public void setSessionTimeout(int sessionTimeout) { if (!isStarting()) throw new IllegalStateException(); if (!_enabled) throw new UnsupportedOperationException(); if (_sessionHandler != null) { long tmp = TimeUnit.MINUTES.toSeconds(sessionTimeout); if (tmp > Integer.MAX_VALUE) tmp = Integer.MAX_VALUE; if (tmp < Integer.MIN_VALUE) tmp = Integer.MIN_VALUE; _sessionHandler.setMaxInactiveInterval((int)tmp); } } public T createServlet(Class clazz) throws ServletException { if (!_enabled) throw new UnsupportedOperationException(); try { return createInstance(clazz); } catch (Exception e) { throw new ServletException(e); } } public T createFilter(Class clazz) throws ServletException { if (!_enabled) throw new UnsupportedOperationException(); try { return createInstance(clazz); } catch (Exception e) { throw new ServletException(e); } } public T createListener(Class clazz) throws ServletException { if (!_enabled) throw new UnsupportedOperationException(); try { checkListener(clazz); } catch (IllegalArgumentException e) { //Bizarrely, according to the spec, it is NOT an error to create an instance of //a ServletContextListener from inside a ServletContextListener, but it IS an error //to call addListener with one! if (!ServletContextListener.class.isAssignableFrom(clazz)) throw e; } try { return createInstance(clazz); } catch (Exception e) { throw new ServletException(e); } } public void addListener(String className) { if (!isStarting()) throw new IllegalStateException(); if (!_enabled) throw new UnsupportedOperationException(); try { ClassLoader classLoader = ServletContextHandler.this.getClassLoader(); @SuppressWarnings({"unchecked", "rawtypes"}) Class clazz = classLoader == null ? Loader.loadClass(className) : (Class)classLoader.loadClass(className); if (!_enabled) throw new UnsupportedOperationException(); try { EventListener result; try { result = clazz.getDeclaredConstructor().newInstance(); } catch (Exception e) { throw new ServletException(e); } EventListener el = result; addListener(el); } catch (ServletException e) { throw new IllegalArgumentException(e); } } catch (ClassNotFoundException e) { throw new IllegalArgumentException(e); } } @Override public void addListener(T t) { if (!isStarting()) throw new IllegalStateException(); if (!_enabled) throw new UnsupportedOperationException(); checkListener(t.getClass()); ListenerHolder holder = getServletHandler().newListenerHolder(Source.JAKARTA_API); holder.setListener(t); addProgrammaticListener(t); getServletHandler().addListener(holder); if (_startListeners) { try { holder.start(); } catch (Exception e) { throw new IllegalStateException(e); } } } public void addListener(Class listenerClass) { if (!isStarting()) throw new IllegalStateException(); if (!_enabled) throw new UnsupportedOperationException(); try { EventListener result; try { result = listenerClass.getDeclaredConstructor().newInstance(); } catch (Exception e) { throw new ServletException(e); } EventListener el = result; addListener(el); } catch (ServletException e) { throw new IllegalArgumentException(e); } } @Override public JspConfigDescriptor getJspConfigDescriptor() { return _jspConfig; } public void setJspConfigDescriptor(JspConfigDescriptor d) { _jspConfig = d; } @Override public void declareRoles(String... roleNames) { if (!isStarting()) throw new IllegalStateException(); if (!_enabled) throw new UnsupportedOperationException(); addRoles(roleNames); } @Override public String getRequestCharacterEncoding() { return getDefaultRequestCharacterEncoding(); } @Override public void setRequestCharacterEncoding(String encoding) { if (!isStarting()) throw new IllegalStateException(); setDefaultRequestCharacterEncoding(encoding); } @Override public String getResponseCharacterEncoding() { return getDefaultResponseCharacterEncoding(); } @Override public void setResponseCharacterEncoding(String encoding) { if (!isStarting()) throw new IllegalStateException(); setDefaultResponseCharacterEncoding(encoding); } public ContextHandler getContextHandler() { return ServletContextHandler.this; } @Override public jakarta.servlet.ServletContext getContext(String path) { ContextHandler context = getContextHandler().getCrossContextHandler(path); if (context == null) return null; if (context == ServletContextHandler.this) return this; return new CrossContextServletContext(ServletContextHandler.this, context.getContext()); } @Override public String getMimeType(String file) { return getContext().getMimeTypes().getMimeByExtension(file); } @Override public RequestDispatcher getRequestDispatcher(String uriInContext) { // uriInContext is encoded, potentially with query. if (uriInContext == null) return null; if (!uriInContext.startsWith("/")) return null; try { String contextPath = getContextPath(); // uriInContext is canonicalized by HttpURI. HttpURI.Mutable uri = HttpURI.build(uriInContext); String encodedPathInContext = uri.getCanonicalPath(); if (StringUtil.isEmpty(encodedPathInContext)) return null; if (!StringUtil.isEmpty(contextPath)) { uri.path(URIUtil.addPaths(contextPath, uri.getPath())); encodedPathInContext = uri.getCanonicalPath().substring(contextPath.length()); } return new Dispatcher(ServletContextHandler.this, uri, URIUtil.decodePath(encodedPathInContext)); } catch (Exception e) { LOG.trace("IGNORED", e); } return null; } @Override public String getRealPath(String path) { // This is an API call from the application which may pass non-normalized paths. // Thus, we normalize here, to avoid the enforcement of normalized paths in // ServletContextHandler.this.getResource(path). path = URIUtil.normalizePath(path); if (path == null) return null; if (path.length() == 0) path = "/"; else if (path.charAt(0) != '/') path = "/" + path; try { Resource resource = ServletContextHandler.this.getResource(path); if (resource == null) return null; for (Resource r : resource) { // return first if (Resources.exists(r)) { Path resourcePath = r.getPath(); if (resourcePath != null) { String realPath = resourcePath.normalize().toString(); if (Files.isDirectory(resourcePath)) realPath = realPath + "/"; return realPath; } } } // A Resource was returned, but did not exist return null; } catch (Exception e) { LOG.trace("IGNORED", e); } return null; } @Override public URL getResource(String path) throws MalformedURLException { // This is an API call from the application which may pass non-normalized paths. // Thus, we normalize here, to avoid the enforcement of normalized paths in // ServletContextHandler.this.getResource(path). path = URIUtil.normalizePath(path); if (path == null) return null; // Assumption is that the resource base has been properly setup. // Spec requirement is that the WAR file is interrogated first. // If a WAR file is mounted, or is extracted to a temp directory, // then the first entry of the resource base must be the WAR file. Resource resource = ServletContextHandler.this.getResource(path); if (resource == null) return null; for (Resource r: resource) { // return first if (Resources.exists(r)) return r.getURI().toURL(); } // A Resource was returned, but did not exist return null; } @Override public InputStream getResourceAsStream(String path) { try { URL url = getResource(path); if (url == null) return null; Resource r = ResourceFactory.of(ServletContextHandler.this).newResource(url); // Cannot serve directories as an InputStream if (r.isDirectory()) return null; return IOResources.asInputStream(r); } catch (Exception e) { LOG.trace("IGNORED", e); return null; } } @Override public Set getResourcePaths(String path) { // This is an API call from the application which may pass non-normalized paths. // Thus, we normalize here, to avoid the enforcement of normalized paths in // ServletContextHandler.this.getResource(path). path = URIUtil.normalizePath(path); if (path == null) return null; return ServletContextHandler.this.getResourcePaths(path); } @Override public void log(String msg) { getLogger().info(msg); } @Override public void log(String message, Throwable throwable) { if (throwable == null) getLogger().warn(message); else getLogger().warn(message, throwable); } @Override public Enumeration getInitParameterNames() { return ServletContextHandler.this.getInitParameterNames(); } @Override public Object getAttribute(String name) { return getContext().getAttribute(name); } @Override public Enumeration getAttributeNames() { return Collections.enumeration(getContext().getAttributeNameSet()); } @Override public void setAttribute(String name, Object value) { Object oldValue = getContext().setAttribute(name, value); if (!_servletContextAttributeListeners.isEmpty()) { ServletContextAttributeEvent event = new ServletContextAttributeEvent(_servletContext, name, oldValue == null ? value : oldValue); for (ServletContextAttributeListener listener : _servletContextAttributeListeners) { if (oldValue == null) listener.attributeAdded(event); else if (value == null) listener.attributeRemoved(event); else listener.attributeReplaced(event); } } } @Override public void removeAttribute(String name) { Object oldValue = getContext().removeAttribute(name); if (oldValue != null && !_servletContextAttributeListeners.isEmpty()) { ServletContextAttributeEvent event = new ServletContextAttributeEvent(_servletContext, name, oldValue); for (ServletContextAttributeListener listener : _servletContextAttributeListeners) { listener.attributeRemoved(event); } } } @Override public String getServletContextName() { String name = ServletContextHandler.this.getDisplayName(); if (name == null) name = ServletContextHandler.this.getContextPath(); return name; } @Override public String getContextPath() { return getRequestContextPath(); } @Override public String toString() { return "ServletContext@" + ServletContextHandler.this; } public void checkListener(Class listener) throws IllegalStateException { boolean ok = false; int startIndex = (isExtendedListenerTypes() ? EXTENDED_LISTENER_TYPE_INDEX : DEFAULT_LISTENER_TYPE_INDEX); for (int i = startIndex; i < SERVLET_LISTENER_TYPES.length; i++) { if (SERVLET_LISTENER_TYPES[i].isAssignableFrom(listener)) { ok = true; break; } } if (!ok) throw new IllegalArgumentException("Inappropriate listener class " + listener.getName()); } public void setExtendedListenerTypes(boolean extended) { _extendedListenerTypes = extended; } public boolean isExtendedListenerTypes() { return _extendedListenerTypes; } @Override public ClassLoader getClassLoader() { // no security manager just return the classloader ClassLoader classLoader = ServletContextHandler.this.getClassLoader(); if (isUsingSecurityManager()) { // check to see if the classloader of the caller is the same as the context // classloader, or a parent of it, as required by the javadoc specification. ClassLoader callerLoader = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE) .getCallerClass() .getClassLoader(); while (callerLoader != null) { if (callerLoader == classLoader) return classLoader; else callerLoader = callerLoader.getParent(); } SecurityUtils.checkPermission(new RuntimePermission("getClassLoader")); } return classLoader; } public void setEnabled(boolean enabled) { _enabled = enabled; } public boolean isEnabled() { return _enabled; } @Override public String getVirtualServerName() { List hosts = getVirtualHosts(); if (hosts != null && !hosts.isEmpty()) return hosts.get(0); return null; } } /** * Bean that is added to the ServletContextHandler to start all of the * ServletContainerInitializers by starting their corresponding * ServletContainerInitializerHolders when this bean is itself started. * Note that the SCIs will be started in order of addition. */ public static class ServletContainerInitializerStarter extends ContainerLifeCycle implements ServletContainerInitializerCaller { public void addServletContainerInitializerHolders(ServletContainerInitializerHolder... holders) { for (ServletContainerInitializerHolder holder:holders) addBean(holder, true); } public java.util.Collection getServletContainerInitializerHolders() { return getContainedBeans(ServletContainerInitializerHolder.class); } @Override protected void doStart() throws Exception { if (LOG.isDebugEnabled()) LOG.debug("Starting SCIs"); super.doStart(); } @Override protected void doStop() throws Exception { //remove all of the non-programmatic holders java.util.Collection holders = getServletContainerInitializerHolders(); for (ServletContainerInitializerHolder h : holders) { if (h.getSource().getOrigin() != Source.Origin.EMBEDDED) removeBean(h); } super.doStop(); } } /** * The interface used by {@link ServletApiRequest} to access the {@link ServletContextRequest} without * access to the unwrapped {@link Request} methods. */ public interface ServletRequestInfo { String getDecodedPathInContext(); ManagedSession getManagedSession(); Charset getQueryEncoding(); default Request getRequest() { return getServletChannel().getRequest(); } List getRequestAttributeListeners(); ServletScopedContext getServletContext(); HttpInput getHttpInput(); MatchedResource getMatchedResource(); AbstractSessionManager.RequestedSession getRequestedSession(); ServletChannel getServletChannel(); ServletContextHandler getServletContextHandler(); ServletChannelState getServletRequestState(); SessionManager getSessionManager(); ServletChannelState getState(); void setQueryEncoding(String s); } /** * The interface used by {@link ServletApiResponse} to access the {@link ServletContextResponse} without * access to the unwrapped {@link Response} methods. */ public interface ServletResponseInfo { String getCharacterEncoding(boolean setContentType); String getCharacterEncoding(); String getContentType(); EncodingFrom getEncodingFrom(); Locale getLocale(); OutputType getOutputType(); Response getResponse(); Supplier> getTrailers(); ResponseWriter getWriter(); boolean isWriting(); void setCharacterEncoding(String encoding, EncodingFrom encodingFrom); void setLocale(Locale locale); void setOutputType(OutputType outputType); void setTrailers(Supplier> trailers); void setWriter(ResponseWriter responseWriter); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy