How to handle global exceptions in java spring? - java

Currently I am working on a small system to log all the uncaught exceptions and store them into a database for further debugging / development.
To do so I am using an UncaughtExceptionHandler for a specific thread:
public class GlobalExceptionHandler implements Thread.UncaughtExceptionHandler{
#Autowired
private LoggedExceptionService service;
public GlobalExceptionHandler() {
}
#Override
public void uncaughtException(Thread t, Throwable e) {
System.err.println("IN UNCAUGHTEXCEPTION METHOD");
this.service.saveException(new LoggedException(e));
}
}
As you can see the field service is injected and when I catch an exception, then I get a NullPointerException because the field is null.
The main problem is the usage of the GlobalExceptionHandler. If I inject using the constructor (like in this code snippet):
private LoggedExceptionService service;
#Autowired
public GlobalExceptionHandler(LoggedExceptionService service) {
this.service = service;
}
then the field is not null, but then I can not declare it as the exception handler, because I can not autowire it to java-native methods. The call would be:
Thread.setDefaultUncaughtExceptionHandler(new GlobalExceptionHandler());
Is there a possibility to autowire the handler to the thread method or what would be a good way to go?

Make it a component and set the derault exception handler in an #PostContruct method.
#Component
public class GlobalExceptionHandler implements Thread.UncaughtExceptionHandler{
#Autowired
private LoggedExceptionService service;
public GlobalExceptionHandler() {
}
#PostContruct
public void init(){
Thread.setDefaultUncaughtExceptionHandler(this);
}
#Override
public void uncaughtException(Thread t, Throwable e) {
System.err.println("IN UNCAUGHTEXCEPTION METHOD");
this.service.saveException(new LoggedException(e));
}
}
This allows you to automatically set the handler as methods annotated with #PostContruct in components are automatically executed on startup.
Making GlobalExceptionHandler a spring component also allows to autowire service that would never been set otherwise. Anyways, I would recommend you to usd constructor autowiring:
#Component
public class GlobalExceptionHandler implements Thread.UncaughtExceptionHandler{
private final LoggedExceptionService service;
#Autowired // #Autowired is actually not necessary if this is the only constructor
public GlobalExceptionHandler(LoggedExceptionService service) {
this.service=service
}
#PostContruct
public void init(){
Thread.setDefaultUncaughtExceptionHandler(this);
}
#Override
public void uncaughtException(Thread t, Throwable e) {
System.err.println("IN UNCAUGHTEXCEPTION METHOD");
this.service.saveException(new LoggedException(e));
}
}

Related

Bean life cycle - afterPropertiesSet()

I am trying to create CustomWebServiceMessageReceiverHandlerAdapter which extends org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.
WebServiceMessageReceiverHandlerAdapter extends abstract WebServiceMessageReceiverObjectSupportwhich implements InitializingBean.
I have a problem, because I don’t understand why I have to call
afterPropertiesSet() in custom handler. I get an error without calling this method: “factory message is required”. But, this method is calling in abstract class, so my custom handler should run afterPropertiesSet() from abstract class. If you know the solution, let me know. Thanks a lot.
edit: This is my CustomWebServiceMessageReceiverHandlerAdapter :
public class CustomWebServiceMessageReceiverHandlerAdapter extends WebServiceMessageReceiverHandlerAdapter {
#Override
protected void handleInvalidXmlException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler,
InvalidXmlException ex) throws Exception {
//code
}
#Override
public void afterPropertiesSet() {
}
}
WebServiceMessageReceiverHandlerAdapter and WebServiceMessageReceiverObjectSupport are from Spring Framework
public class WebServiceMessageReceiverHandlerAdapter extends WebServiceMessageReceiverObjectSupport{}
There is no afterPropertiesSet()
public abstract class WebServiceMessageReceiverObjectSupport implements InitializingBean {
private WebServiceMessageFactory messageFactory;
/** Returns the {#code WebServiceMessageFactory}. */
public WebServiceMessageFactory getMessageFactory() {
return messageFactory;
}
/** Sets the {#code WebServiceMessageFactory}. */
public void setMessageFactory(WebServiceMessageFactory messageFactory) {
this.messageFactory = messageFactory;
}
#Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(messageFactory, "messageFactory is required");
}
And now, when I am removing afterPropertiesSet() from my custom handler an exception is thrown. In my opinion, I don't understand something about life cycle of bean.
I'm unsure about your specific case. In general if a bean implements InitializingBean and thus the afterPropertiesSet this method is called after instantiation of the bean instance and after Spring injected all #Autowired properties/values.
In your specific case you need to ensure that messageFactory property of your (via inheritance) class is set. Typically this is done by Spring, if you provide a suitable setter for autowiring:
#Autowired
#Override
public void setMessageFactory(WebServiceMessageFactory messageFactory) {
super.setMessageFactory(messageFactory);
}
If you overrride afterPropertiesSet without calling super.afterPropertiesSet() creation of the bean will work as the assertion of the super implementation is skipped. But you will likely encounter problems further down the line as the messageFactory property is not properly initialized.

