CDI + EJB 3 + EJB Transaction - ejb

I need to audit invocations of ejb beans. Saying audit I mean write informations such as current logged user, method name, additional description to a database. I decided to do it by use of CDI decorator:
#Decorator
public class AccountServiceBeanDecorator implements AccountService {
#Inject
#Delegate
#Any
AccountService accountService;
#EJB
private AuditService auditService;
#Override
public Account createAccount(Account account) {
auditService.saveAudit("Method: createAccount", currentUser, "Creating account by admin");
return accountService.createAccount(account);
}
}
and the decorated class:
#Stateless
public class AccountServiceBean implements AccountService {
#Override
public Account createAccount(Account account) {
...
}
}
Now if I call AccountService from another ejb stateless bean, what will happen with transaction?:
#Stateless
public ApplicationFacadeBean implements ApplicationFacade {
#EJB
private AccountService accountService;
#Override
public Account createAccount(Account account) {
return accountService.createAccount(account);
}
}
I wanted to log transaction status in decorator (AccountServiceBeanDecorator) and decorated class (AccountServiceBean), so I injected TransactionSynchronizationRegistry as a resource in both classes:
#Decorator
public class AccountServiceBeanDecorator implements AccountService {
#Inject
#Delegate
#Any
AccountService accountService;
#EJB
private AuditService auditService;
#Resource
private TransactionSynchronizationRegistry reg;
#Override
public Account createAccount(Account account) {
log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
log.info("tx ({}): {}", new Object[] {reg.getTransactionStatus(), reg.getTransactionKey()});
log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
auditService.saveAudit("Method: createAccount", currentUser, "Creating account by admin");
return accountService.createAccount(account);
}
}
and
#Stateless
public class AccountServiceBean implements AccountService {
#Resource
private TransactionSynchronizationRegistry reg;
#Override
public Account createAccount(Account account) {
log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
log.info("tx ({}): {}", new Object[] {reg.getTransactionStatus(), reg.getTransactionKey()});
log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
...
}
}
I received strange behavior:
log from decorator
tx (0): JavaEETransactionImpl: txId=6 nonXAResource=null jtsTx=null localTxStatus=0 syncs=[com.sun.ejb.containers.ContainerSynchronization#68fb15d0]]]
NullPointerException on second log (reg is null).
Can anybody explain it to me? Wheter AccountServiceBean class is called within the same transaction as ApplicationFacade?
Thank you

first: i would not mixing ejbs with cdi interceptors. ejbs has it on interceptor implementations.
second: interceptors are executed in the same transaction as the ejb where the interceptor is around.
possible solution:
create a correct ejb interceptor
put the interceptor around the method / class
create a second ejb (MyLoggerBean) with a method like this logToDatabase(String message) and annotate this method with #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
inside the interceptor create a class member like this: #EJB private MyLoggerBean loggerBean
inside your #AroundInvoke annotated method you could call loggerBean. logToDatabase(...)
this would create a new transaction from inside the current transaction of the ejb where the interceptor is around
--> i know my english is not very good. but i hope that you understand what i think should work. if i have the time, i make e example on github...

Hmm... what container are you using? Generally I wouldn't suspect a CDI decorator to work on an EJB... I can't think of anything in the JEE spec that I've encountered that would give evidence either way.
Faced with your problem though, I did this with an interceptor, not a decorator. These are supported by the EJB spec... Anyway, here's my code, you would need to grab the variables from the context in your case:
import java.lang.reflect.Method;
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
public class InvocationCountInterceptor {
#Inject
private InvocationCounter counter;
#AroundInvoke
public Object intercept(InvocationContext ctx) throws Exception {
Object returnValue = ctx.proceed();
Class<? extends Object> className = ctx.getTarget().getClass();
Method methodName = ctx.getMethod();
counter.incrementCounter(className, methodName);
return returnValue;
}
}
Then whatever EJB or EJB Method you want to audit, I just added this: #Interceptors(InvocationCountInterceptor.class)

Related

Why do I get an "Ambiguous dependencies for interface" Exception when I'm already uses the #Produces annotation?

I'm using two Messaging Oriented Middleware in my project. RabbitMQ and Apache Kafka. I have an consumer interface IConsume which are implemented by ConsumerRabbitMQ and ConsumerKafka. At startup going through some conditions I use the #Produces annotation to choose an implementation for the Interface Bean that I will inject, but it gives me this error.
Exception 1:
org.jboss.weld.exceptions.DeploymentException: WELD-001409: Ambiguous dependencies for type IConsume with qualifiers #Default
at injection point [BackedAnnotatedField] #Inject private com.mycompany.chatapp.startup.RunConsumers.ct
at com.mycompany.chatapp.startup.RunConsumers.ct(RunConsumers.java:0)
Possible dependencies:
- Session bean [class com.mycompany.chatapp.messagegateway.ConsumerRabbitMQ with qualifiers [#Any #Default]; local interfaces are [IConsume],
- Producer Method [IConsume] with qualifiers [#Any #Default] declared as [[BackedAnnotatedMethod] #Produces public com.mycompany.chatapp.startup.MOMConfigBean.produceIConsume()],
- Session bean [class com.mycompany.chatapp.messagegateway.ConsumerKafka with qualifiers [#Any #Default]; local interfaces are [IConsume]
#Default and #Alternative works, but I want it to choose by checking which of the Middleware is running.
The lookup works, I also tried beanName. I think the problem is with the #Produces, but I can't find to seem what.
import javax.enterprise.inject.Produces;
#Singleton
#Startup
public class MOMConfigBean {
private String mom;
#PostConstruct
public void init() {
mom = "Kafka";
}
#EJB(lookup = "java:global/Chatapp/ConsumerKafka!com.mycompany.chatapp.messagegateway.IConsume")
IConsume kafkaConsumer;
#EJB(lookup = "java:global/Chatapp/ConsumerRabbitMQ!com.mycompany.chatapp.messagegateway.IConsume")
IConsume rabbitConsumer;
#Produces
public IConsume produceIConsume() {
if ("Kafka".equals(mom)) {
return kafkaConsumer;
} else {
return rabbitConsumer;
}
}
public interface IConsume {
// some code
}
#Stateless
public class ConsumerKafka implements IConsume{
// some code
}
#Stateless
public class ConsumerRabbitMQ implements IConsume {
// some code
}
public class runConsumers{
#Inject
private IConsume ct;
}
You have three ambiguous sources of IConsume instances:
a ConsumerKafka EJB
a ConsumerRabbitMQ EJB
an #Produces public IConsume produceIConsume() method.
You need to disambiguate the source of the IConsume instances using a qualifier.
This qualifier would look something like:
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD})
public #interface ConditionalMom {
}
Then qualify the producer:
#Produces
#ConditionalMom
public IConsume produceIConsume() {
if ("Kafka".equals(mom)) {
return kafkaConsumer;
} else {
return rabbitConsumer;
}
}
and the injection site:
public class runConsumers{
#Inject
#ConditionalMom
private IConsume ct;
}
Now you have a single source of #ConditionalMom IConsume instances so it is no longer ambiguous.
You will find that you will be using qualifiers all over the place as you start to further exploit CDI features.

How do I register a HandlerInterceptor with constructor dependencies in Spring Boot

My use case is running custom code before a controller method by annotating methods.
HandlerInterceptor seems the way to go but it seems impossible to inject dependencies into it because it needs to be registered before the context is being created.
All examples I've found so far use empty constructors (see spring boot adding http request interceptors) or autowire properties in the configuration which fails because I declare dependent beans in the same configuration (Requested bean is currently in creation: Is there an unresolvable circular reference?).
Is there a better way that does not involve AOP?
Assume that your interceptor has constructor dependencies like that:
public class CustomInterceptor extends HandlerInterceptor {
private final DependentBean bean;
public CustomInterceptor(DependentBean bean) {
this.bean = bean;
}
}
Then you can register your handler like that:
#Configuration
public WebConfig extends WebMvcConfigurerAdapater {
#Bean
public DependentBean dependentBean() {
return new DependentBean();
}
#Bean
public CustomInterceptor customInterceptor() {
return new CustomInterceptor(dependentBean());
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customInterceptor());
}
}
#Configuration will ensure each Bean method call return the same bean instance
Building on the answer above from Mạnh, if using component scan for dependency injection of the dependency, then that can be Autowired in the WebConfig
#Configuration
public WebConfig extends WebMvcConfigurerAdapater {
#Autowired
DependentBean dependentBean;
#Bean
public CustomInterceptor customInterceptor() {
return new CustomInterceptor(dependentBean);
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customInterceptor());
}
}
Also building on previous answers, and if you use Lombok, you can further simplify.
Have your interceptor implementation been a #Component
Add a private final DependentBean field to it.
Also add a #RequiredArgsConstructor annotation to it, to have Lombok generating a constructor with a single DependentBean parameter.
In your WebConfig, use the same technic to have a private final CustomInterceptor field been injected by Spring IOC.
This way the CustomInterceptor instance will be available & initialized the right way when addInterceptors will be called
Here are the corresponding code samples :
The CustomInterceptor :
#Component
#RequiredArgsConstructor
public class CustomInterceptor implements HandlerInterceptor {
private final DependentBean dependentBean;
#Override
public boolean preHandle( final HttpServletRequest request,
final HttpServletResponse response,
final Object handler ) throws Exception {
// your Interceptor Implementation goes here ...
}
}
The WebConfig :
#Configuration
#RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
private final CustomInterceptor customInterceptor;
#Override
public void addInterceptors( final InterceptorRegistry registry ) {
registry.addInterceptor( customInterceptor );
}
}

Can two stateless EJB's share the same request scoped entity manager?

I've decided to use a request scoped container managed EntityManager and I've created a producer for that reason:
#RequestScoped
public class EntityManagerProducer {
#PersistenceContext(unitName = "PU", type = PersistenceContextType.TRANSACTION)
private EntityManager entityManager;
#Produces
public EntityManager getEntityManager() {
return entityManager;
}
}
I've got two EJB's that expose a remote client view:
#Remote
public interface EJB1 {
void createPerson(int id, String firstName, String lastName);
}
#Remote
public interface EJB2 {
void containsEntity(Person person);
}
#Stateless
public class EJB1Impl implements EJB1 {
#Inject
private EntityManager entityManager;
#EJB
private EJB2 ejb2;
#Override
public void createPerson(final int id, final String firstName, final String lastName) {
Person person = new Person();
person.setId(id);
person.setFirstName(firstName);
person.setLastName(lastName);
entityManager.persist(person);
System.out.println("EJB1Impl: persistence context contains entity: " + entityManager.contains(person));
ejb2.containsEntity(person);
}
}
#Stateless
public class EJB2Impl implements EJB2 {
#Inject
private EntityManager entityManager;
#Override
public void containsEntity(final Person person) {
System.out.println("EJB2Impl: PersistenceContext contains entity: " + entityManager.contains(entity));
person.setLastName("new name");
//entityManager.merge(person);
}
}
The EJB's are deployed on WildFly 10. I access them by a remote client using this tutorial. The current version throws this exception: org.jboss.weld.context.ContextNotActiveException: WELD-001303 No active contexts for scope type javax.enterprise.context.RequestScoped. If I remove the #RequestScoped annotation from the producer, I don't get an exception but the EntityManager injected into the second EJB returns false when asked if it contains the entity and if I want the changes made to the entity (the change of the last name) to have any effect, I have to call entityManager.merge(person), which apparently means that the entity is detached. I'm sure that the second EJB executes withing the same transaction because if I inject an EJBContext and call setRollbackOnly() the transaction started in EJB1 is rolled back and a new person is not inserted into the data base.
The documentation of javax.enterprise.context.RequestScoped says that the request scope is active during any remote method invocation of any EJB, so what gives? How can I share an EntityManager during a request across multiple EJB's

The CDI Stereotype is not worked with EJB Session Bean

I've a CDI Sterotypes which contains some of InterceptorBinding as the following: -
#Inherited
#InterceptorBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({
ElementType.METHOD,
ElementType.TYPE})
#Documented
public #interface MyInterceptable {
}
#Interceptor
#MyInterceptable
public class MyInterceptor {
#AroundInvoke
public Object perform(InvocationContext context) throws Exception {
log.info("My Interceptor begin.");
Object result =context.proceed();
log.info("My Interceptor end.");
return result;
}
}
#Stereotype
#MyInterceptable
#Retention(RetentionPolicy.RUNTIME)
#Target({
ElementType.TYPE
})
#Documented
public #interface MyStereoable {
}
When I define this Sterotype at the non-EJB it is worked correctly. The message is printed before and after the doing1() execution.
#Singleton
#MyStereoable
public class MyCustomized {
public void doning1(){
//print something.
}
}
Anyhow, when I've tried to use this with the Stateless EJB, it is not worked. There is no any message printed by the Interceptor.
#Remote
public interface HelloServiceable extends Serializable {
void doning2();
}
#Stateless
#MyStereoable
public class HelloService implements HelloServiceable {
public void doing2() {
//Print something
}
}
Then I mix the case 1 and case2 as the following: -
#Stateless
#MyStereoable
public class HelloService implements HelloServiceable {
#Inject
private MyCustomized myBean;
public void doing2() {
this.myBean.doing1();
//Print something
}
}
The MyCustomized can be intercepted and the message is printed, but not for the Stateless EJB.
I'm not sure if I am misunderstand or confused about the CDI and EJB or not. Could you please help to advise further? Thank you very much for your help in advance. I'm looking forward to hearing from you soon.
Regards,
Charlee Ch.
I've changed my project from EAR/EJB project to standalone Web project. The CDI Stereotype is now worked with EJB Session Bean.
I've created a small demonstration at github. Please take note, It uses the JBoss Arquillian for testing.
I hope this information maybe useful.
Regards,
Charlee Ch.

