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
Related
I have created Oracle service with #CordaService annotation
I am getting Exception while installing Corda Service.
It will not get defined service type, got size 0 in List object in first argument of constructor.
public NumberVerifierOracle(PluginServiceHub services){
this(services.getMyInfo().serviceIdentities(NumberVerifierOracleType.getNumberVerifierOracleType().getServiceType()).get(0),services);
}
The defined service type is:
public class NumberVerifierOracleType {
private static ServiceType serviceType;
private static NumberVerifierOracleType numberVerifierOracleType = new NumberVerifierOracleType();
private NumberVerifierOracleType(){
serviceType = ServiceType.Companion.getServiceType("com.template.oracle.service","numberVerifierService_NumberVerifierOracle");
}
public static NumberVerifierOracleType getNumberVerifierOracleType() {
return numberVerifierOracleType;
}
public ServiceType getServiceType() {
return serviceType;
}
}
The Package hierarchy is:
com.template.oracle.service.NumberVerifierOracle class
I have resolved this error by making service with Public modifier.
Like this:
public static ServiceType type;
static{
type= NumberVerifierOracleType.getNumberVerifierOracleType().getServiceType();
}
It will not work if we have private modifier and use directly getter method.
When I intentionally throw an exception within an Objectify transaction, my transaction is not being rolled back. What am I doing wrong?
#Inject
Dao dao
...
public void testTransaction(){
dao.transact(new VoidWork() {
#Override
public void vrun() {
Key aclKey= dao.save().entity(acl).now(); //expecting this to be rolled back
//throw exception
if(true) throw new IllegalArgumentException();
//expecting rollback of acl save
}
});
}
I am using a setup like this:
#Singleton
public class DaoFactory extends ObjectifyFactory {
private Injector injector;
#Inject
public DaoFactory(Injector injector) {
this.injector = injector;
registerEntities();
}
private void registerEntities() {
}
#Override
public <T> T construct(Class<T> type) {
return injector.getInstance(type);
}
#Override
public Objectify begin() {
Dao dao = new Dao(this);
return dao;
}
}
Where:
public class Dao extends ObjectifyImpl<Dao> {
#Inject
public Dao(ObjectifyFactory fact) {
super(fact);
}
}
and
public class DaoService {
#Inject
public static void setObjectifyFactory(DaoFactory fact) {
ObjectifyService.setFactory(fact);
}
}
are all injected using Guice for DI.
I stepped through the code, and objectify does call txnOfy.getTransaction().rollback(); in TransactorNo.class
but, when I check app-engine local db, I see a an entity created for the acl (sometimes taking a few seconds longer to show up)
Transaction state is attached to a specific ObjectifyImpl instance. You're starting a transaction (which makes a new ObjectifyImpl available via the static ofy() method) and then re-using the old, nontransactional ObjectifyImpl instance.
So even though you are rolling back the transaction, your save operation utilized the nontransactional Objectify impl from outside the transaction.
This is why the documentation suggests that you never hold on to an Objectify instance in a variable; always use the static ofy() method. You can make your own static ofy() (or whatever) method that returns your Dao type instead. Look at the example code for http://www.motomapia.com/
Because transaction and sessions states are thread-local concerns, injecting persistence contexts is just a bad idea, even under JPA.
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)
Now I use Jersey to create restful web service on Websphere 8.5. I also want the restful web service has the EJB 3.1's capacity.
My restful web service code as follow:
#Stateless
#Path("/tagServiceRS/{tagid}")
#Interceptors(TestTagServiceInterceptor.class)
public class TagServiceRS implements Serializable{
private static final long serialVersionUID = 5L;
private static final Logger log = LoggerFactory.getLogger(TagServiceRS.class);
#EJB
private TagTestService tagTestService;
#PersistenceContext(unitName = "tag-ejb")
private EntityManager entityManager;
#GET
#Produces("text/plain")
public String findTagById(#PathParam("tagid") String tagid) {
return "TAG";
}
/**
* #return the tagTestService
*/
public TagTestService getTagTestService() {
return tagTestService;
}
/**
* #param tagTestService the tagTestService to set
*/
public void setTagTestService(TagTestService tagTestService) {
this.tagTestService = tagTestService;
}
When I deploy the war on the Websphere 8.5. The TagServiceRS was successful deployed as a restful web service.And I test it. It's OK.But the TagServiceRS was failed deploy as a EJB session bean. The TagServiceRS's entityManager and tagTestService fields are all null.
I see the log, there is no error or warning log.
Below is my TagTestServiceBean code.
#Stateless
public class TagTestServiceBean implements TagTestService, Serializable {
private static final long serialVersionUID = 5L;
private static final Logger log = LoggerFactory.getLogger(TagTestServiceBean.class);
#Override
public Tag testFindTagById(Long id) {
log.info("testFindTagById ++++++++++++++++++++++++++++++++++++++++ invoked for id: {}", id);
return new Tag();
}
}
#Remote
public interface TagTestService extends Serializable {
/**
* #param id
* the ID from database
* #return a tag, may null
*/
Tag testFindTagById(Long id);
}
If Any Answers.Thanks a lot.
Change the annotation from #EJB to #Resource
#Resource
private TagTestService tagTestService;
The class itself need not to be annotated with #Stateless annotation.
Additionally, JAX-RS root resource and provider classes must have a JCDI specified scope. Scopes control the lifecycle of a JCDI managed bean. Root resource classes can have any valid scope such as #javax.enterprise.context.RequestScoped, which makes the JAX-RS root resource class behave the same as in a non-JCDI enabled application. The javax.ws.rs.core.Application subclasses and JAX-RS providers must have the #javax.enterprise.context.ApplicationScoped annotation.
More info here.
We are trying to build a system, which "produces" an entitymanager depending on the logged-in user (kind of multitenancy). Therefor we implemented a stateless ejb like this:
#Stateless
#TransactionAttribute(TransactionAttributeType.SUPPORTS)
public class CustomEntityManagerFactory {
#PersistenceContext(unitName = "EM1")
private EntityManager em1;
#PersistenceContext(unitName = "EM2")
private EntityManager em2;
#Produces
#RequestScoped
public EntityManager getEntityManager() {
// check which entitymanager to return
}
}
The entitymanager is injected like this:
#Stateless
public class EmployeeService {
#Inject
private EntityManager em;
...
}
This producer works as long as only an entitymanager without extended persistence context is needed (in stateless ejb). Unfortunately we also have some stateful ejbs, which need the extended persistence context. Is there a way to implement a CDI producer for this purpose or does this approach only work for stateless ejb with transactional entitymanager?
My guess is you need this:
Create two factories here:
#ApplicationScoped
public class EntityManagerFactoryProducer {
private static EntityManagerFactory factory;
private static EntityManagerFactory factory2;
#Produces
public EntityManagerFactory getEntityManagerFactory(InjectionPoint ip) {
// if the field is named exactly factory2 then factory2 is produced
if (ip.getMember().getName().equals("factory2")) {
if (factory2 == null) {
factory2 = Persistence.createEntityManagerFactory("EM2");
}
return factory2;
}
else {
if (factory == null) {
factory = Persistence.createEntityManagerFactory("EM1");
}
return factory;
}
}
Use factories to createEntityManagers
public class EntityManagerProducer {
#Inject
private EntityManagerFactory factory;
#Inject
private EntityManagerFactory factory2;
#Produces
#RequestScoped
public EntityManager getEntityManager() {
return factory.createEntityManager();
}
#Produces
#RequestScoped
#MyCustomQualifier
public EntityManager getEntityManager2() {
return factory2.createEntityManager();
}
}
The qualifier used to differ between entitymanagers
#Qualifier
#Retention(RUNTIME)
#Target({FIELD })
public #interface MyCustomQualifier {
#Nonbinding
public String value() default "";
}
end usage:
#Inject
EntityManager em;
#Inject
#MyCustomQualifier
EntityManager em2;
Of course you can use the qualifier to differ between factories instead of checking the name of the field. I just wanted to get some more depth into the answer.