Deploy Angular Maven WebApp on Tomcat

August 8, 2016 By Ashish Doneriya

In this post I’m taking about Angular vesion 2 and above.

For angular app development we can run angular app on tomcat by putting the angular app content ( contents inside target directory when you build, default is ‘dist’ for angular ) directly inside /src/main/webapp directory.

For Maven projects we will do all the things automatically (build, copy etc).

So here is my project structure for maven project.

.
├── angular
│   ├── angular-cli-build.js
│   ├── angular-cli.json
│   ├── config
│   ├── dist
│   │   ├── app
│   │   ├── bundle.js
│   │   ├── favicon.ico
│   │   ├── index.html
│   │   ├── main.js
│   │   ├── system-config.js
│   │   └── vendor
│   ├── e2e
│   ├── package.json
│   ├── public
│   ├── README.md
│   ├── src
│   ├── tmp
│   ├── tslint.json
│   ├── typings
│   └── typings.json
├── cat.txt
├── pom.xml
├── src
│   └── main
│       ├── java
│       ├── resources
│       └── webapp
│           └── WEB-INF
│               └── web.xml
├── target
└── temp

And when I run command mvn clean install, what I want in .war file is

.
├── app
├── cat.txt
├── favicon.ico
├── index.html
├── main.js
├── META-INF
├── system-config.js
├── vendor
└── WEB-INF
    ├── lib
    └── web.xml

So as you can see in the war file I have put all the contents of /angular/dist/ in root of war file.

What we did is

  1. Installed nodejs and npm
  2. Ran the command “npm install” in /angular/ to install dependencies and compile typescript files.
  3. Ran the command “npm run build” in /angular/
  4. Finally copied the contents of /angular/dist/ in root of .war file.

We will not do all these things by ourselves but will the help of two maven plugins. One is frontend-maven-plugin which will install nodejs and build the angular project. Another plugin is maven-resources-plugin which will copy the contents of ‘dist’ directory.

I am writing the code first that you have to add in pom.xml. I will explain after that.

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
    </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-war-plugin</artifactId>
      <version>2.6</version>
    </plugin>

    <!-- Plugin to execute command  "npm install" and "npm run build" inside /angular directory -->
    <plugin>
      <groupId>com.github.eirslett</groupId>
      <artifactId>frontend-maven-plugin</artifactId>
      <version>1.0</version>
      <configuration>
        <workingDirectory>angular</workingDirectory>
        <installDirectory>temp</installDirectory>
      </configuration>
      <executions>
        <!-- It will install nodejs and npm -->
        <execution>
          <id>install node and npm</id>
          <goals>
            <goal>install-node-and-npm</goal>
          </goals>
          <configuration>
            <nodeVersion>v6.3.1</nodeVersion>
            <npmVersion>3.9.5</npmVersion>
          </configuration>
        </execution>

        <!-- It will execute command "npm install" inside "/angular" directory -->
        <execution>
          <id>npm install</id>
          <goals>
            <goal>npm</goal>
          </goals>
          <configuration>
            <arguments>install</arguments>
          </configuration>
        </execution>

        <!-- It will execute command "npm build" inside "/angular" directory to clean and create "/dist" directory-->
        <execution>
          <id>npm build</id>
          <goals>
            <goal>npm</goal>
          </goals>
          <configuration>
            <arguments>run build</arguments>
          </configuration>
        </execution>
      </executions>
    </plugin>

    <!-- Plugin to copy the content of /angular/dist/ directory to output directory (ie/ /target/transactionManager-1.0/) -->
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-resources-plugin</artifactId>
      <version>2.4.2</version>
      <executions>
        <execution>
          <id>default-copy-resources</id>
          <phase>process-resources</phase>
          <goals>
            <goal>copy-resources</goal>
          </goals>
          <configuration>
            <overwrite>true</overwrite>
            <outputDirectory>${project.build.directory}/${project.artifactId}-${project.version}/</outputDirectory>
            <resources>
              <resource>
                <directory>${project.basedir}/angular/dist</directory>
              </resource>
            </resources>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

When you compare this code with your pom.xml you will these two new plugins frontend-maven-plugin and maven-resources-plugin. So lets explain the frontend-maven-plugin first.

<configuration>
  <workingDirectory>angular</workingDirectory>
  <installDirectory>temp</installDirectory>
</configuration>

In the above code we are telling frontend-maven-plugin that our angular 2 code is in /angular directory and the directory in which we want it to install nodejs is /temp.

<execution>
  <id>install node and npm</id>
  <goals>
    <goal>install-node-and-npm</goal>
  </goals>
  <configuration>
    <nodeVersion>v6.3.1</nodeVersion>
    <npmVersion>3.9.5</npmVersion>
  </configuration>
</execution>

In the above code we are specifying node and npm version to install it on our system automatically.

<execution>
  <id>npm install</id>
  <goals>
    <goal>npm</goal>
  </goals>
  <configuration>
    <arguments>install</arguments>
  </configuration>
</execution>

It will execute command npm install inside the workingDirectory (ie. /angular/)

<execution>
  <id>npm build</id>
  <goals>
    <goal>npm</goal>
  </goals>
  <configuration>
    <arguments>run build</arguments>
  </configuration>
</execution>

It will execute command npm run build inside the workingDirectory (ie. /angular/)

<execution>
  <id>default-copy-resources</id>
  <phase>process-resources</phase>
  <goals>
    <goal>copy-resources</goal>
  </goals>
  <configuration>
    <overwrite>true</overwrite>
    <outputDirectory>${project.build.directory}/${project.artifactId}-${project.version}/</outputDirectory>
    <resources>
      <resource>
  <directory>${project.basedir}/angular/dist</directory>
      </resource>
    </resources>
  </configuration>
</execution>

It will copy the content of /angular/dist/ directory to output directory (${project.basedir}/angular/dist).

Finally run mvn clean install and your .war is ready.

NOTE :
1. Add index.html in your welcome files list in web.xml.
ie. Paste the below code in web.xml

<welcome-file-list>
  <welcome-file>index.html</welcome-file>
</welcome-file-list>

2. Add “build”: “ng build” script in your package.json

{
  "name": "angular",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": {
    "start": "ng serve",
    	.
    	.
    	.

    "build": "ng build"
  },
  "dependencies": {
   	.
   	.
   	.
  },
  "devDependencies": {
   	.
   	.
  }
}

You can check my projects (have not updated for a long time)
BaceDevotees
TransactionManager

Also don’t forget to read this
https://github.com/ashishdoneriya/LittleBlueBird/wiki
It will describe what’s wrong with urls in Angular.

Another Way

After a long time, I’ve realized maven-frontend plugin sometimes doesn’t work for me. Therefore, in this case, remember the manual way that I told you before
“We can run Angular app on Tomcat by putting the angular app content ( contents inside target directory when you build, the default is ‘dist’ for angular application) directly inside /src/main/webapp directory.”

It means, build your angular project (using ‘ng build’ or ‘ng build –prod’). On successful build, it will generate a directory called ‘dist’ inside your angular project directory. Copy all the contents inside that ‘dist’ directory and paste them directly into ‘src/main/webapp’ directory of your maven/war project. After that compile your maven/war project.

My Personal Opinion : I have found VueJs very easier compared to Angular. If you want to migrate you project from angularjs ( angular 1.x) then give vuejs a try.