Building with a single command

Let's start this connection by combining the build command. Since we put the frontend inside the backend as a subfolder, we will use Maven to control npm. Another important reason of doing this is that Maven has built-in support of various phases of the entire build life cycle that we can leverage. We can use Maven plugins to combine the frontend build steps into Maven's build life cycle. The build life cycle that is shown in Figure 8.8 here is the one we will implement:

Figure 8.8: Building the life cycle

For simplicity, the life cycle diagram doesn't include all of the default phases that Maven supports. You can find out more about it here: https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html.

As you can see from the diagram, the build process starts with the initialization, compilation, and test of the backend. And then we include the build steps of the frontend in the prepare-package phase. Once the frontend is built, we copy the assets from the front-end/dist folder to the src/main/resources folder so that in the package phase, those assets will be added into the final JAR package. Once that is done, we start the Spring Boot application in Maven's pre-integration-test phase and then execute the end-to-end tests that we create in the frontend. When all of the tests pass, we stop the Spring Boot application. And then, Maven installs the JAR package to the repository.

Since we only attach the build steps of the frontend into Maven's packaging phases, when we run mvn test, only the backend tests will be executed, which is desired. To run the frontend tests, we will use the npm commands. In this way, we won't have coupling between the tests execution of the two ends. 

Now, let's see how to implement this unified build process.

