My setup:
I have an EJB bean (deployed in an ear on a JBoss 6.4) with a method that calculates some values and stores it in a database. The method is started in a new transaction. It looks like this:
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void doStuff() {
MyObject value = calculateSomeValues();
entityManager.merge(value);
entitiyManager.flush();
}
By design, it is possible that the flush will fail due to a ConstraintViolationException. As this is a new transaction, the exception will be wrapped in an EJBTransactionRolledbackException and thrown as a RuntimeException. I have set up logging for ejb exceptions as it is useful to find them in the whole system. However as I know that the ConstraintViolationException will occur now and then, I would like to catch it and rollback before the logging system logs it as an error (the constraint violation is not seen as an exception, but it is required from the database's point of view). At the moment, my log file is clogged with entries like this one:
ERROR [org.jboss.as.ejb3.invocation] (default-threads - 49) JBAS014134: EJB Invocation failed on component
How can I catch the exception in such a way that the logger is prevented from printing these errors? (Note: I still want it to log all other EJBTransactionRolledbackExceptions)
I don't have JBoss 6 deployed, so I cannot immediately test whether the precise exception types work also for JBoss 6 (I tried this on WildFly 8).
A possible solution is to put in your EJB a method annotated by #AroundInvoke (at least if you don't have other interceptors on this EJB):
#AroundInvoke
public Object myConstraintViolationInterceptor(InvocationContext ctx) throws Exception
{
try {
return ctx.proceed();
}
catch(PersistenceException e) {
if (e.getCause() instanceof org.hibernate.exception.ConstraintViolationException) {
//some action taken
}
else {
throw e;
}
return null;
}
}
By the way, if your ConstraintViolationException is actually optimistic locking exception in disguise (that is, it is thrown because another user has modified some data in the meantime), maybe it is better to report it to the user.
Related
I am working on an application where in a bean's post construct method i am adding some logic to create an object. Now , what I want to do is, if there is an exception and the creation of the object has some error, then do not let the application start up. Yes, I can see an exception being thrown on the console when it starts up, if there is any issue with the construction of an object, but I would like something better than that to inform me that construction of an object has failed, and what better criteria than application failing to start up.
Any help is much appreciated.
Thank you in advance.
You can look for FailureAnalyzer for this type of requirements where additional information will given in case application start failes. If any exception raised during application start, all the FailureAnalyzer classes will be invoked in a sequence. If any of the FailureAnalyzer class returning FailureAnalysis object then the exception won't be propagated to further FailureAnalysis classes.
Please make sure you register your FailureAnalysis class in resource/META-INF/spring.factories file.
#Component
public class SomeObject {
#PostConstruct
public void init() throws Exception {
throw new Exception("SomeObject init threw exception");
}
}
public class ObjConstructionFailureAnalyzer extends
AbstractFailureAnalyzer<BeanCreationException> {
#Override
protected FailureAnalysis analyze(Throwable rootFailure,
BeanCreationException cause) {
System.out.println("\n===>ObjConstructionFailureAnalyzer::analyze()\n");
String desciption = "Object creation failed, [Reason]: " +
cause.getMessage();
String action = "Please handle exceptions in your init methods";
return new FailureAnalysis(desciption, action, cause);
}
}
In spring.factories file
org.springframework.boot.diagnostics.FailureAnalyzer=examples.stackoverflow.ObjConstructionFailureAnalyzer
Exception stacktrace
===>ObjConstructionFailureAnalyzer::analyze()
2018-02-21 10:16:59.552 ERROR 9500 --- [ main]
o.s.b.d.LoggingFailureAnalysisReporter :
*************************** APPLICATION FAILED TO START
Description:
Object creation failed, [Reason]: Error creating bean with name
'someObject': Invocation of init method failed; nested exception is
java.lang.Exception: SomeObject init threw exception
Action:
Please handle exceptions in your init methods
You can additionally visit here for code sample.
I have the bellow case, Where I call doSomeTask() of BeanA but if doSomeTask() fails I want to persist ErrorInfo into another table as well calling the saveError(ErrorInfo) of BeanA. Both of them has #TransactionAttribute(REQUIRES_NEW).
class BeanA {
#TransactionAttribute(REQUIRES_NEW)
public void doSomeTask(){
if(someCondition){
throw new SomeException();
}
// do task
}
#TransactionAttribute(REQUIRES_NEW)
public void saveError(ErrorInfo error) {
// save error info if doSomeTask fails
}
}
class BeanB {
BeanA beanA;
void performTask(){
try{
beanA.doSomeTask();
}catch(Exception e){
ErrorInfo error = getErrorInfo(e)
beanA.saveError(error);
}
}
}
But when doSomeTask() throws Exception saveError() doesn't work and throws Exception
Caused by: weblogic.transaction.internal.AppSetRollbackOnlyException: setRollbackOnly called on transaction
What am I doing wrong and how to fix this error? Thanks in advance for any help.
Sorry, for the late answer. The problem was resolved.
The actual error was hidden. In my case, the actual error was just a JSR 303 validation error of ErrorInfo instance while persisting. Had to add
Dweblogic.transaction.allowOverrideSetRollbackReason=true
on <domain_home>/bin/setDomainEnv.sh to find out the actual error and fix it. Thanks to this answer https://stackoverflow.com/a/38584687/1563286
I have debugged the similar issue not so long ago. In my case the issue was the following:
there was a top level transaction open when the REQUIRES_NEW method is called
After exception in nested transaction commit of the top level one failed to commit as "marked as rollback only"
It turned out that through a new transaction is started the connection holder is shared on TransactionManager level. When exception is thrown inside nested transaction the connection itself is marked as rollback only. So later this is causing the issue.
I was able to resolve the issue by using the savepoints (available since JDBC 3.0). Usually savepoints are disabled in many environments/ORM by default and using them requires additional configuration.
Hope this is of some help.
I am having issues with committing a transaction within my #Transactional method:
methodA() {
methodB()
}
#Transactional
methodB() {
...
em.persist();
...
em.flush();
log("OK");
}
When I call methodB() from methodA(), the method passes successfuly and I can see "OK" in my logs. But then I get
Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at methodA()...
The context of methodB is completely missing in the exception - which is okay I suppose?
Something within the methodB() marked the transaction as rollback only? How can I find it out? Is there for instance a way to check something like getCurrentTransaction().isRollbackOnly()? - like this I could step through the method and find the cause.
When you mark your method as #Transactional, occurrence of any exception inside your method will mark the surrounding TX as roll-back only (even if you catch them). You can use other attributes of #Transactional annotation to prevent it of rolling back like:
#Transactional(rollbackFor=MyException.class, noRollbackFor=MyException2.class)
I finally understood the problem:
methodA() {
methodB()
}
#Transactional(noRollbackFor = Exception.class)
methodB() {
...
try {
methodC()
} catch (...) {...}
log("OK");
}
#Transactional
methodC() {
throw new ...();
}
What happens is that even though the methodB has the right annotation, the methodC does not. When the exception is thrown, the second #Transactional marks the first transaction as Rollback only anyway.
To quickly fetch the causing exception without the need to re-code or rebuild, set a breakpoint on
org.hibernate.ejb.TransactionImpl.setRollbackOnly() // Hibernate < 4.3, or
org.hibernate.jpa.internal.TransactionImpl() // as of Hibernate 4.3
and go up in the stack, usually to some Interceptor. There you can read the causing exception from some catch block.
I struggled with this exception while running my application.
Finally the problem was on the sql query. i mean that the query is wrong.
please verify your query. This is my suggestion
Found a good explanation with solutions: https://vcfvct.wordpress.com/2016/12/15/spring-nested-transactional-rollback-only/
1) remove the #Transacional from the nested method if it does not really require transaction control. So even it has exception, it just bubbles up and does not affect transactional stuff.
OR:
2) if nested method does need transaction control, make it as REQUIRE_NEW for the propagation policy that way even if throws exception and marked as rollback only, the caller will not be affected.
Look for exceptions being thrown and caught in the ... sections of your code. Runtime and rollbacking application exceptions cause rollback when thrown out of a business method even if caught on some other place.
You can use context to find out whether the transaction is marked for rollback.
#Resource
private SessionContext context;
context.getRollbackOnly();
There is always a reason why the nested method roll back. If you don't see the reason, you need to change your logger level to debug, where you will see the more details where transaction failed. I changed my logback.xml by adding
<logger name="org.springframework.transaction" level="debug"/>
<logger name="org.springframework.orm.jpa" level="debug"/>
then I got this line in the log:
Participating transaction failed - marking existing transaction as rollback-only
So I just stepped through my code to see where this line is generated and found that there is a catch block which did not throw anything.
private Student add(Student s) {
try {
Student retval = studentRepository.save(s);
return retval;
} catch (Exception e) {
}
return null;
}
disable the transactionmanager in your Bean.xml
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
comment out these lines, and you'll see the exception causing the rollback ;)
apply the below code in productRepository
#Query("update Product set prodName=:name where prodId=:id ")
#Transactional
#Modifying
int updateMyData(#Param("name")String name, #Param("id") Integer id);
while in junit test apply below code
#Test
public void updateData()
{
int i=productRepository.updateMyData("Iphone",102);
System.out.println("successfully updated ... ");
assertTrue(i!=0);
}
it is working fine for my code
We are using CDI with CMT (container managed transactions) to connect to the database in the web app and mark methods called from the front-end that require a transaction with:
#Transactional(value=TxType.REQUIRES_NEW)
This will create a new CDI transaction, however now if an exception occurs doing this code block or any other code block called from this method it will throw the error message:
javax.transaction.TransactionalException: Managed bean with Transactional annotation and TxType of REQUIRES_NEW encountered exception during commit javax.transaction.RollbackException: Transaction marked for rollback.
...
Caused by: javax.transaction.TransactionalException: Managed bean with Transactional annotation and TxType of REQUIRES_NEW encountered exception during commit javax.transaction.RollbackException: Transaction marked for rollback.
...
Caused by: javax.transaction.RollbackException: Transaction marked for rollback.
Is there anyway to get CDI to re-throw the nested error so that you can easily debug what the real cause of the rollback was?
(Running on Java-EE7, Glassfish 4.0, JSF 2.2.2)
It seems the easiest way to do this is by using a CDI Interceptor to catch the exceptions. We can define the CDI Interceptor as follows:
#InterceptorBinding
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface TransactionDebugger {
}
Once we have defined the CDI Interceptor, we need to create class that is executed when the Interceptor annotation is used. We define an #AroundInvoke so that our code is called before the code in the method we annotate. invocationContext.proceed() will call the method we annotate and give us the result (if any) it returned. So we can put a try, catch (Exception) around this call to catch any kind of Exception that is thrown. Then we can log this exception using the logger (log4j used here), and re-throw the Exception so that any upstream code is also informed of it.
Re-throwing the Exception will also allow us to use CMT (Container Managed Transactions) as then ultimately the container will catch the Exception and throw a transaction RollbackException. However, you could easily use UserTransactions with this also and perform a manual rollback when an exception is caught instead of re-throwing it.
#Interceptor
#TransactionDebugger
public class TransactionInterceptor {
private Logger logger = LogManager.getLogger();
#AroundInvoke
public Object runInTransaction(InvocationContext invocationContext) throws Exception {
Object result = null;
try {
result = invocationContext.proceed();
} catch (Exception e) {
logger.error("Error encountered during Transaction.", e);
throw e;
}
return result;
}
}
Next we must include our new Interceptor in our beans.xml (generally located in src/META-INF), since Interceptors are not enabled by default in CDI. This must be done in ALL projects where the annotation is used, not just the project where the annotation is defined. This is because CDI initializes Interceptors on a per project basis:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
version="1.1" bean-discovery-mode="all">
<interceptors>
<class>package.database.TransactionInterceptor</class>
</interceptors>
</beans>
Finally, we must annotate the methods that we call with our new CDI Interceptor. Here we annotate them with #Transactional to start a transaction, and #TransactionDebugger to catch any Exception that occurs within the transaction:
#Transactional #TransactionDebugger
public void init() {
...
}
This will now log ANY error that occurs while executing the init() code. The logging granularity can be changed by changing the try, catch from Exception to a sub-class of Exception in the Interceptor implementation class TransactionInterceptor.
The TransactionalException is thrown at the commit time, i.e. after your code has been fully executed. Since the transaction is marked for rollback, commit cannot take place and the exception is thrown.
However, the transaction was marked for rollback sometime during the execution. I assume you do not mark it for rollback manually, therefore an exception must have been thrown. The exception is either a RuntimeException or it is annotated with #ApplicationException(rollback = true). Since you don't get this exception, the exception must have been caught somewhere. Are you sure you do not catch exceptions thrown from a business method in your code?
To answer the question... No, I don't think it is possible to rethrow the original exception, because it is thrown at different time and place.
Below is the exception I am getting while initializing any ejb object.
Could any one tell what is the reason of below exception and how can I resolve it???
Is is Application exception or Environment Exception???
Thanks in Advance... waiting for response ...
[7/12/10 5:05:24:226 EDT] 00000037 ExceptionUtil E CNTR0020E: EJB threw an unexpected (non-declared) exception during invoc
ation of method "init" on bean "BeanId(RDxEAR#vs-tle-beans-server.jar#VLSContextHome, C5E6CBE5-0129-4000-E000-C9DF093361B8)".
Exception data: java.rmi.RemoteException
at com.versata.tl.vls.ejb.VLSContextBean.init(VLSContextBean.java:298)
at com.versata.tl.vls.ejb.EJSRemoteStatefulVLSContextHome_acff79a1.init(Unknown Source)
at com.versata.tl.vls.ejb._EJSRemoteStatefulVLSContextHome_acff79a1_Tie.init(_EJSRemoteStatefulVLSContextHome_acff79a
1_Tie.java:2119)
at com.versata.tl.vls.ejb._EJSRemoteStatefulVLSContextHome_acff79a1_Tie._invoke(_EJSRemoteStatefulVLSContextHome_acff
79a1_Tie.java:395)
at com.ibm.CORBA.iiop.ServerDelegate.dispatchInvokeHandler(ServerDelegate.java:621)
at com.ibm.CORBA.iiop.ServerDelegate.dispatch(ServerDelegate.java:474)
at com.ibm.rmi.iiop.ORB.process(ORB.java:503)
at com.ibm.CORBA.iiop.ORB.process(ORB.java:1571)
at com.ibm.rmi.iiop.Connection.respondTo(Connection.java:2703)
at com.ibm.rmi.iiop.Connection.doWork(Connection.java:2577)
at com.ibm.rmi.iiop.WorkUnitImpl.doWork(WorkUnitImpl.java:62)
at com.ibm.ejs.oa.pool.PooledThread.run(ThreadPool.java:118)
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1473)
[7/12/10 5:05:24:231 EDT] 00000037 LocalTranCoor E WLTC0017E: Resources rolled back due to setRollbackOnly() being called.
Looks like the 'Caused by' information is not there, but it seems by part of the stack trace that the Stateful bean's #Init method corresponding to the EJBHome.create() method is throwing a RuntimeException. The 'Resources rolled back' message is likely not the cause of the problem but simply the container carrying it it's job of cleaning up after the runtime exception.
First thing to do is either:
1. Surround the body of the init() method with something that catches and logs the RuntimeException so you can look at where the real source of the problem is, or better...
2. Put that exception logging into an interceptor so it can easily be reused on any other beans in the future.
If you do find the RuntimeException and determine it is an exception you do what to throw without a transaction rollback or your bean instance being destroyed you can use the #ApplicationException annotation on the exception class to turn it into an exception that is safe to throw. If it is a built in exception you can mark it in the ejb-jar.xml file. Note, though, that this affects all usage of the exception type, so be very careful and avoid doing things like java.lang.RuntimeException. Best to mark very specific subclasses of RuntimeException to avoid completely disabling the container's ability to clean up after unexpected exceptions.