My webapp is part of a larger EAR that is deployed into a websphere server. The server hosts number of other apps on the same virtual server. My webapp has some initialisation/health checks in a servletContextListener->contextInitialized method. I want to make the webapp unavailable if initialisation/health checks fail. What is a realiable way of doing this? Will throwing a RuntimeException from within contextInitialized suffice? Is the rest of the EAR still expected to be available? Thank you.
I'd recommend throwing a RuntimeException from ServletContextListener.contextInitialized.
Servlet 2.3 wasn't very clear on this, but Servlet 2.4 added the following detail:
Some exceptions do not occur under the
call stack of another component in the
application. An example of this is a
… ServletContextListener that
throws an unhandled exception during a
notification of servlet context
initialization…. In this case,
the Developer has no opportunity to
handle the exception. The container
may respond to all subsequent requests
to the Web application with an HTTP
status code 500 to indicate an
application error.
Since it says that the servlet engine "may" disable access to application, you might find a server that does something else. However, Tomcat and WebLogic both disable the application, and the only other reasonable thing I can think of would be to ignore the exception. I can't see a container that did that being very popular—so you'd better test it in WebSphere yourself.
Throwing a RuntimeException will probably make only that servlet unavailable. A safer way might be to implement something like a Spring interceptor that will forward to an error page or something if the checks didn't pan out. That way, you don't need to prevent the app from loading, but can handle it more gracefully at run time.
Related
In our project, we have several Spring-based modules which are deployed on WAS as web applications. We need to skip deployment, or stop a module if its Spring context initialization fails (i.e. ContextLoaderListener#contextInitialized or DispatcherServlet#init throws an exception). Now, if such happens, app is got deployed and starts, but returns HTTP 500 for any request.
Websphere 8.5.5
Related question: https://stackoverflow.com/a/272747/3459206
This APAR seems to be relevant:
https://www-01.ibm.com/support/docview.wss?uid=swg1PI58875
From the APAR text:
Listener exceptions typically should not stop the application
from starting up for service. However, some applications depend
on their listeners to do the necessary setup before the
application is started for service. Such applications prefer to
stop the application from starting up when there is any
exception in their listeners.
Problem conclusion
The WebContainer Container code was modified to provide an
option to stop the application when there is any listener
exception during the application starting up process.
A new WebContainer custom property needs to be set to enable the
behavior provided by this APAR:
For Full Profiles
com.ibm.ws.webcontainer.stopappstartuponlistenerexception = true
(default is false)
For Liberty Profile
stopappstartuponlistenerexception=true
The fix for this APAR is currently targeted for inclusion in
WebSphere Application Server fix packs 8.5.5.11 and 9.0.0.2,
and Liberty 16.0.0.3
See the APAR link for additional information.
You can use jenkins + maven.
Add the part you need to check under your test like junit.
Then if this module do not pass test, jenkins would not deploy it.
But I prefer fix bugs before deployment
Had a very similar issue.
The thing is - webfear - sorry could not resist ;-) does not initialize everything on startup.
To trigger a controlled request, I added a ScheduledEJB to the startup of the application. This bean itself triggered a http-request to a defined URL, which itself triggered:
any filters to get initialized in the chain
any contexts which are needed are initialized
And this itself ensured that my application (EAR or WAR) got very quickly tested after deployment. This works well with some small amout of requests per minute
If you work with high load, means tons of requests per second, you need to choose a different approach.
In this case I added a polling mechanism into the #Startup of the application, which polled every second or 250ms (depends on the load of the application).
This firing to the server ensured, that my #Startup bean was the very first which triggered the possible init issues in the application. If this happened I initialized a filter which always reported a 500 (or better fitting error) to the requestor.
Of course stop your firing bean, as soon as you get the 500, else your admins may like to kill you. (happend to me, since I produced tons or monitoring issues ;-) )
And of course on the regular operation, after your application started properly, you should also disable the polling
Look for a try-catch in the top level of your application code that is catching the Spring exception and allowing the application to continue running.
If the Spring exceptions being thrown are permitted to propagate to the top of the stack, the JVM will stop and there's no way it can keep running, far as I know.
My particular setup is a Java webapp packaged as a war file under Tomcat7, but the question is more general. In our webapp, we have multiple servlets being registered via the Servlet 3.x #WebServlet annotation (and also marked loadOnStartup=1). The question is what to do if one of the servlets fails for some legitimate reason during initialization by throwing an exception. In this case, we're seeing two issues:
Tomcat notices that the servlet failed. But our service keeps getting probed by a load balancer trying to decide whether to include us in the pool. Every probe is an access to a URL mapped to the servlet that failed. Tomcat keeps trying and retrying to initialize the webapp, failing each time. Is there a way to cause Tomcat to just stop trying and give up? We can monitor for initialization failure and take corrective action if required.
The other servlets in the same webapp did not fail (those are also loaded via a WebServlet annotation with loadOnStartup=1). So if we try to access them by hitting an appropriate URL, Tomcat lets us through. We would prefer it if the failure of one servlet in the webapp would cause the whole webapp to fail.
You could place all failable startup operations in a ServletContextListener which is initialized before any servlet. If the init of the ServletContextListener fails the whole webapp is not deployed.
Your servlets could then in their initialization simply ask the singleton ServletContextListener for their already initialized resources.
Here is my approach:
Put all startup operation code in the ServletContextListener.
At the end of all start up operations, put an attribute (true or false) in the context context.setAttribute(). Or put several T/F attributes for different init operation results.
Add a filter for this servlet to handle all requests. Check the context attribute, if startup operation failed, this filter will gracefully respond to the request, either by redirect or send error respond code to load balancer query.
Does CloudBees allow you to define custom error pages beyond what you can to with a traditional Java web app (via web.xml)?
For instance, if a CloudBees app-cell instance goes down due to infrastructure issues, is there a type of exception it will throw that can be caught and handled by the application backend? Or a special exception/error page that can be defined if the app runs out of memory/PermGen? Thanks in advance!
error page in web.xml are used to render application errors, and suppose the application server is up-and-running. Container errors / maintenance / shutdown can't benefit from application resources by nature.
If an app-cell instance goes down, your application has no chance to throw any exception as it will have been killed with the app-cell. An OOM error will automatically trigger an application restart. Generally speaking, the platform will ensure you always have one instance running.
I'm trying to verify some conditions on application startup, such as the presence of critical files in the system.
To do this, I'm using a SerlvetContextListener, which tests for a bunch of conditions on startup.
How can I make so that the application rejects the deployment in Tomcat, if any of these conditions is not met? I could throw an exception, and catch that in the logs, but I'd like the application not to start at all.
Even failing Tomcat service startup would be a solution.
What's the simplest way? Should I try connecting to the Tomcat manager and undeploy the app?
Well...
According to your original question, you'd like the application to "not start at all" if certain files aren't there (or other conditions aren't met). Logically, it follows that the checking of the conditions can't possibly be a part of the application (if the checking is to be done as a part of the application - say, upon startup - then the application must be started for those checks to take place).
So:
If you don't want the application to start at all unless certain conditions are met, then your solution must be external to the application. For example, you could write your own Tomcat startup script that will check for your conditions first, and only then start Tomcat up.
If you don't mind the application starting and performing the checks, then the ServletContextListener is precisely what you should take advantage of. Throwing an exception from within contextInitialized() will result in the application being "unavailable" for serving requests. Alternatively, you could communicate with Tomcat's MBeans to stop the application if checks don't pass.
I asked this question earlier:
Jersey app-run initialization code on startup to initialize application
Is there any way to run this initialization code after the server has initialized the web app fully ? I am running into a catch-22 problem in that I need the web app fully started by Tomcat before I can run my login call once ?
There are a few considerations. First, Jersey is implemented as a servlet, and that's how it gets loaded. The simplest way to decouple servlet startup from servlet context startup is to not load it on startup. Accomplish this by omitting the load-on-startup element from the servlet descriptor in the web.xml. Setting it to a non-positive should also work.
If you must load the servlet on startup but still want to decouple it, which was my case, you'll probably have to write some custom code, which I did. I couldn't think of any other way. Use a listener of some type--a ServletContextListener or Spring ApplicationListener, depending on exactly how your app is set up--to kick off a new thread that will initialize your Jersey servlet by making an HTTP request to it. It feels a little dirty, but like I said, it's the only thing I and my team could come up with.