Webapp startup fails but Jetty LifeCycle claims "started" - java

I'm using embedded Jetty to launch a standard Java webapp. My launcher is something like this:
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.component.LifeCycle.Listener;
import org.eclipse.jetty.webapp.WebAppContext;
...
Listener listener = ...;
Server server = new Server(8080);
WebAppContext webapp = new WebAppContext();
...
webapp.addLifeCycleListener(listener);
server.setHandler(webapp);
server.start();
...
This all works fine in that I can start my app and browse to it and everything appears to be working.
But now I'm trying to add error reporting to my launcher. I've temporarily set my webapp to throw an exception in the contextInitialized() method of a ServletContextListener. The exception is thrown, and I get a log message of
ERROR org.eclipse.jetty.util.log Failed startup of context WebAppContext#...
but my LifeCycleListener does not receieve any failure event. In fact, it receives a started event, and the WebAddContext passed to the listener returns false for LifeCycle#isFailed() and true for LifeCycle#isRunning().
Browsing to my webapp results in 503 Service Unavailable errors.
This happens in Jetty versions 7.0.1.v20091125 and 7.2.1.v20101111. See Jetty 7 api docs.

As per my comments to Heri's answer, WebAppContext swallows exceptions. They would otherwise be caught by AbstractLifeCycle and failure events sent out. This gets me most of the way there:
public class ThrowyWebAppContext extends WebAppContext {
#Override
protected void doStart() throws Exception {
super.doStart();
if (getUnavailableException() != null) {
throw (Exception) getUnavailableException();
}
}
}

If I remember correctly failed life cycles of context elements are not propagated to the life cycle of the context itself (like failed contexts are not propagated to the life cycle of the server itself). Check the life cycle of the SecurityHandler, ServletHandler and SessionHandler of the context, too.

Related

Can a deployment stop itself?

I'm currently searching for a way to stop a deployment on wildfly programmatically.
Background:
The application does some health checks in its startup phase using an MBean.
If the app determines that the environment is not valid because some resources are missing, it needs to stop its own deployment.
The way it was:
The application was formerly running on JBoss 4 and simply stopped the whole app server calling the shutdown command using JMX.
In case this command failed, it simply terminated the whole JVM using System.exit(1).
Current problems:
Calling shutdown() via JMX does not work on wildfly since the whole server hangs when trying to stop it from within a deployed application.
System.exit() will also not work since wildly must be catching the command in any way.
So does anyone know how to stop the server from within the deployment or stop the deployment process or undeploy the app?
Thanks a lot!
I assume the core question is stopping the deployment process if some health checks fail. Throwing a run-time exception during app startup is enough to do the job.
#Startup
#Singleton
public class StartupBean {
#PostConstruct
public void start() {
//your checks
boolean check = doHealthCheck();
if(!check){
throw new RuntimeException("Your error message");
}
}
}
or
#Startup
#Singleton
public class StartupBean {
#PostConstruct
public void start() {
//your checks
boolean check = doHealthCheck();
if(!check){
throw new Error("Your error message");
}
}
}
I suggest you to try WildFly CLI:
Running the CLI
or use Marker Files.
But in any case, I'm not sure how the server will behave. For example what will happen when You add file myWarName.dodeploy when there is myWarName.isdeploying. So let us know when You will earn some experience in this topic (it is quite interesting).
Ok, I did not yet manage to undeploy the app but I've been able to shutdown the server in case of an error. This is not perfect but matches the behavior of the app on the older version of JBoss, so I think it's not too bad at all.
I'm now calling the CLI interface like so
try {
String jbossBinDir = System.getProperty("jboss.server.base.dir").replace("standalone", "bin");
Runtime.getRuntime().exec("sh " + jbossBinDir + "/jboss-cli.sh -c command=:shutdown");
} catch(IOException e) {
...
}
This works reliable for us.
In my comment above I stated that the execution returns with an error code but this was probably the case because I must have had a typo in the command call.
We're using a CDI Extension to abort the deployment if our DB schema doesn't match the application's expectation:
class MyValidatingExtension implements javax.enterprise.inject.spi.Extension {
void deploymentValidationFinished(#Observes AfterDeploymentValidation afterDeploymentValidation) {
if (!stateExpected) {
afterDeploymentValidation.addDeploymentProblem(new IDontLikeThisException());
}
}
}
The deployment of the WAR will fail with the stacktrace of the exception listed as DeploymentProblem, leaving your WAR in an undeployed state. This solution is independent of your container implementation, it uses a CDI standard mechanism only. Note that this will not stop/shutdown the server!

ActiveMQ embedded broker accessible via Weblogic servlet

