/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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.jboss.seam.resteasy;
import static org.jboss.seam.annotations.Install.BUILT_IN;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.SecurityContext;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.core.SynchronousDispatcher;
import org.jboss.resteasy.core.ThreadLocalResteasyProviderFactory;
import org.jboss.resteasy.plugins.server.servlet.HttpServletInputMessage;
import org.jboss.resteasy.plugins.server.servlet.HttpServletResponseWrapper;
import org.jboss.resteasy.plugins.server.servlet.ServletSecurityContext;
import org.jboss.resteasy.plugins.server.servlet.ServletUtil;
import org.jboss.resteasy.specimpl.UriInfoImpl;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.log.Log;
import org.jboss.seam.servlet.ContextualHttpServletRequest;
import org.jboss.seam.web.AbstractResource;
import org.jboss.seam.web.Session;
/**
* Accepts incoming HTTP requests through the <tt>SeamResourceServlet</tt> and
* dispatches the call to RESTEasy. Wraps the call in Seam contexts.
*
* @author Christian Bauer
*/
@Scope(ScopeType.APPLICATION)
@Name("org.jboss.seam.resteasy.resourceAdapter")
@BypassInterceptors
@Install(precedence = BUILT_IN)
public class ResteasyResourceAdapter extends AbstractResource
{
@Logger
Log log;
protected Dispatcher dispatcher;
protected Application application;
@Create
public void init()
{
// No injection, so lookup on first request
dispatcher = (Dispatcher) Component.getInstance("org.jboss.seam.resteasy.dispatcher");
application = (Application) Component.getInstance(Application.class);
if (dispatcher == null)
{
throw new IllegalStateException(
"ReasteasyDispatcher not available, make sure RESTEasy and all required JARs are on your classpath"
);
}
}
@Override
public String getResourcePath()
{
return application.getResourcePathPrefix();
}
@Override
public void getResource(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException, IOException
{
try
{
log.debug("processing REST request");
// TODO: As far as I can tell from tracing RE code: All this thread-local stuff has no effect because
// the "default" provider factory is always used. But we do it anyway, just to mimic the servlet handler
// in RE...
// Wrap in RESTEasy thread-local factory handling
ThreadLocalResteasyProviderFactory.push(dispatcher.getProviderFactory());
// Wrap in RESTEasy contexts (this also puts stuff in a thread-local)
SeamResteasyProviderFactory.pushContext(HttpServletRequest.class, request);
SeamResteasyProviderFactory.pushContext(HttpServletResponse.class, response);
SeamResteasyProviderFactory.pushContext(SecurityContext.class, new ServletSecurityContext(request));
// Wrap in Seam contexts
new ContextualHttpServletRequest(request)
{
@Override
public void process() throws ServletException, IOException
{
try
{
HttpHeaders headers = ServletUtil.extractHttpHeaders(request);
UriInfoImpl uriInfo = extractUriInfo(request, application.getResourcePathPrefix());
HttpResponse theResponse = new HttpServletResponseWrapper(
response,
dispatcher.getProviderFactory()
);
// TODO: This requires a SynchronousDispatcher
HttpRequest in = new HttpServletInputMessage(
request,
theResponse,
headers,
uriInfo,
request.getMethod().toUpperCase(),
(SynchronousDispatcher) dispatcher
);
dispatcher.invoke(in, theResponse);
}
finally
{
/*
* Prevent anemic sessions clog up the server
*
* session.isNew() check - do not close non-anemic sessions established by the view layer (JSF)
* which are reused by the JAX-RS requests (so that the requests do not have to be re-authorized)
*/
if (application.isDestroySessionAfterRequest() && request.getSession().isNew())
{
log.debug("Destroying HttpSession after REST request");
Session.instance().invalidate();
}
}
}
}.run();
}
finally
{
// Clean up the thread-locals
SeamResteasyProviderFactory.clearContextData();
ThreadLocalResteasyProviderFactory.pop();
log.debug("completed processing of REST request");
}
}
protected UriInfoImpl extractUriInfo(HttpServletRequest request, String pathPrefix)
{
try
{
// Append a slash if there isn't one
if (!pathPrefix.startsWith("/")) {
pathPrefix = "/" + pathPrefix;
}
// Get the full path of the current request
URL requestURL = new URL(request.getRequestURL().toString());
String requestPath = requestURL.getPath();
// Find the 'servlet mapping prefix' for RESTEasy (in our case: /seam/resource/rest)
String mappingPrefix =
requestPath.substring(0, requestPath.indexOf(pathPrefix)+pathPrefix.length());
// Still is /<context>/seam/resource/rest, so cut off the context
mappingPrefix = mappingPrefix.substring(request.getContextPath().length());
log.debug("Using request mapping prefix: " + mappingPrefix);
// This is the prefix used by RESTEasy to resolve resources and generate URIs with
return ServletUtil.extractUriInfo(request, mappingPrefix);
}
catch (MalformedURLException e)
{
throw new RuntimeException(e);
}
}
}