I have a function I want to execute straight after tomcat has started and loaded all its attributes successfully. I don't want to use ServletContextListener as this will require the function to start before tomcat starts. Can someone suggest how to go about this?
ServletContextListener.contextInitialized(..) is the method that is called after all servlets and filters have initialized for a given application.
if there are multiple ServletContextListeners, some of them are called before the others (logically)
if there are multiple applications (hence multiple contexts) some of them are started before others.
Update I will now assume your setup, although you didn't share it:
you start spring via a listener (and not with a servlet)
you configure hibernate within spring
In that case, you have two options:
define your ServletContextListener after the one for spring in web.xml, thus guaranteeing it will be invoked after it
use spring's lifecycle processor
You could create a startup servlet and then add that to the end of your web.xml:
<servlet>
<servlet-name>StartupServlet</servlet-name>
<servlet-class>com.your.package.MyStartupServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
public class MyStartupServlet extends HttpServlet {
public void init(ServletConfig config) throws ServletException {
try {
// Startup code here
} catch (Exception e){
// Log exception
}
}
public java.lang.String getServletInfo() {
return "StartupServlet";
}
}
I think JMX Tomcat supports can meet your requirement, even no ServletContextListener is deployed in container.
Related
I have a JSP webpage being run on a Tomcat server in Eclipse. The website has a Java back-end which it communicates with through Servlets. Everything works fine, but I'd like to have a background process running - say, a Main method. This method should start upon server start-up.
The reason I need this is as follows: Say the webpage has different users log in, and I want to track their user names and do something with them - maybe create sessions to track some metrics.
I realize that one way to do this might be to check if our method is running every time somebody logs in. Like so:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (/* Method is not initialized */)
/*call Method*/
I wonder if there's a better way to do start this background method.
You can start your process at deployment time of your webapp. To achieve this, you have to create a ServletContextListener.
Just create a class:
public class YourClassName implements ServletContextListener{
#Override
public void contextInitialized(ServletContextEvent arg0) {
run your background stuff
}
#Override
public void contextDestroyed(ServletContextEvent arg0) {
stop your background stuff
}
}
And define it in your web.xml:
<listener>
<listener-class>
your class fully qualified name
</listener-class>
</listener>
And if you don't want to bother writing the XML definition and you are running Java 6+, you could also annotate your class with
#WebListener
If you want/need to run a process when deploying you application, you must define a ServletContextListener. But from your requirement details, seems that you need a HttpSessionListener to start trackign each user session start. From its javadoc:
Interface for receiving notification events about HttpSession lifecycle changes. (...) Implementations of this interface are invoked at their sessionCreated(javax.servlet.http.HttpSessionEvent) method in the order in which they have been declared, and at their sessionDestroyed(javax.servlet.http.HttpSessionEvent) method in reverse order.
And probably you may check changes in session attributes as well, so use a HttpSessionBindingListener to accomplish this.
One of my projects uses Spring MVC to handle URL mappings and dispatch logic. I have now to use a third party library which uses its own HttpServlet as the main entry point for its functionalities, but as it's an optional drop-in replacement for another library I can't just put the <servlet> declaration in the web.xml: I'd rather use a Controller and Spring profiles to switch between such implementations without having to edit the web.xml.
Is there anything offered OOTB by Spring to handle such cases? I don't seem to find it right away.
Thanks in advance!
Since registering the third party servlet in your web.xml is a no-go, I think your best bet would be to create a singleton instance of the servlet in your ApplicationContext, and then create a custom view that delegates to said servlet's service method.
You can see an example of custom views in action in this tutorial.
Answering my own question here just in case my approach can be useful to others.
There are two key factors I needed to consider:
the proper initialization of the servlet
give the servlet full control over the HTTP layer (e.g. setting HTTP headers, etc)
In my specific case there's no need for properly handling the servlet destruction, as it's a no-op.
I ended up writing a dedicated Controller, to be instantiated only if a specific Spring profile is activated, which takes care of instantiating and initializing the Servlet. Then, all the requests will be directly handled in a void handler method, as follows:
public class ServletDelegatingController implements ServletConfig {
private final DelegateServlet delegate = new DelegateServlet();
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
delegate.service(request, response);
}
// properly initializes the servlet
public void setServletConfig(ServletConfig servletConfig) {
try {
delegate.init(servletConfig);
} catch (ServletException e) {
throw new IllegalStateException("Failure while initializing the servlet", e);
}
}
}
The delegating-servlet.xml for the DispatcherServlet looks like the following:
<beans profile="custom">
<bean id="cmisServiceFactory"
class="com.foo.ServletDelegatingController"/>
</beans>
We have several REST applications using Spring MVC. Time to time some application after deploy don’t start. When our Javascript client tries access resource url, it gets 404 status code. Therefore it assumes, that resource doesn’t exist. More appropriate for us would be http status 500 returned in Tomcat response. Is possible change this default Tomcat behavior?
I've found similar problem with JBoss (uses embedded Tomcat) but no answer:
https://serverfault.com/questions/367986/mod-jk-fails-to-detect-error-state-because-jboss-gives-404-not-500
HTTP proxy
If you have some sort of a proxy in front of your Tomcat server (like apache or nginx), I believe it can be configured to translate 404 into a different status code and error page. If you don't have any proxy or want the solution to remain self-contained:
Custom Spring loader and servlet filter
Since you are using Spring, I guess you are bootstrapping it using ContextLoaderListener in web.xml:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
This class is responsible for bootstrapping Spring and this is the step that in most cases causes the application startup to fail. Simply extend that class and swallow any exception so it never reaches the servlet container, hence Tomcat won't think your application deployment failed:
public class FailSafeLoaderListener extends ContextLoaderListener {
private static final Logger log = LoggerFactory.getLogger(FailSafeLoaderListener.class);
#Override
public void contextInitialized(ServletContextEvent event) {
try {
super.contextInitialized(event);
} catch (Exception e) {
log.error("", e);
event.getServletContext().setAttribute("deployException", e);
}
}
}
Code is pretty simple - if Spring initialization fails, log the exception and store it globally in the ServletContext. The new loader must replace the old one in web.xml:
<listener>
<listener-class>com.blogspot.nurkiewicz.download.FailSafeLoaderListener</listener-class>
</listener>
Now all you have to do is to read that attribute from servlet context in a global filter and reject all requests if application failed to start Spring:
public class FailSafeFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void destroy() {}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
Exception deployException = (Exception) request.getServletContext().getAttribute("deployException");
if (deployException == null) {
chain.doFilter(request, response);
} else {
((HttpServletResponse) response).sendError(500, deployException.toString());
}
}
}
Map this filter to all requests (or maybe only controllers?):
<filter-mapping>
<filter-name>failSafeFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The solution might not be exactly what you want, but I am giving you a general, working example.
Yes it is possible with some change.
What we do :
write a servlet that do something like:
if (req.getContextPath().isEmpty()){
resp.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
}
put jar containing this class into tomcat lib.
change conf/web.xml to add the servlet and map it to *.404
set global error 404 to /404.404.
<error-page>
<error-code>404</error-code>
<location>/404.404</location>
</error-page>
your servlet will be called both with root application and with all deployed application.
Can I write a module/filter that gets put into the processing pipleline in Tomcat BEFORE the web application even gets run?
Something that I could turn on/off for each web application that tomcat is handling.
is this possible?
So basically it would be a re-usable filter that hooks into the web pipeline that could alter the requests behavior or perform/modify the requests. One example would be to log all ip's, or redirect based on the url, block the request, etc.
If you are using Servlet 3.0 you can. What you do is implement either a ServletContextListener or a ServletContainerInitializer. The code below shows withServletContextListener
#WebListener("auto config listeners")
public class MyListenerConfigurator implements ServletContextListener {
public void contextInitialized(ServletContextEvent scEvt) {
ServletContext ctx = scEvt.getServletContext();
FilterRegistration.Dynamic reg = ctx.addFilter("myFilter", "my.listener.class.MyListener");
...
}
See EE6 docs here. Perhaps the only drawback is that you can add but you cannot remove. And you can only at when the application starts up.
Note: code not tested
Have you considered a ServletContextListener in web.xml?
This question already has an answer here:
Using special auto start servlet to initialize on startup and share application data
(1 answer)
Closed 7 years ago.
I am new to Servlets. I want to use a method which is called only once after deploying to server. I looked at HttpServlet#init(). But I figured out it is called with each request. Did I misunderstand it? What are the alternatives to init()?
No, it is not called in each request. It is only called during initialization of the servlet which usually happens only once in webapp's lifetime. Also see this answer for a bit more detail how servlets are created and executed.
If you actually want to do some global/applicationwide initialization (which is thus not per se tied to only the particular servlet), then you would normally use the ServletContextListener for this. You can do the initialization stuff in the contextInitialized() method.
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
#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, 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>
init() is only called upon creation of the servlet. This may happen multiple times during the life of the server. You use it to initialize any variables or logic required for regular use of the servlet.
Edit:
After re-reading your post, it is not technically called with each request because the server is creating a new instance of the servlet for each request. Check your server settings as to whether it will get a new servlet of keep a single servlet for the life of the server.
Are you looking for a ServletContextListener?
How do I run a method before republishing to JBoss?