Incorporating Progressive Web App Support into a JavaServer Faces Web Application

Exploring the possibility of integrating Progressive Web App support into a JavaServerFaces web application has become a focal point for our team. As our JSF app continues to evolve, we foresee the need to provide offline access to certain parts of the application in the future. Along with the appealing features offered by PWAs such as notifications, full screen web apps, and the ability to add to homescreen, our main focus lies on leveraging the offline capabilities enabled by PWA through service workers and caching.

Our clients often find themselves working in areas with limited or no internet connectivity. In such scenarios, it is crucial for them to be able to access the app, authenticate themselves, and navigate to the required section of the app on-site. Here, they would work with existing data or create new datasets. I believe that utilizing the PWA service worker could potentially allow for caching data and storing newly generated information for later synchronization with the server. Is this indeed feasible?

Despite the appeal of PWA support, I harbor doubts about the feasibility of implementing offline mode functionalities in a JSF application. Given that all application logic resides on the server side and clients do not directly engage in API calls like GET/POST/PUT (essential for caching and synchronization), I question the seamless integration of these technologies.

My attempts to find substantial information on the topic of JSF & PWA have yielded little results, prompting me to pose this inquiry. Any guidance or insights on whether JSF apps can effectively harness PWA technologies, particularly in terms of offline access, caching, and synchronization, would be greatly appreciated.

Thank you.

Answer №1

Starting from version 3.7, OmniFaces now includes PWA support through the built-in PWAResourceHandler. You can find detailed usage documentation and demonstrations in the showcase.

  1. To begin, create a class that extends WebAppManifest

    public class YourWebAppManifest extends WebAppManifest {
    }
    
  2. Apply a CDI scope annotation that matches its state, for example, @ApplicationScoped.

    @ApplicationScoped
    public class YourWebAppManifest extends WebAppManifest {
    }
    
  3. Make sure to override/implement properties according to their Javadoc and standards in the W3 spec.

    @ApplicationScoped
    public class YourWebAppManifest extends WebAppManifest {
    
        @Override
        public String getName() {
            return "Your Application";
        }
    
        @Override
        public Collection<ImageResource> getIcons() {
            return Arrays.asList(
                ImageResource.of("logo.svg"),
                ImageResource.of("logo-120x120.png", Size.SIZE_120),
                ImageResource.of("logo-180x180.png", Size.SIZE_180),
                ImageResource.of("logo-192x192.png", Size.SIZE_192),
                ImageResource.of("logo-512x512.png", Size.SIZE_512)
            );
        }
    
        @Override
        public String getOfflineViewId() {
            return "/offline.xhtml";
        }
    }
    
  4. Add it to your HTML head like this, using the library name omnifaces and resource name manifest.json:

    <link rel="manifest" href="#{resource['omnifaces:manifest.json']}" />
    

That's essentially the process. The PWAResourceHandler will automatically create the desired manifest.json and sw.js files, serving the /offline.xhtml template as offline view.

By default, all <welcome-file> entries in web.xml are registered as "cacheable resources", making them available offline. This can be customized in your own WebAppManifest as shown below:

@Override
public Collection<String> getCacheableViewIds() {
    return Arrays.asList("/index.xhtml", "/contact.xhtml", "/support.xhtml");
}

If necessary, ensure JSF forms on these pages are made stateless by including

<f:view transient="true">
to prevent a ViewExpiredException.

The main benefit of using the PWAResourceHandler is that manual creation of the sw.js file is no longer required, allowing easy control over its contents/configuration through a simple CDI bean.

Answer №2

After exploring various technologies, it appears that JSF may not be the most suitable choice for offline functionality. One significant challenge is that certain platforms, such as iOS, can sometimes clear cookies/local storage due to inactivity, causing disruptions when users attempt to resume their sessions. To address this issue, we are currently transitioning our codebase to use JAX-RS endpoints instead.

Answer №3

Exploring a similar concept has caught my attention recently, despite being relatively new to PWAs. JSF functions as a taglib that first goes through a servlet, allowing for easy customization of tags for rendering specific content.

However, it seems that the existing theme and component libraries may not align well with PWA principles. This leads me to believe that starting with pure JSF and creating a prototype might be the best approach.

To fully grasp the inner workings in JSF, I've revisited JSTL. By familiarizing myself with patterns and creating custom taglibs, I hope to gain a deeper understanding of how everything comes together in JSF. (It's worth mentioning that last time I delved into web development, JSTL was still a novel concept, but the core concepts remain timeless)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

scope.$digest completes before triggering scope.$watch in Karma unit tests

