Close
Back

Progressive web apps in Java

What is the big deal about Progressive Web Applications (PWAs) and why are they trending nowadays? Here is one explanation by Google:

“It [PWA] loads quickly, even on flaky networks, sends relevant push notifications, has an icon on the home screen, and loads as a top-level, full screen experience.”

The number of users accessing web applications from a mobile device is bigger than those using desktop. And it’s increasing. If you want your web app to reach a bigger audience, while increasing the usability and experience for your users, then PWA is basically your way to achieve that.

Java app, running on browser without PWA
Same app, running standalone with PWA

PWA sounds like a new framework, or a tool to create some sort of web apps. Many developers link it to JavaScript, and that is only achievable with one of those new framework.js out there.

I’ve been wondering about the truth of the previous statement: can we create a PWA in, let’s say, pure Java? I did some research and in this article, I’m going to present my findings.

  • For Vaadin developers:

    • This is a perfect summary for you, saving a lot of time and hassle. Follow the quick steps, and you get a PWA immediately.
  • For Java web developers:

    • This is still relevant for you. Many concepts are the same for any Java web app.

In the following Devoxx presentation, Vaadin Expert Teemu Suo-Anttila and myself showed how it’s possible to create a PWA with a Java UI framework such as Vaadin:

Extracting the content of that video here, but I’ll not use Spring this time, I’ll keep it as much generic as possible. Here are the steps summary:

  1. Dismantling a PWA. We will take a quick look at an existing PWA and compare it with a normal Java app, to understand the major differences and what needs to be implemented.

  2. Building a PWA.

    1. Add a service worker, It will help us implement modern browser functionalities, and other features that do not require a running web app, such as background sync, caching, and push notifications.

    2. Specify files to be cached, this makes the app load faster in future visits, and makes a part of it available offline.

    3. Update the service worker, what are the best practices to update the service worker when we add more functionalities to it?

  3. Making it app-like. And this part is magically done with the help of the browser, if we only include a standard manifest file.

1) Dismantling a PWA

To practically test that a web application follows PWA specs, there is a tool called lighthouse that generates a report showing how compatible you app is with the PWA specs.

Let’s start by looking at the state of a Java-based application today, by creating a very simple Vaadin application and then running the lighthouse report for it. Unfortunately, at this point I get 22% compatibility and the report shows a lot of missing features in this simple app.

Related commit: Step 0 | Simple Vaadin App

Reverse engineering an already existing PWA is my favorite way to understand how it works. Luckily there are many templates in the open source web that give some guidance on how to start. For example, if you follow the guide in this GitHub project generator-pwa, you will end up with a minimalistic web project that follows most of PWA specs. Lighthouse gives it a score of 88% and we can easily see that there are a couple of small problems, and since we are testing locally, it’s not secure over HTTPS and thus not a perfect score. But still good enough to start with, and let’s make it our benchmark for a PWA with Java.

Related commit: Step 1 | Simple JS PWA

2) Building a PWA

Now we have a working reference to copy from. Yes, I mentioned it right, I will copy the code. The idea here is, I’ll decrease the learning curve. Instead of reading many references telling what to implement, a service worker, full screen, manifest etc, I will just copy working code and build based on it.

At the bottom of the PWA template’s index.html, there is a reference to a Javascript file

And by opening app.js we find that it’s responsible for registering and communicating with the service worker. Here is the code responsible for registering the service worker sw.js

Let’s start by copying app.js into our Vaadin app. The best location to place it would be under webapp/VAADIN, so I create a new folder called js and place it there.

To link to app.js inside our Vaadin app, we can use the @JavaScript annotation on top of the main UI class:

Related commit: Step 2 | Add JS responsible for SW

2.1) Add a service worker

To give a correct scope for the service worker, it must be placed in the context-root of our Java application. That’s why I will copy sw.js to the resources folder, and update the reference in app.js

We don’t want the ‘/’ scope but the current context-path given by the application server is ‘java-pwa’ in my case here, or ‘./’ in general.

To be able to inject the service worker in the root of the application, we need to make some modifications in the default servlet to handle this specific request. Here is a code that handles ./sw.js request and serves the content of our sw.js file.

Related commit: Step 3 | Add service worker

2.2) Specify files to be cached

Inside sw.js we find a reference to the files to be cached:

I removed irrelevant files from the filesToCache list and update paths:

I kept the /images/touch/chrome-touch-icon-192x192.png to be cached, because it’s needed by the app shortcut icon. You can either copy the one provided in the template, or add your own.

