
org.glassfish.grizzly.servlet.WebappContext Maven / Gradle / Ivy
/*
* Copyright (c) 2022 Contributors to the Eclipse Foundation
* Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright 2004 The Apache Software Foundation
*
* Licensed 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.glassfish.grizzly.servlet;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
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.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.http.server.HttpHandler;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.http.server.ServerConfiguration;
import org.glassfish.grizzly.http.server.SessionManager;
import org.glassfish.grizzly.http.server.StaticHttpHandlerBase;
import org.glassfish.grizzly.http.server.util.ClassLoaderUtil;
import org.glassfish.grizzly.http.server.util.DispatcherHelper;
import org.glassfish.grizzly.http.server.util.Enumerator;
import org.glassfish.grizzly.http.server.util.Mapper;
import org.glassfish.grizzly.http.server.util.MappingData;
import org.glassfish.grizzly.http.util.DataChunk;
import org.glassfish.grizzly.http.util.MimeType;
import org.glassfish.grizzly.localization.LogMessages;
import jakarta.servlet.Filter;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.Servlet;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletContextAttributeEvent;
import jakarta.servlet.ServletContextAttributeListener;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.ServletException;
import jakarta.servlet.SessionTrackingMode;
import jakarta.servlet.descriptor.JspConfigDescriptor;
import jakarta.servlet.http.HttpUpgradeHandler;
/**
*
* This class acts as the foundation for registering listeners, servlets, and filters in an embedded environment.
*
*
*
* Additionally, this class implements the the requirements set forth by the Servlet 2.5 specification of
* {@link ServletContext}, however, it also exposes the dynamic registration API of Servlet 3.0.
*
*
*
* TODO: Provide code examples once the api firms up a bit.
*
*
* @since 2.2
*/
public class WebappContext implements ServletContext {
private static final Logger LOGGER = Grizzly.logger(WebappContext.class);
private static final Map DEPLOYED_APPS = new HashMap<>();
private static final Set DEFAULT_SESSION_TRACKING_MODES = EnumSet.of(SessionTrackingMode.COOKIE);
/* Servlet major/minor versions */
private static final int MAJOR_VERSION = 4;
private static final int MINOR_VERSION = 0;
/* Logical name, path spec, and filesystem path of this application */
private final String displayName;
private final String contextPath;
private final String basePath;
/* Servlet context initialization parameters */
private final Map contextInitParams = new LinkedHashMap<>(8, 1.0f);
/* Registrations */
protected final Map servletRegistrations = new HashMap<>(8, 1.0f);
protected final Map filterRegistrations = new LinkedHashMap<>(4, 1.0f);
protected final Map unmodifiableFilterRegistrations = Collections.unmodifiableMap(filterRegistrations);
/* SessionManager that will be used for the ServletHandlers */
private SessionManager sessionManager = ServletSessionManager.instance();
/* ServletHandlers backing the registrations */
private Set servletHandlers;
/* Listeners */
private final Set eventListenerInstances = new LinkedHashSet<>(4, 1.0f); // TODO - wire this in
private EventListener[] eventListeners = new EventListener[0];
/* Application start/stop state */
protected boolean deployed;
/* Factory for creating FilterChainImpl instances */
final private FilterChainFactory filterChainFactory;
/* Servlet context attributes */
private final ConcurrentMap attributes = new ConcurrentHashMap<>(16, 0.75f, 64);
/* Server name; used in the Server entity header */
private volatile String serverInfo = "grizzly/" + Grizzly.getDotedVersion();
/* Thread local data used during request dispatch */
/* TODO: seems like this may cause a leak - when is this ever cleared? */
private final ThreadLocal dispatchData = new ThreadLocal<>();
/* Request dispatcher helper class */
private DispatcherHelper dispatcherHelper;
private ClassLoader webappClassLoader;
int sessionTimeoutInSeconds;
private String requestEncoding;
private String responseEncoding;
/**
* Session cookie config
*/
private jakarta.servlet.SessionCookieConfig sessionCookieConfig;
private Set sessionTrackingModes;
/**
* Destroy listener to be registered on {@link ServletHandler} to make sure we undeploy entire application when
* {@link ServletHandler#destroy()} is invoked.
*/
private final Runnable onDestroyListener = new Runnable() {
@Override
public void run() {
if (deployed) {
undeploy();
}
}
};
/**
* The list of filter mappings for this application, in the order they were defined in the deployment descriptor.
*/
private final List filterMaps = new ArrayList<>();
// ------------------------------------------------------------ Constructors
protected WebappContext() {
displayName = "";
contextPath = "";
basePath = "";
filterChainFactory = new FilterChainFactory(this);
}
/**
*
* Creates a simple WebappContext
with the root being "/".
*
*
* @param displayName
*/
public WebappContext(final String displayName) {
this(displayName, "");
}
public WebappContext(final String displayName, final String contextPath) {
this(displayName, contextPath, ".");
}
public WebappContext(final String displayName, final String contextPath, final String basePath) {
if (displayName == null || displayName.length() == 0) {
throw new IllegalArgumentException("'displayName' cannot be null or zero-length");
}
if (contextPath == null) {
throw new IllegalArgumentException("'contextPath' cannot be null");
}
if (contextPath.length() > 0) {
if (contextPath.charAt(0) != '/') {
throw new IllegalArgumentException("'contextPath' must start with a forward slash");
}
if (!contextPath.equals("/")) {
if (contextPath.charAt(contextPath.length() - 1) == '/') {
throw new IllegalArgumentException("'contextPath' must not end with a forward slash");
}
}
}
this.displayName = displayName;
this.contextPath = contextPath;
try {
this.basePath = new File(basePath).getCanonicalPath();
} catch (IOException ioe) {
throw new IllegalArgumentException("Unable to resolve path: " + basePath);
}
filterChainFactory = new FilterChainFactory(this);
Mapper.setAllowReplacement(true);
}
// ---------------------------------------------------------- Public Methods
/**
* Set the value of the Server
header to be sent in the response. If the value is a zero-length String or
* null, no Server
header will be sent.
*
* @param serverInfo the string to be sent with the response.
*/
public void setServerInfo(final String serverInfo) {
this.serverInfo = serverInfo;
}
/**
*
* @param targetServer
*/
public synchronized void deploy(final HttpServer targetServer) {
if (!deployed) {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.log(Level.INFO, "Starting application [{0}] ...", displayName);
}
boolean error = false;
try {
webappClassLoader = ClassLoaderUtil.createURLClassLoader(new File(getBasePath()).getCanonicalPath());
if (getSessionCookieConfig().getName() == null) {
final SessionManager manager = targetServer.getServerConfiguration().getSessionManager();
if (manager != null) {
getSessionCookieConfig().setName(manager.getSessionCookieName());
}
}
String serverName = targetServer.getServerConfiguration().getHttpServerName();
if (serverName != null) {
String serverVersion = targetServer.getServerConfiguration().getHttpServerVersion();
if (serverVersion != null) {
serverName += '/' + serverVersion;
}
}
setServerInfo(serverName);
sessionTimeoutInSeconds = targetServer.getServerConfiguration().getSessionTimeoutSeconds();
initializeListeners();
contextInitialized();
initServlets(targetServer);
initFilters();
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.log(Level.INFO, "Application [{0}] is ready to service requests. Root: [{1}].", new Object[] { displayName, contextPath });
}
DEPLOYED_APPS.put(this, targetServer);
deployed = true;
} catch (Exception e) {
error = true;
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE, "[" + displayName + "] Exception deploying application. See stack trace for details.", e);
}
} finally {
if (error) {
undeploy();
}
}
// TODO : DO SOMETHING
}
}
/**
*
*/
public synchronized void undeploy() {
try {
if (deployed) {
deployed = false;
final HttpServer server = DEPLOYED_APPS.remove(this);
destroyServlets(server);
// destroy filter instances
destroyFilters();
// invoke servlet context listeners
contextDestroyed();
}
} catch (Exception e) {
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE, "[" + displayName + "] Exception undeploying application. See stack trace for details.", e);
}
}
}
/**
*
* @param name
* @param value
*/
public void addContextInitParameter(final String name, final String value) {
if (!deployed) {
contextInitParams.put(name, value);
}
}
/**
*
* @param name
*/
@SuppressWarnings({ "UnusedDeclaration" })
public void removeContextInitParameter(final String name) {
if (!deployed) {
contextInitParams.remove(name);
}
}
/**
*
*/
@SuppressWarnings({ "UnusedDeclaration" })
public void clearContextInitParameters() {
if (!deployed) {
contextInitParams.clear();
}
}
// --------------------------------------------- Methods from ServletContext
/**
* Adds the filter with the given name and class type to this servlet context.
*
*
* The registered filter may be further configured via the returned {@link FilterRegistration} object.
*
*
* If this WebappContext already contains a preliminary FilterRegistration for a filter with the given
* filterName, it will be completed (by assigning the name of the given filterClass to it) and
* returned.
*
* @param filterName the name of the filter
* @param filterClass the class object from which the filter will be instantiated
*
* @return a FilterRegistration object that may be used to further configure the registered filter, or null if
* this WebappContext already contains a complete FilterRegistration for a filter with the given filterName
*
* @throws IllegalStateException if this WebappContext has already been initialized
*/
@Override
public FilterRegistration addFilter(final String filterName, final Class extends Filter> filterClass) {
if (deployed) {
throw new IllegalArgumentException("WebappContext has already been deployed");
}
if (filterName == null) {
throw new IllegalArgumentException("'filterName' cannot be null");
}
if (filterClass == null) {
throw new IllegalArgumentException("'filterClass' cannot be null");
}
FilterRegistration registration = filterRegistrations.get(filterName);
if (registration == null) {
registration = new FilterRegistration(this, filterName, filterClass);
filterRegistrations.put(filterName, registration);
} else {
if (registration.filterClass != filterClass) {
registration.filter = null;
registration.filterClass = filterClass;
registration.className = filterClass.getName();
}
}
return registration;
}
/**
* Registers the given filter instance with this WebappContext under the given filterName.
*
*
* The registered filter may be further configured via the returned {@link FilterRegistration} object.
*
*
* If this WebappContext already contains a preliminary FilterRegistration for a filter with the given
* filterName, it will be completed (by assigning the class name of the given filter instance to it) and
* returned.
*
* @param filterName the name of the filter
* @param filter the filter instance to register
*
* @return a FilterRegistration object that may be used to further configure the given filter, or null if this
* WebappContext already contains a complete FilterRegistration for a filter with the given filterName or if
* the same filter instance has already been registered with this or another WebappContext in the same container
*
* @throws IllegalStateException if this WebappContext has already been initialized
*
* @since Servlet 3.0
*/
@Override
public FilterRegistration addFilter(final String filterName, final Filter filter) {
if (deployed) {
throw new IllegalArgumentException("WebappContext has already been deployed");
}
if (filterName == null) {
throw new IllegalArgumentException("'filterName' cannot be null");
}
if (filter == null) {
throw new IllegalArgumentException("'filter' cannot be null");
}
FilterRegistration registration = filterRegistrations.get(filterName);
if (registration == null) {
registration = new FilterRegistration(this, filterName, filter);
filterRegistrations.put(filterName, registration);
} else {
if (registration.filter != filter) {
registration.filter = filter;
registration.filterClass = filter.getClass();
registration.className = filter.getClass().getName();
}
}
return registration;
}
/**
* Adds the filter with the given name and class name to this servlet context.
*
*
* The registered filter may be further configured via the returned {@link FilterRegistration} object.
*
*
* The specified className will be loaded using the classloader associated with the application represented by
* this WebappContext.
*
*
* If this WebappContext already contains a preliminary FilterRegistration for a filter with the given
* filterName, it will be completed (by assigning the given className to it) and returned.
*
* @param filterName the name of the filter
* @param className the fully qualified class name of the filter
*
* @return a FilterRegistration object that may be used to further configure the registered filter, or null if
* this WebappContext already contains a complete FilterRegistration for a filter with the given filterName
*
* @throws IllegalStateException if this WebappContext has already been initialized
*/
@Override
public FilterRegistration addFilter(final String filterName, final String className) {
if (deployed) {
throw new IllegalArgumentException("WebappContext has already been deployed");
}
if (filterName == null) {
throw new IllegalArgumentException("'filterName' cannot be null");
}
if (className == null) {
throw new IllegalArgumentException("'className' cannot be null");
}
FilterRegistration registration = filterRegistrations.get(filterName);
if (registration == null) {
registration = new FilterRegistration(this, filterName, className);
filterRegistrations.put(filterName, registration);
} else {
if (!registration.className.equals(className)) {
registration.className = className;
registration.filterClass = null;
registration.filter = null;
}
}
return registration;
}
/**
* Adds the servlet with the given name and class type to this servlet context.
*
*
* The registered servlet may be further configured via the returned {@link ServletRegistration} object.
*
*
* If this WebappContext already contains a preliminary ServletRegistration for a servlet with the given
* servletName, it will be completed (by assigning the name of the given servletClass to it) and
* returned.
*
*
* @param servletName the name of the servlet
* @param servletClass the class object from which the servlet will be instantiated
*
* @return a ServletRegistration object that may be used to further configure the registered servlet, or null
* if this WebappContext already contains a complete ServletRegistration for the given servletName
*
* @throws IllegalStateException if this WebappContext has already been initialized
*
*/
@Override
public ServletRegistration addServlet(final String servletName, final Class extends Servlet> servletClass) {
if (deployed) {
throw new IllegalArgumentException("WebappContext has already been deployed");
}
if (servletName == null) {
throw new IllegalArgumentException("'servletName' cannot be null");
}
if (servletClass == null) {
throw new IllegalArgumentException("'servletClass' cannot be null");
}
ServletRegistration registration = servletRegistrations.get(servletName);
if (registration == null) {
registration = new ServletRegistration(this, servletName, servletClass);
servletRegistrations.put(servletName, registration);
} else {
if (registration.servletClass != servletClass) {
registration.servlet = null;
registration.servletClass = servletClass;
registration.className = servletClass.getName();
}
}
return registration;
}
/**
* Registers the given servlet instance with this WebappContext under the given servletName.
*
*
* The registered servlet may be further configured via the returned {@link ServletRegistration} object.
*
*
* If this WebappContext already contains a preliminary ServletRegistration for a servlet with the given
* servletName, it will be completed (by assigning the class name of the given servlet instance to it) and
* returned.
*
* @param servletName the name of the servlet
* @param servlet the servlet instance to register
*
* @return a ServletRegistration object that may be used to further configure the given servlet, or null if
* this WebappContext already contains a complete ServletRegistration for a servlet with the given servletName
* or if the same servlet instance has already been registered with this or another WebappContext in the same container
*
* @throws IllegalStateException if this WebappContext has already been initialized
*
* @throws IllegalArgumentException if the given servlet instance implements {@link jakarta.servlet.SingleThreadModel}
*/
@SuppressWarnings({ "deprecation" })
@Override
public ServletRegistration addServlet(final String servletName, final Servlet servlet) {
if (deployed) {
throw new IllegalArgumentException("WebappContext has already been deployed");
}
if (servletName == null) {
throw new IllegalArgumentException("'servletName' cannot be null");
}
if (servlet == null) {
throw new IllegalArgumentException("'servlet' cannot be null");
}
ServletRegistration registration = servletRegistrations.get(servletName);
if (registration == null) {
registration = new ServletRegistration(this, servletName, servlet);
servletRegistrations.put(servletName, registration);
} else {
if (registration.servlet != servlet) {
registration.servlet = servlet;
registration.servletClass = servlet.getClass();
registration.className = servlet.getClass().getName();
}
}
return registration;
}
/**
* Adds the servlet with the given name and class name to this servlet context.
*
*
* The registered servlet may be further configured via the returned {@link ServletRegistration} object.
*
*
* The specified className will be loaded using the classloader associated with the application represented by
* this WebappContext.
*
*
* If this WebappContext already contains a preliminary ServletRegistration for a servlet with the given
* servletName, it will be completed (by assigning the given className to it) and returned.
*
*
* @param servletName the name of the servlet
* @param className the fully qualified class name of the servlet
*
* @return a ServletRegistration object that may be used to further configure the registered servlet, or null
* if this WebappContext already contains a complete ServletRegistration for a servlet with the given
* servletName
*
* @throws IllegalStateException if this WebappContext has already been initialized
*/
@Override
public ServletRegistration addServlet(final String servletName, final String className) {
if (deployed) {
throw new IllegalArgumentException("WebappContext has already been deployed");
}
if (servletName == null) {
throw new IllegalArgumentException("'servletName' cannot be null");
}
if (className == null) {
throw new IllegalArgumentException("'className' cannot be null");
}
ServletRegistration registration = servletRegistrations.get(servletName);
if (registration == null) {
registration = new ServletRegistration(this, servletName, className);
servletRegistrations.put(servletName, registration);
} else {
if (!registration.className.equals(className)) {
registration.servlet = null;
registration.servletClass = null;
registration.className = className;
}
}
return registration;
}
/**
* Gets the FilterRegistration corresponding to the filter with the given filterName.
*
* @return the (complete or preliminary) FilterRegistration for the filter with the given filterName, or null
* if no FilterRegistration exists under that name
*/
@Override
public FilterRegistration getFilterRegistration(final String name) {
if (name == null) {
return null;
}
return filterRegistrations.get(name);
}
/**
* Gets a (possibly empty) Map of the FilterRegistration objects (keyed by filter name) corresponding to all filters
* registered with this WebappContext.
*
*
* The returned Map includes the FilterRegistration objects corresponding to all declared and annotated filters, as well
* as the FilterRegistration objects corresponding to all filters that have been added via one of the addFilter
* methods.
*
*
* Any changes to the returned Map must not affect this WebappContext.
*
* @return Map of the (complete and preliminary) FilterRegistration objects corresponding to all filters currently
* registered with this WebappContext
*/
@Override
public Map getFilterRegistrations() {
return unmodifiableFilterRegistrations;
}
/**
* Gets the ServletRegistration corresponding to the servlet with the given servletName.
*
* @return the (complete or preliminary) ServletRegistration for the servlet with the given servletName, or
* null if no ServletRegistration exists under that name
*/
@Override
public ServletRegistration getServletRegistration(final String name) {
if (name == null) {
return null;
}
return servletRegistrations.get(name);
}
/**
* Gets a (possibly empty) Map of the ServletRegistration objects (keyed by servlet name) corresponding to all servlets
* registered with this WebappContext.
*
*
* The returned Map includes the ServletRegistration objects corresponding to all declared and annotated servlets, as
* well as the ServletRegistration objects corresponding to all servlets that have been added via one of the
* addServlet methods.
*
*
* If permitted, any changes to the returned Map must not affect this WebappContext.
*
* @return Map of the (complete and preliminary) ServletRegistration objects corresponding to all servlets currently
* registered with this WebappContext
*
* @since Servlet 3.0
*/
@Override
public Map getServletRegistrations() {
return Collections.unmodifiableMap(servletRegistrations);
}
/**
* Adds the given listener class to this WebappContext.
*
*
* The given listener must be an instance of one or more of the following interfaces:
*
* - {@link ServletContextAttributeListener}
*
- {@link jakarta.servlet.ServletRequestListener}
*
- {@link jakarta.servlet.ServletRequestAttributeListener}
*
- {@link jakarta.servlet.http.HttpSessionListener}
*
- {@link jakarta.servlet.http.HttpSessionAttributeListener}
*
*
*
* If the given listener is an instance of a listener interface whose invocation order corresponds to the declaration
* order (i.e., if it is an instance of {@link jakarta.servlet.ServletRequestListener}, {@link ServletContextListener},
* or {@link jakarta.servlet.http.HttpSessionListener}), then the listener will be added to the end of the ordered list
* of listeners of that interface.
*
* @throws IllegalArgumentException if the given listener is not an instance of any of the above interfaces
*
* @throws IllegalStateException if this WebappContext has already been initialized
*/
@Override
public void addListener(final Class extends EventListener> listenerClass) {
if (deployed) {
throw new IllegalStateException("WebappContext has already been deployed");
}
if (listenerClass == null) {
throw new IllegalArgumentException("'listener' cannot be null");
}
try {
addListener(createEventListenerInstance(listenerClass));
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
/**
* Adds the listener with the given class name to this WebappContext.
*
*
* The class with the given name will be loaded using the classloader associated with the application represented by
* this WebappContext, and must implement one or more of the following interfaces:
*
* - {@link ServletContextAttributeListener}
*
- {@link jakarta.servlet.ServletRequestListener}
*
- {@link jakarta.servlet.ServletRequestAttributeListener}
*
- {@link jakarta.servlet.http.HttpSessionListener}
*
- {@link jakarta.servlet.http.HttpSessionAttributeListener}
*
*
*
* As part of this method call, the container must load the class with the specified class name to ensure that it
* implements one of the required interfaces.
*
*
* If the class with the given name implements a listener interface whose invocation order corresponds to the
* declaration order (i.e., if it implements {@link jakarta.servlet.ServletRequestListener},
* {@link ServletContextListener}, or {@link jakarta.servlet.http.HttpSessionListener}), then the new listener will be
* added to the end of the ordered list of listeners of that interface.
*
* @param className the fully qualified class name of the listener
*
* @throws IllegalArgumentException if the class with the given name does not implement any of the above interfaces
*
* @throws IllegalStateException if this WebappContext has already been initialized
*/
@Override
public void addListener(String className) {
if (deployed) {
throw new IllegalStateException("WebappContext has already been deployed");
}
if (className == null) {
throw new IllegalArgumentException("'className' cannot be null");
}
try {
addListener(createEventListenerInstance(className));
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void addListener(T eventListener) {
if (deployed) {
throw new IllegalStateException("WebappContext has already been deployed");
}
eventListenerInstances.add(eventListener);
}
@SuppressWarnings("unchecked")
@Override
public T createServlet(Class clazz) throws ServletException {
try {
return (T) createServletInstance(clazz);
} catch (Exception e) {
throw new ServletException(e);
}
}
@SuppressWarnings("unchecked")
@Override
public T createFilter(Class clazz) throws ServletException {
try {
return (T) createFilterInstance(clazz);
} catch (Exception e) {
throw new ServletException(e);
}
}
@SuppressWarnings("unchecked")
@Override
public T createListener(Class clazz) throws ServletException {
try {
return (T) createEventListenerInstance(clazz);
} catch (Exception e) {
throw new ServletException(e);
}
}
@Override
public void declareRoles(String... roleNames) {
if (deployed) {
throw new IllegalStateException("WebappContext has already been deployed");
}
}
/**
* {@inheritDoc}
*/
@Override
public String getContextPath() {
return contextPath;
}
/**
* {@inheritDoc}
*/
@Override
public ServletContext getContext(String uri) {
// Validate the format of the specified argument
if (uri == null || !uri.startsWith("/")) {
return null;
}
if (dispatcherHelper == null) {
return null;
}
// Use the thread local URI and mapping data
DispatchData dd = dispatchData.get();
if (dd == null) {
dd = new DispatchData();
dispatchData.set(dd);
} else {
dd.recycle();
}
DataChunk uriDC = dd.uriDC;
// Retrieve the thread local mapping data
MappingData mappingData = dd.mappingData;
try {
uriDC.setString(uri);
dispatcherHelper.mapPath(null, uriDC, mappingData);
if (mappingData.context == null) {
return null;
}
} catch (Exception e) {
// Should never happen
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, "Error during mapping", e);
}
return null;
}
if (!(mappingData.context instanceof ServletHandler)) {
return null;
}
ServletHandler context = (ServletHandler) mappingData.context;
return context.getServletCtx();
}
/**
* {@inheritDoc}
*/
@Override
public int getMajorVersion() {
return MAJOR_VERSION;
}
/**
* {@inheritDoc}
*/
@Override
public int getMinorVersion() {
return MINOR_VERSION;
}
/**
* {@inheritDoc}
*/
@Override
public int getEffectiveMajorVersion() {
return MAJOR_VERSION;
}
/**
* {@inheritDoc}
*/
@Override
public int getEffectiveMinorVersion() {
return MINOR_VERSION;
}
/**
* {@inheritDoc}
*/
@Override
public String getMimeType(String file) {
if (file == null) {
return null;
}
int period = file.lastIndexOf(".");
if (period < 0) {
return null;
}
String extension = file.substring(period + 1);
if (extension.length() < 1) {
return null;
}
return MimeType.get(extension);
}
/**
* {@inheritDoc}
*/
@Override
public Set getResourcePaths(String path) {
// Validate the path argument
if (path == null) {
return null;
}
if (!path.startsWith("/")) {
throw new IllegalArgumentException(path);
}
path = normalize(path);
if (path == null) {
return null;
}
File[] files = new File(basePath, path).listFiles();
Set set = Collections.emptySet();
if (files != null) {
set = new HashSet<>(files.length);
for (File f : files) {
try {
String canonicalPath = f.getCanonicalPath();
// add a trailing "/" if a folder
if (f.isDirectory()) {
canonicalPath = canonicalPath + "/";
}
canonicalPath = canonicalPath.substring(canonicalPath.indexOf(basePath) + basePath.length());
set.add(canonicalPath.replace("\\", "/"));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
return set;
}
/**
* {@inheritDoc}
*/
@Override
public URL getResource(String path) throws MalformedURLException {
if (path == null || !path.startsWith("/")) {
throw new MalformedURLException(path);
}
path = normalize(path);
if (path == null) {
return null;
}
// Help the UrlClassLoader, which is not able to load resources
// that contains '//'
if (path.length() > 1) {
path = path.substring(1);
}
return Thread.currentThread().getContextClassLoader().getResource(path);
}
/**
* {@inheritDoc}
*/
@Override
public InputStream getResourceAsStream(String path) {
String pathLocal = normalize(path);
if (pathLocal == null) {
return null;
}
// Help the UrlClassLoader, which is not able to load resources
// that contains '//'
if (pathLocal.length() > 1) {
pathLocal = pathLocal.substring(1);
}
return Thread.currentThread().getContextClassLoader().getResourceAsStream(pathLocal);
}
/**
* {@inheritDoc}
*/
@Override
public RequestDispatcher getRequestDispatcher(String path) {
// Validate the path argument
if (path == null) {
return null;
}
if (dispatcherHelper == null) {
return null;
}
if (!path.startsWith("/") && !path.isEmpty()) {
throw new IllegalArgumentException("Path " + path + " does not start with ''/'' and is not empty");
}
// Get query string
String queryString = null;
int pos = path.indexOf('?');
if (pos >= 0) {
queryString = path.substring(pos + 1);
path = path.substring(0, pos);
}
path = normalize(path);
if (path == null) {
return null;
}
// Use the thread local URI and mapping data
DispatchData dd = dispatchData.get();
if (dd == null) {
dd = new DispatchData();
dispatchData.set(dd);
} else {
dd.recycle();
}
DataChunk uriDC = dd.uriDC;
// Retrieve the thread local mapping data
MappingData mappingData = dd.mappingData;
try {
if (contextPath.length() == 1 && contextPath.charAt(0) == '/') {
uriDC.setString(path);
} else {
uriDC.setString(contextPath + path);
}
dispatcherHelper.mapPath(null, uriDC, mappingData);
if (mappingData.wrapper == null) {
return null;
}
} catch (Exception e) {
// Should never happen
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, "Error during mapping", e);
}
return null;
}
if (!(mappingData.wrapper instanceof ServletHandler)) {
return null;
}
ServletHandler wrapper = (ServletHandler) mappingData.wrapper;
String wrapperPath = mappingData.wrapperPath.toString();
String pathInfo = mappingData.pathInfo.toString();
// Construct a RequestDispatcher to process this request
return new ApplicationDispatcher(wrapper, uriDC.toString(), wrapperPath, pathInfo, queryString, null);
}
/**
* {@inheritDoc}
*/
@Override
public RequestDispatcher getNamedDispatcher(String name) {
// Validate the name argument
if (name == null) {
return null;
}
if (dispatcherHelper == null) {
return null;
}
// Use the thread local URI and mapping data
DispatchData dd = dispatchData.get();
if (dd == null) {
dd = new DispatchData();
dispatchData.set(dd);
} else {
dd.recycle();
}
DataChunk servletNameDC = dd.servletNameDC;
// Retrieve the thread local mapping data
MappingData mappingData = dd.mappingData;
// Map the name
servletNameDC.setString(name);
try {
dispatcherHelper.mapName(servletNameDC, mappingData);
if (!(mappingData.wrapper instanceof ServletHandler)) {
return null;
} else {
final ServletHandler h = (ServletHandler) mappingData.wrapper;
if (!contextPath.equals(h.getContextPath())) {
return null;
}
}
} catch (Exception e) {
// Should never happen
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, "Error during mapping", e);
}
return null;
}
ServletHandler wrapper = (ServletHandler) mappingData.wrapper;
// Construct a RequestDispatcher to process this request
return new ApplicationDispatcher(wrapper, null, null, null, null, name);
}
/**
* {@inheritDoc}
*/
@Override
public jakarta.servlet.ServletRegistration.Dynamic addJspFile(final String servletName, final String jspFile) {
if (deployed) {
throw new IllegalStateException();
}
if (servletName == null) {
throw new IllegalArgumentException();
}
return null; // JSP not supported out of the box.
}
/**
* {@inheritDoc}
*/
@Override
public int getSessionTimeout() {
return (int) MINUTES.convert(sessionTimeoutInSeconds, SECONDS);
}
/**
* {@inheritDoc}
*/
@Override
public void setSessionTimeout(final int sessionTimeout) {
if (deployed) {
throw new IllegalStateException();
}
sessionTimeoutInSeconds = (int) SECONDS.convert(sessionTimeout, MINUTES);
}
/**
* {@inheritDoc}
*/
@Override
public String getRequestCharacterEncoding() {
return requestEncoding;
}
/**
* {@inheritDoc}
*/
@Override
public void setRequestCharacterEncoding(final String requestEncoding) {
if (deployed) {
throw new IllegalStateException();
}
this.requestEncoding = requestEncoding;
}
/**
* {@inheritDoc}
*/
@Override
public String getResponseCharacterEncoding() {
return responseEncoding;
}
@Override
public void setResponseCharacterEncoding(final String responseEncoding) {
if (deployed) {
throw new IllegalStateException();
}
this.responseEncoding = responseEncoding;
}
/**
* {@inheritDoc}
*/
@Override
public void log(String message) {
LOGGER.log(Level.INFO, String.format("[%s] %s", displayName, message));
}
/**
* {@inheritDoc}
*/
@Override
public void log(String message, Throwable throwable) {
LOGGER.log(Level.INFO, String.format("[%s] %s", displayName, message), throwable);
}
/**
* {@inheritDoc}
*/
@Override
public String getRealPath(String path) {
if (path == null) {
return null;
}
return new File(basePath, path).getAbsolutePath();
}
@Override
public String getVirtualServerName() {
return "server";
}
/**
* {@inheritDoc}
*/
@Override
public String getServerInfo() {
return serverInfo;
}
/**
* {@inheritDoc}
*/
@Override
public String getInitParameter(String name) {
if (name == null) {
throw new NullPointerException();
}
return contextInitParams.get(name);
}
/**
* {@inheritDoc}
*/
@Override
public Enumeration getInitParameterNames() {
return new Enumerator<>(contextInitParams.keySet());
}
/**
* {@inheritDoc}
*/
@Override
public boolean setInitParameter(String name, String value) {
if (name == null) {
throw new NullPointerException();
}
if (!deployed) {
contextInitParams.put(name, value);
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public Object getAttribute(String name) {
if (name == null) {
throw new NullPointerException();
}
return attributes.get(name);
}
/**
* {@inheritDoc}
*/
@Override
public Enumeration getAttributeNames() {
return new Enumerator<>(attributes.keySet());
}
/**
* {@inheritDoc}
*/
@Override
public void setAttribute(String name, Object value) {
if (name == null) {
throw new NullPointerException();
}
// Null value is the same as removeAttribute()
if (value == null) {
removeAttribute(name);
return;
}
Object oldValue = attributes.put(name, value);
ServletContextAttributeEvent event = null;
for (EventListener eventListener : eventListeners) {
if (!(eventListener instanceof ServletContextAttributeListener)) {
continue;
}
ServletContextAttributeListener listener = (ServletContextAttributeListener) eventListener;
try {
if (event == null) {
if (oldValue != null) {
event = new ServletContextAttributeEvent(this, name, oldValue);
} else {
event = new ServletContextAttributeEvent(this, name, value);
}
}
if (oldValue != null) {
listener.attributeReplaced(event);
} else {
listener.attributeAdded(event);
}
} catch (Throwable t) {
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, LogMessages.WARNING_GRIZZLY_HTTP_SERVLET_ATTRIBUTE_LISTENER_ADD_ERROR("ServletContextAttributeListener",
listener.getClass().getName()), t);
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void removeAttribute(String name) {
Object value = attributes.remove(name);
if (value == null) {
return;
}
ServletContextAttributeEvent event = null;
for (EventListener eventListener : eventListeners) {
if (!(eventListener instanceof ServletContextAttributeListener)) {
continue;
}
ServletContextAttributeListener listener = (ServletContextAttributeListener) eventListener;
try {
if (event == null) {
event = new ServletContextAttributeEvent(this, name, value);
}
listener.attributeRemoved(event);
} catch (Throwable t) {
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, LogMessages.WARNING_GRIZZLY_HTTP_SERVLET_ATTRIBUTE_LISTENER_REMOVE_ERROR("ServletContextAttributeListener",
listener.getClass().getName()), t);
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public String getServletContextName() {
return displayName;
}
/**
* {@inheritDoc}
*/
@Override
public jakarta.servlet.SessionCookieConfig getSessionCookieConfig() {
if (sessionCookieConfig == null) {
sessionCookieConfig = new SessionCookieConfig(this);
}
return sessionCookieConfig;
}
/**
* {@inheritDoc}
*/
@Override
public void setSessionTrackingModes(Set sessionTrackingModes) {
if (sessionTrackingModes.contains(SessionTrackingMode.SSL)) {
throw new IllegalArgumentException("SSL tracking mode is not supported");
}
if (deployed) {
throw new IllegalArgumentException("WebappContext has already been deployed");
}
this.sessionTrackingModes = Collections.unmodifiableSet(sessionTrackingModes);
}
/**
* {@inheritDoc}
*/
@Override
public Set getDefaultSessionTrackingModes() {
return DEFAULT_SESSION_TRACKING_MODES;
}
/**
* {@inheritDoc}
*/
@Override
public Set getEffectiveSessionTrackingModes() {
return sessionTrackingModes != null ? sessionTrackingModes : DEFAULT_SESSION_TRACKING_MODES;
}
/**
* {@inheritDoc}
*/
@Override
public JspConfigDescriptor getJspConfigDescriptor() {
return null;
}
/**
* {@inheritDoc}
*/
@Override
public ClassLoader getClassLoader() {
return null;
}
// ------------------------------------------------------- Protected Methods
/**
* Return a context-relative path, beginning with a "/", that represents the canonical version of the specified path
* after ".." and "." elements are resolved out. If the specified path attempts to go outside the boundaries of the
* current context (i.e. too many ".." path elements are present), return null
instead.
*
* @param path Path to be normalized
*/
protected String normalize(String path) {
if (path == null) {
return null;
}
String normalized = path;
// Normalize the slashes and add leading slash if necessary
if (normalized.indexOf('\\') >= 0) {
normalized = normalized.replace('\\', '/');
}
// Resolve occurrences of "/../" in the normalized path
while (true) {
int index = normalized.indexOf("/../");
if (index < 0) {
break;
}
if (index == 0) {
return null; // Trying to go outside our context
}
int index2 = normalized.lastIndexOf('/', index - 1);
normalized = normalized.substring(0, index2) + normalized.substring(index + 3);
}
// Return the normalized path that we have completed
return normalized;
}
/**
*
* @return
*/
protected String getBasePath() {
return basePath;
}
/**
*
* @param dispatcherHelper
*/
protected void setDispatcherHelper(DispatcherHelper dispatcherHelper) {
this.dispatcherHelper = dispatcherHelper;
}
/**
* Sets the {@link SessionManager} that should be used by this {@link WebappContext}. The default is an instance of
* {@link ServletSessionManager}
*
* @param sessionManager an implementation of SessionManager
*/
@SuppressWarnings({ "UnusedDeclaration" })
public void setSessionManager(SessionManager sessionManager) {
this.sessionManager = sessionManager;
}
/**
*
* @return
*/
protected EventListener[] getEventListeners() {
return eventListeners;
}
/**
* Add a filter mapping to this Context.
*
* @param filterMap The filter mapping to be added
*
* @param isMatchAfter true if the given filter mapping should be matched against requests after any declared filter
* mappings of this servlet context, and false if it is supposed to be matched before any declared filter mappings of
* this servlet context
*
* @exception IllegalArgumentException if the specified filter name does not match an existing filter definition, or the
* filter mapping is malformed
*
*/
protected void addFilterMap(FilterMap filterMap, boolean isMatchAfter) {
// Validate the proposed filter mapping
String filterName = filterMap.getFilterName();
String servletName = filterMap.getServletName();
String urlPattern = filterMap.getURLPattern();
if (null == filterRegistrations.get(filterName)) {
throw new IllegalArgumentException("Filter mapping specifies an unknown filter name: " + filterName);
}
if (servletName == null && urlPattern == null) {
throw new IllegalArgumentException("Filter mapping must specify either a or a ");
}
if (servletName != null && urlPattern != null) {
throw new IllegalArgumentException("Filter mapping must specify either a or a ");
}
// Because filter-pattern is new in 2.3, no need to adjust
// for 2.2 backwards compatibility
if (urlPattern != null && !validateURLPattern(urlPattern)) {
throw new IllegalArgumentException("Invalid {0} in filter mapping: " + urlPattern);
}
// Add this filter mapping to our registered set
if (isMatchAfter) {
filterMaps.add(filterMap);
} else {
filterMaps.add(0, filterMap);
}
// if (notifyContainerListeners) {
// fireContainerEvent("addFilterMap", filterMap);
// }
}
protected List getFilterMaps() {
return filterMaps;
}
/**
* Removes any filter mappings from this Context.
*/
protected void removeFilterMaps() {
// Inform interested listeners
// if (notifyContainerListeners) {
// Iterator i = filterMaps.iterator();
// while (i.hasNext()) {
// fireContainerEvent("removeFilterMap", i.next());
// }
// }
filterMaps.clear();
}
/**
* Gets the current servlet name mappings of the Filter with the given name.
*/
protected Collection getServletNameFilterMappings(String filterName) {
HashSet mappings = new HashSet<>();
synchronized (filterMaps) {
for (FilterMap fm : filterMaps) {
if (filterName.equals(fm.getFilterName()) && fm.getServletName() != null) {
mappings.add(fm.getServletName());
}
}
}
return mappings;
}
/**
* Gets the current URL pattern mappings of the Filter with the given name.
*/
protected Collection getUrlPatternFilterMappings(String filterName) {
HashSet mappings = new HashSet<>();
synchronized (filterMaps) {
for (FilterMap fm : filterMaps) {
if (filterName.equals(fm.getFilterName()) && fm.getURLPattern() != null) {
mappings.add(fm.getURLPattern());
}
}
}
return mappings;
}
protected FilterChainFactory getFilterChainFactory() {
return filterChainFactory;
}
@SuppressWarnings("UnusedDeclaration")
protected void unregisterFilter(final Filter f) {
synchronized (filterRegistrations) {
for (Iterator i = filterRegistrations.values().iterator(); i.hasNext();) {
final FilterRegistration registration = i.next();
if (registration.filter == f) {
for (Iterator fmi = filterMaps.iterator(); fmi.hasNext();) {
FilterMap fm = fmi.next();
if (fm.getFilterName().equals(registration.name)) {
fmi.remove();
}
}
f.destroy();
i.remove();
}
}
}
}
protected void unregisterAllFilters() {
destroyFilters();
}
// --------------------------------------------------------- Private Methods
protected void destroyFilters() {
for (final FilterRegistration registration : filterRegistrations.values()) {
registration.filter.destroy();
}
removeFilterMaps();
}
/**
*
* @param server
*/
private void destroyServlets(HttpServer server) {
if (servletHandlers != null && !servletHandlers.isEmpty()) {
ServerConfiguration config = server.getServerConfiguration();
for (final ServletHandler handler : servletHandlers) {
config.removeHttpHandler(handler);
}
}
}
/**
*
*/
private void initializeListeners() throws ServletException {
if (!eventListenerInstances.isEmpty()) {
eventListeners = eventListenerInstances.toArray(new EventListener[eventListenerInstances.size()]);
}
}
/**
*
* @param server
*/
private void initServlets(final HttpServer server) throws ServletException {
boolean defaultMappingAdded = false;
if (!servletRegistrations.isEmpty()) {
final ServerConfiguration serverConfig = server.getServerConfiguration();
servletHandlers = new LinkedHashSet<>(servletRegistrations.size(), 1.0f);
final LinkedList sortedRegistrations = new LinkedList<>(servletRegistrations.values());
Collections.sort(sortedRegistrations);
for (final ServletRegistration registration : sortedRegistrations) {
final ServletConfigImpl sConfig = createServletConfig(registration);
ServletHandler servletHandler = new ServletHandler(sConfig);
if (registration.servlet != null) {
try {
registration.servlet.init(sConfig);
servletHandler.setServletInstance(registration.servlet);
} catch (Exception e) {
throw new RuntimeException(e);
}
} else if (registration.loadOnStartup >= 0) {
try {
Servlet servletInstance = createServletInstance(registration);
LOGGER.log(Level.INFO, "Loading Servlet: {0}", servletInstance.getClass().getName());
servletInstance.init(sConfig);
servletHandler.setServletInstance(servletInstance);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
servletHandler.setServletClass(registration.servletClass);
servletHandler.setServletClassName(registration.className);
servletHandler.setSessionManager(sessionManager);
servletHandler.setContextPath(contextPath);
servletHandler.setFilterChainFactory(filterChainFactory);
servletHandler.setExpectationHandler(registration.expectationHandler);
servletHandler.addOnDestroyListener(onDestroyListener);
servletHandler.setClassLoader(webappClassLoader);
final String[] patterns = registration.urlPatterns.getArray();
if (patterns != null && patterns.length > 0) {
final String[] mappings = new String[patterns.length];
for (int i = 0; i < patterns.length; i++) {
final String pattern = patterns[i];
if (pattern.length() == 0 || "/".equals(pattern)) {
defaultMappingAdded = true;
}
mappings[i] = updateMappings(servletHandler, pattern);
}
serverConfig.addHttpHandler(servletHandler, mappings);
} else {
serverConfig.addHttpHandler(servletHandler, updateMappings(servletHandler, ""));
}
servletHandlers.add(servletHandler);
if (LOGGER.isLoggable(Level.INFO)) {
String p = patterns == null ? "" : Arrays.toString(patterns);
LOGGER.log(Level.INFO, "[{0}] Servlet [{1}] registered for url pattern(s) [{2}].", new Object[] { displayName, registration.className, p });
}
}
}
if (!defaultMappingAdded) {
// add the default servlet
registerDefaultServlet(server);
}
}
private void registerDefaultServlet(HttpServer server) {
final ServerConfiguration serverConfig = server.getServerConfiguration();
Map handlers = serverConfig.getHttpHandlers();
for (final Map.Entry entry : handlers.entrySet()) {
final HttpHandler h = entry.getKey();
if (!(h instanceof StaticHttpHandlerBase)) {
continue;
}
String[] mappings = entry.getValue();
for (final String mapping : mappings) {
if ("/".equals(mapping)) {
final DefaultServlet s = new DefaultServlet((StaticHttpHandlerBase) h);
final ServletRegistration registration = addServlet("DefaultServlet", s);
registration.addMapping("/");
final ServletConfigImpl sConfig = createServletConfig(registration);
try {
s.init(sConfig);
} catch (ServletException ignored) {
// shouldn't happen
}
final ServletHandler servletHandler = new ServletHandler(sConfig);
servletHandler.setServletInstance(registration.servlet);
servletHandler.setServletClass(registration.servletClass);
servletHandler.setServletClassName(registration.className);
servletHandler.setContextPath(contextPath);
servletHandler.setFilterChainFactory(filterChainFactory);
servletHandler.setExpectationHandler(registration.expectationHandler);
servletHandler.addOnDestroyListener(onDestroyListener);
serverConfig.addHttpHandler(servletHandler, updateMappings(servletHandler, "/"));
if (servletHandlers == null) {
servletHandlers = new LinkedHashSet<>(1, 1.0f);
}
servletHandlers.add(servletHandler);
}
}
}
}
/**
*
*/
@SuppressWarnings({ "unchecked" })
private void initFilters() {
if (!filterRegistrations.isEmpty()) {
for (final FilterRegistration registration : filterRegistrations.values()) {
try {
Filter f = registration.filter;
if (f == null) {
f = createFilterInstance(registration);
}
final FilterConfigImpl filterConfig = createFilterConfig(registration);
registration.filter = f;
f.init(filterConfig);
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.log(Level.INFO, "[{0}] Filter [{1}] registered for url pattern(s) [{2}] and servlet name(s) [{3}]", new Object[] { displayName,
registration.className, registration.getUrlPatternMappings(), registration.getServletNameMappings() });
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
/**
*
* @param handler
* @param mapping
* @return
*/
private static String updateMappings(ServletHandler handler, String mapping) {
String mappingLocal = mapping;
if (mappingLocal.length() == 0) {
mappingLocal = "/";
} else {
if (mappingLocal.charAt(0) == '*') {
mappingLocal = "/" + mapping;
}
if (mappingLocal.indexOf("//", 1) != -1) {
mappingLocal = mappingLocal.replaceAll("//", "/");
}
}
String contextPath = handler.getContextPath();
contextPath = contextPath.length() == 0 ? "/" : contextPath;
return contextPath + mappingLocal;
}
// --------------------------------------------------------- Private Methods
/**
*
* @param registration
* @return
*/
private FilterConfigImpl createFilterConfig(final FilterRegistration registration) {
final FilterConfigImpl fConfig = new FilterConfigImpl(this);
fConfig.setFilterName(registration.getName());
if (!registration.initParameters.isEmpty()) {
fConfig.setInitParameters(registration.initParameters);
}
return fConfig;
}
/**
*
* @param registration
* @return
*/
private ServletConfigImpl createServletConfig(final ServletRegistration registration) {
final ServletConfigImpl sConfig = new ServletConfigImpl(this);
sConfig.setServletName(registration.getName());
if (!registration.initParameters.isEmpty()) {
sConfig.setInitParameters(registration.initParameters);
}
return sConfig;
}
/**
*
*/
private void contextInitialized() {
ServletContextEvent event = null;
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(webappClassLoader);
for (EventListener eventListener : eventListeners) {
if (!(eventListener instanceof ServletContextListener)) {
continue;
}
ServletContextListener listener = (ServletContextListener) eventListener;
if (event == null) {
event = new ServletContextEvent(this);
}
try {
listener.contextInitialized(event);
} catch (Throwable t) {
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, LogMessages.WARNING_GRIZZLY_HTTP_SERVLET_CONTAINER_OBJECT_INITIALIZED_ERROR("contextInitialized",
"ServletContextListener", listener.getClass().getName()), t);
}
}
}
Thread.currentThread().setContextClassLoader(loader);
}
/**
*
*/
private void contextDestroyed() {
ServletContextEvent event = null;
for (EventListener eventListener : eventListeners) {
if (!(eventListener instanceof ServletContextListener)) {
continue;
}
ServletContextListener listener = (ServletContextListener) eventListener;
if (event == null) {
event = new ServletContextEvent(this);
}
try {
listener.contextDestroyed(event);
} catch (Throwable t) {
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, LogMessages.WARNING_GRIZZLY_HTTP_SERVLET_CONTAINER_OBJECT_DESTROYED_ERROR("contextDestroyed",
"ServletContextListener", listener.getClass().getName()), t);
}
}
}
}
/**
* Instantiates the given Servlet class.
*
* @return the new Servlet instance
*/
protected Servlet createServletInstance(final ServletRegistration registration) throws Exception {
String servletClassName = registration.className;
if (servletClassName != null) {
return (Servlet) ClassLoaderUtil.load(servletClassName);
} else {
Class extends Servlet> servletClass = registration.servletClass;
return createServletInstance(servletClass);
}
}
/**
* Instantiates the given Servlet class.
*
* @return the new Servlet instance
*/
protected Servlet createServletInstance(Class extends Servlet> servletClass) throws Exception {
return servletClass.newInstance();
}
/**
* Instantiates the given Filter class.
*
* @return the new Filter instance
*/
protected Filter createFilterInstance(final FilterRegistration registration) throws Exception {
String filterClassName = registration.className;
Class extends Filter> filterClass = registration.filterClass;
if (filterClassName != null) {
return (Filter) ClassLoaderUtil.load(filterClassName);
} else {
return createFilterInstance(filterClass);
}
}
/**
* Instantiates the given Filter class.
*
* @return the new Filter instance
*/
protected Filter createFilterInstance(Class extends Filter> filterClass) throws Exception {
return filterClass.newInstance();
}
/**
* Instantiates the given EventListener class.
*
* @return the new EventListener instance
*/
protected EventListener createEventListenerInstance(Class extends EventListener> eventListenerClass) throws Exception {
return eventListenerClass.newInstance();
}
/**
* Instantiates the given EventListener class.
*
* @return the new EventListener instance
*/
protected EventListener createEventListenerInstance(String eventListenerClassname) throws Exception {
return (EventListener) ClassLoaderUtil.load(eventListenerClassname);
}
/**
* Instantiates the given HttpUpgradeHandler class.
*
* @param clazz
* @param
* @return a new T instance
* @throws Exception
*/
public T createHttpUpgradeHandlerInstance(Class clazz) throws Exception {
return clazz.newInstance();
}
/**
* Validate the syntax of a proposed <url-pattern>
for conformance with specification requirements.
*
* @param urlPattern URL pattern to be validated
*/
protected boolean validateURLPattern(String urlPattern) {
if (urlPattern == null) {
return false;
}
if (urlPattern.isEmpty()) {
return true;
}
if (urlPattern.indexOf('\n') >= 0 || urlPattern.indexOf('\r') >= 0) {
LOGGER.log(Level.WARNING, "The URL pattern ''{0}'' contains a CR or LF and so can never be matched", urlPattern);
return false;
}
if (urlPattern.startsWith("*.")) {
if (urlPattern.indexOf('/') < 0) {
checkUnusualURLPattern(urlPattern);
return true;
} else {
return false;
}
}
if (urlPattern.startsWith("/") && !urlPattern.contains("*.")) {
checkUnusualURLPattern(urlPattern);
return true;
} else {
return false;
}
}
/**
* Check for unusual but valid <url-pattern>
s. See Bugzilla 34805, 43079 & 43080
*/
private void checkUnusualURLPattern(String urlPattern) {
if (LOGGER.isLoggable(Level.INFO)) {
if (urlPattern.endsWith("*") && (urlPattern.length() < 2 || urlPattern.charAt(urlPattern.length() - 2) != '/')) {
LOGGER.log(Level.INFO, "Suspicious url pattern: \"{0}" + "\"" + " in context - see" + " section SRV.11.2 of the Servlet specification",
urlPattern);
}
}
}
// ---------------------------------------------------------- Nested Classes
/**
* Internal class used as thread-local storage when doing path mapping during dispatch.
*/
private static final class DispatchData {
public final DataChunk uriDC;
public final DataChunk servletNameDC;
public final MappingData mappingData;
public DispatchData() {
uriDC = DataChunk.newInstance();
servletNameDC = DataChunk.newInstance();
mappingData = new MappingData();
}
public void recycle() {
uriDC.recycle();
servletNameDC.recycle();
mappingData.recycle();
}
} // END DispatchData
}