Contents

Full-stack application in Java: Quick start

Java project quick start

In the article introducing this series, I shared that I wanted to explore full-stack app development. In particular, I wanted to develop a full-stack application that helps people manage their checklists. Users will use a browser to connect to the front-end server, which, in turn, will use the API provided by the back-end server to access and modify the checklists.

This article is the first one describing what I am doing in Java + Spring. I will cover project creation and structure, and build automation. We won't have much of the application implemented by the end of this article, but it should be a good and helpful start. So buckle up and let's get cracking!

Project structure and setup

We are going to use maven for our project and configure a multi-module project with it. We want to have a module for the front end and another one for the back end. We are going to create those modules as individual projects using Spring Initializr and then we will put them together in a multi-module project. This is what I have used for both modules. And, yes, I will be adding more dependencies in future articles.

Front-end module

Project
Maven
Language
Java
Spring Boot
3.5.7 (Latest stable)
Group
dev.jorgeortiz
Artifact
rexlists-fe
Name
RexlistsFrontEnd
Description
Rexlists Front End
Package name
dev.jorgeortiz.rexlists-fe
Packaging
Jar
Properties
YAML
Java
25
Dependencies
  • JTE (for the templates)
  • Spring Web (for the front-end application)
  • Spring Boot DevTools (for live reloading)

Back-end module

Project
Maven
Language
Java
Spring Boot
3.5.5 (Latest stable)
Group
dev.jorgeortiz
Artifact
rexlists-be
Name
RexlistsBackEnd
Description
Rexlists Back End
Package name
dev.jorgeortiz.rexlists-be
Packaging
Jar
Properties
YAML
Java
25
Dependencies
  • Spring Web (for the front-end application)
  • Spring Boot DevTools (for live reloading)

Multi-module

The Spring Boot projects we have created and downloaded serve as a starting point. They have the initial content for both modules and are good enough for producing individual applications. However, we need to introduce changes to make them work together in a single project.

We create a directory for the parent project and call it rexlists. Inside that directory, we uncompress each of the two projects. This is the version that can be found in the initial commit of the repository that I created for this project.

  mkdir rexlists
  cd rexlists
  unzip ~/Downloads/rexlists-fe.zip
  unzip ~/Downloads/rexlists-be.zip

We also want to move the git configuration files to the root directory of the parent project, so none of the files are altered in the process.

  mv rexlists-fe/{.gitignore,.gitattributes} .
  rm rexlists-be/{.gitignore,.gitattributes}

Build automation

MVN configuration

Being a Spring project, build automation is included. I chose to use Maven when I created the two projects, but you can go with Gradle, if you are more comfortable with it.

Now we need to create a pom.xml for the parent project and modify the pom.xml of the modules. We will use the pom.xml from one of the modules to reduce the amount of typing.

  cp rexlists-fe/pom.xml .

The pom.xml for the project should be:

  <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>3.5.5</version>
      <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>dev.jorgeortiz</groupId>
    <artifactId>rexlists</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Rexlists</name>
    <description>Rexlists application project</description>
    <packaging>pom</packaging>
    <url/>

    <licenses>
      <license>
        <name>Apache-2.0</name>
        <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
        <distribution>repo</distribution>
      </license>
    </licenses>
    <developers>
      <developer>
        <id>jdortiz</id>
        <name>Jorge D. Ortiz-Fuentes</name>
        <url>http://jorgeortiz.dev/</url>
      </developer>
    </developers>
    <scm>
      <connection/>
      <developerConnection/>
      <tag/>
      <url/>
    </scm>

    <properties>
      <java.version>25</java.version>
    </properties>

    <modules>
      <module>rexlists-fe</module>
      <module>rexlists-be</module>
    </modules>

  </project>

On the content that I copied from the pom.xml of one of the modules, I have:

  • Changed the name of the artifactId to be rexlists and name and description accordingly.
  • Specify the packaging to be pom, rather than the default jar.
  • Modified the license to be Apache 2.
  • Added some data about me as the developer.
  • Added the <modules> section with the names of the directories of the submodules.
  • Removed all the dependencies from here. We may add the ones shared by all the submodules later.
  • Removed the build section with the plugins.

