Does application exception roll the transaction back? - jpa

I have a stateless bean class TestBean:
package samples;
import javax.ejb.*;
import javax.persistence.*;
#Stateless
public class TestBean {
#PersistenceContext
EntityManager em;
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void doIt() throws Exception {
em.createQuery("UPDATE Employee e SET e.salary = e.salary * 1.05").executeUpdate();
throw new Exception("Let us stop it!");
}
}
Does the EntityManager commit the transaction and the update would take place?

By default EJB bean should rollback the transaction only on system exceptions, that is: RuntimeException, RemoteException. This kind of exceptions are wrapped-up in EJBException.
If you throw application exception (as it is in your example), EJB bean won't rollback the transaction- it expects you to handle the exception. Application exceptions are those that do not extend RuntimeException or RemoteException.
You can make your EJB bean to rollback on appication exceptions by annotating it with:
#ApplicationException(rollback=true)

Related

Is there a way to propagate SessionContext to a new thread (getting WELD-001303)?

there's a session scoped bean 'Identity' which I injected in a #Stateless bean which implements Runnable:
#Stateless
#LocalBean
public class Test implements Runnable {
#Inject
Identity identity;
#Inject
Logger log;
#Override
public void run() {
log.warn("Test: " + this + " " + identity.getAccount().getId());
}
}
There's also a bean which invokes the above Runnable asynchronously:
#Stateless
#LocalBean
public class BeanContextExecutor implements Executor {
#Asynchronous
#Override
public void execute(Runnable command) {
command.run();
}
}
and finally, the invocation looks like this:
#Stateless
public class OtherBean {
#Inject
BeanContextExecutor executor;
...
executor.execute(command);
...
}
When running this I'm getting the following error:
...
Caused by: org.jboss.weld.context.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.SessionScoped
...
Is there any way to propagate the SessionContext to the background thread?
I also tried to submit this Runnable to ManagedExecutorService and even to create a proxy for it with a ContextService and submit a proxy but still getting the same error.
Thanks for any help with this!
As a workaround in BeanContextExecutor I used BoundSessionContext to create a dummy session context for a new thread and also had to manually copy the required session bean to make its state available in the background thread:
#Inject
BoundSessionContext boundSessionContext;
// Backed by a ConcurrentHashMap<Runnable, Identity> which stores the state of the session scoped bean before spawning a new thread
#Inject
GlobalExecutionContext globalExecutionContext;
#Inject
Instance<Identity> identityInstance;
#Inject
Cloner cloner;
#Inject
private BeanManager beanManager;
#Asynchronous
#Override
public void execute(Runnable command) {
HashMap<String, Object> storage = new HashMap<>();
boundSessionContext.associate(storage);
boundSessionContext.activate();
Identity identity = globalExecutionContext.remove(command);
Bean<Identity> bean = (Bean<Identity>) beanManager.resolve(beanManager.getBeans(Identity.class));
Identity localIdentity = beanManager.getContext(bean.getScope()).get(bean, beanManager.createCreationalContext(bean));
cloner.copyPropertiesOfInheritedClass(identity, localIdentity);
command.run();
boundSessionContext.invalidate();
boundSessionContext.deactivate();
boundSessionContext.dissociate(storage);
}
The example is intended to demonstrate the approach, it's possible to improve it like support passing beans of an arbitrary type. But I don't like this approach at all. There should be a better solution for context propagation problem.
Update:
I'd like to keep the caller identity in a background thread even if initial session is expired, it looks like the above solution is suitable for this.

Extended Persistence Context with ViewScoped CDI beans

I am a long time Seam user who tries to move to Java EE7, JSF2.2 and CDI now.
In Seam you tend to use EntityManagers with extended scope most of the time (in the Seam Conversation Scope). You don't get any LIEs on Ajax request etc.
I am trying to do it in a similar way with Java EE7 and CDI but somehow the injected EntityManager is only transaction scoped. When I get a ajax request in the entities that were loaded before are not managed anymore.
I am using the new javax.faces.view.ViewScoped and javax.transactional.Transactional on my CDI bean.
My Producer:
#PersistenceContext(unitName = "primary", type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;
#Produces
#Default
#Dependent
public EntityManager getEntityManager() {
return entityManager;
}
And my CDI bean:
#Named
#ViewScoped
#Transactional
public class TestBean implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Inject
EntityManager entityManager;
Logger log = Logger.getLogger(TestBean.class);
private TestEntity lastTest = null;
public void testAdd(){
TestEntity test = new TestEntity();
test.setVal("Test "+System.currentTimeMillis());
entityManager.persist(test);
entityManager.flush();
log.infov("Created test entity {0}", test);
lastTest = test;
}
public void testRead(){
List<TestEntity> test = entityManager.createQuery("select t from TestEntity t").getResultList();
for(TestEntity t: test){
log.infov("Found {0} managed {1}",t,entityManager.contains(t));
}
if(lastTest!=null){
log.infov("Last Test {0} managed {1}",lastTest,entityManager.contains(lastTest));
}
}
So when I first call testAdd() via Ajax it creates a new test entity. When I then call testRead() it gets all test entities and checks that the last created test entity is still managed (which it should if it is an EntityManager with an extended persistent context). But entityManager.contains(lastTest) always returns false.
What am I doing wrong?
I believe I can't use #PersistenceContext directly in the CDI bean. So how do I get the desired (Seam like) behaviour?
When you specify that an injected EntityManager is an extended persistence context, all object instances remain managed. Extended persistence contexts can only be used within Stateful session beans.
This is according to JBOSS documentation: https://docs.jboss.org/ejb3/app-server/tutorial/extended_pc/extended.html.
Consider packaging your insert/update/delete operations into EJBs while simple read from database can be through CDI beans. But more complex operations involving multiple reads and writes as well as transaction should be within EJBs.

