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!
Related
I have built a web application that uses
SpringBoot v1.3.6.RELEASE
Tomcat 8.0.36
Java 1.8u101
on CentOS 7.2
The web application is also a SOAP client that calls out to another web application.(JAX-WS RI 2.2.9) If the applications remains idle for 15 seconds the first webservice call stalls for nearly 2 seconds. It appears that the stall happens in o.a.c.loader.WebappClassLoaderBase.
After idle 15 seconds
16:02:36.165 : Delegating to parent classloader org.springframework.boot.loader.LaunchedURLClassLoader#45283ce2
16:02:36.170 : Searching local repositories
16:02:36.170 : findResource(META-INF/services/javax.xml.soap.MetaFactory)
16:02:38.533 : --> Resource not found, returning null
16:02:38.533 : --> Resource not found, returning null
Next request no idle time
16:07:09.981 : Delegating to parent classloader org.springframework.boot.loader.LaunchedURLClassLoader#45283ce2
16:07:09.984 : Searching local repositories
16:07:09.985 : findResource(META-INF/services/javax.xml.soap.MetaFactory)
16:07:09.986 : --> Resource not found, returning null
16:07:09.986 : --> Resource not found, returning null
16:07:09.988 : findResources(META-INF/services
All above messages produced by o.a.c.loader.WebappClassLoaderBase and they are apparently being caused by ClientSOAPHandlerTube.processRequest which is from JAX-WS RI.
You'll notice the first call takes over 2 seconds but subsequent calls take only milliseconds.
I'm wondering if anyone has experienced this behavior?
Possible solutions:
Is it possible to change out the classloader used by tomcat in springboot to use ParallelWebappClassLoader
Or maybe this is a product of the reloadable flag on the classloader but I don't see how to change that flag in springboot.
When run using Jetty as the container this does not occur.
Final Solution: (thanks to Gergely Bacso)
#Bean
public EmbeddedServletContainerCustomizer servletContainerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof TomcatEmbeddedServletContainerFactory) {
customizeTomcat((TomcatEmbeddedServletContainerFactory) container);
}
}
private void customizeTomcat(TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory) {
tomcatEmbeddedServletContainerFactory.addContextCustomizers(new TomcatContextCustomizer() {
#Override
public void customize(Context cntxt) {
cntxt.setReloadable(false);
}
});
}
};
}
Actually your findings are quite good and you have 90% answered your question already. These two facts:
"it appears that the stall happens in o.a.c.loader.WebappClassLoaderBase"
"when run using Jetty as the container this does not occur."
show that it is going be a Tomcat-related problem because:
o.a.c. stands for org.apache.catalina
Your code works well on another container. (Jetty)
You also observed, that the issue is happening after 15 seconds of idle time. This perfectly corresponds to Tomcat's default checkInterval setting, which is:
The number of seconds between checks for modified classes and
resources, if reloadable has been set to true. The default is 15
seconds.
So in short: currently your reloadable flag is ON, and Tomcat tries to reload your classes which is handy during development, but unacceptable in any other case. The way to switch it off is not via Spring-boot though.
SOLUTION:
You need to locate your context.xml / server.xml where you will find your Context defined like this:
<Context ... reloadable="true">
Remove the reloadable flag, and you have solved the problem. The file itself can be either in $CATALINA_BASE/conf of $CATALINE_HOME/conf, but in reality these locations can be a bit tricky to find if you are using some IDE to manage Tomcat for you.
In case of embedded Tomcat with Spring-boot:
The class you can use to manipulate Tomcat settings is: EmbeddedServletContainerCustomizer.
Through this you can add a TomcatContextCustomizer (addContextCustomizers) so that you can call setReloadable on the context itself.
I do not see any reason for Spring-boot needing this flag on true.
Log4j2 also uses shutdown hooks to end it's services. But of course I want to log throughout the whole lifecycle of my application - shutdown included. With Log4j this was no problem. Now it seems to be impossible. Logging shuts down, while my application is still working on it. Has anyone some hope for me?
Best regards
Martin
As of 2.0-beta9 this is now configurable in xml
<configuration ... shutdownHook="disable">
Considering its now disabled, I guess I need to manually shutdown the logging system at the end of my shutdown hook. However I couldn't find a means thorough the external interface, only in the internal api
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.config.Configurator;
import org.apache.logging.log4j.core.LoggerContext;
...
public static void main(String[] args) {
final AnnotationConfigApplicationContext springContext = new AnnotationConfigApplicationContext(AppConfig.class)
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
//shutdown application
LOG.info("Shutting down spring context");
springContext.close();
//shutdown log4j2
if( LogManager.getContext() instanceof LoggerContext ) {
logger.info("Shutting down log4j2");
Configurator.shutdown((LoggerContext)LogManager.getContext());
} else
logger.warn("Unable to shutdown log4j2");
}
});
//more application initialization
}
Update:
There is LogManager.shutdown() method since log4j version 2.6
I basically just answered the same question and I tough I'll share my answer here. I encourage you to read the complete answer available here. I'll try to provide a summary here and adapt my answer to the current context.
In the first version, Log4j was providing an API to manually call the shutdown procedure. For reasons we don't have the knowledge of, it was removed from the second version. Now, the right way of doing it (according to the none-existent documentation), is to provide your own implementation of the ShutdownCallbackRegistry interface, which is responsible of the shutdown procedure.
Proposed solution
What I did to fix this issue is that I implemented my own version of the ShutdownCallbackRegistry interface. It mostly does the same things the default implementation does, but instead of registering itself as a shutdown hook to the JVM, it wait until it's invoked manually.
You can find the complete solution and instructions on GitHub/DjDCH/Log4j-StaticShutdown and use it in you own projects. Basically, at the end, you only have to do something like this in your application:
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
#Override
public void run() {
try {
// Do your usual shutdown stuff here that need logging
} finally {
// Shutdown Log4j 2 manually
StaticShutdownCallbackRegistry.invoke();
}
}
}));
I can't say without any doubt that this is the perfect solution and that my implementation is perfect, but I tried to do it the right way. I'll be glad to hear feedback from you, either if you find this solution appropriate or not.
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.
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 .
I need a very simple process that listens on a directory and
does some operation when a new file is created on that directory.
I guess I need a thread pool that does that.
This is very easy to implement using the spring framework, which I normally use but I can't use it now.
I can only use tomcat, How can I implement it? what is the entry point that "starts" that thread?
Does it have to be a servlet ?
thanks
since you refined the question, here comes another answer: how to start a daemon in tomcat:
first, register your Daemons in web.xml:
< listener >
my.package.servlet.Daemons
< /listener >
then implement the Daemons class as an implementation of ServletContextListener like this:
the code will be called every 5 seconds, tomcat will call contextDestroyed when your app shuts down. note that the variable is volatile, otherwise you may have troubles on shutdown on multi-core systems
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class Daemons implements ServletContextListener {
private volatile boolean active = true;
Runnable myDeamon = new Runnable() {
public void run() {
while (active) {
try {
System.out.println("checking changed files...");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
public void contextInitialized(ServletContextEvent servletContextEvent) {
new Thread(myDeamon).start();
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
active = false;
}
}
You could create a listener to start the thread, however this isn't a good idea. When you are running inside a Web container, you shouldn't start your own threads. There are a couple of questions in Stack Overflow for why is this so. You could use Quartz (a scheduler framework), but I guess you couldn't achieve an acceptable resolution.
Anyway, what you are describing isn't a Web application, but rather a daemon service. You could implement this independently from your web application and create a means for them to communicate with each other.
true java-only file notifiaction will be added in java 7. here is a part of the javadoc that describes it roughly.
The implementation that observes events from the file system is intended to map directly on to the native file event notification facility where available
right now you will have to either create a native platform-dependent program that does that for you,
or alternatively implement some kind of polling, which lists the directory every so often to detect changes.
there is a notification library that you can use right now - it uses a C program on linux to detect changes over at sourceforge. on windows it uses polling. i did not try it out to see if it works.