I'd like to do the following and I'm not quite sure if I'm not wasting my time: I'm trying to run ActiveMQ embedded broker inside a weblogic servlet. The idea is, that the clients will be able to connect to JMS via http and the embedded broker would serve the requests. I know that this is a crazy idea, but it's a legacy application and a lot of the client code depends on JMS. The idea is just to switch the connection string and add libraries to clients.
It works fine when I create the tcp connection, but I have no idea how to map a servlet to the internal broker
The restrictions are these:
No changes in weblogic configuration(like datasources, bridges, JMS etc)
No Spring
HTTP only
This is the servlet definition from web.xml:
<servlet>
<servlet-name>ActiveMQServlet</servlet-name>
<servlet-class>com.mycompany.ActiveMQServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ActiveMQ</servlet-name>
<url-pattern>/servlet/ActiveMQServlet</url-pattern>
</servlet-mapping>
Important parts of the servlet:
public class ActiveMQServlet extends HttpServlet {
private BrokerService broker;
private static final Log log = LogFactory.getLog(new Object() {
}.getClass().getEnclosingClass());
#Override
public void init() throws ServletException {
log.info("Load activeMQ");
// configure the broker
try {
TransportConnector connector = new TransportConnector();
connector.setUri(new URI(ACTIVE_MQ_URL));
broker = new BrokerService();
broker.addConnector(connector);
broker.start();
log.info("ActiveMQ loaded succesfully");
} catch (Exception e) {
log.error("Unable to load ActiveMQ!", e);
}
}
#Override
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.info("Received call.... ");
log.info("Request: "+request);
//todo
}
The problem is I have no idea how to do a request/response mapping between the service method of the servlet and internal ActiveMQ broker.
Another thing is, that I'm trying to solve some classpath clashes caused by slf4j which is used by ActiveMQ broker and already spent some hours on it.
But maybe I'm just doing something which is impossible/really stupid.
When I'm trying to connect via simple client, I'm getting this exception
javax.jms.JMSException: Could not connect to broker URL: http://localhost:8888/myapp/servlet/ActiveMQServlet. Reason: java.io.IOException: Failed to perform GET on: http://localhost:8888/myapp/servlet/ActiveMQServlet as response was: Not Found
at org.apache.activemq.util.JMSExceptionSupport.create(JMSExceptionSupport.java:35)
at org.apache.activemq.ActiveMQConnectionFactory.createActiveMQConnection(ActiveMQConnectionFactory.java:293)
at org.apache.activemq.ActiveMQConnectionFactory.createConnection(ActiveMQConnectionFactory.java:191)
...
Caused by: java.io.IOException: Failed to perform GET on: http://localhost:8888/myapp/servlet/ActiveMQServlet as response was: Not Found
at org.apache.activemq.transport.http.HttpClientTransport.doStart(HttpClientTransport.java:275)
at org.apache.activemq.util.ServiceSupport.start(ServiceSupport.java:55)
While doing this, the code in the servlet is not executed. The servlet path is fine. If I open it in the browser, I got empty page and log message.
I'm using ActiveMQ 5.8.0 and Weblogic 10.3.6
I'm trying to do something similar, and something I have found is MessageListenerServlet - I'm still searching for examples on setting it up (I'm relatively new at dealing with servlets) but I think it's supposed to allow this sort of thing. Though I think you're main application needs to be the one setting up the BrokerService at startup, and the servlet just needs to reference the address. In my case, we aren't using websphere, but the general premise of "talk to an embedded ActiveMQ instance from external sources via http" is the same.

Abort Tomcat start-up

Specifically, I want Tomcat to abort start-up when my web app can't initialize (due to a configuration error).
I'm tried to use the Tomcat shutdown port. That's not ready when my web app is starting up.
-Dorg.apache.catalina.startup.EXIT_ON_INIT_FAILURE=true, doesn't work. Tomcat starts up just fine.
Throw an uncaught Exception. I've tried throwing an exception. Tomcat still initializes correctly but my web app is in a non-functional state.
Use the Tomcat JMX API. None of the shutdown methods will cause Tomcat to shut down.
So far, the only other option I've got is System.exit(-1). That does work, but I'm concerned that doing that to Tomcat will cause problems.
Tomcat is designed to be resilient against badly written applications that don't start up.
org.apache.catalina.startup.EXIT_ON_INIT_FAILURE is for stopping Tomcat if one of Tomcat's components, such as one of the connectors, doesn't start (e.g. because the port is in use).
System.exit() is ugly but it will work and it is safe as far as Tomcat is concerned (as for that matter is kill -9). It is applications that may not like this rather brutal approach to shutdown.
There is a Tomcat specific way to do this more cleanly. Add a custom LifecycleListener to the Server component and have it respond to AFTER_START_EVENT. Have the listener navigate the Container hierarchy from the Server to the Contexts [Server->Service(s)->Engine-Host(s)->Context(s)] and check that each context is in the state STARTED. If not, call stop() and then destroy() on the Server.
Here's an implementation of the listener which aborts the Tomcat startup, as mentioned by Mark Thomas. StrictStateCheckListener is an after-start server listener. It checks the state of all Container, including engine, host, and context. The server start-up will be aborted if there's any component failed to start (state different from STARTED).
package com.example.lifecycle;
import java.util.logging.*;
import org.apache.catalina.*;
public class StrictStateCheckListener implements LifecycleListener {
private static final Logger LOGGER = Logger.getLogger(StrictStateCheckListener.class.getName());
private boolean hasFailures;
private String containerState;
#Override
public void lifecycleEvent(LifecycleEvent event) {
String type = event.getType();
Lifecycle lifecycle = event.getLifecycle();
if (lifecycle instanceof Server && type.equals(Lifecycle.AFTER_START_EVENT)) {
Server server = (Server) lifecycle;
hasFailures = false;
// Check status of each container
StringBuilder sb = new StringBuilder("Status:").append(System.lineSeparator());
for (Service service : server.findServices()) {
checkState(service.getContainer(), sb, "");
}
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(sb.toString());
}
if (hasFailures) {
/*
* The server will not be stopped by this listener. Indeed,
* an exception is raised to trigger shutdown hook. See:
* `org.apache.catalina.startup.Catalina`.
*/
throw new IllegalStateException(containerState);
}
}
}
private void checkState(Container container, StringBuilder report, String indent) {
if (!hasFailures && container.getState() != LifecycleState.STARTED) {
hasFailures = true;
containerState = stateOf(container);
}
report.append(indent).append(stateOf(container));
for (Container child : container.findChildren()) {
checkState(child, report, indent + " ");
}
}
private String stateOf(Container container) {
String className = container.getClass().getSimpleName();
String stateName = container.getStateName();
return String.format("%s[%s]: %s%n", className, container.getName(), stateName);
}
}
Register this listener to your server component via server.xml:
<Server>
...
<Listener className="com.example.lifecycle.StrictStateCheckListener" />
...
</Server>
Your point No 1
Do you mean when uninitialized state exception is thrown from application you are calling tomcat shutdown.bat file.Have you tried to call shutdown.bat file from your program.
I know this question is a bit old, but I'm going a bit further here thinking out of the box to solution you are seeking. It looks like your requirement is to stop tomcat when your webapp fails to initialize. This is a bit different than "abort Tomcat start-up". Tomcat itself is already started and will start to deploy your webapp right after it started up. Based on your requirement, I assume that you don't have any other webapp. If that's the case, why don't you just use Spring Boot? Spring boot provides a way to deploy self contained web application. In this case, if initialization fails, application will not start.

