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.
Related
In my spring boot application I have multiple #Service implementations of an interface. Which of these implementations is used at runtime for any given request is configured in a databse.
Something like this:
Value
Service Bean
Hello
ServiceA
World
ServiceB
Foo
ServiceA
Bar
ServiceC
The correct bean is then retrieved using the application context and the defined Service Bean Name from the database. However it could be possible that a Service Bean is mentioned in the database that does not exist in the application context. I'd rather detect this during startup than at runtime.
This question basically boils down to how you add your own validation to the spring boot startup process or what's the best practice? I tried throwing an Exception when creating the bean, that deals with the mapping of values to Service Beans, and handling it with my own FailureAnalyzer. But the FailureAnalyzer never gets called because due to the missing bean an UnsatisfiedDependencyException is also thrown and causes the application to stop.
I found a solution that I'm happy with.
I did not register my FailureAnalyzer in my resource folder in the META-INF/spring.factories file.
As described in the question throwing my Exception during bean creation caused an UnsatisfiedDependencyException. So instead I used an ApplicationListener and perform my check, when the application is ready but has not yet started.
#Component
public class ApplicationReadyListener implements ApplicationListener<ApplicationReadyEvent> {
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
if(!checkConsistency())
throw new MyRuntimeException();
}
}
Now my FailureAnalyzer gets called and handles MyRuntimeException and stops the application with a proper message.
#Component
public class MyRuntimeExceptionFailureAnalyzer extends AbstractFailureAnalyzer<MyRuntimeException> {
#Override
protected FailureAnalysis analyze(Throwable rootFailure, MyRuntimeException cause) {
return new FailureAnalysis(buildErrorMessage(cause), buildActionMessage(cause), cause);
}
}
I need to get some configuration and connect to external resources/objects/systems somewhere and store it in application scope.
I can see two ways to setup my application:
Overriding the init() in the existing servlets and required code there and keeping all constructed objects inside that same servlet.
Having some kind of an initialisation servlet and using its init() to do the work. Then storing created objects in ServletContext to share it with my other servlets.
Which out of above is better approach? Is there any better way to share objects between servlets? Calling them directly from one another or so...?
None of both is the better approach. Servlets are intended to listen on HTTP events (HTTP requests), not on deployment events (startup/shutdown).
CDI/EJB unavailable? Use ServletContextListener
#WebListener
public class Config implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
// Do stuff during webapp's startup.
}
public void contextDestroyed(ServletContextEvent event) {
// Do stuff during webapp's shutdown.
}
}
If you're not on Servlet 3.0 yet and can't upgrade (it would be about time because Servlet 3.0 was introduced more than a decade ago), and thus can't use #WebListener annotation, then you need to manually register it in /WEB-INF/web.xml like below:
<listener>
<listener-class>com.example.Config</listener-class>
</listener>
To store and obtain objects in the application scope (so that all servlets can access them), use ServletContext#setAttribute() and #getAttribute().
Here's an example which lets the listener store itself in the application scope:
public void contextInitialized(ServletContextEvent event) {
event.getServletContext().setAttribute("config", this);
// ...
}
and then obtain it in a servlet:
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
Config config = (Config) getServletContext().getAttribute("config");
// ...
}
It's also available in JSP EL by ${config}. So you could make it a simple bean as well.
CDI available? Use #Observes on ApplicationScoped.class
import jakarta.enterprise.context.ApplicationScoped; // And thus NOT e.g. jakarta.faces.bean.ApplicationScoped
#ApplicationScoped
public class Config {
public void init(#Observes #Initialized(ApplicationScoped.class) ServletContext context) {
// Do stuff during webapp's startup.
}
public void destroy(#Observes #Destroyed(ApplicationScoped.class) ServletContext context) {
// Do stuff during webapp's shutdown.
}
}
This is available in a servlet via #Inject. Make it if necessary also #Named so it's available via #{config} in EL as well.
Noted should be that this is new since CDI 1.1. If you're still on CDI 1.0 and can't upgrade, then pick another approach.
In case you're curious how to install CDI on a non-JEE server such as Tomcat, head to: How to install and use CDI on Tomcat?
EJB available? Consider #Startup#Singleton
#Startup
#Singleton
public class Config {
#PostConstruct
public void init() {
// Do stuff during webapp's startup.
}
#PreDestroy
public void destroy() {
// Do stuff during webapp's shutdown.
}
}
This is available in a servlet via #EJB. The difference with other approaches is that it's by default transactional and in case of #Singleton also read/write locked. So if you would ever need to inject a random EJB (e.g. #Stateless) into a #WebListener or an #ApplicationScoped then you could basically as good merge both into a single #Startup #Singleton.
See also:
How to run a background task in a servlet based web application?
ServletContainerInitializer vs ServletContextListener
How do I force an application-scoped bean to instantiate at application startup?
I need to get some configuration and connect to external resources/objects/systems somewhere and store it in application scope.
I can see two ways to setup my application:
Overriding the init() in the existing servlets and required code there and keeping all constructed objects inside that same servlet.
Having some kind of an initialisation servlet and using its init() to do the work. Then storing created objects in ServletContext to share it with my other servlets.
Which out of above is better approach? Is there any better way to share objects between servlets? Calling them directly from one another or so...?
None of both is the better approach. Servlets are intended to listen on HTTP events (HTTP requests), not on deployment events (startup/shutdown).
CDI/EJB unavailable? Use ServletContextListener
#WebListener
public class Config implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
// Do stuff during webapp's startup.
}
public void contextDestroyed(ServletContextEvent event) {
// Do stuff during webapp's shutdown.
}
}
If you're not on Servlet 3.0 yet and can't upgrade (it would be about time because Servlet 3.0 was introduced more than a decade ago), and thus can't use #WebListener annotation, then you need to manually register it in /WEB-INF/web.xml like below:
<listener>
<listener-class>com.example.Config</listener-class>
</listener>
To store and obtain objects in the application scope (so that all servlets can access them), use ServletContext#setAttribute() and #getAttribute().
Here's an example which lets the listener store itself in the application scope:
public void contextInitialized(ServletContextEvent event) {
event.getServletContext().setAttribute("config", this);
// ...
}
and then obtain it in a servlet:
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
Config config = (Config) getServletContext().getAttribute("config");
// ...
}
It's also available in JSP EL by ${config}. So you could make it a simple bean as well.
CDI available? Use #Observes on ApplicationScoped.class
import jakarta.enterprise.context.ApplicationScoped; // And thus NOT e.g. jakarta.faces.bean.ApplicationScoped
#ApplicationScoped
public class Config {
public void init(#Observes #Initialized(ApplicationScoped.class) ServletContext context) {
// Do stuff during webapp's startup.
}
public void destroy(#Observes #Destroyed(ApplicationScoped.class) ServletContext context) {
// Do stuff during webapp's shutdown.
}
}
This is available in a servlet via #Inject. Make it if necessary also #Named so it's available via #{config} in EL as well.
Noted should be that this is new since CDI 1.1. If you're still on CDI 1.0 and can't upgrade, then pick another approach.
In case you're curious how to install CDI on a non-JEE server such as Tomcat, head to: How to install and use CDI on Tomcat?
EJB available? Consider #Startup#Singleton
#Startup
#Singleton
public class Config {
#PostConstruct
public void init() {
// Do stuff during webapp's startup.
}
#PreDestroy
public void destroy() {
// Do stuff during webapp's shutdown.
}
}
This is available in a servlet via #EJB. The difference with other approaches is that it's by default transactional and in case of #Singleton also read/write locked. So if you would ever need to inject a random EJB (e.g. #Stateless) into a #WebListener or an #ApplicationScoped then you could basically as good merge both into a single #Startup #Singleton.
See also:
How to run a background task in a servlet based web application?
ServletContainerInitializer vs ServletContextListener
How do I force an application-scoped bean to instantiate at application startup?
I'm using a framework to deal with some request, now, I can start it from a main function like this:
public Main{
public static void main(String[] args) {
//init the framework
Init initer = new Init();
initer.initFramework();
//start my besiness code below
}
}
now I need to package my project in a war format and put it into tomcat, how can I be able to excute the initialization code?
When you start a war-file in Tomcat using servlet 2.3 or higher you can use the web.xml file to declare listeners.
<listener>
<listener-class>test.MyListener</listener-class>
</listener>
This listener should implement the ServletContextListener interface. Listeners gets notified of lifecycle changes (such as when the servlet context has been initialized). The listener can be implemented to run your initialization code as illustrated below:
public class MyListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
Init initer = new Init();
initer.initFramework();
}
#Override
public void contextDestroyed(ServletContextEvent event) {
// destroy stuff
}
}
If you are running the war-file in a servlet 3 environment there might not even be a web.xml file. In that case the #WebListener annotation can be used to indicate that your component should be called. The JavaDoc for #WebListener can be found here but basically what you need to do is to add the annotation to your listener.
Just implement org.springframework.web.WebApplicationInitializer and that's it, no xml.
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.