blob: a453c7c181fd6633f94bcfffd4b21171be2b1e6d [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* 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 com.google.gwt.sample.core.linker;
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.AbstractLinker;
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.ext.linker.LinkerOrder;
import com.google.gwt.core.ext.linker.LinkerOrder.Order;
import com.google.gwt.core.ext.linker.impl.SelectionInformation;
import java.util.Arrays;
import java.util.Date;
/**
* Linker for public path resources in the Application Cache.
* <p>
* <strong>Warning</strong>This linker sends all permutations to clients,
* making it inappropriate for most production use.</strong>
* <p>
* To use:
* <ol>
* <li>Add {@code manifest="YOURMODULENAME/appcache.nocache.manifest"} to the
* {@code <html>} tag in your base html file. E.g.,
* {@code <html manifest="mymodule/appcache.nocache.manifest">}</li>
* <li>Add a mime-mapping to your web.xml file:
* <p>
* <pre>{@code <mime-mapping>
* <extension>manifest</extension>
* <mime-type>text/cache-manifest</mime-type>
* </mime-mapping>
* }</pre>
* </li>
* </ol>
* <p>
* On every compile, this linker will regenerate the appcache.nocache.manifest
* file with files from the public path of your module.
* <p>
* To obtain a manifest that contains other files in addition to those
* generated by this linker, create a class that inherits from this one
* and overrides {@code otherCachedFiles()}, and use it as a linker instead:
* <p>
* <pre><blockquote>
* {@code @Shardable}
* public class MyAppCacheLinker extends AbstractAppCacheLinker {
* {@code @Override}
* protected String[] otherCachedFiles() {
* return new String[] {"/MyApp.html","/MyApp.css"};
* }
* }
* </blockquote></pre>
*/
@LinkerOrder(Order.POST)
public class SimpleAppCacheLinker extends AbstractLinker {
private static final String MANIFEST = "appcache.nocache.manifest";
@Override
public String getDescription() {
return "AppCacheLinker";
}
@Override
public ArtifactSet link(TreeLogger logger, LinkerContext context, ArtifactSet artifacts,
boolean onePermutation)
throws UnableToCompleteException {
ArtifactSet toReturn = new ArtifactSet(artifacts);
if (onePermutation) {
return toReturn;
}
if (toReturn.find(SelectionInformation.class).isEmpty()) {
logger.log(TreeLogger.INFO, "DevMode warning: Clobbering " + MANIFEST + " to allow debugging. "
+ "Recompile before deploying your app!");
artifacts = null;
}
// Create the general cache-manifest resource for the landing page:
toReturn.add(emitLandingPageCacheManifest(context, logger, artifacts));
return toReturn;
}
/**
* Override this method to force the linker to also include more files
* in the manifest.
*/
protected String[] otherCachedFiles() {
return null;
}
/**
* Creates the cache-manifest resource specific for the landing page.
*
* @param context the linker environment
* @param logger the tree logger to record to
* @param artifacts {@code null} to generate an empty cache manifest
*/
private Artifact<?> emitLandingPageCacheManifest(LinkerContext context, TreeLogger logger,
ArtifactSet artifacts)
throws UnableToCompleteException {
StringBuilder publicSourcesSb = new StringBuilder();
StringBuilder staticResoucesSb = new StringBuilder();
if (artifacts != null) {
// 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 pathName = ea.getPartialPath();
if (pathName.endsWith("symbolMap")
|| pathName.endsWith(".xml.gz")
|| pathName.endsWith("rpc.log")
|| pathName.endsWith("gwt.rpc")
|| pathName.endsWith("manifest.txt")
|| pathName.startsWith("rpcPolicyManifest")
|| pathName.startsWith("soycReport")) {
// skip these resources
} else {
publicSourcesSb.append(pathName + "\n");
}
}
}
String[] cacheExtraFiles = getCacheExtraFiles();
for (int i = 0; i < cacheExtraFiles.length; i++) {
staticResoucesSb.append(cacheExtraFiles[i]);
staticResoucesSb.append("\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("# Static app files\n");
sb.append(staticResoucesSb.toString());
sb.append("\n# Generated app files\n");
sb.append(publicSourcesSb.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.INFO, "Be sure your landing page's <html> tag declares a manifest:"
+ " <html manifest=" + context.getModuleFunctionName() + "/" + MANIFEST + "\">");
// Create the manifest as a new artifact and return it:
return emitString(logger, sb.toString(), MANIFEST);
}
/**
* Obtains the extra files to include in the manifest. Ensures the returned
* array is not null.
*/
private String[] getCacheExtraFiles() {
String[] cacheExtraFiles = otherCachedFiles();
return cacheExtraFiles == null ?
new String[0] : Arrays.copyOf(cacheExtraFiles, cacheExtraFiles.length);
}
}