Sharing an instance of a class across a spring boot application

I have a particular class used to interface with a service that requires initialization. In the application lifecycle, the only place this makes sense is in the start of the application because the rest of the spring application cannot run without it. I had the idea to do this:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
try {
MyRequiredService mrs = new MyRequiredService();
mrs.connect(); // This will throw if it fails
run(MyApplication.class, args);
} catch(MyException e) {
System.out.println("Failed to connect to MyRequiredService!");
}
}
}
This will launch the service and attempt to connect but I have one big problem. How do I pass this class around the application? I need it's functions in the service endpoints I am writing.
I didn't see anything obvious and searching "passing class instance in spring boot application" turns up a bunch of unrelated topics.
Is there a smart, clean way to do this in spring boot? I apologize for a contrived example. The names of the service are unique enough I didn't want to violate any agreements.
You can make Spring do this for you. First, you need to annotate your class with #Service, so Spring will pick it up when scanning for classes.
Then, define an init() method and annotate it with #PostConstruct. Spring will instantiate your MyRequiredService class and call init()
#Service
public class MyRequiredService {
#PostConstruct
public void init() {
connect();
}
public void connect() {
// ...
}
}
You could call connect() from the constructor, but I don't like to define objects that may throw exceptions out of the constructor.
And then, you can use MyRequiredService in some other class by injecting it via the #Autowired annotation:
#Component
public class MyOtherClass {
private final MyRequiredService service;
public MyOtherClass(final MyRequiredService service) {
this.service = service;
}
// Other methods here.
}
This has the same overall effect as what you're trying to do above. If MyRequiredService fails, the application will not start up.
Make it a bean. Then it will be in the ApplicationContext which then you can pass to your desired other classes through the constructor
#Configuration
public class ApplicationConfiguration
{
#Bean
public MyRequiredService myRequiredService()
{
MyRequiredService mrs = new MyRequiredService();
try {
mrs.connect(); // This will throw if it fails
return mrs;
} catch(MyException e) {
log.error("Failed to connect to MyRequiredService!");
throw new IllegalStateException("MyRequiredService failed connection. Stopping startup");
}
}
#Bean
public SomeOtherService someOtherService(MyRequiredService mrs) {
return new SomeOtherService(mrs);
}
}
IMHO Instead of catching the error and logging it. I would throw it and stop the application from starting, but to keep with your example I added the throw IllegalStateException after the log.
Doing it this way Spring will create your MyRequiredService bean in the ApplicationContext then you can see I added as a parameter needed by the bean below that. Spring will grab that bean out of the ApplicationContext and supply it to the bean. If Spring doesn't find the bean in the ApplicationContext it will throw an error and stop the application from startup.
a class implements BeanFactoryPostProcessor which is init before normal bean
#Configuration
public class MyRequiredService implements BeanFactoryPostProcessor,
PriorityOrdered, InitializingBean {
#Override
public int getOrder() {
return Integer.MIN_VALUE;
}
public void connect() {
// ...
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
#Override
public void afterPropertiesSet() throws Exception {
connect();
}
}

Getting Hibernate Session inside AsyncUncaughtExceptionHandler

I want to create an exception log in the database when an #Async operation fails with an exception.
You can see the implementation for AsyncExecutorConfiguration and AsyncExceptionHandler classes below.
Inside AsyncExceptionHandler class, when I call a service that tries to access the database, I am getting: org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
#Configuration
#EnableAsync
public class AsyncExecutorConfiguration implements AsyncConfigurer {
#Autowired
private AsyncExceptionHandler asyncExceptionHandler;
private static final int CORE_POOL_SIZE = 3;
private static final int MAX_POOL_SIZE = 3;
private static final int QUEUE_CAPACITY = 24;
private static final String THREAD_NAME_PREFIX = "AsynchThread-";
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setMaxPoolSize(MAX_POOL_SIZE);
executor.setQueueCapacity(QUEUE_CAPACITY);
executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return asyncExceptionHandler;
}
}
#Component
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Autowired
private NotificationService notificationService;
#Override
#Transactional(rollbackFor = Exception.class, readOnly = false)
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
AsyncErrorLog log = new AsyncErrorLog(ex);
notificationService.saveLogAndNotify(log); // throws exception "Could not obtain transaction-synchronized Session for current thread"
}
}
#Service
public class MyServiceImpl implements MyService {
#Override
#Async
#Transactional(rollbackFor = Exception.class, readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void doSomething(Long id) {
// I can execute database operations here
}
...
#Async function itself already has a valid session. What should I do to have a valid session in AsyncExceptionHandler class too?
--
UPDATE
Here is the simplified implementations for NotificationServiceImpl and LogDaoImpl.class where we get the error.
#Service
public class NotificationServiceImpl implements NotificationService {
#Autowired
private LogDao logDao;
#Override
#Transactional(rollbackFor = Exception.class, readOnly = false)
public void saveLogAndNotify(Log log) {
return logDao.createLog(log);
}
#Repository
public class LogDaoImpl{
#Autowired
protected SessionFactory sessionFactory;
#Override
public void createLog(Log log) {
sessionFactory.getCurrentSession().saveOrUpdate(log);
}
Per the Hibernate exception; if you're not using Spring Data, you'll have to make sure the notification service explicitly invokes the database calls on the Hibernate session.
On another note, in my experience, the main use cases for the UncaughtExceptionHandler (in general) are used for:
A simple last-resort to handle RuntimeExceptions that may be unknown to the programmer that for some reason cannot (or are not) caught in code
A way to catch exceptions in code that the programmer has no control over (e.g. if you're invoking Async directly on some third party library, etc.)
The commonality between the two is that this Handler is used for something unexpected. In fact, Spring itself accounts for the "unexpectedness" in your own code and Spring Async already sets a default one for you that will log to the console (code here), letting you not have to worry about rogue exceptions killing threads and not knowing why. (Note: The message in the source code says it's catching an "unexpected" exception. Of course exceptions are unexpected, but these are one's that you really didn't know could happen. Spring Async will log it for you.)
That being the case, in your example, since you're doing Spring Database operations and should know exactly what's happening inside of #doSomething, I would just go with removing the AUEH a try-catch (and/or -finally) and handle the exception inside of #doSomething:
#Service
public class MyServiceImpl implements MyService {
// Self autowired class to take advantage of proxied methods in the same class
// See https://stackoverflow.com/questions/51922604/transactional-and-stream-in-spring/51923214#51923214
private MyService myService;
private NotificationService notificationService;
#Override
#Async
public void doSomething(Long id) {
// I can execute database operations here
try {
myService.doDatabaseOperations(...);
} catch(DatabaseAccessException e) {
AsyncErrorLog log = new AsyncErrorLog(ex);
notificationService.saveLogAndNotify(log);
}
// Other exceptions (from DB operations or the notifications service) can be
// handled with other "catches" or to let the SimpleAsyncExHandler log them for you.
// You can also use standard multithreading exception handling for this
}
#Transactional(rollbackFor = Exception.class, readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void doDatabaseOperations(...) {
...
}
}
You can use the applicationContext in your handler to lookup the notificationService. I had the same issue when I used #Autowired for the handler, which in turn injected my LogService. After looking at the logs I saw that the TransactionSynchronizationManager is clearing transaction synchronization after the rollback of the exception and nothing else except the no transaction for ...... error.
After using the applicationContext for looking up the logService bean and changing my handler, I saw the desired result in the logs.
begin
Initializing transaction synchronization
Getting transaction for [....AsyncService.doAsync]
Exception
rolling back
Clearing transaction synchronization
begin
Initializing transaction synchronization
Getting transaction for [.....LogService.save]
Change your config to include the interface ApplicationContextAware which will give you a convenience method to access the applicationContext. Set it as a instance variable.
See my configuration class below.
#Configuration
#EnableAsync
public class AsyncConfig implements AsyncConfigurer, ApplicationContextAware {
private static final int CORE_POOL_SIZE = 3;
private static final int MAX_POOL_SIZE = 3;
private static final int QUEUE_CAPACITY = 24;
private static final String THREAD_NAME_PREFIX = "AsynchThread-";
private ApplicationContext applicationContext;
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setMaxPoolSize(MAX_POOL_SIZE);
executor.setQueueCapacity(QUEUE_CAPACITY);
executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler(this.applicationContext);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
I have removed the #Component from the handler and use it as a POJO.
Every time getAsyncUncaughtExceptionHandler is called with an exception, a new handler instance is created with the applicationContext as a dependency.
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
private final ApplicationContext applicationContext;
public AsyncExceptionHandler(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
Log log = new Log();
log.setEntry(ex.getMessage());
LogService logService = this.applicationContext.getBean(LogService.class);
logService.save(log);
}
}
The save method on logService requires a new transaction every time it is called.
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void save(Log log)
This will help you:
#Override
public void createLog(Log log) {
try {
session = sessionFactory.getCurrentSession();
} catch (HibernateException e) {
session = sessionFactory.openSession();
}
session.saveOrUpdate(log);
}

Spring Boot: how to inject dependencies into a class called by a library?

I'm using Kinesis Client Library (KCL) and Spring boot. To use KCL, I have to implement a class (I named it RecordProcessor) for interface IRecordProcessor. And KCL will call this class and process records from kinesis. But when I tried to use dependency injection, I found it was not succeeded.
Here's the snippet for RecordProcessor:
#Component
public class RecordProcessor implements IRecordProcessor {
#Autowired
private SingleRecordProcessor singleRecordProcessor;
#Override
public void initialize(String shardId) {
...
}
#Override
public void processRecords(List<Record> records, IRecordProcessorCheckpointer checkpointer) {
...
}
}
I use Class SingleRecordProcessor to process single each record from kinesis. And this is my SingleRecordProcessor class snippet:
#Component
public class SingleRecordProcessor {
private Parser parser;
private Map<String, Table> tables;
public SingleRecordProcessor() {
}
#Autowired
private void setParser(Parser parser) {
this.parser = parser;
}
#Autowired
private void setTables(Map<String, Table> tables) {
this.tables = tables;
}
public void process(String record) {
...
}
}
I want to let spring framework automatically inject the SingleRecordProcessor instance into the class and use it. But I found that the field singleRecordProcessor is null.
Any idea why the dependency injection is failed? Or is it impossible to inject dependencies into a class which is called by other framework (in this case it's KCL)? Any suggestions will be appreciated! Really need some help please!!
[UPDATE]:
Sorry for not expressing the error clearly. The error was NullPointerException. I tried to inject singleRecordProcessor and call method process() on it. I think the injection was not successful so the instance singleRecordProcessor is null and there comes the NullPointerException.
More information is as follows:
I have a major class called Application
#SpringBootApplication
public class Application{
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Application.class);
application.addListeners(new ApplicationPidFileWriter("./app.pid"));
ConfigurableApplicationContext ctx = application.run(args);
}
}
And I have the MainProcessor class which will call KCL.
#Service
public final class MainProcessor {
#EventListener(ApplicationReadyEvent.class)
public static void startConsumer() throws Exception {
init();
IRecordProcessorFactory recordProcessorFactory = new RecordProcessorFactory();
Worker worker = new Worker(recordProcessorFactory, kinesisClientLibConfiguration);
...
worker.run(); // this line will call KCL library and eventually call ProcessorRecord class.
}
}
[UPDATE2]
RecordProcessorFactory only has one method like this
#Component
public class RecordProcessorFactory implements IRecordProcessorFactory {
#Autowired
RecordProcessor recordProcessor;
#Override
public IRecordProcessor createProcessor() {
return recordProcessor;
}
}
It creates a new RecordProcessor instance for KCL to use it.
You should autowire an instance of this into your MainProcessor:
#Component
public class RecordProcessorFactory {
#Lookup IRecordProcessor createProcessor() { return null; }
}
Spring will instantiate a RecordProcessorFactory for you, and replace the implementation of createProcessor() in it with one that will return a new IRecordProcessor each time it's called. Both the factory and the processors will be Spring beans - which is what you want.

How to catch transaction exceptions in #Async?

When writing transactional methods with #Async, it's not possible to catch the #Transactional exceptions. Like ObjectOptimisticLockingFailureException, because they are thrown outside of the method itself during eg transaction commit.
Example:
public class UpdateService {
#Autowired
private CrudRepository<MyEntity> dao;
//throws eg ObjectOptimisticLockingFailureException.class, cannot be caught
#Async
#Transactional
public void updateEntity {
MyEntity entity = dao.findOne(..);
entity.setField(..);
}
}
I know I can catch #Async exceptions in general as follows:
#Component
public class MyHandler extends AsyncConfigurerSupport {
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex, method, params) -> {
//handle
};
}
}
But I'd prefer to handle the given exception in a different way ONLY if it occurs within the UpdateService.
Question: how can I catch it inside the UpdateService?
Is the only chance: creating an additional #Service that wraps the UpdateService and has a try-catch block? Or could I do better?
You could try self-injecting your bean which should work with Spring 4.3. While self-injecting is generally not a good idea, this may be one of the use-cases which are legitimate.
#Autowired
private UpdateService self;
#Transactional
public void updateEntity() {
MyEntity entity = dao.findOne(..);
entity.setField(..);
}
#Async
public void updateEntityAsync(){
try {
self.updateEntity();
} catch (Exception e) {
// handle exception
}
}

Categories