Container Managed Transaction - Not marked for roll back - java

Bank.java
#Stateless
#Local
public class Bank implements IBank {
#EJB
IConfigBean iConfigBean;
#EJB
IDbs iDBS;
#EJB
IPosb iPosb;
#Override
public void doTransaction() {
System.out.println("--Bank Transaction Started--");
try {
Config config1 = getConfig(1);
iConfigBean.create(config1);
iDBS.doDBSTransaction();
Config config3 = getConfig(3);
iConfigBean.create(config3);
iPosb.doPOSBTransaction();
Config config5 = getConfig(5);
iConfigBean.create(config5);
} catch (Exception e) {
e.printStackTrace();
System.out.println("---Bank Exception--");
}
System.out.println("--Bank Transaction End--");
}
#Override
public Config getConfig(int inserttionOrderNo) {
Config config = new Config();
config.setType("EJBTransactionTESTING - " + inserttionOrderNo);
return config;
}
}
DBS.java
#Stateless
#Local
public class DBS implements IDbs {
#EJB
IConfigBean iConfigBean;
#Override
public void doDBSTransaction() {
System.out.println("--DBS Transaction Started--");
try {
Config config2 = getConfig(2);
iConfigBean.create(config2);
} catch (Exception e) {
e.printStackTrace();
System.out.println("--DBS Exception--");
}
System.out.println("--DBS Transaction End--");
}
#Override
public Config getConfig(int inserttionOrderNo) {
Config config = new Config();
config.setType("EJBTransactionTESTING - " + inserttionOrderNo);
return config;
}
}
POSB.java
#Stateless
#Local
public class POSB implements IPosb {
#EJB
IConfigBean iConfigBean;
#Override
public void doPOSBTransaction() {
System.out.println("--POSB Transaction Started--");
try {
Config config4 = getConfig(4);
iConfigBean.create(config4);
if (true) {
//For Test 1
//throw new NullPointerException();
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("--POSB Exception--");
}
if (true) {
//For Test 2
// throw new NullPointerException();
}
System.out.println("--POSB Transaction End--");
}
#Override
public Config getConfig(int inserttionOrderNo) {
Config config = new Config();
config.setType("EJBTransactionTESTING - " + inserttionOrderNo);
return config;
}
}
I am new to Stack Overflow and Its my new question so correct me If I am wrong.
Environment is..
Windows 10
Java 1.8
Eclipse
Tomcat 8.5
EJB3
I have Three stateless bean, Please look at the Sequence Diagram of the Transaction flow.
I purposely making NullPointer Exception at two places during the transaction to know the difference and I have marked with Lightening Bold symbol in sequence diagram.
I am not using any #TransactionAttribute to any methods.
Test 1 - Null Pointer in Inside the try block (Lightening Bold symbol with Green)
When I start the testing, Got Null pointer exception and all the transaction are not marked for roll back and data also got inserted in db.
I can only see Null pointer exception in the console log.
Test 2 - Null Pointer in Outside the try - catch method (Lightening Bold symbol with Red)
When I start the testing, Got Null pointer exception plus EJBTransactionRolledbackException and all the transaction marked for roll back and no data inserted in db.
I can see NullPointer and EJBTransactionRolledback Exception in the console log.
Question here is,
Why EJB transaction is not marked for roll back If I made Null pointer inside try block
Why EJB transaction is roll back happens If I made null pointer outside try block
Thanks in advance.

Keep in mind EJB calls, all the "magic" made by container happens there, including transaction markup. It's possible due to the fact that EJB calls are not direct, but always go through proxy.
You have such calls in your code:
iPosb.doPOSBTransaction();
So, if unchecked exception (NPE for example) is thrown in this method and not caught - it's ultimately caught by container due to EJB proxy wrapping the call above. In this case transaction only could be rolled back.
Adding a call to a method of the same bean in your method (without using #EJB reference), does not change that:
#Override
public void doPOSBTransaction() {
try {
Config config4 = getConfig(4);
iConfigBean.create(config4);
if (true) {
newMethod();
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("--POSB Exception--");
}
if (true) {
newMethod();
}
}
private void newMethod(){
throw new RuntimeException();
}
You can easily check that commit/rollback behaviour is just the same in this case, no matter that a method is added to call stack.
So the important thing you have to remember is that all container tricks work only on #EJB calls. So, for example, it's pointless to place transactional annotation on a private method - it won't be used ever.
Another important point is about checked exceptions. By default these do not cause transaction rollback indeed. But it's still possible to annotate your checked exception like below to make it rollback ongoing transaction:
#ApplicationException(rollback = true)

Related

Why is db connection closed after trying and failing to get a lock with spring-data-jpa?

So I would like to wrap a PessimisticLockingFailureException that gets thrown in a jpa repo when trying to get a lock for an entity that is already locked. And handle the wrapped exception in my exception handlers.
But it seems that when spring tries to end the transaction the connection is already closed and spring throws a new exception that overwrites the exception I would like to see.
In the logs I get "Application exception overridden by rollback exception" and it is this I would like to avoid. (Cause of rollback ex is that "Connection is closed")
Is there a solution to this? Or am I doing something wrong?
(Here's some pseudo code of what I'm doing)
String restControllerMethod(String args) {
try {
return service.serviceMethod(args);
} catch (Exception e1) {
throw e1; // org.springframework.orm.jpa.JpaSystemException caused by org.hibernate.TransactionException caused by java.sql.SQLException
}
}
#Transactional
String serviceMethod(String args) {
Entity entity;
try {
entity = repo.repoFindMethod(args);
} catch (Exception e2) {
throw new WrappingException(e2); // org.springframework.dao.PessimisticLockingFailureException caused by org.hibernate.PessimisticLockException
}
// do some processing with entity
return result;
}
#Lock(LockModeType.PESSIMISTIC_READ)
String repoFindMethod(String args);
I'm using spring-boot-starter-parent 2.3.2.RELEASE with spring-boot-starter-web spring-boot-starter-data-jpa and an emmbedded h2 db
Fixed this by adding a com.zaxxer.hikari.SQLExceptionOverride implementation and pointing the
spring.datasource.hikari.exception-override-class-name to it.
This causes hikari to not close the connection when the db throws an exception with the specified error code.
I've also added #QueryHints({#QueryHint(name = "javax.persistence.lock.timeout", value = "0")}) to the locking query since default lock wait times can be vendor specific
The issue with this solution is that it is vendor specific (both for h2 and hikari). And not all vendors support a custom timeout for obtaining locks (h2 for example does not support this but it matters less since it's timeout is very short anyway)
Example of my solution (for h2):
spring.datasource.hikari.exception-override-class-name=com.example.H2SQLExceptionOverride
public class H2SQLExceptionOverride implements SQLExceptionOverride {
private static final Logger logger = LoggerFactory.getLogger(H2SQLExceptionOverride.class);
public static final int LOCK_TIMOUT_ERROR_CODE = 50200;
#java.lang.Override
public Override adjudicate(SQLException sqlException) {
if (sqlException.getErrorCode() == LOCK_TIMOUT_ERROR_CODE) {
logger.debug("Diverting from default hikari impl and continuing transaction with errorCode: "
+ sqlException.getErrorCode() + " and sqlState: " + sqlException.getSQLState());
return Override.DO_NOT_EVICT;
}
return Override.CONTINUE_EVICT;
}
}

ATG: Update Order Pattern

It has been known that you must use the following pattern in order to update an order in ATG Form-Handlers that doesn't inherit from the PurchaseProcessFormHanlder:
boolean acquireLock = false;
ClientLockManager lockManager = getLocalLockManager();
try {
acquireLock = !lockManager.hasWriteLock(profile.getRepositoryId(), Thread.currentThread());
if (acquireLock) {
lockManager.acquireWriteLock(profile.getRepositoryId(), Thread.currentThread());
}
boolean shouldRollback = false;
TransactionDemarcation transactionDemarcation = new TransactionDemarcation();
TransactionManager transactionManager = getTransactionManager();
transactionDemarcation.begin(transactionManager, TransactionDemarcation.REQUIRED);
try {
synchronized (getOrder()) {
...
...
...
}
} catch (final Exception ex) {
shouldRollback = true;
vlogError(ce, "There has been an exception during processing of order: {0}", getOrder().getId());
} finally {
try {
transactionDemarcation.end(shouldRollback);
} catch (final TransactionDemarcationException tde) {
vlogError(tde, "TransactionDemarcationException during finally: {0}", tde.getMessage());
} finally {
vlogDebug("Ending Transaction for orderId: {0}", order.getId());
}
}
} catch (final DeadlockException de) {
vlogError(de, "There has been an exception during processing of order: {0}", order.getId());
} catch (final TransactionDemarcationException tde) {
vlogError(tde, "There has been an exception during processing of order: {0}", order.getId());
} finally {
try {
if (acquireLock) {
lockManager.releaseWriteLock(getOrder().getProfileId(), Thread.currentThread(), true);
}
} catch (final Throwable th) {
vlogError(th, "There has been an error during release of write lock: {0}", th.getMessage());
}
}
In theory, any FormHandler that inherits from the PurchaseProcessFormHandler already implements the following steps OOTB:
Acquire LocalLockManager in order to avoid concurrent threads to modify the same order:
try {
acquireLock = !lockManager.hasWriteLock(profile.getRepositoryId(), Thread.currentThread());
if (acquireLock) {
lockManager.acquireWriteLock(profile.getRepositoryId(), Thread.currentThread());
}
} catch (final DeadlockException de) {
vlogError(de, "There has been an exception during processing of order: {0}", order.getId());
}
Create a new Transaction:
try {
TransactionDemarcation transactionDemarcation = new TransactionDemarcation();
TransactionManager transactionManager = getTransactionManager();
transactionDemarcation.begin(transactionManager, TransactionDemarcation.REQUIRED);
} catch (final TransactionDemarcationException tde) {
vlogError(tde, "There has been an exception during processing of order: {0}", order.getId());
}
Ending the Transaction being used:
try {
TransactionManager transactionManager = getTransactionManager();
Transaction transaction = transactionManager.getTransaction();
// If transaction is elegible for commiting:
transactionManager.commit();
transaction.commit();
// otherwise
transactionManager.rollback();
transaction.rollback();
} catch (final Exception ex) {
error = true;
vlogError(ex, "There has been an exception during processing of order: {0}", order.getId());
} finally {
// handle the error
}
Release the lock being used for the transaction:
finally {
ClientLockManager lockManager = getLocalLockManager();
lockManager.releaseWriteLock(profile.getRepositoryId(), Thread.currentThread(), true);
}
As per ATG documentation, the following methods implement the behaviour descripted above:
Method: beforeSet
Called before any setX methods on this form are set when a form that modifies properties of this form handler is submitted. Creates a transaction if necessary at the beginning of the form submission process, optionally obtaining a local lock to prevent multiple forms from creating transactions that may modify the same order.
Steps: 1 & 2
Method: afterSet
Called after any setX methods on this form are set when a form that modifies properties of this form handler is submitted. Commits or rolls back any transaction created in beforeSet, and releases any lock that was acquired at the time.
Steps: 3 & 4
Such as you will only have to handle the following procedures in order to update the order:
Syncronize the block of code that's going to be used for order updating in order to avoid thread concurrency.
synchronized (getOrder()) {
...
...
...
}
Perform order modifications:
synchronized (getOrder()) {
getOrder().setXXX();
getOrder().removeXXX();
}
Update the order (updateOrder pipeline chain will be invoked):
synchronized (getOrder()) {
...
...
...
getOrderManager().updateOrder(order);
}
This is pretty straightforward, unless you have to edit an order in any of the following scenarios:
Form handlers or custom form handler that are not in the PurchaseProcessFormHandler's hierachy.
Helpers or Tools classes.
Processors
ATG REST Web Services
&c
If so, you will have to implement the Transactional Pattern within your components.
Questions!
Is there any other pattern known to use instead of using the transactional pattern?
Would it be possible to implement/override the beforeSet & afterSet methods in FormHandlers just the same way ATG does it in PurchaseProcessFormHandler
Are you aware of any other approach?
The series of steps you have outlined above is the prescribed series of steps for updating an order.
Feel free to factor it out in any way you find useful. Just ensure that when you update an order, you, or your inherited code, have performed the requisite steps.
One common way that ATG does similar factoring is for a given method, say X(...), you would have a preX(...), doX(...), and postX(...) method. You can create an abstract class with all your boilerplate code in the preX() and postX() methods, maybe even declared final, and have the doX() declared abstract. Your component then will inherit from the abstract class and must implement the doX() method. You may need to handle exceptions explicitly as well.
This is, essentially, what the standard form handlers do (under different names).
For example;
public final void X(...) {
preX(...); // call the pre method
try {
doX(...); // call the do method
} catch (XException xe) {
// handle error
}
postX(...); // call the post method
}
protected final void preX(...) {
// do everything you need to do before your customer code
}
protected final void postX(...) {
// do everything you need to do after your customer code
}
protected abstract void doX(...) throws XException;
Another thing you could do, instead of inheriting from an abstract class, is to define an annotation that has all the boilerplate code.
A third thing you could do, in a similar way, but a lot harder to shoehorn into your ATG code, might be to define an aspect or a method invocation interceptor using third party frameworks.
However, once again, whatever you do, and however you do it, just ensure that you follow all the steps.

BMT in client-side classes?

I have a 3-layer application with a client-container, EJB-container (business-layer and data-access-layer). I want to make transactions in my client by using BMT. My EJBs on the EJB-container are all working with CMT.
But I am not sure if that actually works, since the client is not working in an EJB-container. The client injects the EJBs of the EJB-container with lookups on a Context object, which seem to work fine. I declared my client-side class as:
#Singleton
#TransactionManagement(TransactionManagementType.BEAN)
Is this correct? I guess the client side class which is used by my GUI has to be a singleton.
I have these objects:
private Context jndiCntx;
private UserTransaction tx;
#PostConstruct
public void initialize() throws Exception {
jndiCntx = new InitialContext();
tx = (UserTransaction) jndiCntx.lookup("java:comp/UserTransaction");
tx.begin();
}
And this PostConstruct initializes the transaction.
Here is where I get a NullPointerException (in commitOrder()):
#Override
public void commitOrder() throws ApplicationException {
try {
tx.commit();
} catch (Exception e) {
e.printStackTrace();
throw new ApplicationException("", "commitOrder() failed!");
}
}
#Override
public void cancelOrder() throws ApplicationException {
try {
tx.rollback();
} catch (Exception e) {
throw new ApplicationException("", "cancelorder() failed!");
}
}
So it seems like that I don't get a UserTransaction object. I am slightly confused by the different ways of using contexts and transactions. What am I doing wrong? And by the way: What is the difference between using a Transaction with a TransactionManager and using a UserTransaction?

Problems with routing message to separate errorQueue

I have a MessageBean which reads from a Queue we'll name MainQ.
If the execution of the onMessage code throws a user-based Exception with a type we'll name UserException I want to catch this and put this message on a separate Queue named UserErrorQ.
If the exception is not of this type, the Exception is thrown on to be handled by the DMQ.
Here is my issue:
in my catch block I attempt, through a ErrorQueueHandler, to put this new message on the UserErrorQ. This results in an error when I attempt to connect to the connectionFactory to send the message to the UserErrorQ.
Apparently creating a new connection to a QueueConnectionFactory(javax.jms.ConnectionFactory) is causing problems
Error:
com.sun.messaging.jms.JMSException: MQRA:DCF:allocation failure:createConnection:Error in allocating a connection. Cause: javax.transaction.RollbackException
at com.sun.messaging.jms.ra.DirectConnectionFactory._allocateConnection(DirectConnectionFactory.java:548)
at com.sun.messaging.jms.ra.DirectConnectionFactory.createConnection(DirectConnectionFactory.java:265)
at com.sun.messaging.jms.ra.DirectConnectionFactory.createConnection(DirectConnectionFactory.java:244)`
MessageBean:
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void onMessage(Message message) {
try{
.
.
}catch(Exception e){
if(isUserExceptionWrappedInException(e){
errorQueueHandler.sendToErrorQueue(message);
}
}
}
private boolean isUserExceptionWrappedInException(Throwable t) {
if (t == null)
return false;
else if (t instanceof UserException)
return true;
else
return isUserExceptionWrappedInException(t.getCause());
}
ErrorQueueHandler:
public void sendToErrorQueue(Message message) {
try {
createConnection();
send((TextMessage)message);
} finally {
closeConnection();
}
}
private void createConnection() throws Exception {
try {
connection = connectionfactory.createConnection();
connection.start();
} catch (JMSException e) {
String msg = "Error while attempting to initialize connection to jms destination " + ERROR_QUEUE;
throw new OperationalException(msg, e, OperationalExceptionType.APPLIKASJONSTJENER);
}
}
As mentioned, the error occurs when attempting to make the connection. Anyone have a fix for this?
So, I have figured out the answer to my own question.
The reason for the connectionException was that the ErrorQueueHandler was not an EJB, but rather injected via CDI. There are no new instantiations allowed within a rollback state because the container discards the bean instance, which is why it failed. My REQUIRES_NEW annotation was also ignored as this belongs to the javax api, which will not affect a CDI injected bean.
Here are a few things to note:
Make sure the EJB has either no constructors, or public ones. The modifiers are important as the container needs these to be correct for it to instantiate the EJB.
There are a few problems with this approach.
As I am attempting to write the message to a separate error queue instead of the DMQ, I will have to consume the message and not throw the error on afterwards. Because the MDB is in a rollback state, the JMS spec clearly states that this will cause the message to be redelivered. What you will experience is that after writing to you custom errorQueue, the message will bounce right back to the queue and you now have an infinite loop.
Luckily i also have a solution:
The main issue here is controlling your transactions. For this scenario, i need 3 transactions:
One transaction for the MDB so that it is able to acknowledge the message event though i have a RuntimeException.
One transaction for the logic of the onMessage method so that i am able to do a rollback when i get an exception, but also still be able to write to the ErrorQueue.
One transaction for connecting and writing to the ErrorQueue while in a rollback state.
Code:
MessageBean:
#EJB
QueueService queueService;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void onMessage(Message message) {
try{
queueService.processMessageInNewTrasaction(message);
}catch(Exception e){
throw e;
}
}
QueueService:
import javax.jms.Message;
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
#Stateless
public class QueueService {
#EJB
ErrorQueueHandler errorQueueHandler;
public void processMessageInNewTransaction(Message message){
try {
.
.
} catch(Exception e) {
if(isUserExceptionWrappedInException(e)
errorQueueHandler.sendToErrorQueue(message);
}
}
private boolean isUserExceptionWrappedInException(Throwable t) {
if (t == null)
return false;
else if (t instanceof UserException)
return true;
else
return isUserExceptionWrappedInException(t.getCause());
}
}
ErrorQueueHandler:
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
#Stateless
public class ErrorQueueHandler{
public void sendToErrorQueue(Message message){
.
.
}
}
useful resource: http://weblogic-wonders.com/weblogic/2011/01/10/working-with-jms-and-the-standard-issues-in-jms/

ejb3 isolated (autonomous) transaction inside session bean

I'm using XA (2-phase) transaction. I want to log to one log-table through Log class and Entity Manager. My method inside EJB Session bean looks like:
private void logError(Throwable throwable) {
LogEntity logEntity = new LogEntity();
// Set everything
entityManager.persist(logEntity);
}
I want to it in isolated (autonomous) transaction independent of any "outer" transaction. I have already tried to add #TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) and #TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED) before method name and does not work.
Before I call EJB3 method I create user transaction like:
try {
UserTransaction transaction = (UserTransaction)context.lookup("javax.transaction.UserTransaction");
transaction.begin();
// Call EJB3 method
transaction.commit();
} catch (Throwable t) {
t.printStackTrace();
try {
transaction.rollback();
} catch (SystemException e) {
e.printStackTrace();
}
}
I want to Log no matter if commit is done or not. How to?
Regards
Transaction attributes are only relevant when called through a proxy. They do not apply to direct calls, which includes private methods. Try something like the following (which uses EJB 3.1 no-interface view, though you could create a separate local interface for logging if you only have EJB 3.0):
#Stateless
#Local(BusinessInterface.class)
#LocalBean
public class MyBean {
#EJB MyBean logger;
#TransactionAttribute(REQUIRED)
public void businessMethod() {
try {
...
} catch (Throwable t) {
logger.logError(t);
...
}
}
#TransactionAttribute(NOT_SUPPORTED)
public void logError(Throwable t) {
...
}
}
The important piece is that the call to logError happens through an injected EJB proxy, which allows the container to have control to suspend the XA transaction for the duration of the logError method.

Categories

Resources