I have an EJB with container-managed transactions. I have a method in it (used as a REST call) that calls something from another EJB I've injected using JNDI (not sure if that matters) and it returns an Exception that extends RuntimeException (so it causes a transaction rollback), which translates as a 404 response through an ExceptionMapper.
I want that exception to be what returns from my REST call and I don't mind it being in the logs at all, but I do not want my log to be spammed with the EJBExceptionRolledBackException stacktrace that it causes (the stacktrace gets printed three times for some reason). I believe two out of these three stacktraces get logged before the server even gets back to the final method for the REST call.
Either way, as long as I figure out how to suppress one of these logging actions I'll figure out a way to stop all three. Does anyone have an idea how to suppress this kind of logging?
As it said in the EJB Specification every SystemException must be logged by the container implementation. You can try to catch it or mark as ApplicationException but if you mark it it won't rollback the transaction. I suggest this:
#Stateless
#TransactionManagement(TransactionManagementType.BEAN)
public class MyBean {
#Resource
private UserTransaction tx;
public void myMethod() throws MyApplicationException {
try {
tx.begin();
// call needed methods
tx.commit();
} catch(Exception e) {
// silently rollback;
// exceptions from `UserTransaction` omitted for readability
tx.rollback();
throw new MyApplicationException(e);
}
}
}
Now in your client code of that EJB you can react to MyApplicationException and return whatever you want or log it or don't. By using container managed transactions will ensure that errors are logged by specification (and they are wrapped to another exceptions as bean instances are being destroyed). Also you can mark transaction as rollback-only. Be sure to use this carefully. If you don't want logs from container you need to control all of your flow by yourself.
Related
I have a question about how to handle EJBExceptions in some special cases.
General situation
In our application an EJB (3.0) DataAccessObjects(DAO) are responsible for all database access, these are used in other parts of the application.
Exceptions in the database layer are expected, for instance because of Optimistic Locking or Database constraint violations. The exceptions often thrown outside of the DOA class, because they occur on commit of the automatic JTA transaction. The calling class then receives this exception wrapped in an EJBException.
In most cases ignoring or logging and rethrowing the EJBException is best, as our JMS will trigger automatic retries for MessageDrivenBeans. In two cases, we don't want the exception to be propaged, as they have unwanted side effects.
Handling in JSF
In our JSF website we use the following pattern to display user friendly messages:
#ManagedBean
#ViewScoped
public class MyDataController {
#EJB
private MyDataDao myDataDao ;
public void addData(){
FacesMessage msg;
try {
Data data = new Data();
// Data gets filled
myDataDao.addData(data);
msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Succes",
data.getName());
}
catch (EJBException e) {
LOGGER.warn("Failed to add data"newCompanyName, e);
if (ExceptionUtils.getRootCause(e) instanceof SQLIntegrityConstraintViolationException) {
msg = new FacesMessage(FacesMessage.SEVERITY_FATAL, "Failed",
data.getName());
}
else {
msg = new FacesMessage(FacesMessage.SEVERITY_FATAL,
"Failed to add data for unknown reason", data.getName());
}
}
}
}
Handling in Schedules tasks
In a related case, we call the database from a timed task (created using #Schedule). However this task is destroyed when (two consecutive?) exceptions occur while running it (at least in Weblogic). For us it is very important that this task keeps running even if exceptions during handling occur.
We have achieved this by catching and logging all EJBExceptions, as explained in this answer and in this answer. (Actually in our case we decided to catch all exceptions).
Problem
The above solutions mostly work as intended. However, we recently found that Errors are wrapped in an EJBException as well. For instance an OutOfMemoryError. In this case it caused the error to be swallowed, disabling the normal mechanism in Weblogic that would restart the application in case of an OutOfMemoryError.
Effectively this has downgraded Errors to Exceptions.
Question
The only solution I have is to check the recursively check the exception and its causes for instances of type Error, and if that is the case, rethrow the EJBException.
What is the correct way to handle the EJB exceptions in these situations?
I am trying to find a solution to build reliability into our webapp. The plan is to dump sql along with data if network connectivity/database connection is lost. In current implementation we have Rest controller, Service, DAO. The DAO throws PersistenceExcetpion, and that is propagated till the Controller layer.
Example code:
public MyDAOClass {
public void save(Object object) {
try {
entityManager.persist(object);
} catch (PersistenceException e) {
throw new DBException("Error occurred in save", e);
}
}
}
The DBException is a runtime exception.
Now the comes the actual question. One of the teammate suggested to have custom exceptions like for eg. InsertException, UpdateException etc. And if we encounter any of these exceptions we know which operation was performed on that entity so that it can be saved to a file as appropriate sql.
For example. Lets say the code failed to save Employee entity. This will throw InsertException, and will create an entry in file as insert sql statement for that entity. insert into employeee values ('firstname','lastname');
For me the idea of implementing the creation of sql file when connectivity is lost doest not seem to be as simple as implementing the above.
The questions that I have put forward are
1) How do you handle when multiple actions (like any combination of insert, update, delete) are performed in the service method ?
2) What about different exceptions ? I mean the reason for PerisistenceException can be anything like constraint failure, entity not found etc and not just the connection issue.
Is there any way to implement the above scenario which also considers all the different conditions.
Thanks.
Update:
Based on comments by chrylis. I should have already added this to the question. It's a webapp running locally in different retail stores. And the application can't have a downtime, so if any connectivity issues, the app should keep work. The file will be later synched with the central database server.
With spring you have Hibernate ORM that will store the data to the database. If an exception occurs during any request it will be rolled back by hibernate. This depends on where you'we put the #Transnational annotation.
We use a Service layer that handles the transaction. So if a database operation or any other operation fails in the service layer and throws an exception the transaction is auto rolled back by hibernate. We then use a spring exception resolver to handle any exception and write custom errors in the log and to the user. I guess you could store the exception in another database as well if that is interesting I think logging them should suffice though.
This article teaches you more about general exception handling.
Here is our exception resolver.
import ...
#ControllerAdvice
public class SpringExceptionResolver {
Logger logger = LoggerFactory.getLogger("com.realitylabs.event.controller.RecoverController");
#ExceptionHandler({CorruptedSessionUserException.class})
#ResponseBody
#ResponseStatus(value=HttpStatus.FORBIDDEN)
public ErrorObject userNotFoundExceptionHandler() {
// Handle exception
// log using the logger.
// We usually return an error object in JSON so that we can show custom // error messages.
}
}
Here is how a service might look. We usually call our services from the controllers. If an exception is thrown when coming from a controller the advice will handle it.
import ...
#Service(value="ObjectService")
#Transactional
public class ObjectServiceImpl implements ObjectService {
#Autowired
private ObjectDAO objectDAO;
#Override
public Object get(int id) {
Object o = objectDAO.get(id);
Hibernate.initialize(o.getVoters());
return o;
}
}
I hope this helps.
I have an EJB interceptor and I follow the BCE pattern suggested by Adam Bien, that is, all EJB calls on the boundary starts and finish a transaction which means there is no nested EJB calls (there might be nested CDI injected Bean calls though, but those should be inside the same transaction started at the ejb Boundary).
So in those ejb Boundaries I have an interceptor and I want to intercept or know if after the method call of the EJB the transacction commited already? (that is, if a EntityManager was involved that the COMMIT sql call was sent to the DB and returned succeesfuly)
Will I get that info from inside an Interceptor ?
If not, how can I get notified of a transaction that sucessfully commited or failed ?
NOTE: Of course, if I am the client of the EJB and I am calling the method, after the method call I know what happened with the transaction, but I am interested in intercepting that BEFORE the client receives the response from the EJB.
#AroundInvoke
public Object logMethodEntry(InvocationContext ctx) throws Exception {
Object proceed = null;
try {
proceed = ctx.proceed();
// is the transacction finished/commited already?
// is it still open ?
return proceed;
} catch (Exception e) {
throw e;
}
}
[UPDATE]: I accepted one good answer, but the thing is that THERE IS NO WAY in Java EE to receive an event of a transaction that HAS BEEN COMMITED. So regardless of the good answer, sadly there is no way to be notified in Java EE of a completed transaction, inside the server, of course, if you are the client caller, then you sure know the transaction commited or rolled back...
unless otherwise stated on the exception thrown, if an ejb method invocation throws an exception, it shall be rolled-back. Additionally, provided all calls to the DB were in the same transaction, they shall be deemed committed at the end of the transaction cycle.
In retrospect, all interceptors are invoked within the same transaction on which the ejb method it intercepts, was invoked (That's the reason the interceptor may decide in an event of an exception, to either roll-back or still commit the transaction).
Hence, you can know for sure, that the transaction completed successfully if within your interceptor call, after the proceed has been invoked and returned, there is no exception thrown, with a potential of transaction rollback.
So in your scenario:
#AroundInvoke
public Object logMethodEntry(InvocationContext ctx) throws Exception {
Object proceed = null;
try {
proceed = ctx.proceed();
// is the transacction finished/commited already?
// The transaction is successful, but afaik, it is not yet committed, until this method returns successfully
// is it still open ? More or less. You can still grab the Ejbtransaction and commit it manually or rollback if some other conditions have not been met yet
return proceed;
} catch (Exception e) {
//If this happens, and you propagate it, then for sure the transaction will be rolledback, and never get committed. Since all db calls were being done within this transaction, then no DB commit will be done.
throw e;
}
}
Edit:
for you to actually commit the transaction in an interceptor, you will need to be running application-managed transaction, otherwise, it is prohibited by the EJB specs to call commit on a container managed transaction, you can of course call setOnrollback method of the EJBContext.
Edit
If you truly want to do some DB changes, i would recommend:
user ApplicationManaged transaction, from which you manually start
and commit the transaction within the interceptor
Use the concept of the observer, and listen for #Observes(AFTER_SUCCESS) event which will be invoked when the
transaction is successfully committed and complete, and hence you
can be guaranteed to do a db call, and the new updates will be
available.
If you can ignore the BCE pattern, and spin off a new transaction to do the update, so that after it returns successfully, you will be guaranteed of commit, and then continue normally
```
#Stateless
public class TransactionService {
#TransactionAttribute(REQUIRES_NEW)
public Object executeTransaction(final Callable<Object> task) {
return task.call();
}
}
#Interceptor
public class MyInterceptor {
#EJB
private TransactionService service;
#AroundInvoke
public Object logMethodEntry(InvocationContext ctx) throws Exception {
Object proceed = null;
try {
proceed = service.executeTransactional(()->ctx.proceed());
//If you reach here, you will be guaranteed of commit and then you can do the elastic search update
return proceed;
} catch (Exception e) {
//If this happens, and you propagate it, then for sure the transaction will be rolledback, and never get committed. Since all db calls were being done within this transaction, then no DB commit will be done.
throw e;
}
}
}
Ok - the question is 4 years old now, but I think it makes still sense to give an answer.
You can for sure register a callback to get informed about the Transaction outcome. You simply use the registerSynchronization() API of javax.transaction.Transaction.
Transaction tx = ((TransactionManager) (new InitialContext()).lookup("java:/TransactionManager")).getTransaction();
tx.registerSynchronization(new Synchronization() {
public void beforeCompletion() {
// do stuff before completion
}
public void afterCompletion(int status) {
if (status == Status.STATUS_COMMITTED) {
// do something after successful commit }
}
});
Consider the following code snippet. (I am using Spring 3.1 and Hibernate 3.6)
#Override
#Transactional
public <T extends Termination> void progressToPendingStage(Class<T> entity,
Long terminationId, String userName) throws Exception {
Termination termination = findTerminationById(entity, terminationId);
//TODO improvise such that email does not get sent if data is not saved
if (termination.getStatus().equals(TerminationStatus.BEING_PREPARED.toString())) {
termination.setStatus(TerminationStatus.PENDING.toString());
termination.setSubmittedDate(new Date());
termination.setSubmittedBy(userName);
saveOrUpdateTermination(termination);
//Send an email to SAS
emailHelper.configureEmailAndSend(termination);
}
}
Unit tests for the above method indicate that email will be sent regardless that the saveOrUpdateTermination(termination) throws an exception or not. On further testing and some research I have uncovered that this behavior is the expected behavior. This is not what the business rules desire. An email should be sent only if the termination record was saved successfully. Any suggestions on how to make this behave in the desired manner? One way I can think of is to make the caller handle the exception thrown by the progressToPendingStage method and if no exception was thrown send an email. Am I on the right track or can we alter the way #Transaction behaves.
I have solved this issue by designing around the problem. Sending an Email was never meant to be part of the transaction. I created an object that performed post saving tasks. The object will catch the exception thrown upon saving the termination and if no exceptions were thrown I would then trigger an email to be sent out. One could also put this in an Spring Aspect which could be executed upon successfully returning after a successful save.
Lessons learn't: Don't include steps that don't belong in a method marked with #transaction. If its included in a transaction Spring will silently handle the exception and not throw the exception till the transaction is finished. In short if a method is annotated with #Transaction every line in that method will be execute even though a line in the middle of the method throws an exception.
How must I handle exceptions inside a mdb? I have the funny feeling that the exception happens after the try catch block so I'm not able to catch and log it. Glassfish v3 decides to repeat the whole message. It runns into a infinite loop and writes lot's of logfiles on the harddrive.
I'm using Glassfishv3.01 + Eclipselink 2.0.1
public class SaveAdMessageDrivenBean implements MessageListener {
#PersistenceContext(unitName="QIS")
private EntityManager em;
#Resource
private MessageDrivenContext mdc;
public void onMessage(Message message) {
try {
if (message instanceof ObjectMessage) {
ObjectMessage obj = (ObjectMessage)message;
AnalyzerResult alyzres = (AnalyzerResult)obj.getObject();
save(alyzres);
}
} catch (Throwable e) {
mdc.setRollbackOnly();
log.log(Level.SEVERE, e);
}
}
#TransactionAttribute(TransactionAttributeType.REQUIRED)
private void save(AnalyzerResult alyzres) throws PrdItemNotFoundException {
Some s = em.find(Some.class, somepk);
s.setSomeField("newvalue");
// SQL Exception happens after leaving this method because of missing field for ex.
}
}
You got a bad case of message poisoning...
The main issues I see are that:
you are calling directly the save() method in your onMessage(): this means thet the container has no way to inject the proper transaction handling proxy around the save method
in any case the save() method should have #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) in order to commit in a separate transaction, otherwise it will join the onMessage transaction (which default to REQUIRED) and bypass your exception handling code, beign committed after the successful execution of onMessage
What I woud do is:
Move the save method to a new Stateless session bean:
#Stateless
public class AnalyzerResultSaver
{
#PersistenceContext(unitName="QIS")
private EntityManager em;
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
private void save(AnalyzerResult alyzres) throws PrdItemNotFoundException {
Some s = em.find(Some.class, somepk);
s.setSomeField("newvalue");
// SQL Exception happens after leaving this method
}
}
Inject this bean in your MDB:
public class SaveAdMessageDrivenBean implements MessageListener {
#Inject
private AnalyzerResultSaver saver;
#Resource
private MessageDrivenContext mdc;
public void onMessage(Message message) {
try {
if (message instanceof ObjectMessage) {
ObjectMessage obj = (ObjectMessage)message;
AnalyzerResult alyzres = (AnalyzerResult)obj.getObject();
saver.save(alyzres);
}
} catch (Throwable e) {
mdc.setRollbackOnly();
log.log(Level.SEVERE, e);
}
}
}
Another tip: in this code the message poisoning still exists. Now it derives from the line invoking mdc.setRollbackOnly();.
I'd suggest here to log the exception and transfer the message to a poison queue, thus preventing the container to resubmit the message ad infinitum.
UPDATE:
A 'poison queue' or 'error queue' is simply a mean to guarantee that your (hopefully recoverable) discarded messages will not be completely lost. It is used heavily in integration scenarios, where the correctness of the message data is not guaranteed.
Setting up a poison queue implies defining a destination queue or topic and redeliver the 'bad' messages to this destination.
Periodically, an operator should inspect this queue (via a dedicated application) and either modify the messages and resubmit to the 'good' queue, or discard the message and ask for a resumbit.
I believe that the code that you have posted is mostly OK.
Your use of
#TransactionAttribute(TransactionAttributeType.REQUIRED)
is completely ignored because this (and most other) annotations can only be applied to business methods (including onMessage). That doesn't matter though because your onMessage method gets an implicit one for free.
This leads to the fact that message handling is transactional in a Java EE container. If the transaction fails for any reason the container is required to try and deliver the message again.
Now, your code is catching the exception from the save method, which is good. But then you're explicitly marking the transaction for rollback. This has the effect of telling the container that message delivery failed and that it should try again.
Therefore, if you remove:
mdc.setRollbackOnly();
the container will stop trying to redeliver the message.
If I'm not mistaken, you're letting the container handle the transactions. This way, the entity manager will queue the operations that will be flushed after the method finishes, that's why you're having exceptions after the method is finished.
Using em.flush() directly as a final step in the method will execute all the related queries of the transaction, throwing the exceptions there instead of being thrown later when the flush() is made by the container while commiting the transaction.