public class PWAResourceHandler extends DefaultResourceHandler
This ResourceHandler
generates the manifest.json
and also an offline-aware sw.js
based on any WebAppManifest
found in the runtime classpath. Historical note: this class was introduced in 3.6
as WebAppManifestResourceHandler
without any service worker related logic and since 3.7 renamed to
PWAResourceHandler
after having service worker related logic added.
WebAppManifest
in your web application project.ApplicationScoped
, SessionScoped
or even
RequestScoped
(note that a ViewScoped
won't work).#{resource['omnifaces:manifest.json']}
in your template.
Here's a concrete example:
package com.example; import java.util.Arrays; import java.util.Collection; import javax.enterprise.context.ApplicationScoped; import org.omnifaces.resourcehandler.WebAppManifest; @ApplicationScoped public class ExampleWebAppManifest extends WebAppManifest { @Override public String getName() { return "Example Application Name"; } @Override public String getShortName() { return "EAN"; } @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 getThemeColor() { return "#cc9900"; } @Override public String getBackgroundColor() { return "#ffffff"; } @Override public Display getDisplay() { return Display.STANDALONE; } @Override public Collection<Category> getCategories() { return Arrays.asList(Category.BUSINESS, Category.FINANCE); } @Override public Collection<RelatedApplication> getRelatedApplications() { return Arrays.asList( RelatedApplication.of(Platform.PLAY, "https://play.google.com/store/apps/details?id=com.example.app1", "com.example.app1"), RelatedApplication.of(Platform.ITUNES, "https://itunes.apple.com/app/example-app1/id123456789") ); } }
Reference it in your template exactly as follows, with the exact library name of omnifaces
and
exact resource name of manifest.json
. You cannot change these values.
<link rel="manifest" href="#{resource['omnifaces:manifest.json']}" crossorigin="use-credentials" />
The crossorigin
attribute is optional, you can drop it, but it's mandatory if you've put the
SessionScoped
annotation on your WebAppManifest
bean, else the browser won't retain the session
cookies while downloading the manifest.json
and then this resource handler won't be able to maintain the
server side cache, see also next section.
Note: you do not need to explicitly register this resource handler. It's already automatically registered.
Basically, the CDI scope annotation being used is determinative for the autogenerated v=
query
parameter indicating the last modified timestamp. If you make your WebAppManifest
bean RequestScoped
,
then it'll change on every request and the browser will be forced to re-download it. If you can however guarantee
that the properties of your WebAppManifest
are static, and thus you can safely make it
ApplicationScoped
, then the v=
query parameter will basically represent the timestamp of
the first time the bean is instantiated.
The generated sw.js
will by default auto-register the WebAppManifest.getStartUrl()
and all
welcome files from web.xml
as cacheable resources which are also available offline. You can override
the welcome files with WebAppManifest.getCacheableViewIds()
. E.g.
@Override public Collection<String> getCacheableViewIds() { return Arrays.asList("/index.xhtml", "/contact.xhtml", "/support.xhtml"); }
In case you want to show a custom page as "You are offline!" error page, then you can specify it by overriding
the WebAppManifest.getOfflineViewId()
.
@Override public String getOfflineViewId() { return "/offline.xhtml"; }
Whereby the offline.xhtml
should contain something like this:
<h1>Whoops! You appear to be offline!</h1> <p>Please check your connection and then try refreshing this page.</p>
For each of those "cacheable view IDs" and "offline view IDs", the JSF view briefly will be built in in order to
extract all <x:outputStylesheet>
,<x:outputScript>
and
<x:graphicImage>
resources and add them to cacheable resources of the service worker as well.
If the WebAppManifest.getCacheableViewIds()
returns an empty collection, then no sw.js
will
be generated, and WebAppManifest.getOfflineViewId()
will also be ignored.
In the client side, you can listen on omnifaces.offline
and omnifaces.online
events in the
window
whether the client is currently online or offline.
window.addEventListener("omnifaces.online", function(event) { var url = event.detail.url; // .. }); window.addEventListener("omnifaces.offline", function(event) { var url = event.detail.url; var error = event.detail.error; // ... });
Or when you're using jQuery:
$(window).on("omnifaces.online", function(event) { var url = event.detail.url; // .. }); $(window).on("omnifaces.offline", function(event) { var url = event.detail.url; var error = event.detail.error; // ... });
This gives you the opportunity to set a global flag and/or show some sort of notification.
The event.detail
will contain at least the url
which was being requested through the
service worker, and in case of the omnifaces.offline
event, there will also be an error
which represents the original network error object thrown by
fetch()
.
WebAppManifest
,
https://www.w3.org/TR/appmanifest,
https://developer.mozilla.org/en-US/docs/Web/Manifest,
https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_APIModifier and Type | Field and Description |
---|---|
static String |
MANIFEST_RESOURCE_NAME |
static String |
SCRIPT_INIT |
static String |
SERVICEWORKER_RESOURCE_NAME |
RES_NOT_FOUND
JSF_SCRIPT_LIBRARY_NAME, JSF_SCRIPT_RESOURCE_NAME, LOCALE_PREFIX, RESOURCE_CONTRACT_XML, RESOURCE_EXCLUDES_DEFAULT_VALUE, RESOURCE_EXCLUDES_PARAM_NAME, RESOURCE_IDENTIFIER, WEBAPP_CONTRACTS_DIRECTORY_PARAM_NAME, WEBAPP_RESOURCES_DIRECTORY_PARAM_NAME
Constructor and Description |
---|
PWAResourceHandler(ResourceHandler wrapped)
Creates a new instance of this web app manifest resource handler which wraps the given resource handler.
|
Modifier and Type | Method and Description |
---|---|
Resource |
decorateResource(Resource resource,
String resourceName,
String libraryName)
Decorate the given resource.
|
createResource, createResource, createResource, createResourceFromLibrary, decorateResource, getLibraryName
createResourceFromId, createViewResource, getRendererTypeForResourceName, getViewResources, getViewResources, getWrapped, handleResourceRequest, isResourceRendered, isResourceRequest, isResourceURL, libraryExists, markResourceRendered
public static final String MANIFEST_RESOURCE_NAME
public static final String SERVICEWORKER_RESOURCE_NAME
public static final String SCRIPT_INIT
public PWAResourceHandler(ResourceHandler wrapped)
WebAppManifest
.wrapped
- The resource handler to be wrapped.public Resource decorateResource(Resource resource, String resourceName, String libraryName)
DefaultResourceHandler
The default implementation delegates to DefaultResourceHandler.decorateResource(Resource)
.
decorateResource
in class DefaultResourceHandler
resource
- The resource to be decorated.resourceName
- The resource name.libraryName
- The library name.Copyright © 2012–2022 OmniFaces. All rights reserved.