Injecting EJB into Rest Exception Handler

I'm trying to inject a local #Stateless EJB into a Rest exception handler but getting the following error.
javax.naming.NameNotFoundException: Name [Test] is not bound in this Context. Unable to find [Test].
The maven Web project is running on Apache-tomee-1.7.1-jaxrs.
The EJB:
#Stateless(name = "Test")
public class Test {
public void sayHello() {
System.out.println("Hello");
}
}
The Exception handler which from my understanding I must treat as a client to the EJB.
#Provider
public class TestExceptionHandler implements ExceptionMapper<Throwable> {
#Context
HttpServletRequest request;
#Override
public Response toResponse(Throwable throwable) {
InitialContext context;
try {
context = new InitialContext();
Test test = (Test) context.lookup("Test");
test.sayHello();
} catch (NamingException ex) {
ex.printStackTrace();
}
return Response.ok().build();
}
}
I have also tried to do the following for the lookup: context.lookup("java:comp/env/Test");
The http://openejb.apache.org/jndi-names.html documentation is very difficult to understand.
Also tried the following which was my first attempt. http://blog.iadvise.eu/2015/06/01/jee-using-ejb-and-context-annotations-in-a-jax-rs-provider-class/
Am I missing any configuration in the tomee server or in my code?
The java:comp/env namespace is for the EJB references, not EJBs. You have not declared an EJB reference anywhere.
It's probably easiest to directly look up the EJB using lookup("java:module/Test") (assuming the EJB is packaged in the war, otherwise, java:app/ejbmodname/Test) because JAX-RS does not support EE injection by default. To declare an EJB reference, you would need to make the provider class an EJB itself or a CDI class (add beans.xml to the module), and then declare a field as #EJB(name="Test") Test myBean;.

PersistenceContext propagation and transaction spanning multiple EJBs in EJB 3.x

In our web application, we have a facade EJB which in turn calls multiple EJBs to perform a business function. The flow is like :
SLSB facade -> invokes ejb1, ejb2, ejb 3 etc -> invoke JPA layer
In each of the ejbs in the business layer, i inject entity manager using #PersistenceContext.
A simplified version of the code is as below:
#Stateless
public class facade{
#EJB
private EJB1 ejb1;
#EJB
private EJB2 ejb2;
#EJB ejb3;
private EJB3 ejb3;
public void performAction(..) {
// invoke method on ejb1
// invoke method on ejb2
// invoke method on ejb3
}
}
#Stateless
public class EJB1 implements IEjb1 {
#PersistenceContext(unitName = "pu")
private EntityManager entityManager;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public xxxEntity insert(xxxEntity entity) throws AppException {
// code for persisting the entity
}
}
#Stateless
public class EJB2 implements IEjb2 {
#PersistenceContext(unitName = "pu")
private EntityManager entityManager;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public yyyEntity insert(yyyEntity entity) throws AppException {
// code for persisting the entity
}
}
The application is deployed on Glassfish and uses JTA transaction. Since this is container managed PC, will the same persistence context be propagated to all EJBs? Will they run in the same transaction (reusing the transaction started in EJB1)? Is there a way to verify if the same transaction is used by all EJBs (same transaction id?)
By default all ejbs (1,2,3) will participate in the one, same transaction that is started by facade ejb. You would need to define different transaction attribute for them to use different transactions (e.g. REQIRES_NEW to create new transaction).
If you really need transaction id, you could try to inject TransactionSynchronizationRegistry into your bean like this
#Resource
TransactionSynchronizationRegistry registry;
// and then get key
...
registry.getTransactionKey()
See TransactionSynchronizationRegistry and Container-Managed Transactions

CDI EntityManager fail

Hibernate says "An exception thrown by Hibernate means you have to rollback your database transaction and close the Session immediately".
When persist method throws a SQLException and the entityManager becomes dirty, if I close the EntityManager, it still in Conversation Scope.
i'm using: tomcat 7, cdi 1.1, hibernate 4.1;
Is there any way to produce a new EntityManager for the current conversation to replace the dirty?
#Produces
#ConversationScoped
public EntityManager create(EntityManagerFactory emf) {
EntityManager em = emf.createEntityManager();
...
ViewBean
#Named #ConversationScoped
public class MyView implements Serializable {
enter code here
#Inject #Getter private EntityManager em;
...
public void persist(){
try{
getEm().getTransaction().begin();
getEm().persist(entityInstance);
getEm().getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
if(getEm().getTransaction().isActive()){
getEm().getTransaction().rollback();
}
}
}
No, there is not. Conversation scoped isn't a very good scope for an EntityManager anyway. It should really be Request or Default due to transaction boundaries.