开发者

Is there a way to enforce a deployment order in tomcat6?

I have 3 wars in my webapp folder. Two of them are built on services of the third one. I'm in a testing environment, i.e. I don't ha开发者_开发问答ve control over their architectures, so I'm no able to change a thing. So...

Question: Is there a way to enforce a deployment order in tomcat?

I've ran into one question here in stackoverflow, but there's no solution.

Well, actually the guy suggests that changing the name of the webapps to an alphabetical order would help. However, I'm not willing to do that everytime I need to test those and different wars.

I'm positive that there's a way of doing that configuring one of the tomcat conf .xml files. I just don't know which one.


From the Tomcat Wiki - What order do webapps start (or How can I change startup order)?

There is no expected startup order. Neither the Servlet spec nor Tomcat define one. You can't rely on the apps starting in any particular order.

Tomcat has never supported specifying load order of webapps. There are other containers like JBoss that do, but Tomcat never has. Any apparent behavior that looks like load ordering via the alphabetical order of the names of the web apps is coincidental and not guaranteed to work in all cases.

What you are probably thinking of is the <load-on-startup/> element if the web.xml that specifies order of loading servlets.

That said there is an elegant solution using a service discovery protocol.

One solution is to use something like ZeroConf and register your services when they start up, and then have the dependent apps look for when these services come available and have them connect and do what ever they need to do when the service is ready. This is how I have been handling multiple dependent services for years now. I have Python, Java and Erlang services all discovering each other via ZeroConf seemlessly.


It is true that tomcat does not provide any way to enforce deployment order.

*Tomcat deploys webapps in following order:*

1.Any Context Descriptors will be deployed first.

2.Exploded web applications not referenced by any Context Descriptor will then be deployed. If they have an associated .WAR file in the appBase and it is newer than the exploded web application, the exploded directory will be removed and the webapp will be redeployed from the .WAR

3.WAR files will be deployed

> Here is a proposed solution:


If you want to specify the deployment order then define a context for your web app in $CATALINA_BASE/conf/[enginename]/[hostname]/MyApp.xml

Tomcat scans $CATALINA_BASE/conf/[enginename]/[hostname]/ by performing File listFiles() which returns a File array sorted by hash value (OS dependent).

You may use the following code to check in which order webapps will be deployed

File file = new File("/opt/tomcat/conf/Catalina/localhost");
        File[] files = file.listFiles();
        for (File f : files)
        {
            System.out.println("Filename: " + f.getName());
        }


Apologies for cross posting (Tomcat - starting webapps in a specific order), but I thought it was relevant here too:

You can define mulitple services in your server.xml, which run on different ports. The services are started sequentially according to the order they appear in the server.xml. This means that you can have - for example - a configuration service running in the first service and then the apps that depend on it in the second (I use the default Catalina one for the rest of them...)

You can see more info here: http://wiki.apache.org/tomcat/FAQ/Miscellaneous#Q27

And this is the service that I include before the Catalina Service:

<Service name="ConfigService">
    <Connector port="8081" protocol="HTTP/1.1"
        connectionTimeout="20000"
        redirectPort="8444" />
    <Engine name="ConfigServiceEngine" defaultHost="localhost">
        <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true"
            xmlValidation="false" xmlNamespaceAware="false">

            <Context path="/" reloadable="true" docBase="/path/to/your/service/directory" />
        </Host>
    </Engine>
</Service>

As you can see, I use docbase rather than appBase, but you should be able t configure a different appBase if you prefer...

NB it's important to change the name of both the service and the engine.

HTH


There are three ways to deploy the webapp.war in Tomcat.

  1. Add Context element inside a Host element in the $CATALINA_BASE/conf/server.xml file.

    <Server><Service><Engine><Host>
        <Context path="[webappName]" docBase="[webappPath]"/>
    </Server></Service></Engine></Host>
    
  2. Create the $CATALINA_BASE/conf/[engineName]/[hostName]/[webappName].xml file with content:

    <Context docBase="[webappPath]"/>
    
  3. Add the webapp.war file directly in the $CATALINA_BASE/webapps/ directory.

The following deployment sequence will occur on Tomcat startup:

1→2→3


