Advertisement
Guest User

GWT HTML5 Offline Manifest Generation Linker

a guest
Sep 2nd, 2011
429
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 7.75 KB | None | 0 0
  1. package com.sagarius.offline.linker;
  2.  
  3. /*
  4.  * Copyright 2011 Google Inc.
  5.  *
  6.  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7.  * use this file except in compliance with the License. You may obtain a copy of
  8.  * the License at
  9.  *
  10.  * http://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing, software
  13.  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  14.  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  15.  * License for the specific language governing permissions and limitations under
  16.  * the License.
  17.  */
  18. import java.io.IOException;
  19. import java.util.Arrays;
  20. import java.util.Date;
  21. import java.util.Map;
  22. import java.util.Properties;
  23. import java.util.SortedMap;
  24. import java.util.SortedSet;
  25.  
  26. import org.apache.commons.io.IOUtils;
  27.  
  28. import com.google.gwt.core.ext.LinkerContext;
  29. import com.google.gwt.core.ext.TreeLogger;
  30. import com.google.gwt.core.ext.UnableToCompleteException;
  31. import com.google.gwt.core.ext.linker.AbstractLinker;
  32. import com.google.gwt.core.ext.linker.Artifact;
  33. import com.google.gwt.core.ext.linker.ArtifactSet;
  34. import com.google.gwt.core.ext.linker.CompilationResult;
  35. import com.google.gwt.core.ext.linker.EmittedArtifact;
  36. import com.google.gwt.core.ext.linker.LinkerOrder;
  37. import com.google.gwt.core.ext.linker.LinkerOrder.Order;
  38. import com.google.gwt.core.ext.linker.SelectionProperty;
  39. import com.google.gwt.core.ext.linker.Shardable;
  40. import com.google.gwt.core.ext.linker.impl.SelectionInformation;
  41.  
  42. /**
  43.  * AppCacheLinker - linker for public path resources in the Application Cache.
  44.  * <p>
  45.  * To use:
  46.  * <ol>
  47.  * <li>Add {@code manifest="YOURMODULENAME/appcache.nocache.manifest"} to the
  48.  * {@code <html>} tag in your base html file. E.g.,
  49.  * {@code <html manifest="mymodule/appcache.nocache.manifest">}</li>
  50.  * <li>Add a mime-mapping to your web.xml file:
  51.  * <p>
  52.  *
  53.  * <pre>
  54.  * {@code <mime-mapping>
  55.  * <extension>manifest</extension>
  56.  * <mime-type>text/cache-manifest</mime-type>
  57.  * </mime-mapping>
  58.  * }
  59.  * </pre>
  60.  *
  61.  * </li>
  62.  * </ol>
  63.  * <p>
  64.  * On every compile, this linker will regenerate the appcache.nocache.manifest
  65.  * file with files from the public path of your module.
  66.  * <p>
  67.  * To obtain a manifest that contains other files in addition to those generated
  68.  * by this linker, create a class that inherits from this one and overrides
  69.  * {@code otherCachedFiles()}, and use it as a linker instead:
  70.  * <p>
  71.  *
  72.  * <pre>
  73.  * <blockquote>
  74.  * {@code @Shardable}
  75.  * public class MyAppCacheLinker extends AbstractAppCacheLinker {
  76.  *   {@code @Override}
  77.  *   protected String[] otherCachedFiles() {
  78.  *     return new String[] {"/MyApp.html","/MyApp.css"};
  79.  *   }
  80.  * }
  81.  * </blockquote>
  82.  * </pre>
  83.  */
  84. @Shardable
  85. @LinkerOrder(Order.POST)
  86. public class SimpleAppCacheLinker extends AbstractLinker {
  87.  
  88.     private static final String MANIFEST = "appcache.nocache.manifest";
  89.     private static final String MANIFEST_TEMPLATE = "appcache.manifest.template";
  90.  
  91.     @Override
  92.     public String getDescription() {
  93.         return "AppCacheLinker";
  94.     }
  95.  
  96.     @Override
  97.     public ArtifactSet link(TreeLogger logger, LinkerContext context,
  98.             ArtifactSet artifacts, boolean onePermutation)
  99.             throws UnableToCompleteException {
  100.  
  101.         ArtifactSet toReturn = new ArtifactSet(artifacts);
  102.  
  103.         if (toReturn.find(SelectionInformation.class).isEmpty()) {
  104.             logger.log(TreeLogger.INFO, "DevMode warning: Clobbering "
  105.                     + MANIFEST + " to allow debugging. "
  106.                     + "Recompile before deploying your app!");
  107.             artifacts = null;
  108.         }
  109.  
  110.         String permutation = null;
  111.         String locale = "";
  112.         String userAgent = "";
  113.         SelectionProperty selectionProperty = null;
  114.         Properties props = new Properties();
  115.  
  116.         for (CompilationResult result : artifacts.find(CompilationResult.class)) {
  117.             permutation = result.getStrongName();
  118.             SortedSet<SortedMap<SelectionProperty, String>> propertiesMap = result
  119.                     .getPropertyMap();
  120.             for (SortedMap<SelectionProperty, String> sm : propertiesMap) {
  121.                 for (Map.Entry<SelectionProperty, String> e : sm.entrySet()) {
  122.                     selectionProperty = e.getKey();
  123.                     if ("locale".equals(selectionProperty.getName())) {
  124.                         locale = e.getValue();
  125.                     }
  126.                     if ("user.agent".equals(selectionProperty.getName())) {
  127.                         userAgent = e.getValue();
  128.                     }
  129.                 }
  130.             }
  131.             props.setProperty(userAgent + "." + locale, permutation);
  132.         }
  133.  
  134.         // if (onePermutation) {
  135.         // logger.log(TreeLogger.INFO, "Its one permutation");
  136.         // return toReturn;
  137.         // }
  138.  
  139.         toReturn.add(emitLandingPageCacheManifest(context, logger, artifacts,
  140.                 userAgent));
  141.         return toReturn;
  142.     }
  143.  
  144.     /**
  145.      * Override this method to force the linker to also include more files in
  146.      * the manifest.
  147.      */
  148.     protected String[] otherCachedFiles() {
  149.         return null;
  150.     }
  151.  
  152.     /**
  153.      * Creates the cache-manifest resource specific for the landing page.
  154.      *
  155.      * @param context
  156.      *            the linker environment
  157.      * @param logger
  158.      *            the tree logger to record to
  159.      * @param artifacts
  160.      *            {@code null} to generate an empty cache manifest
  161.      */
  162.     private Artifact<?> emitLandingPageCacheManifest(LinkerContext context,
  163.             TreeLogger logger, ArtifactSet artifacts, String userAgent)
  164.             throws UnableToCompleteException {
  165.         StringBuilder publicSourcesSb = new StringBuilder();
  166.         StringBuilder staticResoucesSb = new StringBuilder();
  167.  
  168.         if (artifacts != null) {
  169.             // Iterate over all emitted artifacts, and collect all cacheable
  170.             // artifacts
  171.             for (@SuppressWarnings("rawtypes")
  172.             Artifact artifact : artifacts) {
  173.                 if (artifact instanceof EmittedArtifact) {
  174.                     EmittedArtifact ea = (EmittedArtifact) artifact;
  175.                     String pathName = ea.getPartialPath();
  176.                     if (pathName.endsWith("symbolMap")
  177.                             || pathName.endsWith(".xml.gz")
  178.                             || pathName.endsWith("rpc.log")
  179.                             || pathName.endsWith("gwt.rpc")
  180.                             || pathName.endsWith("manifest.txt")
  181.                             || pathName.startsWith("rpcPolicyManifest")) {
  182.                         // skip these resources
  183.                     } else {
  184.                         publicSourcesSb.append(pathName + "\n");
  185.                     }
  186.                 }
  187.             }
  188.  
  189.             String[] cacheExtraFiles = getCacheExtraFiles();
  190.             for (int i = 0; i < cacheExtraFiles.length; i++) {
  191.                 staticResoucesSb.append(cacheExtraFiles[i]);
  192.                 staticResoucesSb.append("\n");
  193.             }
  194.         }
  195.  
  196.         String manifest;
  197.  
  198.         try {
  199.             manifest = IOUtils.toString(getClass().getResourceAsStream(
  200.                     MANIFEST_TEMPLATE));
  201.  
  202.             // replace the placeholder with the real data
  203.             manifest = manifest.replace("$UNIQUEID$",
  204.                     (new Date()).getTime() + "." + Math.random()).toString();
  205.  
  206.             manifest = manifest.replace("$STATICAPPFILES$",
  207.                     staticResoucesSb.toString());
  208.  
  209.             manifest = manifest.replace("$GENAPPFILES$",
  210.                     publicSourcesSb.toString());
  211.  
  212.             logger.log(
  213.                     TreeLogger.INFO,
  214.                     "Be sure your landing page's <html> tag declares a manifest:"
  215.                             + " <html manifest="
  216.                             + context.getModuleFunctionName() + "/" + MANIFEST
  217.                             + "\">");
  218.  
  219.             // return manifest;
  220.  
  221.         } catch (IOException e) {
  222.  
  223.             logger.log(TreeLogger.ERROR,
  224.                     "Could not read cache manifest template.", e);
  225.  
  226.             throw new UnableToCompleteException();
  227.         }
  228.  
  229.         /**
  230.          * Manifest name according to user agent
  231.          */
  232.         String manifestName;
  233.         if (userAgent.isEmpty())
  234.             manifestName = MANIFEST;
  235.         else
  236.             manifestName = userAgent + "_" + MANIFEST;
  237.  
  238.         logger.log(TreeLogger.INFO, "Generating offline cache manifest "
  239.                 + manifestName);
  240.         return emitString(logger, manifest, manifestName);
  241.     }
  242.  
  243.     /**
  244.      * Obtains the extra files to include in the manifest. Ensures the returned
  245.      * array is not null.
  246.      */
  247.     private String[] getCacheExtraFiles() {
  248.         String[] cacheExtraFiles = otherCachedFiles();
  249.         return cacheExtraFiles == null ? new String[0] : Arrays.copyOf(
  250.                 cacheExtraFiles, cacheExtraFiles.length);
  251.     }
  252. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement