Which is the best way to handle exception in java MDB? - java

I got this use case:
Here the class diagram
Here the sequence diagram
This diagram represents an enterprise model. Java EE technology on Weblogic 10.3 with the leverage of spring framework for IoC and AOP, JPA for persistence with spring jpatemplate, spring integration for the interaction frame.
As you can see there is no coupling between the Service and the Gateway since spring integration add all the magic sugar needed.
Now I have to deal with the exception handling. All the chain has no checked exceptions: also the data access has no checked exception since jpatemplate wraps all the sql exception in runtime exceptions.
So the only checked exception that I handle is on the MDB
#Override
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void onMessage(Message message) {
try {
TextMessage textMessage = (TextMessage) message;
String stringMessage = textMessage.getText();
OnlineEventMessage<? extends Serializable> event = eventMessageParser.parse(stringMessage);
legacyEventMessageService.handle(event);
} catch (JMSException e) {
logger.error("si e' verificato un errore JMS nel processamento dell'evento {}", message, e);
}
}
I noticed that if I get a NPE for example on some component of the chain the message is rolled back on the JMS queue and the process is looped back.
Which is the best way to handle exceptions in this scenario?
Catch all the runtimeExceptions in the MDB?
Kind regards
Massimo

Which is the best way to handle exceptions in this scenario? Catch all the runtimeExceptions in the MDB?
It depends on what you want to achieve. If feel from your description that you want to prevent the message being rolled back. Is that right?
In that case catching all run time exceptions gets you only so far. The system could also throw errors, which you won't catch then. So you have to catch Throwable instead. But, then the transaction could still time out, resulting in a roll-back.
In short, do you want your MDB to be transactional at all?
Also note that the transaction context from the sender does not propagate to the MDB.
Slightly off topic, but are you really sure you need jpatemplate? It seems pretty much everybody has agreed that the JPA API is fine by itself and doesn't need any 'enhancements' from Spring, including SpringSource themselves.

Related

Handling JMSExceptions when using JMS, specifically ActiveMQ?

In the following snippet:
Connection connection=getConnection();
try {
Session session=connection.createSession();
try {
Queue queue=session.createQueue("foobar");
MessageConsumer consumer=null;
try {
consumer = session.createConsumer(queue);
}
catch(JMSException e) {
// Under what circumstances does this happen?
}
try {
// ...
}
finally {
if(consumer != null) consumer.close();
}
}
finally {
session.close();
}
}
finally {
connection.close();
}
Under what circumstances is the caught JMSException thrown? When it happens, what is the proper way to handle the JMSException? I've read the relevant bits of JMS and ActiveMQ documentation and it doesn't seem to give any guidance on this point (other than use try/catch, obviously.) Also please forgive the contrived code example!
For example, does a JMSException happen if session has "gone bad" in some way, so it's The Right Thing to tear down consumer and session and start over? Or does it mean connection has gone bad, and so I should tear down and rebuild everything in the application that is based on that connection? Or can a call to createConsumer() fail in a transient way, and retrying the call with the same session could succeed?
Similarly, when would these lines throw a JMSException, ignoring something like a closed Session or Connection:
Message message=consumer.receive();
producer.send(message);
I'd like to understand how JMSExceptions should be handled in JMS in general, but answers specific to ActiveMQ are fine, too. (In fact, it's likely that any answer may have to be specific to a specific implementation, since JMS is just a spec.)
The exceptions can be thrown for a number of reasons. The most common would be that the connection was lost due to a down network or broker. Other reasons can be resource exhaustion on the broker, security violations, etc.
To avoid the exceptions caused by network connection issues you can use ActiveMQ's failover transport to have the client do automatic reconnection. There are a number of exception types in JMS so you can test for a given error, like checking to see if consumer creation or producer send failed because of a security exception. If you don't know the actual causes then the most common thing to do is tear down and rebuild your connection resources. This is why using failover is better as you can avoid a lot of work by letting the client handle detecting and responding to this.

