chrx
chrx

Reputation: 2518

Deploy .war on tomcat Docker container

I'm trying to automatize the process of deploying tomcat webapps' .war on docker containers. To this goal, I devleoped a sample spring boot application to use as a test. Since my final goal is to apply the process to old existing war applications, I configured the application to produce a war file instead of a jar with tomcat embedded.

The sample application is just a sample Spring MVC application with a /greeting?id=<id> endpoint that just returns a page with a "Hello " message, where is a string read from a table on a mysql db. The application is deployed on a Docker container based on the base tomcat image, while the database is deployed on a separate mysql container.

When launching the two containers via a docker-compose script, the deployment seems to have success: The two containers are running, the tomcat instance is active, no error message is visible in cotainer logs, and I can see the webapp deployed and marked as running in the tomcat manager interface at http://<IP>:8082/manager/html. The problem is that at http://<IP>:8082/myapp-sample-webapp/greeting?id=<id> I get a 404 error. I tested putting a test.html static file inside the web app, and this is visible at http://<IP>:8082/myapp-sample-webapp/test.html.

Note also that if I configure the application as a tomcat-embedded jar application (of course changing also Dockerfile configuration accordingly) I can access the /greeting?id=<id> endpoint without problems.

A side problem is that I cannot see any application generated log (neither on the docker container's log with docker logs ..., nor in the /usr/local/tomcat/logs/myapp-sample-webapp.log log file that I configure for the app logging (see application.properties below), and this is preventing me to check whether the problems are somehow related to failing the connection to the mysql database.

This is the pom.xml file for the application is the following:

<?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>eu.myapp</groupId>
    <artifactId>myapp-sample-webapp</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>myapp-sample-webapp</name>
    <description>Project created for testing Docker packaging of a .war web application</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.0.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <docker.image.prefix>myapp-h2020</docker.image.prefix>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>${main.class}</mainClass>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>0.2.3</version>
                <configuration>
                    <imageName>${docker.image.prefix}/${project.artifactId}:${project.version}</imageName>
                    <dockerDirectory>src/main/docker</dockerDirectory>
                    <resources>
                        <resource>
                            <targetPath>/</targetPath>
                            <directory>${project.build.directory}</directory>
                            <include>${project.build.finalName}.war</include>
                        </resource>
                    </resources>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

</project>

This is the main controller class:

@Controller
public class GreetingController {

    @Autowired
    private RecordRepository repository;

    @RequestMapping("/greeting")
    public String greeting(@RequestParam(value="id", required=true ) Integer id, Model model) {
        Record record = repository.findById(id);
        String name = ( record == null ? "World" : record.getName() );
        model.addAttribute("name", name);
        return "greeting" ;
    }

}

This is the main application (the commandline runner was written when initially testing the application in tomcat embedded mode):

edit: I updated the class as suggested by Alex's comment

@SpringBootApplication
public class MyAppSampleWebappApplication extends SpringBootServletInitializer  {

    private static final Logger log = LoggerFactory.getLogger(MyAppSampleWebappApplication.class);

    // JAR
    public static void main(String[] args) {
        System.out.println("Starting MyAppSampleWebappApplication (JAR)...");
        log.debug("Starting MyAppSampleWebappApplication (JAR)...");
        SpringApplication.run(MyAppSampleWebappApplication.class, args);
    }

    //WAR
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
            System.out.println("Starting MyAppSampleWebappApplication (WAR)...");
            log.debug("Starting MyAppSampleWebappApplication (WAR)...");
        return application.sources(MyAppSampleWebappApplication.class);
        }
}

This is the application.properties for my file:

# datasource
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.datasource.driverClassName=com.mysql.jdbc.Driver

# for the moment I hardcoded references to mysql database, to replace with info from
# environment variable on "real" version
spring.datasource.url=jdbc:mysql://<IP OF HOST MACHINE>:3306/myapp-demo
spring.datasource.username=myapp
spring.datasource.password=myapp

spring.jpa.hibernate.ddl-auto=update
spring.data.jpa.repositories.enabled=true
spring.jpa.show-sql=true

logging.level.=DEBUG
logging.file=/usr/local/tomcat/logs/myapp-sample-webapp.log

This is the Dockerfile used through mvn build:docker to generate the image for my application:

FROM tomcat
ADD tomcat-users.xml /usr/local/tomcat/conf
ADD myapp-sample-webapp.war /usr/local/tomcat/webapps/
CMD ["catalina.sh", "run"]

And finally, the docker-compose file launching the two containers:

# container for an external mysql service

sample-mysql:
  image: mysql:latest
  environment:
    MYSQL_ROOT_PASSWORD: p4SSW0rd
    MYSQL_DATABASE: myapp-demo
    MYSQL_USER: myapp
    MYSQL_PASSWORD: myapp
  ports:
    - 3306:3306

# myapp-sample-webapp web application container:

myapp-sample-webapp:
  image: myapp-h2020/myapp-sample-webapp:0.0.1-SNAPSHOT
  ports:
    - 8082:8080

Upvotes: 2

Views: 7242

Answers (2)

chrx
chrx

Reputation: 2518

I think I've found the solution:

In my Dockerfile, I built my application's image on the tomcat base image ( "FROM tomcat" )

I found out that this image is based on JRE 7, while in my pom.xml, I had version 1.8 as Java version (<java.version>1.8</java.version>)

By replacing the first line of the Dockerfile with a more specific version "FROM tomcat:7-jre8", I can now see the page generated by my controller.

Upvotes: 4

Patryk Rogosch
Patryk Rogosch

Reputation: 306

I'm not 100% sure without looking into log file.

How your MVC's view layer is configured? I guess that Spring cannot find view's representation for "greeting". Do you have any JSP, Velocity, etc template?

Alternatively try to tamper with @ResponseBody on the method.

Upvotes: -2

Related Questions