/* * 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); } } }