What are some options for java messaging from service layer to front end

So in my Service layer I have some logic that does some stuff. Sometimes it has to check to see if something is possible before it does it OR it just has to get some OK from the front end ("Are you sure you want to do this?" kind of stuff). The front end, of course, sends a transaction to the screen to get the info.
In the past I have used RuntimeExceptions for this. I will throw a
new MessageException("are.you.sure");
and then the controller level will do a
try{
theService.doSomething();
}catch(MessageException me) {
model.addAttribute(me.getMessageKey());
result.addError(new ObjectError());
}
In another application I made a PostOffice object and would put letters and such in it for messages. It was quite elaborate and very nice, but my new app is much smaller and I don't want all that.
So I am looking for some best practices. How do you guys send messages from the service layer to the front end? And keep in mind, I am NOT talking about Exceptions! I am just talking about messages from the service layer.
One beautiful thing about Spring MVC is the Exception handling. Since the DispatcherServlet has a try-catch(Exception) wrapping the handler method (ie. your controller method), it can catch all exceptions thrown and handle them with #ExceptionHandler methods or some other construct (there are alternatives).
What I've started doing is making my Service methods only throw RuntimeException instances (works well with #Transactional) and define all my #ExceptionHandler methods in a #ControllerAdvice annotated class. Something like:
#ControllerAdvice
public class ControllerHandler {
private static final Logger logger = LoggerFactory.getLogger(ControllerHandler.class);
#ExceptionHandler(value = AuthenticationException.class)
public String handleAuthenticationException(AuthenticationException e) {
if (logger.isInfoEnabled()) {
logger.info("An AuthenticationException occurred: {}", e.getMessage());
}
return "redirect:/";
}
...more
}
#ExceptionHandler annotated methods have a few rules, but are very customizable. You can see all possibilities in the javadoc here. You should also take a look at ResponseStatus.
Personally, I rarely throw checked exceptions from my service layer. The only one that often appears is IOException (parsing JSON, opening files) and even that I like to wrap in a RuntimeException, because it's not like I can do anything special about it at that level.

MyBatis mapper injected directly into service class. What about exceptions?

