/* * Copyright 2014 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.dev.codeserver; 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.dev.cfg.ModuleDef; import com.google.gwt.dev.codeserver.CompileDir.PolicyFile; import com.google.gwt.dev.resource.impl.ResourceOracleImpl; import com.google.gwt.thirdparty.guava.common.base.Charsets; import com.google.gwt.thirdparty.guava.common.base.Preconditions; import com.google.gwt.thirdparty.guava.common.io.Files; import com.google.gwt.thirdparty.guava.common.io.Resources; import java.io.File; import java.io.IOException; import java.net.URL; /** * An output directory where other servers pick up files from Super Dev Mode. * This often points to the -war directory for a DevMode server, * but it may point to a subdirectory. * * <p>Underneath this directory, the code server will write one subdirectory for each module. * A nocache.js file, public resource files, and GWT-RPC policy files will be written there. * This is usually enough to launch Super Dev Mode without running the * production GWT compiler first. */ class LauncherDir { private final File launcherDir; private final Options options; /** * @see #maybeCreate */ private LauncherDir(Options options) { this.launcherDir = Preconditions.checkNotNull(options.getLauncherDir()); this.options = options; } /** * Updates files after a successful compile (or on startup). * @param module the module that was compiled * @param compileDir the compiler's output directory */ void update(ModuleDef module, CompileDir compileDir, TreeLogger logger) throws UnableToCompleteException { File moduleDir = new File(launcherDir + "/" + module.getName()); if (!moduleDir.isDirectory()) { if (!moduleDir.mkdirs()) { logger.log(Type.ERROR, "Can't create launcher dir for module: " + moduleDir); throw new UnableToCompleteException(); } } try { String stub = generateStubNocacheJs(module.getName(), options); final File noCacheJs = new File(moduleDir, module.getName() + ".nocache.js"); Files.write(stub, noCacheJs, Charsets.UTF_8); // Remove gz file so it doesn't get used instead. // (We may be writing to an existing war directory.) final File noCacheJsGz = new File(noCacheJs.getPath() + ".gz"); if (noCacheJsGz.exists()) { if (!noCacheJsGz.delete()) { logger.log(Type.ERROR, "cannot delete file: " + noCacheJsGz); throw new UnableToCompleteException(); } } writePublicResources(moduleDir, module, logger); // Copy the GWT-RPC serialization policies so that the subclass of RemoteServiceServlet // can pick it up. (It expects to find policy files in the module directory.) // See RemoteServiceServlet.loadSerializationPolicy. // (An alternate approach is to set the gwt.codeserver.port environment variable // so that the other server downloads policies over HTTP.) for (PolicyFile policyFile : compileDir.readRpcPolicyManifest(module.getName())) { String filename = policyFile.getName(); File src = new File(compileDir.getWarDir(), module.getName() + "/" + filename); File dest = new File(moduleDir, filename); Files.copy(src, dest); } } catch (IOException e) { logger.log(Type.ERROR, "Can't update launcher dir", e); throw new UnableToCompleteException(); } } /** * Returns a new LauncherDir or null if not enabled. */ static LauncherDir maybeCreate(Options options) { if (options.getLauncherDir() == null) { return null; } return new LauncherDir(options); } // TODO: figure out a better home for these static methods. /** * Returns the contents of a nocache.js file that will compile and then run a GWT application * using Super Dev Mode. */ static String generateStubNocacheJs(String outputModuleName, Options options) throws IOException { URL url = Resources.getResource(Recompiler.class, "stub.nocache.js"); final String template = Resources.toString(url, Charsets.UTF_8); return template .replace("__MODULE_NAME__", outputModuleName) .replace("__SUPERDEV_PORT__", String.valueOf(options.getPort())); } static void writePublicResources(File moduleOutputDir, ModuleDef module, TreeLogger compileLogger) throws UnableToCompleteException, IOException { // Copy the public resources to the output. ResourceOracleImpl publicResources = module.getPublicResourceOracle(); for (String pathName : publicResources.getPathNames()) { File file = new File(moduleOutputDir, pathName); File parent = file.getParentFile(); if (!parent.isDirectory() && !parent.mkdirs()) { compileLogger.log(Type.ERROR, "cannot create directory: " + parent); throw new UnableToCompleteException(); } Files.asByteSink(file).writeFrom(publicResources.getResourceAsStream(pathName)); } } }