/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.sling.engine.impl.helper;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import javax.servlet.Filter;
import javax.servlet.FilterRegistration;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import javax.servlet.ServletRegistration.Dynamic;
import javax.servlet.SessionCookieConfig;
import javax.servlet.SessionTrackingMode;
import javax.servlet.descriptor.JspConfigDescriptor;
import org.apache.sling.engine.impl.SlingMainServlet;
import org.apache.sling.engine.impl.request.SlingRequestDispatcher;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The <code>SlingServletContext</code> class is the <code>ServletContext</code>
* which is registered as a service usable by servlets and helpers inside Sling.
* Most methods just call into the servlet context in which the
* {@link SlingMainServlet} is running.
* <dl>
* <dt><b>MIME Type Mapping</b></dt>
* <dd>Just forwards to the servlet context of the {@link SlingMainServlet} for
* MIME type mapping.</dd>
* <dt><b>Resources</b></dt>
* <dd>This class provides access to the resources in the web application by
* means of the respective resource accessor methods. These are not the same
* resources as available through the <code>ResourceResolver</code>.</dd>
* <dt><b>Request Dispatcher</b></dt>
* <dd>The {@link #getRequestDispatcher(String)} method returns a
* {@link SlingRequestDispatcher} which may dispatch a request inside sling
* without going through the servlet container. The
* {@link #getNamedDispatcher(String)} method returns a servlet container
* request dispatcher which always goes through the servlet container.</dd>
* <dt><b>Parameters and Attributes</b></dt>
* <dd>Initialization parameters and context attributes are shared with the
* servlet context in which the {@link SlingMainServlet} is running.</dd>
* <dt><b>Logging</b></dt>
* <dd>Logging is diverted to a logger whose name is the fully qualified name of
* this class.</dd>
* </dl>
* <p>
* This class implements the Servlet API 3.0 {@code ServletContext} interface.
*/
public class SlingServletContext implements ServletContext {
/** default log */
private final Logger log = LoggerFactory.getLogger(getClass());
/** The {@link SlingMainServlet} to which some calls are delegated */
private final SlingMainServlet slingMainServlet;
/**
* The service registration of this service as ServletContext
* @see #SlingServletContext(SlingMainServlet)
* @see #dispose()
*/
private final ServiceRegistration<ServletContext> registration;
/**
* Creates an instance of this class delegating some methods to the given
* {@link SlingMainServlet}. In addition the new instance is registered as
* a<code>ServletContext</code>.
* <p>
* This method must only be called <b>after</b> the sling main servlet
* has been fully initialized. Otherwise the {@link #getServletContext()}
* method may cause a {@link NullPointerException} !
* @see #dispose()
*/
public SlingServletContext(final BundleContext bundleContext,
final SlingMainServlet slingMainServlet) {
this.slingMainServlet = slingMainServlet;
Dictionary<String, Object> props = new Hashtable<String, Object>();
props.put(Constants.SERVICE_DESCRIPTION, "Apache Sling ServletContext");
props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
props.put("name", SlingMainServlet.SERVLET_CONTEXT_NAME); // property to identify this context
registration = bundleContext.registerService(
ServletContext.class, this, props);
}
/**
* Unregisters this servlet context as a service (if registered at all)
* <p>
* This method must be called <b>before</b> the sling main servlet
* is destroyed. Otherwise the {@link #getServletContext()} method may
* cause a {@link NullPointerException} !
* @see #SlingServletContext(SlingMainServlet)
*/
public void dispose() {
if (registration != null) {
registration.unregister();
}
}
// ---------- Web App configuration ----------------------------------------
/**
* Returns the name of the servlet context in which Sling is configured.
* This method calls on the <code>ServletContext</code> in which the
* {@link SlingMainServlet} is running.
*/
@Override
public String getServletContextName() {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
return delegatee.getServletContextName();
}
return null;
}
/** Returns the context path of the web application. (Servlet API 2.5) */
@Override
public String getContextPath() {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
return delegatee.getContextPath();
}
return null;
}
/**
* Returns the init-param of the servlet context in which Sling is
* configured. This method calls on the <code>ServletContext</code> in
* which the {@link SlingMainServlet} is running.
*/
@Override
public String getInitParameter(String name) {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
return delegatee.getInitParameter(name);
}
return null;
}
/**
* Returns the names of the init-params of the servlet context in which
* Sling is configured. This method calls on the <code>ServletContext</code>
* in which the {@link SlingMainServlet} is running.
*/
@Override
public Enumeration<String> getInitParameterNames() {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
return delegatee.getInitParameterNames();
}
return null;
}
// ---------- attributes ---------------------------------------------------
/**
* Returns the named servlet context attribute. This method calls on the
* <code>ServletContext</code> in which the {@link SlingMainServlet} is
* running.
*/
@Override
public Object getAttribute(String name) {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
return delegatee.getAttribute(name);
}
return null;
}
/**
* Returns the names of all servlet context attributes. This method calls on
* the <code>ServletContext</code> in which the {@link SlingMainServlet}
* is running.
*/
@Override
public Enumeration<String> getAttributeNames() {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
return delegatee.getAttributeNames();
}
return Collections.enumeration(Collections.<String>emptyList());
}
/**
* Removes the named servlet context attribute. This method calls on the
* <code>ServletContext</code> in which the {@link SlingMainServlet} is
* running.
*/
@Override
public void removeAttribute(String name) {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
delegatee.removeAttribute(name);
}
}
/**
* Sets the name servlet context attribute to the requested value. This
* method calls on the <code>ServletContext</code> in which the
* {@link SlingMainServlet} is running.
*/
@Override
public void setAttribute(String name, Object object) {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
delegatee.setAttribute(name, object);
}
}
// ---------- Servlet Container information --------------------------------
/**
* Returns the Sling server info string. This is not the same server info
* string as returned by the servlet context in which Sling is configured.
*/
@Override
public String getServerInfo() {
return slingMainServlet.getServerInfo();
}
/**
* Returns the major version number of the Servlet API supported by the
* servlet container in which Sling is running. This method calls on the
* <code>ServletContext</code> in which the {@link SlingMainServlet} is
* running.
*/
@Override
public int getMajorVersion() {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
return delegatee.getMajorVersion();
}
return 3; // hard coded major version as fall back
}
/**
* Returns the minor version number of the Servlet API supported by the
* servlet container in which Sling is running. This method calls on the
* <code>ServletContext</code> in which the {@link SlingMainServlet} is
* running.
*/
@Override
public int getMinorVersion() {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
return delegatee.getMinorVersion();
}
return 0; // hard coded minor version as fall back
}
// ---------- MIME type mapping --------------------------------------------
/**
* Returns a MIME type for the extension of the given file name. This method
* calls on the <code>ServletContext</code> in which the
* {@link SlingMainServlet} is running.
*/
@Override
public String getMimeType(String file) {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
return delegatee.getMimeType(file);
}
return null;
}
// ---------- Request Dispatcher -------------------------------------------
/**
* Returns a {@link SlingRequestDispatcher} for the given path if not
* <code>null</code>. Otherwise <code>null</code> is returned.
*/
@Override
public RequestDispatcher getRequestDispatcher(String path) {
// return no dispatcher if content is null
if (path == null) {
log.error("getRequestDispatcher: No path, cannot create request dispatcher");
return null;
}
return new SlingRequestDispatcher(path, null);
}
/**
* Returns a servlet container request dispatcher for the named servlet.
* This method calls on the <code>ServletContext</code> in which the
* {@link SlingMainServlet} is running.
*/
@Override
public RequestDispatcher getNamedDispatcher(String name) {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
return delegatee.getNamedDispatcher(name);
}
return null;
}
// ---------- Resource Access ----------------------------------------------
/**
* Returns the URI for the given path. This method calls on the
* <code>ServletContext</code> in which the {@link SlingMainServlet} is
* running.
*/
@Override
public URL getResource(String path) throws MalformedURLException {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
return delegatee.getResource(path);
}
return null;
}
/**
* Returns an input stream to the given path. This method calls on the
* <code>ServletContext</code> in which the {@link SlingMainServlet} is
* running.
*/
@Override
public InputStream getResourceAsStream(String path) {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
return delegatee.getResourceAsStream(path);
}
return null;
}
/**
* Returns a set of names for path entries considered children of the given
* path. This method calls on the <code>ServletContext</code> in which the
* {@link SlingMainServlet} is running.
*/
@Override
public Set<String> getResourcePaths(String parentPath) {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
return delegatee.getResourcePaths(parentPath);
}
return null;
}
/**
* Returns the real file inside the web application to which the given path
* maps or <code>null</code> if no such file exists. This method calls on
* the <code>ServletContext</code> in which the {@link SlingMainServlet}
* is running.
*/
@Override
public String getRealPath(String path) {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
return delegatee.getRealPath(path);
}
return null;
}
// ---------- logging ------------------------------------------------------
/** Logs the message and optional throwable at error level to the logger */
@Override
public void log(String message, Throwable throwable) {
log.error(message, throwable);
}
/** Logs the message at info level to the logger */
@Override
public void log(String message) {
log.info(message);
}
/** Logs the message and optional exception at error level to the logger */
@SuppressWarnings("deprecation")
@Override
@Deprecated
public void log(Exception exception, String message) {
log(message, exception);
}
// ---------- foreign Servlets ---------------------------------------------
/**
* Returns the servlet context from the servlet container in which sling is
* running. This method calls on the <code>ServletContext</code> in which
* the {@link SlingMainServlet} is running.
*/
@Override
public ServletContext getContext(String uripath) {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
ServletContext otherContext = delegatee.getContext(uripath);
if (otherContext != null && otherContext != delegatee) {
return wrapServletContext(otherContext);
}
}
return null;
}
/** Returns <code>null</code> as defined in Servlet API 2.4 */
@SuppressWarnings("deprecation")
@Override
@Deprecated
public Servlet getServlet(String name) {
return null;
}
/** Returns an empty enumeration as defined in Servlet API 2.4 */
@SuppressWarnings("deprecation")
@Override
@Deprecated
public Enumeration<String> getServletNames() {
return Collections.enumeration(Collections.<String>emptyList());
}
/** Returns an empty enumeration as defined in Servlet API 2.4 */
@SuppressWarnings("deprecation")
@Override
@Deprecated
public Enumeration<Servlet> getServlets() {
return Collections.enumeration(Collections.<Servlet>emptyList());
}
@Override
public int getEffectiveMajorVersion() {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
return delegatee.getEffectiveMajorVersion();
}
return 3;
}
@Override
public int getEffectiveMinorVersion() {
ServletContext delegatee = getServletContext();
if (delegatee != null) {
return delegatee.getEffectiveMinorVersion();
}
return 0;
}
@Override
public boolean setInitParameter(String name, String value) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public SessionCookieConfig getSessionCookieConfig() {
// result in NPE if context is not set anymore
return getServletContext().getSessionCookieConfig();
}
@Override
public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
// result in NPE if context is not set anymore
return getServletContext().getDefaultSessionTrackingModes();
}
@Override
public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
// result in NPE if context is not set anymore
return getServletContext().getEffectiveSessionTrackingModes();
}
@Override
public JspConfigDescriptor getJspConfigDescriptor() {
// result in NPE if context is not set anymore
return getServletContext().getJspConfigDescriptor();
}
@Override
public ClassLoader getClassLoader() {
// we don't allow access to any class loader here since we are
// running in the OSGi Framework and we don't want code to fiddle
// with class laoders obtained from the ServletContext
throw new SecurityException();
}
@Override
public void declareRoles(String... roleNames) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
// Servlet API 3.0, Section 4.4 Configuration methods
@Override
public Dynamic addServlet(String servletName, String className) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public Dynamic addServlet(String servletName, Servlet servlet) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public <T extends Servlet> T createServlet(Class<T> clazz) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public ServletRegistration getServletRegistration(String servletName) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public Map<String, ? extends ServletRegistration> getServletRegistrations() {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public javax.servlet.FilterRegistration.Dynamic addFilter(String filterName, String className) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public javax.servlet.FilterRegistration.Dynamic addFilter(String filterName, Filter filter) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public javax.servlet.FilterRegistration.Dynamic addFilter(String filterName, Class<? extends Filter> filterClass) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public <T extends Filter> T createFilter(Class<T> clazz) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public FilterRegistration getFilterRegistration(String filterName) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public void addListener(String className) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public <T extends EventListener> void addListener(T t) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public void addListener(Class<? extends EventListener> listenerClass) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
@Override
public <T extends EventListener> T createListener(Class<T> clazz) {
// only supported in ServletContextListener.contextInitialized or
// ServletContainerInitializer.onStartuo
throw new IllegalStateException();
}
// ---------- internal -----------------------------------------------------
/**
* Returns the real servlet context of the servlet container in which the
* Sling Servlet is running.
*/
protected ServletContext getServletContext() {
return slingMainServlet.getServletContext();
}
protected ServletContext wrapServletContext(ServletContext context) {
return new ExternalServletContextWrapper(context);
}
@Override
public String getVirtualServerName() {
return getServletContext().getVirtualServerName();
}
}