/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.resource.servlet;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collections;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.xwiki.component.manager.ComponentLookupException;
import org.xwiki.component.manager.ComponentManager;
import org.xwiki.component.util.DefaultParameterizedType;
import org.xwiki.container.Container;
import org.xwiki.container.servlet.ServletContainerException;
import org.xwiki.container.servlet.ServletContainerInitializer;
import org.xwiki.context.Execution;
import org.xwiki.resource.ResourceReference;
import org.xwiki.resource.ResourceReferenceHandlerException;
import org.xwiki.resource.ResourceReferenceHandlerManager;
import org.xwiki.resource.ResourceReferenceResolver;
import org.xwiki.resource.ResourceType;
import org.xwiki.url.ExtendedURL;
/**
* Handles any Resource Reference discovered by the Routing Filter and put in the HTTP Request. Any module who wish to
* add a new Resource Type in the XWiki URL simply needs to register a Handler component (of role
* {@link org.xwiki.resource.ResourceReferenceHandler}) and any URL matching the corresponding {@link ResourceType} will
* be handled.
*
* @version $Id: 3f67dbcd50fe3b40a9353a0d75ec0e5fc6501fea $
* @since 7.1M1
*/
public class ResourceReferenceHandlerServlet extends HttpServlet
{
/**
* Needed for serialization.
*/
private static final long serialVersionUID = 1L;
private ComponentManager rootComponentManager;
@Override
public void init() throws ServletException
{
super.init();
// Get the Component Manager which has been initialized first in a Servlet Context Listener.
this.rootComponentManager =
(ComponentManager) getServletContext().getAttribute(ComponentManager.class.getName());
}
@Override
protected void service(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws ServletException,
IOException
{
try {
// Before handling the Resource Reference we need to setup the Request/Response so that it's available to
// the Handlers (for writing content to the response for example!)
// Note that we don't initialize other things such as the XWiki Contexts for example since we assume that
// not all Resource Handlers require the XWiki Contexts (the WebJar Resource Handler doesn't need it for
// example). Thus it's up for the specific Resource Handlers to initialize anything else they need to work.
// We just initialize the Request/Response/Session here as we consider them to be basic needs for all
// Resource Handlers.
initializeContainerComponent(httpRequest, httpResponse);
handleResourceReference(getResourceReference(httpRequest));
} finally {
cleanupComponents();
}
}
private ResourceReference getResourceReference(HttpServletRequest httpRequest) throws ServletException
{
// Get the Resource Type from the request's attribute, where it's been put by the RoutingFilter.
ResourceType resourceType = (ResourceType) httpRequest.getAttribute(RoutingFilter.RESOURCE_TYPE_NAME);
// Get the ExtendedURL from the request's attribute too (so that we don't have to compute it again).
ExtendedURL extendedURL = (ExtendedURL) httpRequest.getAttribute(RoutingFilter.RESOURCE_EXTENDEDURL);
// Extract the Resource Reference, passing the already extracted Resource Type
ResourceReferenceResolver<ExtendedURL> urlResolver = getResourceReferenceResolver();
try {
// Note that before this code executes a valid Execution Context must be available as it's required to
// resolve the wiki referenced by the URL (since this means looking for Wiki Descriptors and do queries on
// the store.
return urlResolver.resolve(extendedURL, resourceType, Collections.<String, Object>emptyMap());
} catch (Exception e) {
// This shouldn't happen, raise an exception
throw new ServletException(String.format("Failed to extract the Resource Reference from the URL [%s]",
extendedURL.getWrappedURL()), e);
}
}
private ResourceReferenceResolver<ExtendedURL> getResourceReferenceResolver() throws ServletException
{
ResourceReferenceResolver<ExtendedURL> urlResolver;
try {
Type role = new DefaultParameterizedType(null, ResourceReferenceResolver.class, ExtendedURL.class);
urlResolver = this.rootComponentManager.getInstance(role);
} catch (ComponentLookupException e) {
// Should not happen since a URL provider should exist on the system.
throw new ServletException("Failed to locate an ExtendedURL Resource Reference Resolver component", e);
}
return urlResolver;
}
private void initializeContainerComponent(HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws ServletException
{
ServletContainerInitializer containerInitializer;
try {
containerInitializer = this.rootComponentManager.getInstance(ServletContainerInitializer.class);
} catch (Exception e) {
// This shouldn't happen, raise an exception
throw new ServletException("Failed to locate a ServletContainerInitializer component", e);
}
try {
containerInitializer.initializeRequest(httpRequest);
containerInitializer.initializeResponse(httpResponse);
containerInitializer.initializeSession(httpRequest);
} catch (ServletContainerException e) {
throw new ServletException("Failed to initialize Request/Response or Session", e);
}
}
private void handleResourceReference(ResourceReference resourceReference) throws ServletException
{
ResourceReferenceHandlerManager<?> resourceReferenceHandlerManager;
try {
Type role = new DefaultParameterizedType(null, ResourceReferenceHandlerManager.class, ResourceType.class);
resourceReferenceHandlerManager = this.rootComponentManager.getInstance(role);
} catch (ComponentLookupException e) {
// Should not happen since a Resource Reference Handler should always exist on the system.
throw new ServletException("Failed to locate a Resource Reference Handler Manager component", e);
}
try {
resourceReferenceHandlerManager.handle(resourceReference);
} catch (ResourceReferenceHandlerException e) {
throw new ServletException(String.format("Failed to handle Resource Reference [%s]", resourceReference), e);
}
}
private void cleanupComponents() throws ServletException
{
Container container;
try {
container = this.rootComponentManager.getInstance(Container.class);
} catch (ComponentLookupException e) {
throw new ServletException("Failed to locate a Container component", e);
}
Execution execution;
try {
execution = this.rootComponentManager.getInstance(Execution.class);
} catch (ComponentLookupException e) {
throw new ServletException("Failed to locate a Execution component", e);
}
// We must ensure we clean the ThreadLocal variables located in the Container and Execution components as
// otherwise we will have a potential memory leak.
container.removeRequest();
container.removeResponse();
container.removeSession();
execution.removeContext();
}
}