Some explanations:

  • $CATALINA_BASE

    Refer the base directory against which most relative paths are resolved. If you have not configured Tomcat for multiple instances by setting a CATALINA_BASE directory, then $CATALINA_BASE will be set to the value of $CATALINA_HOME, the directory into which you have installed Tomcat.

  • docBase

    The pathname to the web application WAR file. You may specify an absolute pathname for this WAR file, or a pathname that is relative to the appBase directory of the owning Host.

  • engineName:

    The name of the engine associated to the context. The default name is Catalina.

  • hostName:

    The name of the host associated to the context. The default name is localhost.


Assume that:

  • there are 3 webapps: a.war, b.war and c.war.
  • b.war depends on a.war.
  • c.war depends on b.war.
  • Server.Service.Engine.name = "Catalina"
  • Server.Service.Engine.Host.name = "localhost"
  • Server.Service.Engine.Host.appBase = "webapps"

Try the following steps:

  1. Put all of the war files to the $CATALINA_BASE/webapps/ directory.
  2. create the $CATALINA_BASE/conf/Catalina/localhost/b.xml file with content:

    <Context docBase="b.war"/>
    
  3. Add Context element in the $CATALINA_BASE/conf/server.xml file:

    <Server><Service><Engine><Host>
        <Context path="a" docBase="a.war"/>
    </Server></Service></Engine></Host>
    

Reference:

  • https://tomcat.apache.org/tomcat-9.0-doc/deployer-howto.html
  • http://tomcat.apache.org/tomcat-9.0-doc/config/context.html


You could always move the first .war file into the tomcat directory, then wait for it to deploy, then move the next two into the tomcat directory. This would deploy the first, then deploy the other two.

Alternatively you can possibly use the fact that Tomcat will scan the directory and deploy the first war, then second war, then third war alphabetically


That's quite easy to achieve if you don't care hacking a bit of tomcat code and creating your own Host instance

1) Create a subClass of org.apache.catalina.core.StandardHost, say MyHost:

class MyHost extends org.apache.catalina.core.StandardHost{
    public MyHost (){
    super();
    //changing HashMap for a predictable ordered Map :)
    this.children = new LinkedHashMap();
    }
} 

2) register your class on your server's xml Host tag ()

Incredible as it may seem, it solves the problem as long as you have all your web app declared in the correct order inside of Host tag:

<Host>
 <context app1>
 <context app2>

Thaen app1 will start before app2, no matter which SO you used.


We run multiple tomcats. We can bring up groups of apps independently by putting the first group in one tomcat and the later ones in a second.

This assumes that they are not trying to communicate with each other using the JVM.


Building on from @Luiz's answer for Tomcat 9 (in which children is now final), we had to override several methods, copying the basic functionality in some instances.

This isn't optimised for anything, but it does load the contexts in the order they're defined in your server.xml.

// DeterministicDeployOrderHost.java

import java.lang.Override;
import java.lang.String;
import java.util.LinkedHashSet;
import javax.management.ObjectName;
import org.apache.catalina.Container;

public class DeterministicDeployOrderHost extends org.apache.catalina.core.StandardHost {
    private final LinkedHashSet<String> childrenOrder = new LinkedHashSet<>();

    @Override
    public void addChild(Container container) {
        synchronized (children) {
            super.addChild(container);
            childrenOrder.add(container.getName());
        }
    }

    @Override
    public void removeChild(Container container) {
        synchronized (children) {
            super.removeChild(container);
            childrenOrder.remove(container.getName());
        }
    }

    @Override
    public Container[] findChildren() {
        synchronized (children) {
            var list = new java.util.ArrayList<Container>(children.size());
            for (var childName : childrenOrder) {
                list.add(children.get(childName));
            }
            return list.toArray(new Container[0]);
        }
    }

    @Override
    public ObjectName[] getChildren() {
        var names = new java.util.ArrayList<ObjectName>(children.size());
        for (var childName : childrenOrder) {
            var next = children.get(childName);
            if (next instanceof org.apache.catalina.core.ContainerBase) {
                names.add(next.getObjectName());
            }
        }
        return names.toArray(new ObjectName[0]);
    }
}

Get the tomcat-catalina-*.jar, e.g. from Maven Repository and compile this into DeterministicDeployOrderHost.class:

javac DeterministicDeployOrderHost.java -cp tomcat-catalina-9.0.65.jar

Then copy the compiled .class file into $CATALINA_HOME/lib and add the following to the host:

<Host ... className="DeterministicDeployOrderHost">
  <Context .../>
  <Context .../>
</Host>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