Claudine Broke It

Spring Multi-module deployment to Tomcat

Posted on 19 July 2016

Tomcat

So wow!

The last two weeks have been absolutely incredible for me at work: I switched teams. This is a big change that comes with an ample amount of challenges and learning opportunities; I am taking on a whole new stack of projects that comes with new technological challenges and programming languages. Super exciting stuffs!

Talking about new languages, one of the few things I've been anxiously struggling with in the past months was getting ready for the switch from Php to Java.

Since the main project I am now working with runs on Spring Framework, the last few weeks have been dedicated to messing around with the framework at home as a way to get comfortable with it. The guides and documentation on the product's website are quite straightforward and helpful, but I still had some issues that resulted in hours spent confused on Stack Overflow. Hopefully, this blog post will one day help someone struggling with the same problems I faced with my initial development setup.

Spring Multi-Module Rest API using Maven

The first thing that came to mind as a fun little practice project was to create a REST API project modeled around a bookcase. My requirements were as follows:

The application needs to be...
* deployable to a Tomcat server
* modular; the API services need to be isolated in a seperate module than the model and repository classes
* able to return JSON responses on requests

The complete project files are available on my GitHub account.

Now for the details...

What you need

IDE

I use IntelliJ because of its similarities with Phpstorm, the IDE I'm most familiar with. However, Eclipse is also good.

Spring

You can use the Spring Initializr to generate and download a zip that includes dependencies, basic directory structure and minimal classes for whatever type of project you have in mind. I chose web to get the Full-stack web development with Tomcat and Spring MVC dependencies.

Postman

You should also download Postman to test your Rest Api calls. It isn't a requirement, but it sure is useful!

Maven

Lastly, Maven is required. You may follow the following installation steps to get it running on your machine.

Multi-Module

After I downloaded the Spring basic Web project from the Initializr tool, I created two new modules, model and rest. Located in model is the Book entity that gets returned by the EtagereController on a GET request. Structurally, it looks like this:

+ bibliotheque
    + model
       + src
         + main
          + java
              + it.claudinebroke.model
                 - Book
          + resources
         + test
       - pom.xml
    + rest
       + src
         + main
          + java
              + it.claudinebroke.rest
                 - Application
                 - EtagereController
              + META-INF
                 - MANIFEST.MF
          + resources
              - application.properties
         + test
       - pom.xml
    - pom.xml

bibliotheque pom.xml

To link the modules, you will need to have a pom.xml, i.e. an XML document that enumerates the project's dependencies.

Pay attention to the following:

  • <modules>: list of children modules
  • <packaging>: pom to support multi-module packaging
  • <parent>: spring-boot-starter-parent set to inherit -- and propagate to children modules == basic Spring-related dependencies
  • <artifactId>, <groupId> and <version>: will constitute the <parent> dependency within the children's 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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.6.RELEASE</version>
    </parent>

    <artifactId>bibliotheque</artifactId>
    <packaging>pom</packaging>
    <groupId>it.claudinebroke</groupId>
    <version>1.0-SNAPSHOT</version>

    <modules>
        <module>rest</module>
        <module>model</module>
    </modules>

    <properties>
        <java.version>1.8</java.version>
    </properties>

</project>

Rest pom.xml

As for the RESTful API;

  • <packaging>: war needs to be set in order to deploy to Tomcat
  • <parent>: to inherit basic Spring-boot dependencies
  • <artifactId>model</artifactId>: our other module under the bibliotheque parent. Setting this dependency makes the objects created within the model module available in the present module.
  • spring-boot-starter-web: adds Spring classes to build routes, JSON responses, etc.
  • <build>: responsible of building the war project and all it's dependencies -- including parent and sibling modules -- as a runnable "jar" within the servlet

<?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>bibliotheque</artifactId>
        <groupId>it.claudinebroke</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>rest</artifactId>

    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>it.claudinebroke</groupId>
            <artifactId>model</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Model 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>bibliotheque</artifactId>
        <groupId>it.claudinebroke</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>model</artifactId>

</project>

Rest application.properties

server.port: 8080
management.address: 127.0.0.1

These properties aren't necessary as Spring sets these by default. However, I felt it nice to include this file as it exposes how easily you can map you application's modules to different ports.

Tomcat IntelliJ Setup

I am using IntelliJ Community Edition, which does not come with Tomcat support like the Professional Edition does. No problem; in the Run > Run/Debug Configurations you can select + in the left-hand panel to create a new Maven configuration. Name that config Tomcat and enter the following in the Command line field:

-e clean spring-boot:run

Note that the -e parameter will run the Maven command with error stacktraces turned on

At the bottom of the window, under Before launch: [...], select + and select the cute gift icon with Build 'yourProject:jar' artifact.

Lastly, you will need to indicate a Working directory. This is where I struggled most; I had indicated the parent directory (bibliotheque), when in fact, the correct working directory where all classes are built for deployment are in rest. Therefore, enter the location of your child module in this field, e.g. C:/bibliotheque/rest.

Deploying

With all this put in place you can now run the Maven install command (I use the intelliJ View > Tool Windows > Maven Project sidebar), followed by the previously configured Tomcat Run Configuration. You can then use postman to GET on http://localhost:8080/hello which will output:

{
  "id": 1,
  "content": "Hello, Potato!"
}

The rest is all fun