playn.appcachelinker.AppCacheLinker Maven / Gradle / Ivy
Show all versions of playn-html Show documentation
/**
* Copyright 2011 The PlayN Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package playn.appcachelinker;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import com.google.gwt.core.ext.LinkerContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.Artifact;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.linker.CrossSiteIframeLinker;
/**
* A linker for public path resources in the application cache. To use:
*
*
* - Add {@code manifest="YOURMODULENAME/appcache.nocache.manifest"} to the
* {@code } tag in your main html file. E.g.,
* {@code }
*
* - Add a mime-mapping to your web.xml file:
*
{@code
* manifest
* text/cache-manifest
*
* }
*
*
*
* On every compile, this linker will regenerate the appcache.nocache.manifest file with files
* from the public path of your module.
*
*
This linker has some default behavior with respect to which files will be included in the
* manifest, which can be modified by overriding {@link #accept}.
*
*
To add additional static files to the manifest, override {@link #staticCachedFiles}.
*
*
Note: This linker currently extends {@link CrossSiteIframeLinker}. For better JavaScript
* debugging, this should be changed to extend {@link
* com.google.gwt.core.linker.DirectInstallLinker} instead once
* http://code.google.com/p/chromium/issues/detail?id=87005 has been fixed.
*/
public class AppCacheLinker extends CrossSiteIframeLinker {
private static final HashSet DEFAULT_EXTENSION_WHITELIST = new HashSet(
Arrays.asList(new String[] {
// .wav files explicitly excluded, since HTML games use .mp3
"js", "html", "jpg", "jpeg", "png", "gif", "mp3", "ogg", "mov", "avi", "wmv",
"webm", "css", "json", "flv", "swf",}));
private static final String MANIFEST = "appcache.nocache.manifest";
@Override
public String getDescription() {
return "AppCacheLinker";
}
/*
* Standard linker that also outputs a manifest file based on the public
* resources
*/
@Override
public ArtifactSet link(TreeLogger logger, LinkerContext context, ArtifactSet artifacts)
throws UnableToCompleteException {
ArtifactSet toReturn = super.link(logger, context, artifacts);
// Create the general cache-manifest resource for the landing page:
toReturn.add(emitLandingPageCacheManifest(context, logger, toReturn, staticCachedFiles()));
return toReturn;
}
/**
* Determines whether our not the given should be included in the app cache
* manifest. Subclasses may override this method in order to filter out
* specific file patterns.
*
* @param path the path of the resource being considered
* @return true if the file should be included in the manifest
*/
protected boolean accept(String path) {
// GWT Development Mode files
if (path.equals("hosted.html") || path.endsWith(".devmode.js")) {
return false;
}
// Default or welcome file
if (path.equals("/")) {
return true;
}
// Whitelisted file extension
int pos = path.lastIndexOf('.');
if (pos != -1) {
String extension = path.substring(pos + 1);
if (DEFAULT_EXTENSION_WHITELIST.contains(extension)) {
return true;
}
}
// Not included by default
return false;
}
/**
* Creates the cache-manifest resource specific for the landing page.
*/
private Artifact> emitLandingPageCacheManifest(LinkerContext context, TreeLogger logger,
ArtifactSet artifacts, String[] staticFiles) throws UnableToCompleteException {
// Create a string of cacheable resources
StringBuilder publicSourcesSb = new StringBuilder();
StringBuilder publicStaticSourcesSb = new StringBuilder();
// Iterate over all emitted artifacts, and collect all cacheable artifacts
for (@SuppressWarnings("rawtypes")
Artifact artifact : artifacts) {
if (artifact instanceof EmittedArtifact) {
EmittedArtifact ea = (EmittedArtifact) artifact;
String path = "/" + context.getModuleFunctionName() + "/" + ea.getPartialPath();
if (accept(path)) {
publicSourcesSb.append(path + "\n");
}
}
}
// Iterate over all static files
if (staticFiles != null) {
for (String staticFile : staticFiles) {
if (accept(staticFile)) {
publicStaticSourcesSb.append(staticFile + "\n");
}
}
}
// build cache list
StringBuilder sb = new StringBuilder();
sb.append("CACHE MANIFEST\n");
sb.append("# Unique id #" + (new Date()).getTime() + "." + Math.random() + "\n");
// we have to generate this unique id because the resources can change but
// the hashed cache.html files can remain the same.
sb.append("# Note: must change this every time for cache to invalidate\n");
sb.append("\n");
sb.append("CACHE:\n");
sb.append(publicSourcesSb.toString());
sb.append("# Static cached files\n");
sb.append(publicStaticSourcesSb.toString());
sb.append("\n\n");
sb.append("# All other resources require the user to be online.\n");
sb.append("NETWORK:\n");
sb.append("*\n");
logger.log(TreeLogger.DEBUG, "Make sure you have the following"
+ " attribute added to your landing page's tag: ");
// Create the manifest as a new artifact and return it:
return emitString(logger, sb.toString(), MANIFEST);
}
/**
* Override this method to force the linker to include additional files in the
* manifest.
*
* @return array of additional files to include in the manifest.
*/
protected String[] staticCachedFiles() {
return null;
}
}