The pom.xml of the rexlists-fe module should change to be like this:

  <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
      <groupId>dev.jorgeortiz</groupId>
      <artifactId>rexlists</artifactId>
      <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>rexlists-fe</artifactId>
    <name>Rexlists Back End</name>
    <description>Rexlists Front End</description>
    <url/>

    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
        <groupId>gg.jte</groupId>
        <artifactId>jte-spring-boot-starter-3</artifactId>
        <version>3.1.16</version>
      </dependency>

      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
      </dependency>
    </dependencies>

    <build>
      <plugins>
        <plugin>
          <groupId>gg.jte</groupId>
          <artifactId>jte-maven-plugin</artifactId>
          <version>3.1.16</version>
          <executions>
            <execution>
              <id>jte-generate</id>
              <phase>generate-sources</phase>
              <goals>
                <goal>generate</goal>
              </goals>
              <configuration>
                <sourceDirectory>${project.basedir}/src/main/jte</sourceDirectory>
                <contentType>Html</contentType>
                <binaryStaticContent>true</binaryStaticContent>
                <targetResourceDirectory>${project.build.outputDirectory}</targetResourceDirectory>
              </configuration>
            </execution>
          </executions>
        </plugin>
        <plugin>
  	<groupId>org.springframework.boot</groupId>
  	<artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
      </plugins>
    </build>

  </project>
  • Change the whole parent section to be our parent project with no relativePath, which, in turn, inherits from the Spring Boot one.
  • Remove the groupId and the version, because they are inherited from the parent (and we are going to use the same version in all the modules.)
  • Remove the licenses, developers, scm, and properties sections, because they are inherited from the parent project.

And the pom.xml of the back end should have the following contents.

  <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
      <groupId>dev.jorgeortiz</groupId>
      <artifactId>rexlists</artifactId>
      <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>rexlists-be</artifactId>
    <name>RexlistsBackEnd</name>
    <description>Rexlists Back End</description>
    <url/>

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

      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
      </dependency>
    </dependencies>

    <build>
      <plugins>
        <plugin>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
      </plugins>
    </build>

  </project>
  • Change the whole parent section to be our parent project. Same as for the rexlists-fe.
  • Remove the groupId and the version. Same as for the rexlists-fe.
  • Remove the licenses, developers, scm, and properties sections. Same as for the rexlists-fe configuration.
  • Keep the dependencies and build section as they are. Same as for the rexlists-fe configuration.

We also want to move some configuration to the parent project.

  mv rexlists-fe/.mvn .
  rm -rf rexlists-be/.mvn

And before we move on, use mvn validate to verify that all the changes are fine.

Make both modules run together

If you had a simple Spring Boot project, a mvn spring-boot:start would launch the application. However, we have configured a multi-module project and we need to take an extra step (package or install) and run each module separately. Let's start with the front end.

  mvn -pl rexlists-fe clean package spring-boot:start

So far, so good. Now we launch the back end too.

  mvn -pl rexlists-be clean package spring-boot:start

It will try to start the back end, but if will fail with an exception.

Error: Exception thrown by the agent: java.rmi.server.ExportException: Port already in use: 9001

This is the JMX management interface provided by the spring-boot-maven-plugin, and it is already being used by the front end. We can change the port it listens to in the pom.xml of the back end. The plugin configuration should be replaced with the following one.

  <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
      <jmxPort>9002</jmxPort>
    </configuration>
  </plugin>

Let's try again to run the back end using the same command. This time, we get a different error.

Web server failed to start. Port 8080 was already in use.

Certainly, our front end is also using that port for its embedded Tomcat server, because it is the default one in a Spring Boot applications. This time we can change the configuration for the back end by adding the following line to rexlists-be/src/main/resources/application.properties.

server:
  port: 8090

We can use a web browser to connect to the two servers http://localhost:8080/ and http://localhost:8090/ and get… an error from each. 😱

Wait! All this effort to get two errors? We can indeed make a successful connection to both servers. However, they just reply with an error, because we haven't defined any content yet.

You don't need to do it now, but be aware that they can also be stopped separately using the following commands.

  mvn -pl rexlists-fe spring-boot:stop
  mvn -pl rexlists-be spring-boot:stop

Summary

In this first article, I explained how to create the structure for our multi-module project and made the necessary fixes so that the building tasks would work as expected. I must acknowledge that most of the work was provided by the Spring Boot Initializr, that has the necessary configurability built-in, but we tweaked the configuration to make it work.

The full code repository for this project with individual commits for each part of the explanation is available for you to check what I did and where, and code along with me.

In the next article we will work on the front end to make it more useful.

Stay curious. Hack your code. See you next time!