How to halt the execution a Servlet WebApp from a ContextListener?

I have a java web application, that has a very strict security requirement. on Start-up it tries to configure the web container to force it use SSL session id to establish the http session id. If it fails in this configuration I want the app to stop and not process an request. I can use System.exit() as I am doing in the sample below, but I am wondering if there is a nice way to do this without killing the JVM. Also a security manager can get in the way of System.exit()
I am using tomcat 7.
public class SessionTrackingModeListener implements ServletContextListener
{
#Override
public void contextInitialized(ServletContextEvent event)
{
try
{
ServletContext context = event.getServletContext();
EnumSet<SessionTrackingMode> modes = EnumSet.of(SessionTrackingMode.SSL);
context.setSessionTrackingModes(modes);
System.out.println("SSL based Session tracking enabled");
} catch (Exception e)
{
System.err.println("Unable to setup SSL based session tracking");
e.printStackTrace();
System.exit(1);
// Question How do I get the container to not start the app without using exit?
}
}
#Override
public void contextDestroyed(ServletContextEvent event)
{
}
}
First of all I think it is not a good idea to use
System.exit(1)
in a servlet environment, since some web containers may use a security manager to prevent your webapp to kill the entire server.
The servlet spec is not strict about what happens when you throw a runtime exception in the contextInitialized function. In my experience the servlet containers abort the startup of the webapp, but again it may depend on your container so you should test it.
If you are using plain old servlets it maybe a good choice to create a ServletFilter which checks whether the security constraints are ok or redirect the request to an error page.
Would you please explain more about "want the app to stop and not process an request".You mean yours web application OR web container.Do you want to show some human readable message on web application when there is some problem in container to user ?.As when there is exception the tomcat does not get started start.You have to start it again .

How to make my class that implements Struts PlugIn init method to forward to erroneous page on exception?

Following class implements Struts org.apache.struts.action.PlugIn, i have overriden the init method.
My web server is tomcat.
Following method does an RMI Look up and stores the server address and port in an object when the first request comes in
public void init(ActionServlet servlet, ModuleConfig moduleConfig) {
try
{
// Some Code
remoteObject= lookup(strIpAddress, strRMIPort);
}
catch (Exception e)
{
System.out.println("Remote server not configured")
}
}
The look up code does an remote lookup to our server and fetches the adrress and port.
I have a problem over here. When my 'remote server' is not started, I get an exception in my tomcat console, but I need to forward to another error.jsp page , since the Init method is a void method, and am not sure how to get the request and response object in this method, am struck over here.
Can you kindly tell, how will I delegate to an error page when there is an exception in that code?
A plugin's init method should be called when the module's action servlet spins up.
Redirecting to an error page from within a plugin doesn't make a lot of sense: they're internal framework classes and have no direct user visibility. Instead an application-wide flag (or similar mechanism) should be set, and an error can be reported to an app user based on its presence (or value, etc.)
To indicate web app startup failures a fatal log message is appropriate. You could also send an email, update a dashboard, send a JMS message, etc. depending on monitoring mechanisms in place.

Categories

Resources