I am interested in testing this specific directive: .directive('uniqueDirective', function () { return { restrict: 'A', scope: { uniqueDirective: '@', tooltip: '@', placement: '@&apo ...

Why won't the button's color change when I try clicking on it?

I am currently learning vue and facing some challenges. The code I have is supposed to change the button color when clicked, but it's not working as expected. Any advice on how to fix this issue would be greatly appreciated. Thank you! let app = ...

When using angular $resource.save for savings, the view is forced to redraw and reflow because of the collection watcher

One of the challenges I'm facing involves loading and storing a model using $resource in AngularJS. This particular model is an aggregate with nested collections, which are displayed in an HTML view using ng-repeat. The structure of the model looks l ...

Retrieve a div element using two specific data attributes, while excluding certain other data attributes

Here are some examples of divs: <div id="1" data-effect-in="swing" data-effect-out="bounce"></div> <div id="2" data-effect-in="swing"></div> <div id="3" data-effect-out="swing"></div> <div id="4" data-effect-out data ...

What is the process for accessing a table in indexedDB using Dexie.js immediately after it has been opened?

I am faced with the challenge of needing to verify if a specific table already exists in IndexedDB right after it has been opened. However, I am unsure how to access the DexieDB object inside the 'then' statement. this.db = new Dexie("DBNAME"); ...

How to accentuate search results using Angular filters and conceal non-matching text?

Recently, I came across an interesting example of using Angular filter to highlight search results. It works well in highlighting the word 'suit', but I noticed that all non-matching text remains visible. If you'd like to see the example I ...

Modifying JavaScript prototypes on the fly can lead to troublesome issues

My curiosity has been piqued by the concept of dynamically changing a constructor's prototype in JavaScript, leading me to the findings above. It appears that an already constructed instance does not inherit the properties of the newly changed protot ...

Incorporate npm libraries into vanilla JavaScript for HTML pages

Attempting to incorporate an npm package into plain JS/HTML served using Python and Flask. <script type="module"> import { configureChains, createClient } from "./node_modules/@wagmi/core"; import { bsc } from "./nod ...

Can two writable stores in Svelte be set up to subscribe to each other simultaneously?

There is a unique scenario where two objects share data, yet have different structures. For instance, the 'Team' object has the team ID as its key. The 'Team' object includes 'name' and 'users' objects as its values ...

Guidelines for sending JSON Data via Request Body using jQuery

I'm not well-versed in jQuery, so consider me a beginner. Here's my code that is not correctly handling JSON data submission via Request Body. <!doctype html> <html lang="en> <head> <title>jQuery JSON Data Submission ...

Modifying button appearance upon clicking using JavaScript

JavaScript: const buttons = document.getElementsByClassName("togglebtn"); for (let i = 0; i < buttons.length; i++) { buttons[i].onclick = function () { this.classList.toggle("active"); } } html: <md-butt ...

Do the "Save to Drive" buttons require manual cleaning?

Utilizing the Google Drive API for JavaScript within a VueJS application can be done as follows: In your index.html <script type="text/javascript"> window.___gcfg = { parsetags: 'explicit', lang: 'en-US' }; </scri ...

The functionality of the Toastr "options" seems to be malfunctioning

I am having some trouble with my Toastr notification messages. While the message does display, I seem to be unable to make use of any options that I set. Despite specifying some options, they do not seem to work as intended. $('#editButton').c ...

Tips for creating a navigation bar item that displays a component depending on its active state

Trying to enhance the modularity of my code but facing difficulties. I have a tab bar and I want to render a specific component based on the clicked nav/tab item. Struggling with passing props properly, as the current code only recognizes the children valu ...

problem with creating a django template script

Hello, I am currently working on a code that is responsible for executing various functions within the template. I have utilized scripts to verify these functions using if-else statements and for loops. However, I am encountering some errors during this pr ...

Migrate the JavaScript variable created with 'passport' to an Express router

In my quest for authentication, I created the essential passportAuth.js file: module.exports = function(passport, FacebookStrategy, config, mongoose, fbgraph){ passport.serializeUser(function(user,done){ //to make user reference available across pages ...

Setting a variable in Angular after a successful AJAX call

Working on a new small application and experimenting with Angular. Encountering an issue where I am making an AJAX GET request upon clicking a button. After receiving the response, I want to set a variable to hold the result, but for some reason the variab ...

Issue encountered in Vite Preview: Uncaught (in promise) SyntaxError: JSON.parse found an unexpected character at the beginning of the JSON data, line 1 column 1

Encountering the error message Uncaught (in promise) SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data when running vite preview after running vite build. Here is my vite.config.js: import { defineConfig } from "vite&q ...

Retrieving & Refreshing Data with ajax and Jquery

I have been working on developing a forum system using jQuery, PHP, Bootstrap, and other technologies. The forum allows users to post, delete, and edit their posts. I have implemented an edit button for the author of the post, which triggers a modal wind ...

Are the events objects failing to render or is the scope not being broadcasted properly

I'm having trouble displaying my events using the ionic-calendar plugin by calling the $scope.loadEvents method. Unfortunately, the events are not showing up on the calendar. Here is the link to the plugin I am using: https://github.com/twinssbc/Ioni ...