Injecting HttpServletRequest into ContainerRequestFilter causes exception
I have the following JAX-RS Filter which implements the ContainerRequestFilter:
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
@Context
HttpServletRequest request;
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// ...
}
}
Upon trying to run the Jetty server where the Servlet is deployed, I receive the following MultiException:
... Caused by: A MultiException has 4 exceptions. They are: 1. java.lang.IllegalArgumentException: interface org.glassfish.hk2.api.ProxyCtl is not visible from class loader 2. java.lang.IllegalArgumentException: While attempting to create a Proxy for javax.servlet.http.HttpServletRequest in scope org.glassfish.jersey.process.internal.RequestScoped an error occured while creating the proxy 3. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of resteasy.AuthenticationFilter errors were found 4. java.lang.IllegalStateException: Unable to perform operation: resolve on resteasy.AuthenticationFilter
As far as I have been able to tell, this used to be a bug in Jersey <2.4, which should have been fixed. However, every newer Jersey version I have tried has yielded the same result, and I cannot implement a token-based authentication method as a result (i.e. no request.login() ).
Additional details:
pom.xml entry:
<dependency>
<groupId>org.glassfish.jersey.containers<groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.23.1</version>
</dependency>
Server start up and deployment:
public class JettyServer {
public static void main(String[] args) throws Exception {
WebAppContext webapp = new WebAppContext();
webapp.setDescriptor("./src/webapp/WEB-INF/web.xml");
webapp.setWar("/tmp/resteasy.war");
webapp.setSessionHandler(new SessionHandler());
Server server = new Server(20080);
HashLoginService loginService = new HashLoginService("DefaultRealm");
loginService.setConfig("./src/webapp/default-realm.txt");
server.addBean(loginService);
server.setHandler(webapp);
ServletHolder jerseyServlet = webapp.addServlet(ServletContainer.class, "/v1/*");
jerseyServlet.setInitOrder(0);
Map<String, String> params = new HashMap<String, String>();
params.put("jersey.config.server.provider.packages", "resteasy");
jerseyServlet.setInitParameters(params);
try {
server.start();
server.join();
}
catch (Exception e) {
e.printStackTrace();
}
finally {
server.destroy();
}
}
}
Environment
JAX-RS 3.0.18.Final Jersey Servlet Container 2.23.1 Jetty 9.3.11.v20160721 JDK 8 Debian 8.5 Jessie
Affected Versions
[2.23.1]
- Issue Imported From: https://github.com/jersey/jersey/issues/3422
- Original Issue Raised By:@glassfishrobot
- Original Issue Assigned To: @mpotociar
@glassfishrobot Commented Reported by fpanovski
@glassfishrobot Commented fpanovski said: Update: I found a very similar situation outlined in a SO question and answer here: http://stackoverflow.com/questions/29974887/jersey-containerrequestfilter-does-not-get-context-servletrequest
However, delegating the injection to a Provider implementing DynamicFeature, registering the Filter to featureContext, and passing the request to the AuthenticationFilter still yields the exact same exception.
@glassfishrobot Commented @pavelbucek said: What do you mean by "JAX-RS 3.0.18.Final"? Aren't you by any chance using RESTEasy?
@glassfishrobot Commented fpanovski said: Hello Pavel,
Thanks for the response and for fixing the tags. Yes, I am in fact using RESTEasy together with Swagger (with my server resource stubs having been generated using Swagger Codegen for JAX-RS/RESTEasy). If it is of any help, here is my full pom.xml (I am using the latest non-milestone versions of every dependency, as far as I know):
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>resteasy</groupId>
<artifactId>resteasy</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<jersey.version>2.23.1</jersey.version>
<jetty.version>9.3.11.v20160721</jetty.version>
</properties>
<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>
<version>3.0.12.Final</version>
</dependency>
<!-- RESTEasy Servlet Initializer -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-servlet-initializer</artifactId>
<version>3.0.18.Final</version>
</dependency>
<!-- Jersey Servlet Container -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey.version}</version>
</dependency>
<!-- Swagger -->
<dependency>
<groupId>com.wordnik</groupId>
<artifactId>swagger-jaxrs_2.10</artifactId>
<version>1.3.13</version>
</dependency>
<!-- Swagger core -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jaxrs</artifactId>
<version>1.5.9</version>
</dependency>
<!-- Jetty Server core -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${jetty.version}</version>
</dependency>
<!-- Jetty Web app plugin-->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>${jetty.version}</version>
</dependency>
<!-- Jetty JMX -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId>
<version>${jetty.version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.21</version>
</dependency>
<!-- Google Gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.7</version>
<scope>compile</scope>
</dependency>
<!-- memcached client -->
<dependency>
<groupId>com.whalin</groupId>
<artifactId>Memcached-Java-Client</artifactId>
<version>3.0.2</version>
</dependency>
<!-- genson JSON converter -->
<dependency>
<groupId>com.owlike</groupId>
<artifactId>genson</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.0</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
<modules>
<module>?</module>
</modules>
</project>
@glassfishrobot Commented fpanovski said: Update: In case anyone is following this report and looking for a solution as well, I have found a workaround in the meantime that accomplishes the exact same thing that I wanted to do by injecting the HttpServletRequest. It works by implementing the javax.servlet.Filter interface:
public class AuthenticationFilter implements Filter {
public void init(...) {...}
public void destroy(...) {...}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
// Get request and response objects, we will need to pass them down the filter chain later on
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if(!request.getRequestURI().equals("/auth")) {
// Check Authorization header and return a 401 if invalid
String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
try {
if(authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
response.setStatus(401);
return;
}
// Get username, either from custom header or elsewhere...
validateToken(username, authToken);
request.login(username, password);
chain.doFilter(req, res);
} catch (Exception e) {
response.setStatus(401);
return;
}
}
chain.doFilter(req, res);
}
}
The filter also needs to be declared in web.xml, but there are already many examples for that. I hope this helps others in the meantime until there is a solution for the original issue.
@glassfishrobot Commented This issue was imported from java.net JIRA JERSEY-3150
@visadb Commented This is causing some trouble for me, I hope this gets fixed soon.
@mrerhuo Commented This is causing some trouble for me, I hope this gets fixed soon.
@mrerhuo Commented @glassfishrobot http://www.cnblogs.com/mrer/p/8267266.html
I am getting the same exception while trying to override Jersey libraries in WebLogic 12c. Any updates on the fix for this issue?
This sounds like a classloading issue. When HK2 creates a proxy to an interface that is to be injected, such as HttpServletRequest in this case, it uses JDK Proxy#newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h), and it adds HK2 ProxyCtl interface to the list of interfaces. For the classloader, the classloader from the interface (HttpServletRequest in this case) is being used, and Proxy calls the following:
interfaceClass = Class.forName(intf.getName(), false, loader);
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
but if the injected interface (HttpServletRequest) is loaded by a different classloader then ProxyCtl class, the Class.forName uses the classloader from the interface, a new ProxyCtl class is loaded and the IAE is being thrown.
This can happen for instance when the WAR application contains Jersey, HK2, (perhaps the servlet api, too) in the WEB-INF/lib, all the app is loaded by an app classloader, but javax.servlet.http.HttpServletRequest is not defined to be used by the same classloader. While it looks like an issue with ProxyCtl, it actually is the HttpServletRequest that uses a different classloader than the rest of the application.
@jansupol Thanks for clarifying. I have the exact same situation as you describe with jersey/hk2 in the webapp. I ran into the issue after upgrading from jersey 2.14 to 2.29.1 Is there any fix for this issue?
Anybody managed to resolve this issue?