Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I deploy a war file to the built-in Tomcat server of a Spring Boot Application that resides INSIDE the standalone? [duplicate]

Spring Boot's embedded tomcat is very handy, for both development and deploy.

But what if an another (3rd-party) WAR file (for example, GeoServer) should be added?

Perhaps the following is the normal procedure:

  1. Install a normal Tomcat server.
  2. Build the Spring Boot application as a WAR file, and add it to the webapps folder of the Tomcat.
  3. Also add an another (3rd-party) WAR file to the webapps folder.

But it would be nice if the following configuration were possible.

  1. Build the Spring boot application as a standalone Jar, which includes the embedded Tomcat.
  2. Deploy the Spring boot application Jar.
  3. Add an another (3rd-party) WAR file to a folder which the embedded Tomcat recognizes.
  4. Serve both the Spring boot application contents and the another WAR's contents using the embedded Tomcat.

How can it be done?

UPDATE

When the spring boot application is made of fat jar(=executable jar), the code in the answer is not enough. The revised one is as follows:

@Bean
public EmbeddedServletContainerFactory servletContainerFactory() {
    return new TomcatEmbeddedServletContainerFactory() {

        @Override
        protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
                Tomcat tomcat) {
            try {
                Context context = tomcat.addWebapp("/foo", "/path/to/foo.war");
                WebappLoader loader =
                    new WebappLoader(Thread.currentThread().getContextClassLoader());
                context.setLoader(loader);
            } catch (ServletException ex) {
                throw new IllegalStateException("Failed to add webapp", ex);
            }
            return super.getTomcatEmbeddedServletContainer(tomcat);
        }

    };
}

Since the jar files in a fat jar cannot be loaded by the system classloader, an explicit parent classloader must be specified. Otherwise, the additional WAR cannot load the library jars in the fat jar of the spring boot application that added the WAR.

like image 938
zeodtr Avatar asked Jul 13 '15 02:07

zeodtr


People also ask

Can we deploy WAR file in spring boot?

By using Spring Boot application, we can create a war file to deploy into the web server.

How can I deploy WAR file in Tomcat?

Perhaps the simplest way to deploy a WAR file to Tomcat is to copy the file to Tomcat's webapps directory. Copy and paste WAR files into Tomcat's webapps directory to deploy them. Tomcat monitors this webapps directory for changes, and if it finds a new file there, it will attempt to deploy it.

Can spring boot application be deployed to external Tomcat server?

Creating a war file of Spring Boot Application But when we want to deploy on an external tomcat server then you need to add some additional code. We have to extend the class SpringBootServletInitializer and override the method configure(SpringApplicationBuilder application).


2 Answers

You can add a war file to embedded Tomcat using Tomcat.addWebapp. As its javadoc says, it's the "equivalent to adding a web application to Tomcat's web apps directory". To use this API in Spring Boot, you need to use a custom TomcatEmbeddedServletContainerFactory subclass:

@Bean
public EmbeddedServletContainerFactory servletContainerFactory() {
    return new TomcatEmbeddedServletContainerFactory() {

        @Override
        protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
                Tomcat tomcat) {
            // Ensure that the webapps directory exists
            new File(tomcat.getServer().getCatalinaBase(), "webapps").mkdirs();

            try {
                Context context = tomcat.addWebapp("/foo", "/path/to/foo.war");
                // Allow the webapp to load classes from your fat jar
                context.setParentClassLoader(getClass().getClassLoader());
            } catch (ServletException ex) {
                throw new IllegalStateException("Failed to add webapp", ex);
            }
            return super.getTomcatEmbeddedServletContainer(tomcat);
        }

    };
}
like image 137
Andy Wilkinson Avatar answered Sep 27 '22 02:09

Andy Wilkinson


The accepted answer covers Spring Boot 1.x. The class mentioned is no longer present in Spring Boot 2.x. When using version 2, you need to use a different one:

    @Bean
    @ConditionalOnProperty(name = "external.war.file")
    public TomcatServletWebServerFactory servletContainerFactory(@Value("${external.war.file}") String path,
                                                                 @Value("${external.war.context:}") String contextPath) {
        return new TomcatServletWebServerFactory() {

            @Override
            protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
                new File(tomcat.getServer().getCatalinaBase(), "webapps").mkdirs();

                Context context = tomcat.addWebapp(contextPath, path);
                context.setParentClassLoader(getClass().getClassLoader());

                return super.getTomcatWebServer(tomcat);
            }

        };
    }

Also, Spring boot enbedded Tomcat does not by default contain dependencies for JSPs. If you are using JSPs in your external war, you need to include them.

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
</dependency>

UPDATE: I've written a more detailed blog post on how to set this up for both Spring Boot 1 and 2.

like image 30
Vojtech Ruzicka Avatar answered Sep 25 '22 02:09

Vojtech Ruzicka