blob: 76cb5833a5939d5e6e0385acb7d35193b9adb43e [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.shell;
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.ArtifactSet;
import com.google.gwt.core.ext.linker.impl.StandardLinkerContext;
import com.google.gwt.dev.DevMode.HostedModeOptions;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.util.arg.OptionMethodNameDisplayMode;
import com.google.gwt.thirdparty.guava.common.base.Stopwatch;
import com.google.gwt.thirdparty.guava.common.collect.ListMultimap;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* Starts a superdev-mode codeserver.
*/
public class SuperDevListener implements CodeServerListener {
private final TreeLogger logger;
private final int codeServerPort;
private List<String> codeServerArgs;
/**
* Listens for new connections from browsers.
*/
public SuperDevListener(TreeLogger treeLogger, HostedModeOptions options) {
this.logger = treeLogger;
this.codeServerPort = chooseCodeServerPort(treeLogger, options);
// This directory must exist when the Code Server starts.
ensureModuleBaseDir(options);
codeServerArgs = makeCodeServerArgs(options, codeServerPort);
}
@Override
public int getSocketPort() {
return codeServerPort;
}
@Override
public URL makeStartupUrl(String url) throws UnableToCompleteException {
try {
return new URL(url);
} catch (MalformedURLException e) {
logger.log(TreeLogger.ERROR, "Invalid URL " + url, e);
throw new UnableToCompleteException();
}
}
@Override
public void writeCompilerOutput(StandardLinkerContext linkerStack, ArtifactSet artifacts,
ModuleDef module, boolean isRelink) throws UnableToCompleteException {
// The code server will do this.
}
@Override
public void setIgnoreRemoteDeath(boolean b) {
}
@Override
public void start() {
try {
Stopwatch watch = Stopwatch.createStarted();
logger.log(Type.INFO, "Running CodeServer with parameters: " + codeServerArgs);
runCodeServer(codeServerArgs.toArray(new String[0]));
logger.log(Type.INFO, "Code server started in " + watch + " ms");
} catch (Exception e) {
logger.log(Type.INFO, "Unable to start Code server");
throw new RuntimeException(e);
}
}
private void runCodeServer(String[] mainArgs) throws Exception {
// Using reflection so as we don't create a circular dependency between
// dev.jar && codeserver.jar
Method mainMethod;
try {
Class<?> clazz = Class.forName("com.google.gwt.dev.codeserver.CodeServer");
mainMethod = clazz.getMethod("main", String[].class);
} catch (ClassNotFoundException e) {
logger.log(TreeLogger.ERROR, "Unable to find main() method for Super Dev Mode "
+ "code server. Hint: verify that gwt-codeserver.jar is in your classpath.");
throw e;
}
mainMethod.invoke(null, new Object[] {mainArgs});
}
private static int chooseCodeServerPort(TreeLogger logger, HostedModeOptions options) {
int port = options.getCodeServerPort();
if (port == 0) {
// Automatically choose an unused port.
try {
ServerSocket serverSocket = new ServerSocket(0);
port = serverSocket.getLocalPort();
serverSocket.close();
return port;
} catch (IOException e) {
logger.log(TreeLogger.ERROR, "Unable to get an unnused port.");
throw new RuntimeException(e);
}
} else if (port < 0 || port == 9997) {
// 9997 is the default non-SuperDevMode port from DevModeBase. TODO: use constant.
return 9876; // Default Super Dev Mode port
} else {
return port; // User-specified port
}
}
private static void ensureModuleBaseDir(HostedModeOptions options) {
File dir = options.getModuleBaseDir();
if (!dir.isDirectory()) {
dir.mkdirs();
if (!dir.isDirectory()) {
throw new RuntimeException("unable to create module base directory: " +
dir.getAbsolutePath());
}
}
}
private static List<String> makeCodeServerArgs(HostedModeOptions options, int port) {
List<String> args = new ArrayList<String>();
args.add("-noprecompile");
args.add("-port");
args.add(String.valueOf(port));
args.add("-sourceLevel");
args.add(String.valueOf(options.getSourceLevel()));
if (options.getBindAddress() != null) {
args.add("-bindAddress");
args.add(options.getBindAddress());
}
if (options.getWorkDir() != null) {
args.add("-workDir");
args.add(String.valueOf(options.getWorkDir()));
}
args.add("-launcherDir");
args.add(options.getModuleBaseDir().getAbsolutePath());
if (options.getLogLevel() != null) {
args.add("-logLevel");
args.add(String.valueOf(options.getLogLevel()));
}
if (options.shouldGenerateJsInteropExports()) {
args.add("-generateJsInteropExports");
}
for (String regex : options.getJsInteropExportFilter().getValues()) {
if (regex.startsWith("-")) {
args.add("-excludeJsInteropExports");
args.add(regex.substring(1));
} else {
args.add("-includeJsInteropExports");
args.add(regex);
}
}
if (!options.isIncrementalCompileEnabled()) {
args.add("-noincremental");
}
if (options.getMethodNameDisplayMode() != OptionMethodNameDisplayMode.Mode.NONE) {
args.add("-XmethodNameDisplayMode");
args.add(options.getMethodNameDisplayMode().name());
}
args.add("-style");
args.add(options.getOutput().name());
if (options.isStrict()) {
args.add("-strict");
}
if (options.getProperties().size() > 0) {
args.addAll(makeSetPropertyArgs(options.getProperties()));
}
for (String mod : options.getModuleNames()) {
args.add(mod);
}
return args;
}
private static List<String> makeSetPropertyArgs(
ListMultimap<String, String> properties) {
List<String> propertyArgs = Lists.newArrayList();
for (String propertyName : properties.keySet()) {
propertyArgs.add("-setProperty");
StringBuilder nameValues = new StringBuilder(propertyName + "=");
for (String propertyValue : properties.get(propertyName)) {
nameValues.append(propertyValue + ",");
}
propertyArgs.add(nameValues.substring(0, nameValues.length() - 1));
}
return propertyArgs;
}
}