We wan't to use aop methodinterceptors using guice as provider under tomcat 8.5. We are currently already using interceptors and guice in our Java-FX applications without any problems.
Trying to do the same under Tomcat does not work. Objects are injected into servlets via guice, but the annotated methods of those objects are not bound to the interceptor. It looks as if guice might think that cglib/asm are not available and revert to none-aop.
Are there any special preconditions the servlet containers needs to fullfil in order to use guice's aop? As stated above the same configuration of guice/cglib/asm works for us in none webapp projects.
#Singleton
public class TestServlet extends HttpServlet {
#Inject
X x;
public TestServlet() {
System.out.println("constructor");
try {
throw new IOException();
} catch (final Exception e) {
e.printStackTrace();
}
}
#Override
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
throws ServletException, IOException {
testMethod(resp);
}
protected void testMethod(final HttpServletResponse resp) throws IOException {
x.test(resp);
}
}
We wan't X in our servlet to contain methods that are intercepted.
By putting the throw/catch thing in the constructor we verified that the constructor is called through guice.
public class X {
#Y
public int test(final ServletResponse res) throws IOException {
res.getWriter().write("123");
return 1;
}
}
public class TestInterceptor implements MethodInterceptor {
#Override
public Object invoke(final MethodInvocation arg0) throws Throwable {
System.out.println("METHOD INTERCEPTOR " + arg0);
return arg0.proceed();
}
}
public class Module extends AbstractModule {
#Override
protected void configure() {
System.out.println("TestInterceptor configuration");
bindInterceptor(Matchers.any(), Matchers.annotatedWith(Y.class), new TestInterceptor());
}
}
#WebListener
public class BootStrap extends GuiceServletContextListener {
...
#Override
protected Injector getInjector() {
final Injector inject = Guice.injector(new Module(), new ServletModule() {
#Override
protected void configureServlets() {
super.configureServlets();
System.out.println("Injector");
serve("/test2/*").with(TestServlet.class);
}
});
return inject;
}
}
The servlet is reachable and X is none-null but looking at it in the debugger it is clear no binary code modifications were done.
Are we missing somethings here? Can anyone link an example of a working guice (4+)/tomcat (8+)/aop example?
Edit
It turned out to be unrelated to the servlet container. The trouble was with guice itself. Sorry for the confusion, this problem was very hard to fence in.
For those interested we opened an issue
https://github.com/google/guice/issues/1094
Note that at the time of writing this is not accepted as a bug. It could also be that we missinterpreted the javadoc.
I've used Guice AOP with Tomcat before (though it was an older version of Tomcat) and AOP worked. (I can't link because it was proprietary code).
One thing I notice looking at your posted code is that you are not using the GuiceFilter, which I believe is required.
As stated here you need to configure it at the top of your web.xml like so:
<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Edit based on comments:
You shouldn't have to modify/hack the classloader to use Guice interceptors in a servlet container. They should work out of the box with no additional changes.
Related
I migrate the code to springboot and our API works well. Only interceptor can't be triggerred.
I googled related solutions and modify the code to right format which still failed to trigger the interceptor.
In our project, we also have the filter which extends OncePerRequestFilter and works.
It makes me confused.
They should be no big difference.
Btw, AOP is used in the project.
It's my code.
JerseyConfig.class
#Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig(){
packages("com.xxx");
}
}
VaultAuthorizationInterceptor.class
#Component
public class VaultAuthorizationInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(VaultAuthorizationInterceptor.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("test");
return true;
}
}
VaultAuthConfig.class
#Configuration
public class VaultAuthConfig implements WebMvcConfigurer {
#Bean
public VaultAuthorizationInterceptor getVaultInterceptor() {
return new VaultAuthorizationInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getVaultInterceptor()).addPathPatterns("/**");
}
}
When you are using the spring-boot-starter-jersey, you use jersey as your web stack. That means any requests will processed by jersey. So you have to register a jersey filter or interceptor. Take a look at the jersey documantation. There is described how to use filters and interceptors. I think you want to use a filter because interceptors in the jersey stack used to manipulate the input or output stream.
I have a Spring Boot v1.4.0 application configured with Jersey for delivery of RESTful services.
I have a working app, but I'm now trying to enhance it with a multi-tenancy SCHEMA awareness strategy. I was hoping to set a TenantContext based on client auth headers using a Spring's HandlerInterceptor framework...
BUT, there seems to be an issue with the Interceptors being fired with Jersey. I can hit the APIs fine, ( i.e. curl -i -H "Accept: application/json" -X GET http://localhost:8080/api/products ), but the interceptors just won't fire. If I wire up a more basic app without Jersey for resource management, they fire fine?
Here is the current application set-up:
#SpringBootApplication
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).run(args);
}
}
Registering the Interceptor
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Autowired
HandlerInterceptor tenantInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tenantInterceptor);
}
}
The Interceptor itself
#Component
public class TenantInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
// FIXME: Put in a Logger impl
System.out.println("++++++++++++=======+++++++++ TenantInterceptor.preHandle() Checking for Tenant Routing");
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
TenantContext.clear();
// FIXME: Put in a Logger impl
System.out.println("++++++++++++=======+++++++++ TenantInterceptor.postHandle() Reset Tenant to " + TenantContext.getCurrentTenant());
}
}
The JerseyConfig
#Component
#ApplicationPath("api")
public class JerseyConfig extends ResourceConfig {
#PostConstruct
private void init() {
registerClasses(TenantsResource.class);
registerClasses(UsersResource.class);
registerClasses(ProductsResource.class);
}
}
I played around with the JerseyConfig #ApplicationPath("api") and the WebMvcConfig registry.addInterceptor(tenantInterceptor).addPathPatterns("patterns");. Tried the following one after the other, but no joy.
registry.addInterceptor(tenantInterceptor).addPathPatterns("/*");
registry.addInterceptor(tenantInterceptor).addPathPatterns("/**");
registry.addInterceptor(tenantInterceptor).addPathPatterns("/api/**");
registry.addInterceptor(tenantInterceptor).addPathPatterns("/api/*");
registry.addInterceptor(tenantInterceptor).addPathPatterns("/api/products");
registry.addInterceptor(tenantInterceptor).addPathPatterns("/api/products/");
Any help - much appreciated, or else I'll be resorting to hacking the Resource Controllers with smelly code :(.
Thanks - Derm
As mentioned by M.Deinum, HandlerInterceptor is not for Jersey, and it not some "underversal" interceptor. It is only for Spring MVC. For Jersey, you can use a ContainerRequestFilter. You would register it with you ResourceConfig.
See also:
Jersey docs for Filters and Interceptors
I'm building a web application and I'm using the Guice Servlet extension to bootstrap everything.
Following the user guide and examples I found, my web.xml has a single listener which extends GuiceServletContextListener. In my listener, I create my injector as described here.
My app has a few components which need to be initialized and destroyed when the servlet context is initialized and destroyed respectively. Some examples are a cache manager, a client which fetches data from a 3rd party API, a client to access a Cassandra store, etc.
I'm trying to find the right place to init/destroy these components. Without Guice, I would probably do that directly in my context listener, but it seems that Guice doesn't promote that.
What I have seen is the use of a servlet filter for each service. By implementing init/destroy in each filter, I can start and stop each service. However, if I have no actual filtering to do, this seems like overkill just to hook into the servlet lifecycle:
#Singleton
public final class MyServiceFilter implements Filter {
private final MyService service;
#Inject
public MyServiceFilter(MyService service) {
this.service = service;
}
#Override
public void init(FilterConfig filterConfig) {
service.start();
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
#Override
public void destroy() {
service.stop();
}
}
Are there any other options?
public class URLFilter implements Filter {
private URLFilter() {
//New instances are not permitted
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
}
}
Gives me the error:
Class org.apache.catalina.core.DefaultInstanceManager can not access a
member of class com.example.singleton.URLFilter with modifiers
"private"
How else can I prevent multiple instances of servlet filters being created?
If you configure your filter with web.xml or annotations, then you need to provide a public, no args constructor for the container to instantiate it (Section 6.2 of Servlet Spec 3.1):
The application developer creates a filter by implementing the
javax.servlet.Filter interface and providing a public constructor taking no
arguments
Section 6.2.1 also says:
Only one instance per declaration in the deployment descriptor is
instantiated per JVM of the container
If you need finer control over filter instantiation, you need to use programmatic registration of the filter (See section 4.4.2 in Servlet Specification 3.1).
I would like to create a class whose objects can be injected using the #Context annotation (or better yet a custom annotation for cases where I need to pass an argument to the annotation) into resource methods. In Jersey 1.* I would have used InjectableProvider (in my case together with AbstractHttpContextInjectable). What I'm trying to achieve is something like #Auth [1] from dropwizard (which uses Jersey 1.7).
The injection capabilities of Jersey were replaced by HK2 as far as I know and I could not find any example of what I'm describing.
Edit: See this question for further problems I have encountered while trying to follow Michal's guide.
You need to implement InjectionResolver<T> interface from HK2. Take a look at existing implementations that are present in Jersey workspace:
ContextInjectionResolver handling #Context
ParamInjectionResolver handling #PathParam, #QueryParam, ... (via it's subclasses)
AutowiredInjectResolver handling #Autowired
Once you have this, you need to extend AbstractBinder from HK2 and bind your InjectionResolver via it's #configure() method:
public class MyResolverBinder extends AbstractBinder {
#Override
protected void configure() {
bind(MyInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<MyAnnotation>>() {})
.in(Singleton.class);
}
}
... and register an instance of this binder in your application class (or via feature):
Feature:
public class MyFeature implements Feature {
#Override
public boolean configure(final FeatureContext context) {
context.register(new MyResolverBinder());
return true;
}
}
register MyFeature into Application:
public class JaxRsApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
final HashSet<Class<?>> classes = new HashSet<Class<?>>();
classes.add(MyFeature.class);
// Register other providers or resources.
return classes;
}
}
register MyResolverBinder or Feature in the ResourceConfig
new ResourceConfig()
// Register either MyFeature
.register(MyFeature.class)
// or MyResolverBinder
.register(new MyResolverBinder())
// Register other providers or resources
.packages("my.package");
Providing an implementation of InjectionResolver only helps with injection, not when resolving values for the parameters of a resource method.
At least with Jersey 2.11, you need to define a ValueFactoryProvider annotated with #Provider.
#Provider
public class MyValueFactoryProvider implements ValueFactoryProvider {
#Inject
private MyFactory factory;
#Override
public Factory<?> getValueFactory(Parameter parameter) {
if (parameter.getAnnotation(MyAnnotationParam.class) != null) {
return factory;
}
return null;
}
#Override
public PriorityType getPriority() {
return Priority.NORMAL;
}
}
If you also want to get the value injected in, e.g., members and constructor parameters, then InjectionResolver works well.