Vue application as WAR file

It has been quite long time that we have been using web servers and application servers to serve our web applications developed using Java. While the application development technologies are evolving, lots of things are also changing accordingly. Today, indutry is heding java develoment container based or with support of out of the box Tomcat servers aka spring boot.  Technology changes in frontend is far beyond backend side. Now everyone in the industry is talking about Node-js based applications. Node js libraries seem promising. With this opinion, we have initiated our next frontend project using VueJs instead of our old JSF components. But not everything changes with the same velocity. Our application servers are still remaining. We have Oracle Weblogic instances on production stage. While we develop VueJs applications daily basis, new questions and challenges emerge for us. One obvious question is how to handle vuejs application on java servers. Here you will find our problems and solutions.

Developing VueJs Application

First thing first, why did we choose Vue js instead of React or angular which are dominating the industry nowadays?

The answer is so easy. We found that VueJs is much easier to get along with. You can create an html file and put required js files using script tags and that’s it. Just go and code. If you want to use more advanced features, you can jump to webpack and NodeJs backed structure.

VueJs uses webpack in the backend. It give us opportunity to seperate files for easier coding and efficient management over the project. Then when we compiled files, it combines page codes written separately into one place. It is sufficient to use commands such as build, service to run the project. This saves us from doing many configurations.

Creating Project

For taking advantages of node js you can simply install vue cli using following code and start your project.

npm install -g @vue/cli
vue create hello-world

After we create the project, we can run “npm install” command and let npm download necessary packages. The project will look like the following.

  • The “node_modules” folder contains the downloaded packages. We will not make any changes here, it will update automatically.
  • Babel will do the job of converting the vue code to javascript code for browsers.
  • If you are familiar with Java technologies like Maven and Ant, “package.json” is our main file like pom.xml.  For the ones who are not familiar, this file contains all the instructions for running, testing and compiling operations.

Running the project

If we would like to run a development server so that we can see the current status of our screen, you need to run the “npm run serve” command.  The application will start running at the default address as follows.

Without touching any file we can run “npm run build” command to build the project. After running the command, the “dist” folder will also be created.

We have created and ran an empty project so far. I assume that we have completed developing our application and reached the deployment phase. Our problems emerged from this point on.

Problem 1: Deploying the application

As a java developer I get a question as to how we will deploy the application to a java web/application server using this “dist” folder? If you use Apache Tomcat or Oracle Web Logic like we use, it will ask for a war file to deploy.

Deployment Trials

First I wondered if it is ok to deploy a zip file to server which I think not. But for just in case, we tried to deploy a zip file. The result is as expected. It did not work. To do this I simply zipped the dist folder and paste it to Tomcat app folder. Nothing happened.

Why did I think it would have worked? Because war file is a sort of a zip file.

Then we changed only the zip file’s extension to war and tried to deploy it again. Guess what? It worked. Only problem is that I dont want to create a zip file manually everytime for deployment. In any case, we have a solution at least. First, find a solution then make it better.

Create war with node

We would like to make zip file with an automated way. For this purpose, we have created a script named “war.js” at the same level as the last package.json. This script needs to be triggered after compiling the project. It creates a zip file using content of dist folder and named it as a war file. There is a npm package which handles zipping a folder. The package is very straightforward. You can simply install it using

npm install zip-a-file

Then the following code helps you create a war file.

const zipper = require('zip-a-folder')

zipper.zip('dist/', 'app.war')
.then(console.log('war file created successfully'))
.catch(error => console.log(error))

A WAR file is a single file that contains all the assets of a web application such as JAR files, JavaServer Pages, Java Servlets, Java classes, XML files, static web pages.

We used “node war.js” command to run the file. Since we are using npm, we would like to trigger the operation using npm commands. We defined an npm command in package json.

"scripts": {
    "serve": "vue-cli-service serve",
    "prod": "vue-cli-service build --no-clean",
    "test": "vue-cli-service build --mode test --no-clean",  
    "lint": "vue-cli-service lint",
    "war": "node war.js"
  },

