package fr.lteconsulting.hexa.linker; import java.util.UUID; 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.Shardable; import com.google.gwt.core.ext.linker.impl.SelectionInformation; /** * 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"} to the {@code <html>} tag * in your base html file. E.g., {@code <html manifest="mymodule.appcache">}</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> * */ @Shardable @LinkerOrder( Order.POST ) public class AppCacheLinker extends AbstractLinker { private static String MANIFEST; @Override public String getDescription() { return "AppCacheLinker"; } @Override public ArtifactSet link( TreeLogger logger, LinkerContext context, ArtifactSet artifacts, boolean onePermutation ) throws UnableToCompleteException { MANIFEST = context.getModuleName() + ".appcache"; 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 ); // artifacts = null; return toReturn; } // Create the general cache-manifest resource for the landing page: toReturn.add( emitLandingPageCacheManifest( context, logger, artifacts ) ); return toReturn; } /** * Obtains the extra files to include in the manifest. Ensures the returned * array is not null. */ protected String[] getCacheExtraFiles( TreeLogger logger, LinkerContext context ) { return new String[] { "SQLiteExplorer.html", "SQLiteExplorer.css", "favicon.ico" }; } /** * 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 */ @SuppressWarnings( "rawtypes" ) 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( 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" ) || pathName.endsWith( ".cssmap" ) ) { continue;// skip these resources } else { publicSourcesSb.append( context.getModuleName() ).append( "/" ).append( pathName.replace( "\\", "/" ) ).append( "\n" ); } } } String[] cacheExtraFiles = getCacheExtraFiles( logger, context ); 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( "# " + UUID.randomUUID() + "-" + System.currentTimeMillis() + "\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 ); sb.append( "\n# Generated app files\n" ); sb.append( publicSourcesSb ); sb.append( "\n\n" ); sb.append( "# All other resources require the user to be online.\n" ); sb.append( "NETWORK:\n" ); sb.append( "*\n" ); sb.append( "http://*\n" ); sb.append( "\n\n" ); sb.append( "# Available values: fast, prefer-online\n" ); sb.append( "SETTINGS:\n" ); sb.append( "fast\n" ); logger.log( TreeLogger.DEBUG, "Be sure your landing page's <html> tag declares a manifest: <html manifest=" + MANIFEST + "\">" ); // Create the manifest as a new artifact and return it: return emitString( logger, sb.toString(), "../" + MANIFEST ); } }