Deploy java REST service on tomcat docker

Viewed 1858

I'm trying to deploy a simple REST service to a tomcat that is running on a docker, but I can't seam to reach the service that I tried to deploy.

The simple service that I created is in this class:

@Path("/test")
public class RestTest {

    public static final String JSON_RPC = "2.0";

    @GET
    @Path("/hello")
    @Produces(MediaType.APPLICATION_JSON)
    public Response processHelloRequestGet() {
        String answer = "Hello There!";
        JsonRpcResponse rpcResponse = new JsonRpcResponse();
        rpcResponse.setId("42");
        rpcResponse.setJsonRpc(JSON_RPC);
        rpcResponse.setResult(answer);
        return Response.status(Status.OK).entity(rpcResponse).build();
    }
}

It just creates a simple, static answer in form of a JSON-RPC response.

My web.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">

    <display-name>REST Test</display-name>

    <servlet>
        <servlet-name>jerseyServlet</servlet-name>
        <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>net.jfabricationgames.rest_test</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>jerseyServlet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
</web-app>

and my pom.xml looks like this:

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.jfabricationgames.rest_test</groupId>
    <artifactId>RestTest</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>RestTest Maven Webapp</name>
    <url>http://maven.apache.org</url>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- JAX-RS for REST -->
        <dependency>
            <groupId>javax.ws.rs</groupId>
            <artifactId>javax.ws.rs-api</artifactId>
            <version>2.1-m07</version><!-- <version>2.0</version> -->
        </dependency>

        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-jdk-http</artifactId>
            <version>2.26</version><!-- <version>2.10.1</version> -->
        </dependency>

        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet</artifactId>
            <version>2.26</version><!-- <version>2.10.1</version> -->
        </dependency>

        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-jackson</artifactId>
            <version>2.26</version><!-- <version>2.10.1</version> -->
        </dependency>

        <dependency>
            <groupId>org.glassfish.jersey.inject</groupId>
            <artifactId>jersey-hk2</artifactId>
            <version>2.26</version>
        </dependency>

        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>

        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-json</artifactId>
            <version>1.18.1</version>
        </dependency>
        <dependency>
            <groupId>com.owlike</groupId>
            <artifactId>genson</artifactId>
            <version>0.99</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/asm/asm -->
        <dependency>
            <groupId>asm</groupId>
            <artifactId>asm</artifactId>
            <version>3.3.1</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.sun.jersey/jersey-bundle -->
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-bundle</artifactId>
            <version>1.19.2</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.json/json -->
        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20160810</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.sun.jersey/jersey-server -->
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-server</artifactId>
            <version>1.19.2</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.sun.jersey/jersey-core -->
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-core</artifactId>
            <version>1.19.2</version>
        </dependency>

    </dependencies>
    <build>
        <finalName>RestTest</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <path>/rest_test</path>
                    <port>8080</port>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Now if I run the project on a tomcat server from within eclipse everything is working just as expected (using the run configuration Base directory: ${workspace_loc:/RestTest} and Goals: tomcat7:run) and I can reach the service using the URL http://localhost:8080/rest_test/rest/test/hello.

The problem now is that I want my service to run on a tomcat inside a docker on a server, which doesn't seem to be working.

What I tried so far is exporting the project into a .war file (using maven (gloals: clean install compile) or the eclipse export function), placing them in the directory from where I build the docker file, creating the docker file and running it.

The Dockerfile I'm using (following this tutorial) looks like this:

FROM tomcat:8.0-alpine
LABEL maintainer="deepak@softwareyoga.com"

ADD rest_test.war /usr/local/tomcat/webapps/

EXPOSE 8080
CMD ["catalina.sh", "run"]

When running the docker container using the command docker run -p 80:8080 mywebapp I get the following output (which seems to be correct to me):

