CDI Interceptors and Jersey JAX/RS - jersey

Using CDI API and Weld 3.0.x as the impl.
I have a simple CDI managed bean/#Named that has a method marked to be intercepted. The interceptor is a simple logging like interceptor. This all just works fine and as expected in a 'container' (like a jboss or wildfly); it also works in a Java SE program (booting CDI via SeContainerInitializer, etc).
Now using the SAME exact managed bean and interceptor, BUT the injection point of the managed bean is in a very simple JAX/RS controller (Jersey Runtime 2.27 with jersey-cdi2-se); the interceptor doesn't fire.
In this environment, the injection resolves fine, as do most other simple cdi things, like producers and post constructs, observes, etc. The rest end-point method can invoke the injectable's methods, but the interceptor will not fire.
There is no servlet or EE container in play; just Jersey and CDI/Weld -- so it is much like JavaSE case - nothing is booting a jetty/grizzly or other simple bootable HTTP container thing.
A bit more backstory:
Basically, I have a vanilla CDI BDU/JAR with some #Nameds in it; some of which have some interceptor annotations. The DBU doesn't contain an impl for the Interceptor annotations. I want to be able include the BDU in few different projects while allowing the host project to provide a specific implementation of interceptors (or omit it) that makes the most sense for the project.
This strategy works OK for host projects that target an EE container or JavaSE. Now I have this odd JavaSE/JAX-RS/Jersey mashup; everything but the interceptor works. It is like there is some interference in the jersey-cdi2-se stuff (or somewhere); or maybe some 'switch' that needs to be thrown. Weld bootstrap logging suggests that interceptor is in play.
Is there something special or other trick to getting CDI interceptors to work along side JAX-RS/Jersey?
This odd SE/jax-rs mashup is really just using AWS servless java container -- so the "http container" is provided by AWS API Gateway and proxied lambda.
(NOTE: I am aware it maybe questionable to use some/all/any of the above techs in a FAAS/Lambda environment. I will tackle the 'should I do this' just after I finish answering the 'can I do this'.)
EDIT & Update:
Is the interceptor enabled? Yes as far as I can tell, it is. I've scraped the following bits out of log (after turning on a bit of WELD logging):
[main] DEBUG org.jboss.weld.Bootstrap - WELD-000105: Enabled interceptor types for Weld BeanManager for /var/task [bean count=5]:
- class org.jboss.weld.contexts.activator.ActivateRequestContextInterceptor,
- class org.jboss.weld.contexts.activator.CdiRequestContextActivatorInterceptor,
- class org.jboss.weld.environment.se.contexts.activators.ActivateThreadScopeInterceptor,
- class com.pin.faas.MyTransactionalInterceptor
.....
[main] DEBUG org.jboss.weld.Bootstrap - WELD-000107: Interceptor: Interceptor [class com.pin.faas.MyTransactionalInterceptor intercepts #MyTransactional
As for some bits of code:
The interceptor binding:
package com.pin.api.annotation;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import javax.interceptor.InterceptorBinding;
#Inherited
#InterceptorBinding
#Target({ TYPE, METHOD })
#Retention(RUNTIME)
public #interface MyTransactional
{
}
The interceptor impl:
package com.pin.faas;
import java.util.logging.Logger;
import javax.interceptor.*;
import com.pin.api.annotation.MyTransactional;
#Interceptor
#MyTransactional
public class MyTransactionalInterceptor
{
private static final Logger LOGGER = Logger.getLogger("DateServices");
public MyTransactionalInterceptor()
{
LOGGER.info("TransactionalInterceptor constructed");
}
#AroundInvoke
public Object handleTransactionBoudary(InvocationContext context)
throws Exception
{
LOGGER.info("handleTransactionBoudary called!");
return context.proceed();
}
}
The CDI managed bean that has an interceptor marker:
package com.pin.services.impl.businesslogic;
import com.pin.api.DatesService;
import com.pin.api.businesslogic.validations.BadCodeException;
import javax.inject.Inject;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
import com.pin.api.annotation.MyTransactional;
#Named
#RequestScoped
public class DatesServiceImpl implements DatesService
{
#Inject
private SomeDao dao;
#Override
#MyTransactional
public String insertRecordIntoXferLog(Integer exceptionCode)
throws BadCodeException
{
dao.insertABrnch();
if(exceptionCode !=null && -1 == exceptionCode)
{
throw new BadCodeException("exception trigger value happenend, tossing an exception and expecting a rollback");
}
return("inserted");
}
}
Finally the JAX-RS controller injects a service:
package com.pin.api.rest;
import javax.enterprise.context.RequestScoped;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.logging.Logger;
import javax.inject.Inject;
import com.pin.api.DatesService;
import com.pin.api.businesslogic.validations.BadCodeException;
import java.util.logging.Level;
#Path("/tx")
#RequestScoped
public class TxTestController
{
private static final Logger LOGGER = Logger.getLogger("DateServices");
#Inject
private DatesService service;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response testAtTransactional(#QueryParam("c") Integer code)
{
Integer val = 1;
LOGGER.info("testAtTransactional invoked");
if(null == code)
{
LOGGER.info("testAtTransactional DID NOT get a code value, assuming value = " + val);
}
else
{
val = code;
LOGGER.info("testAtTransactional DID get a code value = " + val);
}
String result = "called service.insertRecordIntoXferLog(" + val + ")";
try
{
service.insertRecordIntoXferLog(val);
}
catch(BadCodeException bce)
{
result = bce.getMessage();
}
catch(Exception other)
{
result = other.getMessage();
LOGGER.log(Level.SEVERE,"ERROR",other);
}
return Response.status(200).entity(result).build();
}
}
UPDATES
As per some comments I added an #Priority annotation. That didn't change anything. I also adjusted the packaging in a variety of ways in case there was some issue with BDU/Jar loading and CDI bean initialization; these packaging changes made no difference.
I think there is interference with JAX-RS CDI2-SE and interceptors. Looking at how jax-rs-cdi2-se boots a CDI container, it does NOT do anything for interceptors. Maybe it shouldn't need too, as the interceptor is in beans.xml. However in a standalone SE CDI2 sample (no jax-rs - just a console app), I found I had to explicitly enable the interceptor via initializer.enableInterceptors() before the interceptor would work. Explicitly enabling interceptors was needed even with the beans.xml interceptor activation. Maybe this is more of a JAX-RS issue or a Weld issue?

Related

Dependency injection does not work in RestClientBuilderListener

I followed the rest client guide in Quarkus web site. It works fine. But when registering a global provider using the ServiceLoader pattern, as described in the specification, the CDI beans injection did not work, they are all null. I downloaded the example and simply added the following classes:
package org.acme.rest.client;
import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;
import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.core.Response;
#ApplicationScoped
public class MyExceptionMapper implements ResponseExceptionMapper<Exception> {
#Override
public Exception toThrowable (Response response) {
return new Exception();
}
}
package org.acme.rest.client;
import org.eclipse.microprofile.rest.client.RestClientBuilder;
import org.eclipse.microprofile.rest.client.spi.RestClientBuilderListener;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
#ApplicationScoped
public class MyListener implements RestClientBuilderListener {
#Inject MyExceptionMapper myExceptionMapper;
#Override
public void onNewBuilder (RestClientBuilder builder) {
builder.register(myExceptionMapper);
}
}
I also added the file META-INF/services/org.eclipse.microprofile.rest.client.spi.RestClientBuilderListener with the content org.acme.rest.client.MyListener. The MyListener onNewBuilder method is invoked, but the injected provider MyExceptionMapper is null. How to register a global provider in Quarkus client?
Implementation of RestClientBuilderListener are not CDI beans - they are just objects that are created via the normal Java ServiceLoader mechanism when RestClientBuilder is being used.
So if you want to obtain CDI beans when onNewBuilder is called, you can do something like:
CDI.current().select(MyExceptionMapper.class).get()
Furthermore, you need to annotate MyExceptionMapper with #Provider, not #ApplicationScoped.

Element Cannot be Resolved as Variable error in a custom annotation for method interception

New to Spring and AOP programming. Working on a spring AOP tutorial to write aspects that intercept method calls. Would like to enable time logging.
As instructed by the tutorial I created a custom annotation for logging and an aspect to define what should be done when this annotation is called.
The code below is the TrackTime annotation:
package com.in28minutes.springboot.tutorial.basics.example.aop;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface TrackTime {}
However Eclipse is displaying the errors –
“Element Cannot be Resolved as Variable/Retention Cannot be resolved to a variable”
I then created an aspect called MethodExecutionCalculationAspect with the ‘TrackTime’ annotation.
#Around("#annotation(com.in28minutes.springboot.tutorial.
basics.example.aop.TrackTime)")
MethodExecutionCalculationAspect
package com.in28minutes.springboot.tutorial.basics.example.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
#Aspect
#Configuration
public class MethodExecutionCalculationAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
#Around("#annotation
(com.in28minutes.springboot.tutorial.basics.example.aop.TrackTime)")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
joinPoint.proceed();
long timeTaken = System.currentTimeMillis() - startTime;
logger.info("Time Taken by {} is {}", joinPoint, timeTaken);
}
}
#Around uses an around advice. It intercepts the method call and uses joinPoint.proceed() to execute the method.
#annotation(com.in28minutes.springboot.tutorial.basics.example.aop.TrackTime) is the pointcut to define interception based on an annotation — #annotation
followed by the complete type name of the annotation.
Once I correct the annotation and the advice, I’m hoping to use the annotation on methods for time tracking. as shown below:
#Service
public class Business1 {
#TrackTime
public String calculateSomething(){
Any help would be appreciated.
Information about the project is as follows:
SpringBootTutorialBasicsAplication.java:
The Spring Boot application class generated with Spring Initializer. This class acts as the launching point for the application.
• pom.xml: Contains all the dependencies needed to build this project using Spring Boot Starter AOP.
• Business1.java, Business2.java, Dao1.java, Dao2.java: Business classes are dependent on DAO classes.
• We would write aspects to intercept calls to these business and DAO classes.
• AfterAopAspect.java: Implements a few After advices.
• UserAccessAspect.java: Implements a Before advice to do an access check.
• BusinessAopSpringBootTest.java: The unit test that invokes the business methods.
• Maven 3.0+ is your build tool
• Eclipse.
• JDK 1.8+
Your TrackTime is missing imports for RetentionPolicy and Target.
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

Jersey and EJB stateless example, EJB instance is null

I am making a simple RESTful service using JAVAX RS and EJB in order to create a singleton object as a global variable.
I initiated the service as follows:
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
final ResourceConfig rc = new ResourceConfig().packages("service_folder");
GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
I am currently just testing the #Stateless example.
Firstly, I create a class in a random folder, and the content as follows:
import javax.ejb.Stateless;
import java.util.Date;
#Stateless
public class Service {
public Date getCurrentDate(){
return new Date();
}
}
Then I create the resource file in the service_folder mentioned in the very beginning:
#Stateless
#Path("current")
public class ServiceFacade {
#EJB
Service service;
#GET
public String getDate(){
return service.getCurrentDate().toString();
}
}
The current situation is that whenever I access BASE_URI/current, grizzly simply throw an error, and the reason is because service in getDate() is null.
My guess is that during the grizzly init., the Service class bean isn't really registered yet.
Please let me know where did I do wrong, thanks!

Multiple ApplicationRunners on classpath, how to make SpringApplication.run() only run one

Context: I have a project with some utilities to do things like data fixing. Each utility is a Java application, i.e. class with main() method. I want to define them as Spring Boot applications so I can use the ApplicationRunner and ApplicationArguments facility. The Spring configuration is defined via annotations in a shared configuration class. I've put a minimal example of this setup below.
Expectation: if I call SpringApplication.run(SomeClass.class, args) where SomeClass is an ApplicationRunner, it runs the run() on that class and not on any other classes that may be in the app context.
What actually happens: it calls all ApplicationRunners that it has in the context.
Why? I understood SpringApplication.run(Class, String[]) to mean, "run this class" whereas it appears to mean "load an app context from this class and run anything you can find in it". How should I fix it to run only 1 class? I don't mind if my other application class isn't in the app context, because all the configuration I need is in the shared config class. But I don't want to have to edit code (e.g. add or remove annotations) according to which class I need to run.
Minimal example:
A Spring config class (shared):
package com.stackoverflow.example;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class ExampleSpringConfig {
/** Some bean - just here to check that beans from this config are injected */
#Bean public FooService fooService () {
return new FooService();
}
}
Two application classes
package com.stackoverflow.example;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import javax.annotation.Resource;
#SpringBootApplication
public class SomethingJob implements ApplicationRunner {
#Resource private FooService fooService;
public void run(ApplicationArguments args) throws Exception {
System.out.println("Doing something"); // do things with FooService here
}
public static void main(String[] args) {
SpringApplication.run(SomethingJob.class, args);
}
}
and another that is identical except that it prints "Doing something else".
Output:
[Spring Boot startup logs...]
Doing something else
Doing something
[Spring Boot shutdown logs...]
Firstly, only one class should be annotated with #SpringBootApplication. As you've noticed in your answer, this defines the external "main" entry point. I would recommend this is a different class to your ApplicationRunner classes for clarity and conceptual separation.
To only have some but not all runners run, I've done this by parsing the arguments, and quickly exiting from the runner which should not be called. e.g.
package com.stackoverflow.example;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import javax.annotation.Resource;
#Component
public class SomethingJob implements ApplicationRunner {
#Resource private FooService fooService;
public void run(ApplicationArguments args) throws Exception {
if (!args.containsOption("something")) return
System.out.println("Doing something"); // do things with FooService here
}
}
That way you can do java -jar myjar.jar --something or java -jar myjar.jar --something-else depending which one you want to be run.
I found a workaround while experimenting with my minimal example.
#SpringBootApplication is just an alias for #ComponentScan, #EnableAutoConfiguration and #Configuration. By applying them separately, I discovered that it's the #Configuration annotation that causes this behaviour. If I only apply the other 2, I don't get the issue.
I guess this is because #Configuration means "I'm a configuration class, and any beans I define should be pulled into the context during component scan" and although this class doesn't define an ApplicationRunner, it is one, which has the same effect. Therefore if you have 2 such beans on the classpath, they both get pulled into the app context.
Without #Configuration, the bean you want to run still gets registered since it's referenced by the call to run(), but other ApplicationRunners on the classpath don't.
This fixes my immediate problem by making sure I only have one ApplicationRunner in my app context. But it doesn't answer the wider question, "If I do have several ApplicationRunners, how do I tell Spring Boot which one to run?" So I'd still appreciate any more complete answer or suggestions for a different approach.

Jersey;: Registering Injected ExceptionMapper Instances via SpringServlet

Jersey: 1.12
Spring: 3.11
JDK: 1.6.0_35
Tomcat: 6..0.33
I am trying to register ExceptionMapper instances for a Jersey servlet that are instantiated by a Spring IoC container in the context of a SpringServlet, and am running into ye olde "The scope of the component class X must be a singleton" exception during the Jersey servlet initialization. I'm not in a position to post the actual code at this time, so I'm going to give the essentials of the implementation to see if something obvious jumps out at someone, or if I can be pointed at some documentation that I have missed.
The ExceptionMapper instance is essentially as follows:
// ... package definitions omitted ...
#Provider
#Singleton
public class MyExceptionMapper
implements ExceptionMapper<MyException>
{
// ... injected attributes omitted ...
#Override
public Response toResponse(MyException exception)
{
// ... specific handling logic omitted ...
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}
The bean definition in applicationContext.xml is as follows, which defaults to singleton scope (and adding an explicit scope specifier doesn't seem to change the behavior):
<bean id="myExceptionMapper" class="my.package.MyExceptionMapper" />
The Spring context loader listener and SpringServlet configuration in web.xml are essentially boilerplate, and the servlet will load, initialize and operate properly with other inject attributes when the bean definition for MyExceptionMapper is commented out of the applicationContext.xml. But when the bean definition is present in applicationContext.xml, I get log messages to the effect of:
SpringProviderFactory - Registering Spring bean, myExpectionMapper, of type my.package.MyExceptionMapper as a provider class
SpringProviderFactory - Registering Spring bean, myServiceController, of type my.package.MyServiceController as a root resource class
... other root resource classes loading ...
SpringServlet - Exception occurred when initialization
java.lang.RuntimeException: The scope of the component class my.package.MyExceptionMapper must be a singleton
at som.sun.jersey.core.spi.component.ioc.IoCProviderFactory.wrap(...)
I have tried placing MyExceptionMapper in the scanned packages hierarchy to be picked up by the SpringServlet during initialization, and in a separate package hierarchy, and the results do not change.
Any assistance or guidance on this would be greatly appreciated.
My ExceptionMappers are under the structure where I'm doing component scan, and mine are simply annotated with #Component for Spring registry and #Provider for Jersey.
My component scan just looks like:
<context:component-scan base-package="com.company.web"/>
and the ExceptionMapper looks pretty much like yours:
package com.mycompany.web.provider;
import com.mycompany.exception.SpecialException;
import com.mycompany.exception.Error;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
#Provider
#Component
public class SpecialExceptionMapper implements ExceptionMapper<SpecialException> {
#Autowired
private MessageSource messageSource;
#Override
public Response toResponse(SpecialException exception) {
Error error = new Error();
error.errorMessage = messageSource.getMessage(exception.getResourceBundleKey(), exception.getArgs());
return Response.status(Response.Status.BAD_REQUEST).
entity(error).
type(MediaType.APPLICATION_JSON).
build();
}
}
Adding the scope explicitly in the spring config did the trick for me:
<bean id="exceptionMapper" class="my.pkg.MyExceptionMapper" scope="singleton" />
Stepped on same problem with Spring 3.2.3 and Jersey 2.3.1 integration.
What worked for me was combination of two:
Annotating jersey #Provider class with #Service or making it explicitly singleton in app-context xml
#Service //implies a singleton-scope
#Provider
public class UnhandledExceptionMapper implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable exception) { //...
Adding package that contains provider class to jersey packages, I prefer to do this in MyApplication class:
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("com.mycompany.api.controller", "com.mycompany.api.jersey");
//....
}
}
I had the same issue, but with a MessageReaderWriterProvider class:
package my.provider.package;
...
#Provider
#Consumes({MediaType.APPLICATION_JSON, "text/json"})
#Produces({MediaType.APPLICATION_JSON, "text/json"})
public class ReRwProvider extends AbstractMessageReaderWriterProvider<Object> {
...
}
The solution was to add the package of this class in an init-param tag for Jersey's SpringServlet in web.xml:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>my.provider.package</param-value>
</init-param>
Note that you should not define this bean in your Spring context.
Looks to me that the ExceptionMapper has to be configured as a "singleton" with Spring. If you want to use the annotation configuration you have to pay attentions because jersey has a "#Singleton" annotation too (with a different meaning of the javax.inject.Singleton interpreted by spring)
To avoid any confusion I prefer use the spring #Service annotation, that implies a singleton scope... so:
import org.springframework.stereotype.Service;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
#Service //implies a singleton-scope
#Provider
public class UnhandledExceptionMapper implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable exception) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(new ErrorBean(false, exception.getMessage(), null))
.build();
}
}
I also found sometime coustom ExceptionMapper not work, and it's not always work or not work.
so i debug the jersey's source code .
class: org.glassfish.jersey.server.ServerRuntime, methed:mapException
...
final long timestamp = tracingLogger.timestamp(ServerTraceEvent.EXCEPTION_MAPPING);
**ExceptionMapper mapper = runtime.exceptionMappers.findMapping(throwable);**
if (mapper != null) {
request.getRequestEventBuilder().setExceptionMapper(mapper);
...
if the mapper is null, the coustom ExceptionMapper will be not work.
class: org.glassfish.jersey.internal.ExceptionMapperFactory methed: ExceptionMapperFactory
the Exception Mapper :(there two Exception mapping one same Exception: java.lang.Exception)
org.glassfish.jersey.server.mvc.internal.ErrorTemplateExceptionMapper#6473fc2,class java.lang.Exception
...
com.baidu.ssp.web.ws.exception.BaseExceptionMapper#7a84639c,class java.lang.Exception
it's because in MvcFeature:
#Override
public boolean configure(final FeatureContext context) {
final Configuration config = context.getConfiguration();
if (!config.isRegistered(ErrorTemplateExceptionMapper.class)) {
context.register(ErrorTemplateExceptionMapper.class);
context.register(new MvcBinder());
return true;
}
return false;
}
the ErrorTemplateExceptionMapper is also add to ExceptionMapper.
so i change my custom MapperException's genericity type : ExceptionMapper to ExceptionMapper
may be my resolution is not fit for you , the main problem is the ExceptionMapper

Resources