We will use Exec Maven Plugin (https://www.mojohaus.org/exec-maven-plugin/index.html) to execute those npm commands and use Maven Resources Plugin (https://maven.apache.org/plugins/maven-resources-plugin) to copy the frontend resources, and use Spring Boot Maven Plugin, which is already included in the generated scaffold, to start and stop the application. All of the required changes are made to the <plugins> section of the <build> section in pom.xml.

First of all, let's add the Exec Maven Plugin to the <plugins> section, as follows:

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions></executions>
<configuration>
<workingDirectory>${basedir}/front-end</workingDirectory>
</configuration>
</plugin>

The <executions> section is where we will add details of those npm commands. And <workingDirectory> is to tell exec-maven-plugin that the npm commands will be executed inside the front-end/ folder.

The following is <execution> that tells exec-maven-plugin to execute the npm install command:

<execution>
<id>font-end install</id>
<goals>
<goal>exec</goal>
</goals>
<phase>prepare-package</phase>
<configuration>
<executable>npm</executable>
<arguments>
<argument>install</argument>
</arguments>
</configuration>
</execution>

In this <execution><id> is where you name <execution> uniquely among the entire build process. The exec goal is to tell exec-maven-plugin that npm, which is specified in the <executable> tag, is an external program. And <phase> is to tell Maven this plugin's execution should be performed during the prepare-package phase. The <arguments> section is for specifying the arguments that the executable requires.

The following section is for executing the npm run unit during the prepare-package phase:

<execution>
<id>font-end unit test</id>
<goals>
<goal>exec</goal>
</goals>
<phase>prepare-package</phase>
<configuration>
<executable>npm</executable>
<arguments>
<argument>run</argument>
<argument>test:unit</argument>
</arguments>
</configuration>
</execution>

As you can see, since we need to pass two arguments, run and test:unit, to npm, we list them in the <arguments> part accordingly. And, since Maven 3.0.3, the <execution> of different plugins will be executed in the order that they are listed in pom.xml.

The following <execution> section is for exec-maven-plugin to execute the npm run build command during the prepare-package phase:

<execution>
<id>font-end build package</id>
<goals>
<goal>exec</goal>
</goals>
<phase>prepare-package</phase>
<configuration>
<executable>npm</executable>
<arguments>
<argument>run</argument>
<argument>build</argument>
</arguments>
</configuration>
</execution>

The following is <execution> for exec-maven-plugin to invoke the npm run test:e2e command using the integration-test phase:

<execution>
<id>front-end e2e test</id>
<goals>
<goal>exec</goal>
</goals>
<phase>integration-test</phase>
<configuration>
<executable>npm</executable>
<arguments>
<argument>run</argument>
<argument>test:e2e</argument>
</arguments>
</configuration>
</execution>

Now, we have attached all the npm commands to the build process. Let's add the Maven Resources Plugin to copy the build result of the frontend to the src/main/resources directory.

Here is how the maven-resources-plugin section looks overall:

<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
<executions></executions>
</plugin>

Because Spring Boot put the HTML template files in the src/main/resources/templates folder and static assets in the src/main/resources/static folder, we will need to copy front-end/dist/index.html and front-end/dist/static separately, in two <execution> instances. 

The following <execution> will copy index.html to the src/main/resources/templates folder during the prepare-package phase:

<execution>
<id>copy front-end template</id>
<goals>
<goal>copy-resources</goal>
</goals>
<phase>prepare-package</phase>
<configuration>
<outputDirectory>${basedir}/src/main/resources/templates</outputDirecto ry>
<resources>
<resource>
<directory>front-end/dist</directory>
<includes><include>index.html</include></includes>
</resource>
</resources>
</configuration>
</execution>

The following is <execution> that will copy the entire static folder into the src/main/resources/static folder:

<execution>
<id>copy front-end static assets</id>
<goals>
<goal>copy-resources</goal>
</goals>
<phase>prepare-package</phase>
<configuration>
<outputDirectory>${basedir}/src/main/resources/static</outputDirectory>
<resources>
<resource>
<directory>front-end/dist</directory>
<excludes><exclude>index.html</exclude></excludes>
</resource>
</resources>
</configuration>
</execution>

As you can see, the only difference this <execution> has with the previous one is that we use <excludes> to skip index.html during the copying.

Since we specify these two copy <execution> instances to be executed in the prepare-package phase, it is easy to assume that Maven will package the frontend assets into the JAR package. In fact, Maven will do that, but it will only do it the second time you run the mvn install command. Let me explain this. In Maven's default build life cycle, between the initialize phase and the compile phase, there are another four phases, including the following:

  • generate-sources
  • process-sources
  • generate-resources
  • process-resources

It is in the process-resources phase that Maven will copy and process the resources into the destination directory, which is the target/ directory, ready for packaging, as shown in Figure 8.9. And the packaging in the package phase happens in the target/ directory:

Figure 8.9: Copying resources to the target directory

As you can see, after we copy the frontend assets to src/main/resources in the prepare-package phase, Maven won't touch src/main/resources anymore. It is only the next time when you execute mvn install that those frontend assets will be picked up by Maven for packaging but, at that moment, they are already stalled since they are not the fresh version that is generated in the later prepare-package phase.

To fix this, let's add another two copy <execution> instances to copy frontend assets to the target/classes directory. The only differences are that they will have a different execution ID and different values for outputDirectory, as you can see here:

<execution>
<id>copy front-end template to target</id>
...
<configuration>
<outputDirectory>${basedir}/target/classes/templates</outputDirectory>
...
</configuration>
</execution>
<execution>
<id>copy front-end assets to target</id>
...
<configuration>
<outputDirectory>${basedir}/target/classes/static</outputDirectory>
...
</configuration>
</execution>

Now, let's get the last part done, which is to start and stop Spring Boot. It is quite straightforward with spring-boot-maven-plugin. The following is the change we make to the plugin:

<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>pre integration test</id>
<goals>
<goal>start</goal>
</goals>
</execution>
<execution>
<id>post integration test</id>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>

As you can see, we simply add two <execution> sections which only contain the start and stop goals. The Spring Boot Maven plugin will tell Maven that the start goal is for the pre integration test phase and the stop goal is for the post integration test phase.

Now, if you run the mvn install command, you should be able to see the following simplified log information in the output:

[INFO] --- maven-compiler-plugin:3.7.0:compile (default-compile) @ app ---
[INFO] --- maven-surefire-plugin:2.21.0:test (default-test) @ app ---
[INFO] --- exec-maven-plugin:1.6.0:exec (font-end install) @ app ---
[INFO] --- exec-maven-plugin:1.6.0:exec (font-end unit test) @ app ---
[INFO] --- exec-maven-plugin:1.6.0:exec (font-end build package) @ app ---
[INFO] --- maven-resources-plugin:3.1.0:copy-resources (copy front-end template) @ app ---
[INFO] --- maven-resources-plugin:3.1.0:copy-resources (copy front-end assets) @ app ---
[INFO] --- maven-resources-plugin:3.1.0:copy-resources (copy front-end template to target) @ app ---
[INFO] --- maven-resources-plugin:3.1.0:copy-resources (copy front-end assets to target) @ app ---
[INFO] --- maven-jar-plugin:3.0.2:jar (default-jar) @ app ---
[INFO] --- spring-boot-maven-plugin:2.0.2.RELEASE:start (pre integration test) @ app ---
[INFO] --- exec-maven-plugin:1.6.0:exec (front-end e2e test) @ app ---
[INFO] --- spring-boot-maven-plugin:2.0.2.RELEASE:stop (post integration test) @ app ---
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ app ---

As you can see, our build processes of the backend and the frontend are now unified under a single command, mvn install. Every time before we commit the code, we should run this command.

Beside a default life cycle, Maven has a clean life cycle, which allows users to use a plugin such as the Maven Clean Plugin (maven-clean-plugin) to remove resources that might affect the build, such as those generated resources in previous installs before starting the default life cycle. And, you might also have noticed that those frontend assets that we will copy into the src/main/resources folder should be cleaned up. Let's add this plugin underneath maven-resources-plugin, as follows:

<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<filesets>
<fileset>
<directory>${basedir}/src/main/resources/static/static</directory>
</fileset>
<fileset>
<directory>${basedir}/src/main/resources/templates</directory>
<includes>
<include>index.html</include>
</includes>
</fileset>
</filesets>
</configuration>
</plugin>

As you can see, we don't need to specify the phase and the goal for this plugin. We just tell it what we need to remove. And, in order to trigger this plugin, we need to use the mvn clean or mvn clean install commands. And, it is preferred to use mvn clean install to run the build because it makes sure the installation is not affected by previous builds.

One last thing before we commit our code: let's change the port that webpack-dev-server uses from 8080 to 3000. To do that, we need to create the front-end/vue.config.js file, as follows:

module.exports = {
devServer: {
port: 3000
}
}

@vue/cli-service will pick up this configuration and use it to start up webpack-dev-server. With this change, our frontend will run under port 3000 while the backend runs under port 8080.

Now, here is the commit to improve the build process:

Figure 8.10: Combining the build process commits
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.255.178