docker run -p 80:8080 mywebapp
21-Oct-2019 16:49:55.404 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version:        Apache Tomcat/8.0.53
21-Oct-2019 16:49:55.407 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built:          Jun 29 2018 14:42:45 UTC
21-Oct-2019 16:49:55.408 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server number:         8.0.53.0
21-Oct-2019 16:49:55.408 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name:               Linux
21-Oct-2019 16:49:55.408 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Version:            5.0.0-31-generic
21-Oct-2019 16:49:55.409 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Architecture:          amd64
21-Oct-2019 16:49:55.409 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home:             /usr/lib/jvm/java-1.7-openjdk/jre
21-Oct-2019 16:49:55.409 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Version:           1.7.0_181-b01
21-Oct-2019 16:49:55.410 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Vendor:            Oracle Corporation
21-Oct-2019 16:49:55.410 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_BASE:         /usr/local/tomcat
21-Oct-2019 16:49:55.411 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_HOME:         /usr/local/tomcat
21-Oct-2019 16:49:55.411 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties
21-Oct-2019 16:49:55.412 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
21-Oct-2019 16:49:55.412 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djdk.tls.ephemeralDHKeySize=2048
21-Oct-2019 16:49:55.412 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.protocol.handler.pkgs=org.apache.catalina.webresources
21-Oct-2019 16:49:55.413 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dignore.endorsed.dirs=
21-Oct-2019 16:49:55.413 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.base=/usr/local/tomcat
21-Oct-2019 16:49:55.413 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.home=/usr/local/tomcat
21-Oct-2019 16:49:55.414 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.io.tmpdir=/usr/local/tomcat/temp
21-Oct-2019 16:49:55.414 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent Loaded APR based Apache Tomcat Native library 1.2.17 using APR version 1.6.3.
21-Oct-2019 16:49:55.414 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
21-Oct-2019 16:49:55.418 INFO [main] org.apache.catalina.core.AprLifecycleListener.initializeSSL OpenSSL successfully initialized (OpenSSL 1.0.2o  27 Mar 2018)
21-Oct-2019 16:49:55.528 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-apr-8080"]
21-Oct-2019 16:49:55.556 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["ajp-apr-8009"]
21-Oct-2019 16:49:55.561 INFO [main] org.apache.catalina.startup.Catalina.load Initialization processed in 1095 ms
21-Oct-2019 16:49:55.677 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service Catalina
21-Oct-2019 16:49:55.677 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet Engine: Apache Tomcat/8.0.53
21-Oct-2019 16:49:55.760 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive /usr/local/tomcat/webapps/rest_test.war
21-Oct-2019 16:49:56.810 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive /usr/local/tomcat/webapps/rest_test.war has finished in 1,050 ms
21-Oct-2019 16:49:56.820 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive /usr/local/tomcat/webapps/sample.war
21-Oct-2019 16:49:56.911 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive /usr/local/tomcat/webapps/sample.war has finished in 91 ms
21-Oct-2019 16:49:56.913 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory /usr/local/tomcat/webapps/docs
21-Oct-2019 16:49:56.986 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory /usr/local/tomcat/webapps/docs has finished in 73 ms
21-Oct-2019 16:49:56.992 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory /usr/local/tomcat/webapps/examples
21-Oct-2019 16:49:57.799 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory /usr/local/tomcat/webapps/examples has finished in 807 ms
21-Oct-2019 16:49:57.801 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory /usr/local/tomcat/webapps/ROOT
21-Oct-2019 16:49:57.882 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory /usr/local/tomcat/webapps/ROOT has finished in 82 ms
21-Oct-2019 16:49:57.885 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory /usr/local/tomcat/webapps/manager
21-Oct-2019 16:49:57.984 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory /usr/local/tomcat/webapps/manager has finished in 99 ms
21-Oct-2019 16:49:57.993 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory /usr/local/tomcat/webapps/host-manager
21-Oct-2019 16:49:58.081 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory /usr/local/tomcat/webapps/host-manager has finished in 88 ms
21-Oct-2019 16:49:58.088 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-apr-8080"]
21-Oct-2019 16:49:58.156 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["ajp-apr-8009"]
21-Oct-2019 16:49:58.187 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 2625 ms

