01 March 2019

© Copyright 2015-2019 by TECHNIA AB

All rights reserved.

PROPRIETARY RIGHTS NOTICE: This documentation is proprietary property of TECHNIA AB. In accordance with the terms and conditions of the Software License Agreement between the Customer and TECHNIA AB, the Customer is allowed to print as many copies as necessary of documentation copyrighted by TECHNIA relating to the software being used. This documentation shall be treated as confidential information and should be used only by employees or contractors with the Customer in accordance with the Agreement.

This product includes software developed by the Apache Software Foundation. (http://www.apache.org/).

2. Adding Custom Service Worker Tutorial

In this tutorial, we’ll add support for custom service worker for caching static resources from a directory, <webapp>/mydomain/scripts & <webapp>/mydomain/styles.

Things that will be covered:

  • Creating Service Worker Settings Provider

  • Registering Settings Provider

  • Creating Service Worker script

  • Caching static resources using service worker script

2.1. Prerequisites

  • Service Worker/ Client side caching is enabled

  • A running TVC/ Helium environment (release 2019.1.0 or higher)

  • Basic javascript understanding

  • Service Worker knowledge

  • Using Workbox JS

2.2. Creating Service Worker Settings Provider

The first thing to do is, create a Settings Provider for your module. As the name suggests, a settings provider is used to provide settings to Service Worker script, e.g. service worker script path, application version etc.

Create a Java class that extends com.technia.tvc.core.taglib.serviceworker.SettingsProvider.

public class MyServiceWorkerSettingsProvider implements SettingsProvider {

    /**
     * Prefix that'll be prefixed to each key in the settings when forming query string to have unique keys,
     * for e.g. if prefix is "custom"
     * and key is "cacheEnabled", the query param would be "custom-cacheEnabled"
     */
    @Override
    public String getPrefix() {
        return "mymodule";
    }

    /**
     * Used for sending query params to the Service Worker. Implementations should probably consider having only those
     * settings are needed in the Service Worker script. Any change in the parameter list/ values will result in loading
     * of new Service Worker.
     */
    @Override
    public Map<String, String> provideSettings(HttpServletRequest request) {
        Map<String, String> settings = new TreeMap<>();
        settings.put("version", "<my-app-release-version>");
        return settings;
    }

    /**
     * Path of Service Worker JavaScript file that will be used to fetch service worker,
     * e.g. if service worker is located in
     * <webapp>/tvc/sw-tvc.js, the method should return 'tvc/sw-tvc.js'
     */
    @Override
    public String getServiceWorkerJsPath() {
        return "mymodule/sw-mymodule.js";
    }
}

2.3. Registering Settings Provider

Next, we need to tell TVC about this Settings Provider so that it is registered with TVC when server starts up. This should be done in your module’s Plugin file,

public class Plugin extends TVCPluginAdapter {

    ...
    @Override
    public void init() {
        SettingsFactory.getInstance().registerProvider(new MyServiceWorkerSettingsProvider());
    }
    ...
}
For information on how to register a plugin, see Plugin Registration

2.4. Creating Service Worker script

Next, we will create a service worker script. Create a script, sw-mymodule.js in <webapp>/mymodule/sw-mymodule.js.

The location should be the same as mentioned in the Settings Provider.
var MyModuleSW = (function() {
    /**
     * Service Worker initialization
     */
    var init = function(searchParams) {};

    /**
     * Return list of cache names. These will be cleared out when registering a new Service Worker version
     */
    var getValidCacheNames = function() {
        return [];
    };

    /**
     * List of files along with versions that should be pre-cached, i.e. when service worker installs.
     * Typically populated during build process, e.g. using workbox-cli
     */
    var precache = function() {baseUrl, workbox};

    /**
     * Register routes fpr caching content using different workbox strategies
     */
    var registerRoutes = function(workbox) {};

    return {
        init: init,
        precache: precache,
        registerRoutes: registerRoutes,
        getValidCacheNames: getValidCacheNames
    };
})();

// Register My Module's Service Worker fragment with top level service worker
if (typeof registerSubServiceWorker !== 'undefined') {
    registerSubServiceWorker(MyModuleSW);
}
The service worker module should return the methods as in above snippet. These will be invoked at different stages of service worker setup.

2.5. Caching static resources using service worker script

In order to cache static resources, we need to define caching strategy using Workbox JS. At a minimum, it needs Cache Name, URL pattern to match requests and type of strategy to use. Below shows an example of using Cache First strategy to look for static resources within path <webapp>/mydomain.

/**
 * @param  {String} de-resourceUrl Absolute URL to the application, e.g. https://acme:444/3dspace/
 * @param  {String} mymodule-version current version for my Application (set in Settings Provider)
 */
var MyModuleSW = (function() {
    var STATIC_RESOURCES_ENDINGS =
        '\\.(js|css|png|bmp|gif|eot|svg|ttf|woff|woff2|json|handlebars)(\\?.*)?';
    var urlParams;
    var CACHE_NAMES = {};

    /**
     * Service Worker initialization
     */
    var init = function(searchParams) {
        urlParams = searchParams;
        CACHE_NAMES = {
            MY_MODULE_STATIC:
                'mymodule-static-v' + urlParams.get('mymodule-version')
        };
    };

    /**
     * Return list of cache names. These will be cleared out when registering a new Service Worker version
     */
    var getValidCacheNames = function() {
        return CACHE_NAMES.MY_MODULE_STATIC;
    };

    /**
     * List of files along with versions that should be pre-cached, i.e. when service worker installs.
     *
     */
    var precache = function(baseUrl, workbox) {
        // Typically populated by build process
        const preCacheList = [
            {
                url: 'mydomain/scripts/product.js', // should probably be absolute path
                revision: 'cdb0ad00d71eff5be6ca7305cff83ce9'
            }
        ];
        workbox.precaching.precacheAndRoute(preCacheList);
    };

    /**
     * Register routes for caching content using different workbox strategies
     */
    var registerRoutes = function(workbox) {
        var baseUrl = urlParams.get('de-resourceUrl');
        var myModulePattern = baseUrl + 'mydomain/' + '.*' + STATIC_RESOURCES_ENDINGS + '$';

        // Define caching strategy for our routes. For more information, see Workbox JS documentation
        workbox.routing.registerRoute(
            new RegExp(myModulePattern),
            workbox.strategies.cacheFirst({
                cacheName: CACHE_NAMES.TVC_STATIC
            })
        );
    };

    return {
        init: init,
        precache: precache,
        registerRoutes: registerRoutes,
        getValidCacheNames: getValidCacheNames
    };
})();

// Register My Module's Service Worker fragment with top level service worker
if (typeof registerSubServiceWorker !== 'undefined') {
    registerSubServiceWorker(MyModuleSW);
}

2.6. Verification

Verification can be done using a browser that supports Service Workers.

  • Google Chrome: Open DevTools, navigate to "Applications" tab and inspect "Service Workers" and "Cache Storage" sections.

  • Firefox: Open about:debugging#workers in a browser tab. Open your application in another tab, open DevTools, "Storage", "Cache Storage" section.

Also, check Console tab for errors related to service workers