I'm currently working with MyBatis-Spring integration framework and that's what I read from docs:
Rather than code data access objects (DAOs) manually using SqlSessionDaoSupport or SqlSessionTemplate,
Mybatis-Spring provides a proxy factory: MapperFactoryBean. This class lets you inject data mapper interfaces
directly into your service beans. When using mappers you simply call them as you have always called your
DAOs, but you won't need to code any DAO implementation because MyBatis-Spring will create a proxy for
you.
That's a very nice feature... but what about exception handling? Where should I translate SQL errors? In my service layer? But wouldn't it violate service-DAO patterns?
Example:
public final class AccountServiceImpl implements AccountService {
(...)
private AccountMapper accountMapper;
(...)
#Override
public void addAccount(Account account) throws AccountServiceException {
//Validating, processing, setting timestamps etc.
(...)
//Persistence:
int rowsAffected;
try {
rowsAffected = accountMapper.insertAccount(account);
} catch (Exception e) {
String msg = e.getMessage();
if (msg.contains("accounts_pkey"))
throw new AccountServiceException("Username already exists!");
if (msg.contains("accounts_email_key"))
throw new AccountServiceException("E-mail already exists!");
throw new AccountServiceException(APP_ERROR);
}
LOG.debug("Rows affected: '{}'", rowsAffected);
if (rowsAffected != 1)
throw new AccountServiceException(APP_ERROR);
}
Is it OK to translate exceptions in service layer?
How should it be done?
Thanks in advance for you advice.
Having recently used mybatis-spring for a project I came across the same stumbling block. I also didn't want to litter my service class with DAO exception handling, particularly since some methods in my service layer required read-only access to a lot of different tables.
The solution I arrived at was to catch the exceptions in the service layer but create your own exception type that takes the caught exception as a parameter. This can then filter out what kind of error message should be contained when the exception is actually constructed and remove the need for string matching (in the service layer at least).
You are close to that there, except the AccountServiceException would have a constructor that took the Exception e as a parameter. I also chose to try and do all my data access as early as possible and wrap it all in a single try/catch. Since the MapperFactoryBean always translates thrown exceptions in to Spring DataAccessExceptions you don't have to worry about catching other kinds of exceptions when doing data access.
I hesitate to consider this an answer as such - more of a sharing of experience given I came across that and hesitated as well.
Translating low level DataAccessExceptions thrown by MyBatis to application-defined ones in service layer is a standard practice.
It's usually connected to transaction handling as you can't handle the transaction spanning multiple DAOs in DA layer.
So yes it's OK and even recommended.
Normally I log the exceptions thrown by DAO in error log and rethrow something defined by application.

Proper EJB Exception handling - ClassNotFoundException from client

I have some EJBs that use Hibernate to persist data to the database. I have a thick Swing client that talks to these EJBs. The client knows nothing about the database (no driver jar).
During one transaction a Hibernate ConstraintViolationException can be thrown. I catch all exceptions and wrap them in an EJBException like so:
catch(HibernateException e) {
e.printStackTrace();
throw new EJBException(e);
}
The problem I am getting is that when the exception is unmarshalled by the JBoss Invoker on the client side, a ClassNotFoundException is thrown (for PSQLException) since the client has no sql driver jar in the classpath.
I changed this application to always pass the caught exception to the ejbexception constructor like this so we could have a stack trace history. Now I am finding why the original developers didn't do this.
At this point I see two options - either include the postgres driver jar with the client, or remove passing the caught exception to the EJBException constructor. I am curious if anyone has any other suggestions and also how others handle exceptions in their EJBs?
My take is that the client, end user, doesn't need to know the technical details of the problem. Hence at various layer boundaries it's quite reasonble to convert a technical exception to a general "An error of nature XYZ ocurred".
A scheme I've seen used is for the server to allocate a unique error number at the point the exception is detected. It then writes diagnistics to its logs including that number. Messages reported to the client simply include the number. A support desk can then correlate the user's report of the issue via that specific error number.

EJB3 - handling RollBackExceptions

I have an EJB3 application which consists of some EJB's for accessing a DB, and exposed via a Session Bean as a web service.
Now there are two things I need to find out:
1) Is there any way I can stop SQL exceptions from causing the web service from throwing a SOAP Fault? The transactions are handled by the container, and currently sql exceptions cause a RollBackException to be thrown, and consequently the transaction to be rolled back (desired behaviour) and the web service to throw a fault (not desired).
2) I wish to extend the webservice to be able to take in a list of entities, and the session bean to persist each. However, I want each entity to be executed in its own transaction, so that if one fails the others are not affected (and again the web service should not fault).
For (1) I have tried to catch the RollBackException, but I assume this is thrown somewhere on another thread, as the catch block is never reached. I assume for (2) I will need to look into User Transactions, but firstly would prefer the container to manage this, and secondly do not know how to force the use of user transactions.
Thanks.
no, you can do all this with container managed transactions (and this is definitely preferable, as managing transactions is a pain).
the gist of the solution is to create a second EJB with a local interface only and the transaction semantics you desire. then your "public" ejb, which the web-service is calling directly, calls into this second ejb via its local interface to do the actual work.
something along the lines of:
public class MyPublicEjb {
#EJB
private MyPrivateImpl impl;
public void doSomething() {
try {
impl.doSomething();
} catch(TXRolledBack) {
// handle rollback ...
}
}
}
I know this looks sort of ugly, but trust me, this is far preferable to directly manipulating transactions.
For (1): Debug your code to find out where the exception is being thrown and what is causing it. Then handle the exception there.
For (2): Wrap each instance with beginTransaction() and commit().
for(each Entity){
try{
//begin transaction
//save entity
//commit
} catch(Exception e) {
//handle Exception, but continue on
}
}

Categories