/* * 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 org.geogebra.web.linker; import java.io.InputStream; import org.geogebra.common.GeoGebraConstants; import com.google.gwt.core.ext.LinkerContext; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.TreeLogger.Type; 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.Shardable; import com.google.gwt.core.ext.linker.impl.SelectionInformation; /** * AppCacheLinker - linker for public path resources in the Application Cache. * <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) @Shardable public class AppCacheLinker extends AbstractLinker { private static final String SWORKER = "sworker.js"; 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: generating empty " + MANIFEST); } else { emitLandingPageCacheManifest(context, logger, artifacts, toReturn); } // Create the general cache-manifest resource for the landing page: return toReturn; } /** * 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 * @param toReturn */ private void emitLandingPageCacheManifest(LinkerContext context, TreeLogger logger, ArtifactSet artifacts, ArtifactSet toReturn) 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.endsWith("cssmap") || pathName.endsWith("MANIFEST.MF") || pathName.endsWith(".txt") || pathName.endsWith(".php") || pathName.endsWith("README") || pathName.endsWith("COPYING") || pathName.endsWith("LICENSE") || pathName.endsWith("oauthWindow.html") || pathName.endsWith("windowslive.html") || pathName.endsWith("devmode.js") || pathName.startsWith("js/properties_")) { // skip these resources } else { publicSourcesSb .append("\"https://download.geogebra.org/web/5.0/" + GeoGebraConstants.VERSION_STRING + "/web3d/" + pathName.replace("\\", "/") + "\",\n"); } } } String[] cacheExtraFiles = AppCacheLinkerSettings .otherCachedFiles(); for (int i = 0; i < cacheExtraFiles.length; i++) { staticResoucesSb.append("\""); staticResoucesSb.append(cacheExtraFiles[i]); staticResoucesSb.append("\""); if (i < cacheExtraFiles.length - 1) { staticResoucesSb.append(",\n"); } } } // build manifest String id = GeoGebraConstants.VERSION_STRING + ":" + System.currentTimeMillis(); // we have to generate this unique id because the resources can change // but // the hashed cache.html files can remain the same. // build cache list StringBuilder sb = new StringBuilder(); // logger.log( // TreeLogger.INFO, // "Make sure you have the following" // + " attribute added to your landing page's <html> tag: <html // manifest=\"" // + context.getModuleFunctionName() + "/" + MANIFEST // + "\">"); // Create the manifest as a new artifact and return it: try { InputStream s = AppCacheLinker.class.getResourceAsStream( "/org/geogebra/web/worker_template.js"); byte[] contents = new byte[1024]; int bytesRead = 0; while ((bytesRead = s.read(contents)) != -1) { sb.append(new String(contents, 0, bytesRead) .replace("%URLS%", publicSourcesSb.toString() + staticResoucesSb.toString()) .replace("%ID%", id)); } // fbr.close(); } catch (Exception e) { e.printStackTrace(); logger.log(Type.ERROR, e.getMessage()); } // toReturn.add(emitString(logger, sbM.toString(), MANIFEST)); toReturn.add(emitString(logger, sb.toString(), SWORKER)); toReturn.add(emitString(logger, ("{\n" + publicSourcesSb.toString() + staticResoucesSb.toString()).replaceAll("\\n", "\n ") + "\n}", "files.json")); } }