blob: 484bf75b51fbfbf47fe96294330c1e29b092481f [file] [log] [blame]
/*
* 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));
}
}
}