The lighthouse score at this point is 34%, it seems there is some progress. At least we can see a detected service worker, and those cached files make the application actually work offline!

Related commit: Step 4 | Update cached files

2.3) Update the service worker

In our PWA template, the functionalities of the service worker were kept to minimum, to make it just work as a proof of concept. But how to update the service worker running on the client, once we add extra functionalities to it?

Just like any installed app, there is a life cycle in which, at some point you must kill the running app, and replace it with a new one.

The most interesting fact with the service worker is that, once its code is modified on the server, the browser will download the newer version and attempt to ‘install’ it. By default the new version will be on a ‘waiting’ state, till the currently opened page is closed. Google explains the process nicely, but there is more you can do!
So you open the web page, it downloads a new service worker, installs it, but still uses the older version? You can overcome this by ordering it to skip waiting. This would be done in our code as the following:

We will then have to claim the current active client as following:

Related commit: Step 9 | Skip waiting when updating the service worker

3) Make it app-like

The previous report shows that some tags are missing from the header of the app, such as meta theme-color, meta viewport, and more importantly the manifest.json file.

The manifest file is needed to install the app on the homescreen and it contains some information about the app, such as the name, description, icon for splash screen etc. We need to link to it in the <head> tag, so I will use the HeaderTags Add-on which will help me modify the bootstrap easily.

After installing the maven dependency, I can do something like this:

And include the manifest.json in webapp/VAADIN directory, after making some modifications to be relevant to our app. At least the start_url should be updated, as well as the icons list.

Related commit: Step 5 | Include manifest file

We can also include some other header tags, as pointed out in the report. At this point we have a score of 86%, which is a huge improvement from the original 22%.

Related commit: Step 6 | Add relevant meta tags

The score is not 88% yet, we can see a slight improvement once we turn on production mode, compress resources, and cache widgetset.

Related commit: Step 7 | Compression and production mode

Summary

Throughout this process, we have seen clearly that integrating PWA functionalities is not limited to JavaScript apps. The trickiest part was including the service worker in the context-root. For offline functionalities, currently it will show an empty page. It can be improved by either showing a fallback “no connection” screen, or using alternative components that work offline and do not rely on the server side.

There are some other small limitations like wrong tabindex, and no possibility to inject app.js at the bottom of the page instead of head. But the good news is, Vaadin Framework Team promised to make this way easier and much more straightforward in upcoming Vaadin releases (Vaadin 8.x maybe?). PWA is still a new concept, but our team is making sure to incorporate all new functionalities and make them part of the framework and ready to use once standardized.

Overall, next time you hear someone telling you that JavaScript is required for PWA, please show them this article! The service worker is an exception at the moment, so unless you will copy the one provided here as it is, currently adding extra functionalities to the service worker requires writing (or copying) some JS code into the sw.js file. I’m working on improving this to make it even easier, I’ve heard many suggestions from the R&D team and I’m exploring more ways to make the experience pure Java and with the least amount of steps possible.

Spoiler alert! Right now I’m working on getting web notifications to work, as well as a fallback screen when offline.

Do you have more ideas? Contributions?

What else would you like to see as part of the PWA specs in your app?

The GitHub project is waiting for your input, you can also place your ideas as comments below.

Here is the final app with some data, and working on mobile:

Related commit: Step 8 | Show data in a working app

Get the source code along with detailed steps!

You can also take a look at a similar concept done with Spring boot: https://github.com/tsuoanttila/sportstracker

Comments
Add Comment
It seems that title "Progressive web apps in Java " does not match the content. There is only Java server side and this app does not work if network is unavailable.
Posted on 3/12/17 12:56 PM.
Hi Yuriy,
The app does not work offline, this is explained in the first paragraph of the summary section. But it implements most of the PWA specs, such as manifest and service worker.

This is the first iteration of a work in progress, I'm gathering feedback on what would be more interesting for developers and still investigating on best ways to implement them. So expect more updates on the Github project emoticon
Posted on 3/13/17 9:20 AM.
Hi AMady,

Great to hear your working progress related to PWA, Also we would like to check the sneak peek of the 100% java version of PWA. Please share any details of it. Thanks
Posted on 3/14/17 7:47 AM in reply to AMahdy Abdelaziz.
Hi Chatura,

I will make a new post once there are new major updates, otherwise, the Github project is a good place to follow the latest updates.
Posted on 3/14/17 1:25 PM in reply to chatura kalansooriya.