3

We're using embedded Tomcat 7 to host a web application.

It works very well, with one slight exception.

The reason that we're using embedded Tomcat is because we need to operate on multiple platforms and our architect has made the call.


The Problem

We would like to give our users the ability to check for WAR updates while the wrapper/container Java application is running (live update). Currently they have to restart our application which is much less desirable.

Basically, our code just checks a remote server for newer WAR files, if they exist they are downloaded. The problem is that we can't seem to get the Tomcat server to shutdown or release lock that it has on the exploded WAR folders.

If we completely destroy the Tomcat instance, then we can deploy the WAR files and delete the exploded WAR folders, but Tomcat won't explode and host them until we completely kill the wrapper/container JAVA application and re-launch. Once we re-launch, Tomcat explodes the WAR files and hosts the applications nicely.


What I'm Looking For

I'm hoping that there is a way to either un-deploy the application being updated or properly shutdown/stop the Tomcat server.


Code Sample

I simplified the below code sample by not including the implementation for the downloading as this works fine.

The Tomcat instance isn't public, but just made it so for ease of use. Also, the sleep within the Thread was just thown-in to simplify the example.

The endless-loop was just put in for this sample.

import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.startup.Tomcat;

...

public class Testing123
{

    public static void main(String[] args)
    {
        final Testing123 test = new Testing123();
        test.createTomcat();
        test.downloadUpdate();
        (new Thread())
        {
            public void run()
            {
                Thread.sleep(10000);
                test.downloadUpdate();
            }
        }.start();
        while (true)
        {
            // this loop is just for this example...
        }
    }

    public Tomcat tomcat = null;
    private String webappsPath = "c:\\tomcat\\webapps";

    private void createTomcat()
    {
        this.tomcat = new Tomcat();
        this.tomcat.setBaseDir(this.webappsPath);
        this.tomcat.setPort("5959");
        this.tomcat.getServer().setPort("5960");
        this.tomcat.getServer().setShutdown("SHUTDOWN");
        this.tomcat.getHost().setCreateDirs(true);
        this.tomcat.getHost().setDeployIgnore(null);
        this.tomcat.getHost().setDeployOnStartup(true);
        this.tomcat.getHost().setAutoDeploy(true);
        this.tomcat.getHost().setAppBase(this.webappsPath);
        Context ctx = this.tomcat.addWebapp(null, "/app1", "APP1");
        ctx.setLoader(new WebappLoader(Testing123.class.getClassLoader()));
        ctx.setReloadable(true);
    }

    private boolean isTomcatRunning()
    {
        if ((this.tomcat == null) || (LifecycleState.STARTED == this.tomcat.getServer().getState()))
        {
            return true;
        }
        return false;
    }

    private void shutdownTomcat() throws Exception
    {
        if (this.isTomcatRunning())
        {
            if ((this.tomcat!= null) && (this.tomcat.getServer() != null))
            {
                this.tomcat.stop();
                while ((LifecycleState.STOPPING == this.tomcat.getServer().getState()) || (LifecycleState.STOPPING_PREP == this.tomcat.getServer().getState()))
                {
                    // wait for the server to stop.
                }
            }
        }
    }

    private void startTomcat() throws Exception
    {
        if (this.isTomcatRunning())
        {
            if ((this.tomcat!= null) && (this.tomcat.getServer() != null))
            {
                try
                {
                    this.tomcat.init();
                    while (LifecycleState.INITIALIZING == this.tomcat.getServer().getState())
                    {
                        // wait for the server to initialize.
                    }
                }
                catch (Throwable e)
                {
                    // ignore
                }
                this.tomcat.start();
                while ((LifecycleState.STARTING == this.tomcat.getServer().getState()) || (LifecycleState.STARTING_PREP == this.tomcat.getServer().getState()))
                {
                    // wait for the server to start.
                }
            }
        }
    }

    private void downloadUpdate()
    {
        this.shutdownTomcat();
        // Download the WAR file and delete the exploded WAR folder...
        this.startTomcat();
    }

}
Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
MattWeiler
  • 789
  • 1
  • 12
  • 21
  • 1
    Do you have the possiblity to deploy the new war under another path and run it? – fge Jun 07 '13 at 20:48
  • Thanks for the quick reply, but no that won't work for us. We have servlets which require specific paths. – MattWeiler Jun 07 '13 at 20:54
  • 1
    That is not what I meant ;) I was asking if you could do it programatically or not. If you can, there may be a possibility to make TOmcat also programatically "alias" your required path to that of the new running war. – fge Jun 07 '13 at 20:57
  • I'm not sure how to do that. Are you able to provide an example of how to have Tomcat create an alias? – MattWeiler Jun 07 '13 at 21:01
  • Uh no, I thought you'd have an idea ;) A really wild thought that popped into my head was to make a webapp for that which would only pass the data through, to which you could atomically write the path to redirect to... But I'm sure there are other solutions – fge Jun 07 '13 at 21:03

0 Answers0