Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,15 @@
import static com.amazonaws.serverless.proxy.RequestReader.LAMBDA_CONTEXT_PROPERTY;

/**
* Servlet filter class that calls Jersey's ApplicationHandler. Given a Jax RS Application object, this class
* initializes a Jersey {@link ApplicationHandler} and calls its <code>handle</code> method. Requests are transformed
* to <code>ContainerRequest</code> objects by the {@link #servletRequestToContainerRequest(ServletRequest)} method.
* Calls Jersey's ApplicationHandler. Given a Jax RS Application object, this class initializes a Jersey
* {@link ApplicationHandler} and calls its <code>handle</code> method. Requests are transformed to
* <code>ContainerRequest</code> objects by the {@link #servletRequestToContainerRequest(ServletRequest)} method.
* Jersey responses are written directly to the ServletResponse by the {@link JerseyServletResponseWriter}.
*
* {@link JerseyLambdaContainerHandler} invokes this class from the servlet at the end of the filter chain, so that
* application filters run before a JAX-RS resource. It is no longer registered as a servlet filter itself. The
* <code>Filter</code> interface is still implemented because this class is public, but {@link #doFilter} is
* deprecated: Jersey writes the response, so there is nothing for a filter after it to do.
*/
public class JerseyHandlerFilter implements Filter, Container {
public static final String JERSEY_SERVLET_REQUEST_PROPERTY = "com.amazonaws.serverless.jersey.servletRequest";
Expand Down Expand Up @@ -86,9 +91,28 @@ public void init(FilterConfig filterConfig) {
}


/**
* @deprecated This class is not registered as a filter any more, and nothing in the library calls this method.
* It runs Jersey and ignores the chain, because Jersey writes the response: a filter placed after it could
* only run once the resource had already executed. Register filters on the servlet context instead, through
* {@link com.amazonaws.serverless.proxy.internal.servlet.AwsLambdaServletContainerHandler#onStartup}, and
* they will run before Jersey.
*/
@Deprecated
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
handle(servletRequest, servletResponse);
}

/**
* Runs the request through Jersey and waits for the response to be written. {@link JerseyLambdaContainerHandler}
* calls this from the servlet at the end of the filter chain, so the resource is only reached once every filter
* registered with the servlet context has passed the request on.
* @param servletRequest The incoming servlet request
* @param servletResponse The response Jersey writes to
*/
void handle(ServletRequest servletRequest, ServletResponse servletResponse) {
Timer.start("JERSEY_FILTER_DOFILTER");
// we use a latch to make the processing inside Jersey synchronous
CountDownLatch jerseyLatch = new CountDownLatch(1);
Expand All @@ -106,7 +130,6 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
throw new InternalServerErrorException(e);
}
Timer.stop("JERSEY_FILTER_DOFILTER");
filterChain.doFilter(servletRequest, servletResponse);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@
import org.glassfish.jersey.process.internal.RequestScoped;
import org.glassfish.jersey.server.ResourceConfig;

import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
import javax.servlet.ServletRegistration;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.Application;

import java.util.EnumSet;
import java.util.concurrent.CountDownLatch;


Expand Down Expand Up @@ -73,7 +73,10 @@ public class JerseyLambdaContainerHandler<RequestType, ResponseType> extends Aws
// Variables - Private
//-------------------------------------------------------------

private static final String JERSEY_SERVLET_NAME = "JerseyServlet";

private JerseyHandlerFilter jerseyFilter;
private JerseyServlet jerseyServlet;
private boolean initialized;

//-------------------------------------------------------------
Expand Down Expand Up @@ -175,6 +178,7 @@ protected void configure() {
}

this.jerseyFilter = new JerseyHandlerFilter(jaxRsApplication);
this.jerseyServlet = new JerseyServlet(jerseyFilter);
Timer.stop("JERSEY_CONTAINER_CONSTRUCTOR");
}

Expand All @@ -195,7 +199,7 @@ protected void handleRequest(HttpServletRequest httpServletRequest, AwsHttpServl
((AwsHttpServletRequest)httpServletRequest).setServletContext(getServletContext());
}

doFilter(httpServletRequest, httpServletResponse, null);
doFilter(httpServletRequest, httpServletResponse, jerseyServlet);
Timer.stop("JERSEY_HANDLE_REQUEST");
}

Expand All @@ -208,12 +212,13 @@ protected AwsHttpServletResponse getContainerResponse(HttpServletRequest request
public void initialize() {
Timer.start("JERSEY_COLD_START_INIT");

// manually add the spark filter to the chain. This should the last one and match all uris
FilterRegistration.Dynamic jerseyFilterReg = getServletContext().addFilter("JerseyFilter", jerseyFilter);
jerseyFilterReg.addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.INCLUDE, DispatcherType.FORWARD),
true, "/*"
);
// Jersey runs as the servlet at the end of the filter chain, the way DispatcherServlet does for Spring. Every
// filter registered with the servlet context, before or after this method, runs first and can stop the
// request before it reaches a resource.
ServletRegistration.Dynamic jerseyServletReg = getServletContext().addServlet(JERSEY_SERVLET_NAME, jerseyServlet);
jerseyServletReg.addMapping("/*");
// the filter chain reads the servlet name from its config, so the servlet has to be initialized up front
jerseyServlet.init(((AwsServletRegistration) jerseyServletReg).getServletConfig());

Timer.stop("JERSEY_COLD_START_INIT");
initialized = true;
Expand All @@ -227,7 +232,53 @@ public InjectionManager getInjectionManager() {
return jerseyFilter.getApplicationHandler().getInjectionManager();
}

/**
* The servlet Jersey runs as, which is the terminal step of the filter chain for every request this handler
* routes. This returned null while Jersey ran as a filter and there was no servlet to hand back.
* @return The Jersey servlet
*/
public Servlet getServlet() {
return null;
return jerseyServlet;
}

//-------------------------------------------------------------
// Inner Class -
//-------------------------------------------------------------

/**
* The servlet at the end of the filter chain. It hands the request to Jersey, which writes the response.
*/
private static final class JerseyServlet implements Servlet {
private final JerseyHandlerFilter jerseyFilter;
private ServletConfig config;

JerseyServlet(JerseyHandlerFilter jerseyFilter) {
this.jerseyFilter = jerseyFilter;
}

@Override
public void init(ServletConfig servletConfig) {
config = servletConfig;
}

@Override
public ServletConfig getServletConfig() {
return config;
}

@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) {
jerseyFilter.handle(servletRequest, servletResponse);
}

@Override
public String getServletInfo() {
return "";
}

@Override
public void destroy() {

}
}
}
Loading
Loading