Skip to content

How to create one Jar with two or more servlets on one embedded tomcat. Spring Boot

Michael Latushko edited this page Jul 18, 2017 · 4 revisions

Building one artefact which includes two servlets.

It is impossible to allocate two servlets at one port. To overcome this restriction one needs to use a servlet-container with the ability to change a port number of a servlet. The method customize(ConfigurableEmbeddedServletContainer configurableEmbeddedServletContainr) of the class EmbeddedServletContainerCustomizer implements the mentioned functionality.

Step 1. Maven pom.xml of root

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>ru.kotovaleksandr.springboot</groupId>
    <artifactId>manyservlets</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <modules>
        <module>firstservlet</module>
        <module>secondservlet</module>
        <module>server-core</module>
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.3.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>


</project>

Step 2. The structure of the project.

The are three modules:

  • firstservlet
  • secondservlet
  • server-core

structure

Step 3. Modules of Pom.xml and configuration classes

firstservlet pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>manyservlets</artifactId>
        <groupId>ru.kotovaleksandr.springboot</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
    <artifactId>firstservlet</artifactId>


</project>

SpringBootWebConfigFirst.class

package ru.kotovaleksandr.springboot.firstservlet;


import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.context.annotation.Bean;

import javax.servlet.*;
import java.io.IOException;

@SpringBootApplication
public class SpringBootWebConfigFirst implements EmbeddedServletContainerCustomizer {


    @SuppressWarnings("serial")
    @Bean
    public Servlet dispatcherServlet() {
        return new GenericServlet() {
            @Override
            public void service(ServletRequest req, ServletResponse res)
                    throws ServletException, IOException {
                res.setContentType("text/plain");
                res.getWriter().append("Servlet one");
            }
        };
    }


    @Override
    public void customize(ConfigurableEmbeddedServletContainer configurableEmbeddedServletContainer) {
        configurableEmbeddedServletContainer.setPort(8888);
    }
}

secondservlet pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>manyservlets</artifactId>
        <groupId>ru.kotovaleksandr.springboot</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <packaging>jar</packaging>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>secondservlet</artifactId>


</project>

SpringBootWebConfigSecond.class

package ru.kotovaleksandr.springboot.secondservlet;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.context.annotation.Bean;

import javax.servlet.*;
import java.io.IOException;


@SpringBootApplication
public class SpringBootWebConfigSecond implements EmbeddedServletContainerCustomizer {


    @SuppressWarnings("serial")
    @Bean
    public Servlet dispatcherServlet() {
        return new GenericServlet() {
            @Override
            public void service(ServletRequest req, ServletResponse res)
                    throws ServletException, IOException {
                res.setContentType("text/plain");
                res.getWriter().append("Servlet two");
            }
        };
    }


    @Override
    public void customize(ConfigurableEmbeddedServletContainer configurableEmbeddedServletContainer) {
        configurableEmbeddedServletContainer.setPort(8889);
    }
}

Server-core module

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>manyservlets</artifactId>
        <groupId>ru.kotovaleksandr.springboot</groupId>
        <version>1.0-SNAPSHOT</version>
        <!--<relativePath />-->
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
    <artifactId>server-core</artifactId>
    <dependencies>
        <dependency>
            <groupId>ru.kotovaleksandr.springboot</groupId>
            <artifactId>firstservlet</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>ru.kotovaleksandr.springboot</groupId>
            <artifactId>secondservlet</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

MainConfiguration.class

package ru.kotovaleksandr.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
import ru.kotovaleksandr.springboot.firstservlet.SpringBootWebConfigFirst;
import ru.kotovaleksandr.springboot.secondservlet.SpringBootWebConfigSecond;


@SpringBootApplication
@Import({SpringBootWebConfigFirst.class, SpringBootWebConfigSecond.class})
public class MainConfiguration {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootWebConfigFirst.class, args);
        SpringApplication.run(SpringBootWebConfigSecond.class, args);
    }
}

Build

cd <root project folder>
mvn clean install

run it

cd <root project folder>/server-core/target
java -jar server-core-1.0-SNAPSHOT.jar 

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.4.3.RELEASE)

