/*
* 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.SingleThreadModel;
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 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
/**
* <p>
* This class acts as the foundation for registering listeners, servlets, and filters in an embedded environment.
* </p>
*
* <p>
* 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.
* </p>
*
* <p>
* TODO: Provide code examples once the api firms up a bit.
* </p>
*
* @since 2.2
*/
public class WebappContext implements ServletContext {
private static final Logger LOGGER = Grizzly.logger(WebappContext.class);
private static final Map<WebappContext, HttpServer> DEPLOYED_APPS = new HashMap<>();
private static final Set<SessionTrackingMode> 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<String, String> contextInitParams = new LinkedHashMap<>(8, 1.0f);
The security roles for this application
/**
* The security roles for this application
*/
private final List<String> securityRoles = new ArrayList<>();
/* Registrations */
protected final Map<String, ServletRegistration> servletRegistrations = new HashMap<>(8, 1.0f);
protected final Map<String, FilterRegistration> filterRegistrations = new LinkedHashMap<>(4, 1.0f);
protected final Map<String, FilterRegistration> unmodifiableFilterRegistrations = Collections.unmodifiableMap(filterRegistrations);
/* SessionManager that will be used for the ServletHandlers */
private SessionManager sessionManager = ServletSessionManager.instance();
/* ServletHandlers backing the registrations */
private Set<ServletHandler> servletHandlers;
/* Listeners */
private final Set<EventListener> 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<String, Object> 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> dispatchData = new ThreadLocal<>();
/* Request dispatcher helper class */
private DispatcherHelper dispatcherHelper;
private ClassLoader webappClassLoader;
int sessionTimeoutInSeconds;
private String requestEncoding;
private String responseEncoding;
Session cookie config
/**
* Session cookie config
*/
private jakarta.servlet.SessionCookieConfig sessionCookieConfig;
private Set<SessionTrackingMode> sessionTrackingModes;
Destroy listener to be registered on ServletHandler
to make sure we undeploy entire application when ServletHandler.destroy()
is invoked. /**
* 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.
/**
* The list of filter mappings for this application, in the order they were defined in the deployment descriptor.
*/
private final List<FilterMap> filterMaps = new ArrayList<>();
// ------------------------------------------------------------ Constructors
protected WebappContext() {
displayName = "";
contextPath = "";
basePath = "";
filterChainFactory = new FilterChainFactory(this);
}
Creates a simple WebappContext
with the root being "/".
Params: - displayName –
/**
* <p>
* Creates a simple <code>WebappContext</code> with the root being "/".
* </p>
*
* @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.
Params: - serverInfo – the string to be sent with the response.
/**
* Set the value of the <code>Server</code> header to be sent in the response. If the value is a zero-length String or
* null, no <code>Server</code> header will be sent.
*
* @param serverInfo the string to be sent with the response.
*/
public void setServerInfo(final String serverInfo) {
this.serverInfo = serverInfo;
}
Params: - targetServer –
/**
*
* @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);
}
}
}
Params: - name –
- value –
/**
*
* @param name
* @param value
*/
public void addContextInitParameter(final String name, final String value) {
if (!deployed) {
contextInitParams.put(name, value);
}
}
Params: - name –
/**
*
* @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 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.
Params: - filterName – the name of the filter
- filterClass – the class object from which the filter will be instantiated
Throws: - IllegalStateException – if this WebappContext has already been initialized
Returns: 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
/**
* Adds the filter with the given name and class type to this servlet context.
*
* <p>
* The registered filter may be further configured via the returned {@link FilterRegistration} object.
*
* <p>
* If this WebappContext already contains a preliminary FilterRegistration for a filter with the given
* <tt>filterName</tt>, it will be completed (by assigning the name of the given <tt>filterClass</tt> 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 <tt>null</tt> if
* this WebappContext already contains a complete FilterRegistration for a filter with the given <tt>filterName</tt>
*
* @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 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.
Params: - filterName – the name of the filter
- filter – the filter instance to register
Throws: - IllegalStateException – if this WebappContext has already been initialized
Returns: 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 Since: Servlet 3.0
/**
* Registers the given filter instance with this WebappContext under the given <tt>filterName</tt>.
*
* <p>
* The registered filter may be further configured via the returned {@link FilterRegistration} object.
*
* <p>
* If this WebappContext already contains a preliminary FilterRegistration for a filter with the given
* <tt>filterName</tt>, 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 <tt>null</tt> if this
* WebappContext already contains a complete FilterRegistration for a filter with the given <tt>filterName</tt> 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 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.
Params: - filterName – the name of the filter
- className – the fully qualified class name of the filter
Throws: - IllegalStateException – if this WebappContext has already been initialized
Returns: 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
/**
* Adds the filter with the given name and class name to this servlet context.
*
* <p>
* The registered filter may be further configured via the returned {@link FilterRegistration} object.
*
* <p>
* The specified <tt>className</tt> will be loaded using the classloader associated with the application represented by
* this WebappContext.
*
* <p>
* If this WebappContext already contains a preliminary FilterRegistration for a filter with the given
* <tt>filterName</tt>, it will be completed (by assigning the given <tt>className</tt> 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 <tt>null</tt> if
* this WebappContext already contains a complete FilterRegistration for a filter with the given <tt>filterName</tt>
*
* @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 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.
Params: - servletName – the name of the servlet
- servletClass – the class object from which the servlet will be instantiated
Throws: - IllegalStateException – if this WebappContext has already been initialized
Returns: 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
/**
* Adds the servlet with the given name and class type to this servlet context.
*
* <p>
* The registered servlet may be further configured via the returned {@link ServletRegistration} object.
*
* <p>
* If this WebappContext already contains a preliminary ServletRegistration for a servlet with the given
* <tt>servletName</tt>, it will be completed (by assigning the name of the given <tt>servletClass</tt> 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 <tt>null</tt>
* if this WebappContext already contains a complete ServletRegistration for the given <tt>servletName</tt>
*
* @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 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.
Params: - servletName – the name of the servlet
- servlet – the servlet instance to register
Throws: - IllegalStateException – if this WebappContext has already been initialized
- IllegalArgumentException – if the given servlet instance implements
SingleThreadModel
Returns: 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
/**
* Registers the given servlet instance with this WebappContext under the given <tt>servletName</tt>.
*
* <p>
* The registered servlet may be further configured via the returned {@link ServletRegistration} object.
*
* <p>
* If this WebappContext already contains a preliminary ServletRegistration for a servlet with the given
* <tt>servletName</tt>, 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 <tt>null</tt> if
* this WebappContext already contains a complete ServletRegistration for a servlet with the given <tt>servletName</tt>
* 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");
}
if (servlet instanceof SingleThreadModel) {
throw new IllegalArgumentException("SingleThreadModel Servlet instances are not allowed.");
}
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 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.
Params: - servletName – the name of the servlet
- className – the fully qualified class name of the servlet
Throws: - IllegalStateException – if this WebappContext has already been initialized
Returns: 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
/**
* Adds the servlet with the given name and class name to this servlet context.
*
* <p>
* The registered servlet may be further configured via the returned {@link ServletRegistration} object.
*
* <p>
* The specified <tt>className</tt> will be loaded using the classloader associated with the application represented by
* this WebappContext.
*
* <p>
* If this WebappContext already contains a preliminary ServletRegistration for a servlet with the given
* <tt>servletName</tt>, it will be completed (by assigning the given <tt>className</tt> 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 <tt>null</tt>
* if this WebappContext already contains a complete ServletRegistration for a servlet with the given
* <tt>servletName</tt>
*
* @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.
Returns: the (complete or preliminary) FilterRegistration for the filter with the given filterName, or null
if no FilterRegistration exists under that name
/**
* Gets the FilterRegistration corresponding to the filter with the given <tt>filterName</tt>.
*
* @return the (complete or preliminary) FilterRegistration for the filter with the given <tt>filterName</tt>, 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.
Returns: Map of the (complete and preliminary) FilterRegistration objects corresponding to all filters currently
registered with this WebappContext
/**
* Gets a (possibly empty) Map of the FilterRegistration objects (keyed by filter name) corresponding to all filters
* registered with this WebappContext.
*
* <p>
* 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 <tt>addFilter</tt>
* methods.
*
* <p>
* 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<String, ? extends FilterRegistration> getFilterRegistrations() {
return unmodifiableFilterRegistrations;
}
Gets the ServletRegistration corresponding to the servlet with the given servletName.
Returns: the (complete or preliminary) ServletRegistration for the servlet with the given servletName, or
null if no ServletRegistration exists under that name
/**
* Gets the ServletRegistration corresponding to the servlet with the given <tt>servletName</tt>.
*
* @return the (complete or preliminary) ServletRegistration for the servlet with the given <tt>servletName</tt>, 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.
Returns: Map of the (complete and preliminary) ServletRegistration objects corresponding to all servlets currently
registered with this WebappContext Since: Servlet 3.0
/**
* Gets a (possibly empty) Map of the ServletRegistration objects (keyed by servlet name) corresponding to all servlets
* registered with this WebappContext.
*
* <p>
* 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
* <tt>addServlet</tt> methods.
*
* <p>
* 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<String, ? extends ServletRegistration> 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:
ServletContextAttributeListener
ServletRequestListener
ServletRequestAttributeListener
HttpSessionListener
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 ServletRequestListener
, ServletContextListener
, or 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
- IllegalStateException – if this WebappContext has already been initialized
/**
* Adds the given listener class to this WebappContext.
*
* <p>
* The given listener must be an instance of one or more of the following interfaces:
* <ul>
* <li>{@link ServletContextAttributeListener}</tt>
* <li>{@link jakarta.servlet.ServletRequestListener}</tt>
* <li>{@link jakarta.servlet.ServletRequestAttributeListener}</tt>
* <li>{@link jakarta.servlet.http.HttpSessionListener}</tt>
* <li>{@link jakarta.servlet.http.HttpSessionAttributeListener}</tt>
* </ul>
*
* <p>
* 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:
ServletContextAttributeListener
ServletRequestListener
ServletRequestAttributeListener
HttpSessionListener
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 ServletRequestListener
, ServletContextListener
, or HttpSessionListener
), then the new listener will be added to the end of the ordered list of listeners of that interface.
Params: - 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
- IllegalStateException – if this WebappContext has already been initialized
/**
* Adds the listener with the given class name to this WebappContext.
*
* <p>
* 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:
* <ul>
* <li>{@link ServletContextAttributeListener}</tt>
* <li>{@link jakarta.servlet.ServletRequestListener}</tt>
* <li>{@link jakarta.servlet.ServletRequestAttributeListener}</tt>
* <li>{@link jakarta.servlet.http.HttpSessionListener}</tt>
* <li>{@link jakarta.servlet.http.HttpSessionAttributeListener}</tt>
* </ul>
*
* <p>
* 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.
*
* <p>
* 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}
/**
* {@inheritDoc}
*/
@Override
public <T extends EventListener> void addListener(T eventListener) {
if (deployed) {
throw new IllegalStateException("WebappContext has already been deployed");
}
eventListenerInstances.add(eventListener);
}
@SuppressWarnings("unchecked")
@Override
public <T extends Servlet> T createServlet(Class<T> clazz) throws ServletException {
try {
return (T) createServletInstance(clazz);
} catch (Exception e) {
throw new ServletException(e);
}
}
@SuppressWarnings("unchecked")
@Override
public <T extends Filter> T createFilter(Class<T> clazz) throws ServletException {
try {
return (T) createFilterInstance(clazz);
} catch (Exception e) {
throw new ServletException(e);
}
}
@SuppressWarnings("unchecked")
@Override
public <T extends EventListener> T createListener(Class<T> 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");
}
securityRoles.addAll(Arrays.asList(roleNames));
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public String getContextPath() {
return contextPath;
}
{@inheritDoc}
/**
* {@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}
/**
* {@inheritDoc}
*/
@Override
public int getMajorVersion() {
return MAJOR_VERSION;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public int getMinorVersion() {
return MINOR_VERSION;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public int getEffectiveMajorVersion() {
return MAJOR_VERSION;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public int getEffectiveMinorVersion() {
return MINOR_VERSION;
}
{@inheritDoc}
/**
* {@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}
/**
* {@inheritDoc}
*/
@Override
public Set<String> 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<String> 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}
/**
* {@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}
/**
* {@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}
/**
* {@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}
/**
* {@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}
/**
* {@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}
/**
* {@inheritDoc}
*/
@Override
public int getSessionTimeout() {
return (int) MINUTES.convert(sessionTimeoutInSeconds, SECONDS);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public void setSessionTimeout(final int sessionTimeout) {
if (deployed) {
throw new IllegalStateException();
}
sessionTimeoutInSeconds = (int) SECONDS.convert(sessionTimeout, MINUTES);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public String getRequestCharacterEncoding() {
return requestEncoding;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public void setRequestCharacterEncoding(final String requestEncoding) {
if (deployed) {
throw new IllegalStateException();
}
this.requestEncoding = requestEncoding;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public String getResponseCharacterEncoding() {
return responseEncoding;
}
@Override
public void setResponseCharacterEncoding(final String responseEncoding) {
if (deployed) {
throw new IllegalStateException();
}
this.responseEncoding = responseEncoding;
}
{@inheritDoc}
Deprecated:
/**
* {@inheritDoc}
*
* @deprecated
*/
@Override
@Deprecated
public Servlet getServlet(String name) throws ServletException {
return null;
}
{@inheritDoc}
Deprecated:
/**
* {@inheritDoc}
*
* @deprecated
*/
@Override
@Deprecated
public Enumeration<Servlet> getServlets() {
return new Enumerator<>(Collections.emptyList());
}
{@inheritDoc}
Deprecated:
/**
*
* {@inheritDoc}
*
* @deprecated
*/
@Override
@Deprecated
public Enumeration<String> getServletNames() {
return new Enumerator<>(Collections.emptyList());
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public void log(String message) {
LOGGER.log(Level.INFO, String.format("[%s] %s", displayName, message));
}
{@inheritDoc}
Deprecated:
/**
* {@inheritDoc}
*
* @deprecated
*/
@Override
@Deprecated
public void log(Exception e, String message) {
log(message, e);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public void log(String message, Throwable throwable) {
LOGGER.log(Level.INFO, String.format("[%s] %s", displayName, message), throwable);
}
{@inheritDoc}
/**
* {@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}
/**
* {@inheritDoc}
*/
@Override
public String getServerInfo() {
return serverInfo;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public String getInitParameter(String name) {
if (name == null) {
throw new NullPointerException();
}
return contextInitParams.get(name);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public Enumeration<String> getInitParameterNames() {
return new Enumerator<>(contextInitParams.keySet());
}
{@inheritDoc}
/**
* {@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}
/**
* {@inheritDoc}
*/
@Override
public Object getAttribute(String name) {
if (name == null) {
throw new NullPointerException();
}
return attributes.get(name);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public Enumeration<String> getAttributeNames() {
return new Enumerator<>(attributes.keySet());
}
{@inheritDoc}
/**
* {@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 (int i = 0, len = eventListeners.length; i < len; i++) {
if (!(eventListeners[i] instanceof ServletContextAttributeListener)) {
continue;
}
ServletContextAttributeListener listener = (ServletContextAttributeListener) eventListeners[i];
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}
/**
* {@inheritDoc}
*/
@Override
public void removeAttribute(String name) {
Object value = attributes.remove(name);
if (value == null) {
return;
}
ServletContextAttributeEvent event = null;
for (int i = 0, len = eventListeners.length; i < len; i++) {
if (!(eventListeners[i] instanceof ServletContextAttributeListener)) {
continue;
}
ServletContextAttributeListener listener = (ServletContextAttributeListener) eventListeners[i];
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}
/**
* {@inheritDoc}
*/
@Override
public String getServletContextName() {
return displayName;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public jakarta.servlet.SessionCookieConfig getSessionCookieConfig() {
if (sessionCookieConfig == null) {
sessionCookieConfig = new SessionCookieConfig(this);
}
return sessionCookieConfig;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public void setSessionTrackingModes(Set<SessionTrackingMode> 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}
/**
* {@inheritDoc}
*/
@Override
public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
return DEFAULT_SESSION_TRACKING_MODES;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
return sessionTrackingModes != null ? sessionTrackingModes : DEFAULT_SESSION_TRACKING_MODES;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@Override
public JspConfigDescriptor getJspConfigDescriptor() {
return null;
}
{@inheritDoc}
/**
* {@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.
Params: - path – Path to be normalized
/**
* 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 <code>null</code> 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;
}
Returns:
/**
*
* @return
*/
protected String getBasePath() {
return basePath;
}
Params: - dispatcherHelper –
/**
*
* @param dispatcherHelper
*/
protected void setDispatcherHelper(DispatcherHelper dispatcherHelper) {
this.dispatcherHelper = dispatcherHelper;
}
Sets the SessionManager
that should be used by this WebappContext
. The default is an instance of ServletSessionManager
Params: - sessionManager – an implementation of SessionManager
/**
* 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;
}
Returns:
/**
*
* @return
*/
protected EventListener[] getEventListeners() {
return eventListeners;
}
Add a filter mapping to this Context.
Params: - filterMap – The filter mapping to be added
- 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
Throws: - IllegalArgumentException – if the specified filter name does not match an existing filter definition, or the
filter mapping is malformed
/**
* 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 <url-pattern> or a <servlet-name>");
}
if (servletName != null && urlPattern != null) {
throw new IllegalArgumentException("Filter mapping must specify either a <url-pattern> or a <servlet-name>");
}
// 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 <url-pattern> {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<FilterMap> getFilterMaps() {
return filterMaps;
}
Removes any filter mappings from this Context.
/**
* Removes any filter mappings from this Context.
*/
protected void removeFilterMaps() {
// Inform interested listeners
// if (notifyContainerListeners) {
// Iterator<FilterMap> 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.
/**
* Gets the current servlet name mappings of the Filter with the given name.
*/
protected Collection<String> getServletNameFilterMappings(String filterName) {
HashSet<String> 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.
/**
* Gets the current URL pattern mappings of the Filter with the given name.
*/
protected Collection<String> getUrlPatternFilterMappings(String filterName) {
HashSet<String> 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<FilterRegistration> i = filterRegistrations.values().iterator(); i.hasNext();) {
final FilterRegistration registration = i.next();
if (registration.filter == f) {
for (Iterator<FilterMap> 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();
}
Params: - server –
/**
*
* @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()]);
}
}
Params: - server –
/**
*
* @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<ServletRegistration> 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<HttpHandler, String[]> handlers = serverConfig.getHttpHandlers();
for (final Map.Entry<HttpHandler, String[]> 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);
}
}
}
}
Params: - handler –
- mapping –
Returns:
/**
*
* @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
Params: - registration –
Returns:
/**
*
* @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;
}
Params: - registration –
Returns:
/**
*
* @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 (int i = 0, len = eventListeners.length; i < len; i++) {
if (!(eventListeners[i] instanceof ServletContextListener)) {
continue;
}
ServletContextListener listener = (ServletContextListener) eventListeners[i];
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 (int i = 0, len = eventListeners.length; i < len; i++) {
if (!(eventListeners[i] instanceof ServletContextListener)) {
continue;
}
ServletContextListener listener = (ServletContextListener) eventListeners[i];
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.
Returns: the new Servlet instance
/**
* 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.
Returns: the new Servlet instance
/**
* 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.
Returns: the new Filter instance
/**
* 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.
Returns: the new Filter instance
/**
* 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.
Returns: the new EventListener instance
/**
* 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.
Returns: the new EventListener instance
/**
* 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.
Params: - clazz –
Type parameters: - <T> –
Throws: Returns: a new T instance
/**
* Instantiates the given HttpUpgradeHandler class.
*
* @param clazz
* @param <T>
* @return a new T instance
* @throws Exception
*/
public <T extends HttpUpgradeHandler> T createHttpUpgradeHandlerInstance(Class<T> clazz) throws Exception {
return clazz.newInstance();
}
Validate the syntax of a proposed <url-pattern>
for conformance with specification requirements.
Params: - urlPattern – URL pattern to be validated
/**
* Validate the syntax of a proposed <code><url-pattern></code> 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
/**
* Check for unusual but valid <code><url-pattern></code>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.
/**
* 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
}