We built the project. Then we ran the war command. Thus, the dist folder was created and all files were added into it.

Now we can append the package script to prod or any required phase script as follows.

"scripts": {
    "serve": "vue-cli-service serve",
    "prod": "vue-cli-service build --no-clean && node war.js",
    "test": "vue-cli-service build --mode test --no-clean && node war.js",  
    "lint": "vue-cli-service lint",
    "war": "node war.js"
  },

For problem one we have more than one solution.

Now we can jump into a new problem.

Problem 2: Relative addressing

Once we deployed the war file to Weblogic server, we had problems with js and image files. Because npm compiles the project as if the project is located on the root folder of the server like following

http://someserver.com/index.html

However, java developers will see that easily our server has several projects and all were deployed like

http://someserver.com/xapp/index.html

When our page is loaded to browser, all ajax requests and images are being requested starting with root folder’s uri.

Using relative addressing

First option we had was that we opened the index file and checked included js files. It seems all files are requested like “/xxx.js”. We added “.” To the beginning of js import lines like “./xxx.js” and tried using img object with relative addressing instead of root located address. All these operations are handled manually.

The result was successful. Page is loaded as we wish. But we were not convinced about this solution because it was a manual operation we need to operate every time. But it worked for testing. While our team was testing the project we started searching for a better option.

Vue config

Just like we thought we had a better solution which was adding a single line to our vue.config.js file. Basically we needed to configure the project’s public folder and it solved our problems. You can find the configuration below.

module.exports = {
    publicPath: '.'
}

Just for information, you need to create a file in the root folder (next to package.json) named vue.config.js and put the code snipped into it. It is that simple.

You can see before and after generated index.html code.

First default code.

<!DOCTYPE html>
<html lang="">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link rel="icon" href="/favicon.ico">
    <title>hello-world</title>
    <link href="/css/app.fb0c6e1c.css" rel="preload" as="style">
    <link href="/js/app.0e6fc338.js" rel="preload" as="script">
    <link href="/js/chunk-vendors.28d0d835.js" rel="preload" as="script">
    <link href="/css/app.fb0c6e1c.css" rel="stylesheet">
</head>

<body><noscript><strong>We're sorry but hello-world doesn't work properly without JavaScript enabled. Please enable it
            to continue.</strong></noscript>
    <div id="app"></div>
    <script src="/js/chunk-vendors.28d0d835.js"></script>
    <script src="/js/app.0e6fc338.js"></script>
</body>

</html>

Now vue.config.js enabled code

<!DOCTYPE html>
<html lang="">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link rel="icon" href="favicon.ico">
    <title>hello-world</title>
    <link href="css/app.fb0c6e1c.css" rel="preload" as="style">
    <link href="js/app.3a69e41c.js" rel="preload" as="script">
    <link href="js/chunk-vendors.28d0d835.js" rel="preload" as="script">
    <link href="css/app.fb0c6e1c.css" rel="stylesheet">
</head>

<body><noscript><strong>We're sorry but hello-world doesn't work properly without JavaScript enabled. Please enable it
            to continue.</strong></noscript>
    <div id="app"></div>
    <script src="js/chunk-vendors.28d0d835.js"></script>
    <script src="js/app.3a69e41c.js"></script>
</body>

</html>

You can clearly see that “link href” and “script” source urls have been changed. Config enabled code uses relative addressing whereas default makes root folder as base.

For the future

We realized that we can rearrange all our folders to be compatible with maven which I am inspired by Laravel framework. Laravel has good support for vuejs with its own folder structure. We can create our war file using maven command and eventually we can trigger node command with a maven plugin.

At the end of the day, we are happy to be able to continue using vuejs together with java.

I would like to thank Hilal who spent a lot of effort together with me during this challenge. You can find her personal blog if you interest with hybris and java related technologies. This article has also been shared by her with the following link.