What is working so far is that the docker container seems to be started correctly and that I can reach the tomcat 'homepage' by entering localhost into the browser (see the picture below). Also my .war file seems to be added and extracted correctly (there is a rest_test.war file and a rest_test directory in the directory webapps of the tomcat docker container). Also everything is working just fine when starting it from within eclipse.

Tomcat 'homepage' is reachable

What is NOT working yet is to reach the rest service I created and deployed. I tried to access it using the url localhost/rest_test/rest/test/hello but I get an HTTP error 404 - Not Found.

What is kinda strange is that I don't get a Message in the HTTP error 404 when calling the url localhost/rest_test/rest/..., but I get a message when calling any other url (like shown in the pictures below), which makes me think that it's not completely wrong, what I'm trying.

The 'assumed correct' url doesn't contain a message in the HTTP error 404

A wrong URL seems to contain a message in the http 404 error

Thanks in advance.

EDIT: Updated to tomcat 9 using the docker image tomcat:9.0-alpine, but that didn't seem to change anything. The content of the .war file looks like this:

enter image description here

1 Answers

It seems that the war file is not built correctly. The WEB-INF/libs directory containing the jar application dependencies is missing. More about the directory Layout of a Web Application Archive file here.

Exceptions thrown during startup are logged into /usr/local/tomcat/logs/localhost.<timestamp>.log. That is the reason everything seems to work normally. localhost.log should contain the error:

java.lang.ClassNotFoundException: com.sun.jersey.spi.container.servlet.ServletContainer

As mentioned in the comments jersey 2.26 cannot run with java 1.7.0_181. As the libraries don't get loaded during application startup, there are no java version incompatibility errors in the output.

Now, assuming you run with tomcat 9 and java 8, the first step is to fix the build. I suggest running maven from command line. Your pom.xml looks just fine and it should create a valid war.

Next step is to set the scope of javax.ws.rs-api library to provided. The jersey-bundle library has a slightly different version of the javax.ws.rs classes and if you keep both your may run into classloading issues.

 <dependency>
    <groupId>javax.ws.rs</groupId>
    <artifactId>javax.ws.rs-api</artifactId>
    <version>2.1</version>
    <scope>provided</scope>
</dependency>

This will fix things but the configuration still has issues because you are mixing jersey 1.x with jersey 2.x libraries. In addition the jersey-bundle should not be used with maven. The bundle is only intended for developers that do not use Maven's dependency system.

A basic dependency configuration using the jersey 2 libraries should be like the following:

<properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>

    <jersey2.version>2.26</jersey2.version>
    <jaxrs.version>2.1.1</jaxrs.version>
  </properties>
  <dependencies>
    <!-- JAX-RS -->
    <dependency>
      <groupId>javax.ws.rs</groupId>
      <artifactId>javax.ws.rs-api</artifactId>
      <version>${jaxrs.version}</version>
    </dependency>
    <!-- Jersey -->
    <dependency>
      <groupId>org.glassfish.jersey.containers</groupId>
      <artifactId>jersey-container-servlet</artifactId>
      <version>${jersey2.version}</version>
    </dependency>
    <dependency>
      <groupId>org.glassfish.jersey.core</groupId>
      <artifactId>jersey-server</artifactId>
      <version>${jersey2.version}</version>
    </dependency>
    <dependency>
      <groupId>org.glassfish.jersey.core</groupId>
      <artifactId>jersey-client</artifactId>
      <version>${jersey2.version}</version>
    </dependency>
    <dependency>
      <groupId>org.glassfish.jersey.media</groupId>
      <artifactId>jersey-media-json-jackson</artifactId>
      <version>${jersey2.version}</version>
    </dependency>
    <dependency>
      <groupId>org.glassfish.jersey.inject</groupId>
      <artifactId>jersey-hk2</artifactId>
      <version>${jersey2.version}</version>
    </dependency>
  </dependencies>

The web.xml should be modified too:

<servlet>
        <servlet-name>jersey-serlvet</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
             <param-name>jersey.config.server.provider.packages</param-name>
             <param-value>net.jfabricationgames.rest_test</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>jersey-serlvet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>

Hope it helps.

Related