2017-07-18 11:48:28.744  INFO 11345 --- [           main] r.k.springboot.MainConfiguration         : Starting MainConfiguration v1.0-SNAPSHOT on CTNB001154 with PID 11345 (/home/alexkotov/mine/java-projects/Exersices/manyservlets/server-core/target/server-core-1.0-SNAPSHOT.jar started by alexkotov in /home/alexkotov/mine/java-projects/Exersices/manyservlets/server-core/target)
2017-07-18 11:48:28.759  INFO 11345 --- [           main] r.k.springboot.MainConfiguration         : No active profile set, falling back to default profiles: default
2017-07-18 11:48:28.847  INFO 11345 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@108c4c35: startup date [Tue Jul 18 11:48:28 MSK 2017]; root of context hierarchy
2017-07-18 11:48:30.718  INFO 11345 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8888 (http)
2017-07-18 11:48:30.750  INFO 11345 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2017-07-18 11:48:30.751  INFO 11345 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.6
2017-07-18 11:48:31.541  INFO 11345 --- [ost-startStop-1] org.apache.jasper.servlet.TldScanner     : At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
2017-07-18 11:48:31.590  INFO 11345 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2017-07-18 11:48:31.590  INFO 11345 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2752 ms
2017-07-18 11:48:31.683  INFO 11345 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]
2017-07-18 11:48:31.687  INFO 11345 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2017-07-18 11:48:31.687  INFO 11345 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2017-07-18 11:48:31.687  INFO 11345 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2017-07-18 11:48:31.687  INFO 11345 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2017-07-18 11:48:32.047  INFO 11345 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@108c4c35: startup date [Tue Jul 18 11:48:28 MSK 2017]; root of context hierarchy
2017-07-18 11:48:32.121  INFO 11345 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2017-07-18 11:48:32.122  INFO 11345 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2017-07-18 11:48:32.162  INFO 11345 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-07-18 11:48:32.162  INFO 11345 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-07-18 11:48:32.203  INFO 11345 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-07-18 11:48:32.784  INFO 11345 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-07-18 11:48:32.863  INFO 11345 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8888 (http)
2017-07-18 11:48:32.868  INFO 11345 --- [           main] r.k.springboot.MainConfiguration         : Started MainConfiguration in 4.803 seconds (JVM running for 5.312)

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.4.3.RELEASE)

2017-07-18 11:48:32.908  INFO 11345 --- [           main] r.k.springboot.MainConfiguration         : Starting MainConfiguration v1.0-SNAPSHOT on CTNB001154 with PID 11345 (/home/alexkotov/mine/java-projects/Exersices/manyservlets/server-core/target/server-core-1.0-SNAPSHOT.jar started by alexkotov in /home/alexkotov/mine/java-projects/Exersices/manyservlets/server-core/target)
2017-07-18 11:48:32.909  INFO 11345 --- [           main] r.k.springboot.MainConfiguration         : No active profile set, falling back to default profiles: default
2017-07-18 11:48:32.913  INFO 11345 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@6a472554: startup date [Tue Jul 18 11:48:32 MSK 2017]; root of context hierarchy
2017-07-18 11:48:33.341  INFO 11345 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8889 (http)
2017-07-18 11:48:33.342  INFO 11345 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2017-07-18 11:48:33.342  INFO 11345 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.6
2017-07-18 11:48:33.983  INFO 11345 --- [ost-startStop-1] org.apache.jasper.servlet.TldScanner     : At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
2017-07-18 11:48:34.050  INFO 11345 --- [ost-startStop-1] o.a.c.c.C.[Tomcat-1].[localhost].[/]     : Initializing Spring embedded WebApplicationContext
2017-07-18 11:48:34.050  INFO 11345 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1137 ms
2017-07-18 11:48:34.127  INFO 11345 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]
2017-07-18 11:48:34.128  INFO 11345 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2017-07-18 11:48:34.128  INFO 11345 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2017-07-18 11:48:34.128  INFO 11345 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2017-07-18 11:48:34.128  INFO 11345 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2017-07-18 11:48:34.299  INFO 11345 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@6a472554: startup date [Tue Jul 18 11:48:32 MSK 2017]; root of context hierarchy
2017-07-18 11:48:34.312  INFO 11345 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2017-07-18 11:48:34.312  INFO 11345 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2017-07-18 11:48:34.337  INFO 11345 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-07-18 11:48:34.337  INFO 11345 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-07-18 11:48:34.362  INFO 11345 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-07-18 11:48:35.024  INFO 11345 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-07-18 11:48:35.033  INFO 11345 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8889 (http)
2017-07-18 11:48:35.035  INFO 11345 --- [           main] r.k.springboot.MainConfiguration         : Started MainConfiguration in 2.163 seconds (JVM running for 7.479)

one two

profit!