Java - spring - quartz application
I have Java application with spring 3.x which use quartz scheduler to process some data.
I was looking at the following article where they defined global Global Servlet container exception handler
I want to to configure spring to catch any exception occur into my application.
Above mentioned article uses "org.springframework.web.servlet.handler.HandlerExceptionResolverComposite" it is servlet speicfic implementation that's why i can't use it.
any help is appreciated.
You did not mention what you want to do when an exception is caught. If you just want to log the exception, you could use AOP for that.
A probably simpler solution would be to wrap your tasks with a class that catches the exception.
The class you mention is used in a catch block in the Spring MVC DispatcherServlet for matching specific Exceptions to handlers. AFAIK there isn't a class to do this out-of-the-box with vanilla Spring, but there's no reason you couldn't create a similar execution container for your app:
public interface ExceptionHandler {
public void handle(Exception e);
}
public class ExecutionEnvironment {
private Map<Class, ExceptionHandler> executionHandlers;
public void run() {
try {
// Your app code...
} catch (Exception e) {
if(executionHandlers.get(e.getClass()) != null) {
executionHandlers.get(e.getClass()).handle(e);
} else {
throw new RuntimeException(e);
}
}
}
}
Then use a context configuration to set up your exception handlers. Hope this helps.
Related
I have a Spring Boot server application where exceptions are caught, logged and handled when possible and when not possible they are handled by #ControllerAdvice as the last resort.
I want to add more logic for exception handling, for example, sending a metric to a monitoring solution on each exception. For example there's a Spring bean MetricsBean which is a service which sends metrics. Using MetricsBean is easy for errors which are handled by #ControllerAdvice because it's also a Spring bean.
However using MetricsBean is not as straightforward in such code:
try {
// business logic
} catch (Exception ex) {
logger.error(ex)
}
because that would mean manually autowiring MetricsBean into each service class. Also logger object is not a Spring bean.
So I have a dilemma how do I send a metric from inside or around logger.error() method when the logger class itself is not a Spring bean. I'm wondering what the best practice is in such cases. Here're some of the options:
Should logger also be a Spring bean? (seems tedious and error-prone)
Should there be a custom Exception extending class which would be a Spring bean and will send a metric inside its constructor? (seems tedious and error-prone)
Should Spring AOP be used with the pointcut being logger.error() method?
I'm surprised that there's not an established solution for this pattern for such an established framework as Spring (most of the solutions focus on handling errors in #ControllerAdvice).
I'd like the solution to be as generic as possible (point 3 seems to win there) but perhaps there's a pattern which I'm missing.
Not sure if it is the best practice but I would use a UtilityClass as a facade exposing a static method. In that method, wire the metrics registry using Metrics.globalRegistry to avoid injection. This will avoid coupling the metric details from where it is used.
You can then either invoke the static method everywhere you are handling an exception (so besides logger.error(ex)) or combine this with your third option with the pointcut.
Simple example:
#UtilityClass
public class ExceptionUtility {
public static void handleException(Throwable throwable) {
Counter.builder("some.name.exception")
.tag("name", throwable.getClass().getName())
.register(Metrics.globalRegistry)
.increment();
}
}
Usage:
try {
...
} catch (RestClientException restClientException) {
ExceptionUtility.handleException(restClientException);
}
Spring Boot automatically initializes the underlying logging system using the LoggingApplicationListener. This is a nice thing if the application I'm developing runs isolated or standalone.
However I'm developing a web application that will be deployed into the WSO2 Application Server, which offers unified logging (using log4j), with features like central log level management (at runtime via web interface), business reporting etc.
If I use Spring Boot "as is", it logs everything completely on its own. My first shot was, to remove spring-boot-starter-logging and manually add slf4j-api as provided. This works to some extent, since the LoggingApplicationListener now overrides settings of the global logmanager provided by WSO2 (and even causes global appenders to be closed).
The only "solution" I came up with is to remove the listener via reflection. Then Spring Boot starts to behave exactly as it should (logging via the global logger and not overriding the pre defined log levels, output formats, appenders, etc.)
That "solution" looks like this:
#SpringBootApplication
public class MyApp extends SpringBootServletInitializer {
public static void main(String... args) {
SpringApplication.run(MyApp.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
try {
Field appField = SpringApplicationBuilder.class.getDeclaredField("application");
appField.setAccessible(true);
SpringApplication app = (SpringApplication)appField.get(builder);
Field listenersField = SpringApplication.class.getDeclaredField("listeners");
listenersField.setAccessible(true);
List<ApplicationListener<?>> listeners = (List<ApplicationListener<?>>) listenersField.get(app);
for (int i = listeners.size() - 1; i >= 0; --i) {
if (listeners.get(i) instanceof LoggingApplicationListener) {
listeners.remove(i);
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return builder.sources(MyApp.class);
}
}
Is there any better solution to my problem that's maybe less hacky which I may have overlooked during my research and code analysis?
thank you for you post it is very helpful.
I had the same problem with Websphere Aplication Server: After spring boot context initialized I had no more logs. This solution is equivalent but less dirty by overriding the run method of SpringBootServletInitializer:
#Override
protected WebApplicationContext run(SpringApplication application) {
Collection<ApplicationListener<?>> listeners =
new ArrayList<>();
for (ApplicationListener<?> listener: application.getListeners()) {
if (!(listener instanceof LoggingApplicationListener)) {
listeners.add(listener);
}
}
application.setListeners(listeners);
return super.run(application);
}
Since Spring Boot 1.4 the LoggingSystem autoconfiguration can be disabled.
Take a look at the Custom Log Configuration section of the Spring documentation:
You can force Spring Boot to use a particular logging system by using the org.springframework.boot.logging.LoggingSystem system property. The value should be the fully qualified class name of a LoggingSystem implementation. You can also disable Spring Boot’s logging configuration entirely by using a value of none.
For Tomcat, for example, set the environment variable JAVA_OPTS:
JAVA_OPTS="-Dorg.springframework.boot.logging.LoggingSystem=none"
My Application - Java 1.6, Spring 3.1.2, Hibernate 4.1.1, Thymeleaf 2.0.15
Currently in my application, there is no any exception/error handling mechanism is implemented. I handling exceptions in ordinary cultural way. But now I need to introduce a "Robust Error Handling Mechanism". Please suggest me to implement Custom Error/Exception Handling mechanism with Example.
Thanks and appriciate from experts like you.
I'll make as an answer its easier to format. When you say "But I need to implement some Generalized Custom Exception so that same Exception could be thrown acrross the application." Its how I understood it and something that #ControllerAdvice is directly handling, but it applies only to Controllers. Than again, all you lower layers can declare throws on the method, and delegate it to Controller for exception handling. As an example, the following would be the error handling controller handling your custom exception
#ControllerAdvice
public class GlobalErrorHandler {
#ExceptionHandler(value = VermaException.class)
#ResponseBody
public String heightError(VermaException ex) {
return "error";
}
}
Your exception
public class VermaException extends Exception {
}
now whenever the the exception is thrown from the controller class, it will be captured and handled in your GlobalErrorHandler.
Again pasting the reference http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
UPDATE after comment
#ControllerAdvice is added in the version 3.2, for the earlier version you can have a CommonController extended by your controller, containing the error handler methods e.g. per Controller solution
#Controller
public class CommonController {
#ExceptionHandler
public #ResponseBody String handle(VermaException e) {
return "error";
}
}
an extending contorller
#Controller
public class ExceptionController extends CommonController {
#RequestMapping("/exception")
public #ResponseBody String exception() {
throw new VermaException();
}
}
I use tomcat 7.0.23 and spring 3.0.5+. I want to generate event if application has started succeessfully or failed. I can generate successful event: I create bean ProductPostInitializer implements InitializingBean which depends on my service beans and if method afterPropertiesSet calls I generate successful event.
How to generate error event?
Subclass ContextLoaderListener:
public class NotifyingContextLoaderListener extends ContextLoaderListener {
#Override
public void contextInitialized(ServletContextEvent event) {
try {
super.contextInitialized(event);
//generate success event
}
catch (RuntimeException e) {
//generate failure event
throw e;
}
}
}
And use it in your web.xml instead of ContextLoaderListener:
<listener>
<listener-class>com.example.NotifyingContextLoaderListener</listener-class>
</listener>
Note that your solution for generating success event is not completely safe. It generates event when ProductPostInitializer bean is successfully created, not the whole application/context. This means the event can be generated even if the context startup fails afterwards (e.g. beans depending on ProductPostInitializer fails to start).
Solution above solves this issue.
I am using EJB3 on Glassfish using the default TopLink persistance manager. Within a Session Bean, when the persistence manager catches a DB exception, it marks the transaction to be rolled back, and throws an EJBException, in turn wrapping a RollbackException. Now I was expecting to be able to get the original jdbc exception out of the caused by exception of one of these exceptions, but it is not.
It is important that I do retrieve the original exception, as I need to report back to the users what the problem is, and to do this I need to analyse the SQL error codes.
Does anyone know if it is possible to get this information from Toplink? Or whether Hibernate makes it possible?
Thanks,
I had the same issue. I ended up using the AroundInvoke interceptor method , that way you can catch any exception on the server side , and extract whatever info you want to and wrap it to throw your own exception , and set the EjbContext to rollback the transaction.
I can provide you with an example if you don't come right.
Good question, Ant
I know you want to throw a database exception but when it occurs the application, in most of the time, is not able to restore its initial state or it does not know how to recover from it. So it should be handled as a runtime exception. Some problems in database exceptions includes
database connection failure
query is wrong
table or column does not exist
Above you see the application is not be able to restore its initial state. If you think it is possible restore its initial state so you should use a application exception. Client will get the same application exception thrown by your business method. If you want to be able to get the exact exception thrown by your business method you have two choices:
Use a business delegate pattern to access your EJB
As you know, runtime exception is wrapped by a EJBException, so you shold use something like
Let's suppose you have this Stateless session bean
#Stateless
public class BeanImpl implements Bean {
public void doSomething() {
try {
// some code
} catch(SomeException e) {
throw new EJBException(e);
}
}
}
So you wrap your session bean through a business delegate
public class BeamBusinessDelegate implements Bean {
// your stateless session bean goes here
private Bean bean;
public BeamImpl() {
InitialContext i = new InitialContext();
bean = (Bean) i.lookup(<GLOBAL_JNDI_ADDRESS_OR_RELATIVE_ENVIRONMENT_NAMING_CONTEXT_ADDRESS>);
}
public void doSomething() {
try {
bean.doSomething()
} catch(EJBException e) {
throw e.getCause();
}
}
}
Or you can extends EJBException according to your needs
public class DatabaseException extends EJBException {
}
So in your business method
#Stateless
public class BeanImpl implements Bean {
public void doSomething() {
try {
// some code
} catch(SomeException e) {
throw new DatabaseException();
}
}
}
regards,
The only way I've found to do what I want, is to force the manager to write to the db using manager.flush(), and then catch the PersistenceException that that throws. I can then log the database error as I want, and throw an EJBException to force rollback. Leaving the container to do the flush seems to irretrievably lose any useful messages with TopLink.
I have the same question : how to get the SQL error message generated from JPA?
I haven't found the solution either but, I added this line in my persistence.xml
<properties>
<property name="toplink.logging.level" value="FINE" />
</properties>
and now, I can see the sql commands issued.
Reference :
http://www.jairrillo.com/blog/2008/09/04/introduction-to-jpa-part-1-getting-started/