EJB 3.1. Is #Local annotation needed?

So far, I almost always worked with no-interface EJBs and have a slight understanding about the need of #Local annotation. Consider this example:
public interface MyBeanIntf { void doStuff(); }
#Stateless
public class MyBean implements MyBeanIntf { public void doStuff(){ } }
Should the MyBeanIntf be marked as #Local? I don't see any benefit from that, because even when I don't annotate it as #Local, I still can use DI to properly inject it into UI Controller:
#Named
#SessionScoped
public class TestController implements Serializable {
// injection works perfectly, even when MyBeanIntf is not marked as #Local
#Inject
private MyBeanIntf myBean;
// or even like this:
// #EJB
// private MyBeanIntf myBean;
}
Let's make it more complex:
public interface MyBeanIntf { void doStuff(); }
public class MySuperBean implements MyBeanIntf { public void doStuff() { } }
#Stateless
public class MyBean extends MySuperBean { }
Is MyBean now considered a valid Local EJB bean? I have some doubts because it implements the interface indirectly.
If your EJB implements some interface but you don't specify (neither on the EJB nor the interface itself) which interface it is (#Remote, #Local) than it's assumed that it's a #Local one.
Therefore your code:
public interface MyBeanIntf { void doStuff(); }
#Stateless
public class MyBean implements MyBeanIntf { public void doStuff(){ } }
is semantically identical to the following:
#Local
public interface MyBeanIntf { void doStuff(); }
#Stateless
public class MyBean implements MyBeanIntf { public void doStuff(){ } }
When it comes to the second part of your question, I think that section 4.9.2.1 Session Bean Superclasses from EJB 3.1 FR spec would be interesting for you. From my understanding (so it might not be correct), it seems that your bean should not be considered as exposing a valid Local interface because of the following excerpt:
#Stateless
public class A implements Foo { ... }
#Stateless
public class B extends A implements Bar { ... }
Assuming Foo and Bar are local business interfaces and there is no
associated deployment descriptor, session bean A exposes local
business interface Foo and session bean B exposes local business
interface Bar, but not Foo.
Session bean B would need to explicitly include Foo in its set of
exposed views for that interface to apply.
Update:
As an addition one more excerpt from the spec:
A session bean class is permitted to have superclasses that are
themselves session bean classes. However, there are no special rules
that apply to the processing of annotations or the deployment
descriptor for this case. For the purposes of processing a particular
session bean class, all superclass processing is identical regardless
of whether the superclasses are themselves session bean classes.

Resources