Add support for HostedMode with OOPHM.
Patch by: jat, scottb
Review by: scottb, jat
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5036 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/ServletContainerLauncher.java b/dev/core/src/com/google/gwt/core/ext/ServletContainerLauncher.java
index 0785fb1..e45e953 100644
--- a/dev/core/src/com/google/gwt/core/ext/ServletContainerLauncher.java
+++ b/dev/core/src/com/google/gwt/core/ext/ServletContainerLauncher.java
@@ -25,6 +25,22 @@
public abstract class ServletContainerLauncher {
/**
+ * @return a path to a 24-pixel high image file (relative to the classpath) to
+ * be used for this servlet container, or null if none.
+ */
+ public String getIconPath() {
+ return null;
+ }
+
+ /**
+ * @return a short human-readable name of this servlet container, or null
+ * if no name should be displayed.
+ */
+ public String getName() {
+ return "Web Server";
+ }
+
+ /**
* Start an embedded HTTP servlet container.
*
* @param logger the server logger
diff --git a/dev/core/src/com/google/gwt/dev/HostedMode.java b/dev/core/src/com/google/gwt/dev/HostedMode.java
index 0773528..cea85a4 100644
--- a/dev/core/src/com/google/gwt/dev/HostedMode.java
+++ b/dev/core/src/com/google/gwt/dev/HostedMode.java
@@ -297,6 +297,8 @@
new Compiler(newOptions).run(logger);
}
+ @Deprecated
+ @Override
protected void compile(TreeLogger logger, ModuleDef moduleDef)
throws UnableToCompleteException {
throw new UnsupportedOperationException();
diff --git a/dev/core/src/com/google/gwt/dev/HostedModeBase.java b/dev/core/src/com/google/gwt/dev/HostedModeBase.java
index 640f813..226a91e 100644
--- a/dev/core/src/com/google/gwt/dev/HostedModeBase.java
+++ b/dev/core/src/com/google/gwt/dev/HostedModeBase.java
@@ -458,7 +458,6 @@
* @param logger TreeLogger to use
* @param typeOracle
* @param moduleDef
- * @param genDir
* @return ShellModuleSpaceHost instance
*/
protected final ShellModuleSpaceHost doCreateShellModuleSpaceHost(
diff --git a/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java b/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java
index 8a73bb5..ea06563 100644
--- a/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java
+++ b/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java
@@ -74,6 +74,16 @@
widgetShell.setCursor(normalCursor);
}
}
+
+ public ModuleSpaceHost createModuleSpaceHost(TreeLogger logger,
+ String moduleName, String userAgent, String remoteEndpoint)
+ throws UnableToCompleteException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void unloadModule(ModuleSpaceHost moduleSpaceHost) {
+ throw new UnsupportedOperationException();
+ }
}
/**
diff --git a/dev/core/src/com/google/gwt/dev/shell/BrowserWidgetHost.java b/dev/core/src/com/google/gwt/dev/shell/BrowserWidgetHost.java
index b917a1b..9253b34 100644
--- a/dev/core/src/com/google/gwt/dev/shell/BrowserWidgetHost.java
+++ b/dev/core/src/com/google/gwt/dev/shell/BrowserWidgetHost.java
@@ -41,10 +41,18 @@
@Deprecated
void compile(String[] modules) throws UnableToCompleteException;
- // Factor this out if BrowserWidget becomes decoupled from hosted mode
+ /**
+ * For SWT.
+ */
ModuleSpaceHost createModuleSpaceHost(TreeLogger logger,
BrowserWidget widget, String moduleName) throws UnableToCompleteException;
+ /**
+ * For OOPHM.
+ */
+ ModuleSpaceHost createModuleSpaceHost(TreeLogger logger, String moduleName,
+ String userAgent, String remoteEndpoint) throws UnableToCompleteException;
+
TreeLogger getLogger();
/**
@@ -69,5 +77,13 @@
String normalizeURL(String whatTheUserTyped);
+ /**
+ * For SWT.
+ */
BrowserWidget openNewBrowserWindow() throws UnableToCompleteException;
+
+ /**
+ * For OOPHM.
+ */
+ void unloadModule(ModuleSpaceHost moduleSpaceHost);
}
diff --git a/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java b/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java
index 5cd8c0b..72a35ba 100644
--- a/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java
+++ b/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java
@@ -187,10 +187,12 @@
this.appRootDir = appRootDir;
}
+ @Override
public int getPort() {
return actualPort;
}
+ @Override
public void refresh() throws UnableToCompleteException {
String msg = "Reloading web app to reflect changes in "
+ appRootDir.getAbsolutePath();
@@ -212,6 +214,7 @@
branch.log(TreeLogger.INFO, "Reload completed successfully");
}
+ @Override
public void stop() throws UnableToCompleteException {
TreeLogger branch = logger.branch(TreeLogger.INFO,
"Stopping Jetty server");
@@ -416,6 +419,19 @@
Log.getLog();
}
+ @Override
+ public String getIconPath() {
+ return JettyLauncher.class.getPackage().getName().replace('.', '/')
+ + "/icon24.png";
+ }
+
+ @Override
+ public String getName() {
+ // Use only the icon for the tab.
+ return null;
+ }
+
+ @Override
public ServletContainer start(TreeLogger logger, int port, File appRootDir)
throws Exception {
checkStartParams(logger, port, appRootDir);
diff --git a/dev/core/src/com/google/gwt/dev/shell/jetty/icon24.png b/dev/core/src/com/google/gwt/dev/shell/jetty/icon24.png
new file mode 100644
index 0000000..f51491e
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/jetty/icon24.png
Binary files differ
diff --git a/dev/oophm/overlay/com/google/gwt/dev/GWTShell.java b/dev/oophm/overlay/com/google/gwt/dev/GWTShell.java
index eb3df67..bba102a 100644
--- a/dev/oophm/overlay/com/google/gwt/dev/GWTShell.java
+++ b/dev/oophm/overlay/com/google/gwt/dev/GWTShell.java
@@ -17,100 +17,30 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.EmittedArtifact;
-import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.GWTCompiler.GWTCompilerOptionsImpl;
import com.google.gwt.dev.cfg.ModuleDef;
-import com.google.gwt.dev.cfg.ModuleDefLoader;
import com.google.gwt.dev.shell.ArtifactAcceptor;
-import com.google.gwt.dev.shell.BrowserListener;
import com.google.gwt.dev.shell.BrowserWidget;
-import com.google.gwt.dev.shell.BrowserWidgetHost;
-import com.google.gwt.dev.shell.ModuleSpaceHost;
-import com.google.gwt.dev.shell.OophmSessionHandler;
-import com.google.gwt.dev.shell.ShellMainWindow;
-import com.google.gwt.dev.shell.ShellModuleSpaceHost;
import com.google.gwt.dev.shell.WorkDirs;
import com.google.gwt.dev.shell.tomcat.EmbeddedTomcatServer;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.arg.ArgHandlerOutDir;
-import com.google.gwt.dev.util.log.AbstractTreeLogger;
-import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
import com.google.gwt.util.tools.ArgHandlerExtra;
-import com.google.gwt.util.tools.ArgHandlerString;
-import java.awt.Cursor;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
import java.net.URL;
-import java.util.HashSet;
-import java.util.IdentityHashMap;
-import java.util.Map;
import java.util.Set;
import javax.swing.ImageIcon;
-import javax.swing.JFrame;
-import javax.swing.JTabbedPane;
-import javax.swing.WindowConstants;
/**
* The main executable class for the hosted mode shell.
*/
@SuppressWarnings("deprecation")
@Deprecated
-public class GWTShell extends HostedModeBase {
-
- /**
- * Handles the -portHosted command line flag.
- */
- protected static class ArgHandlerPortHosted extends ArgHandlerString {
-
- private final OptionPortHosted options;
-
- public ArgHandlerPortHosted(OptionPortHosted options) {
- this.options = options;
- }
-
- @Override
- public String[] getDefaultArgs() {
- return new String[] {"-portHosted", "9997"};
- }
-
- @Override
- public String getPurpose() {
- return "Listens on the specified port for hosted mode connections";
- }
-
- @Override
- public String getTag() {
- return "-portHosted";
- }
-
- @Override
- public String[] getTagArgs() {
- return new String[] {"port-number | \"auto\""};
- }
-
- @Override
- public boolean setString(String value) {
- if (value.equals("auto")) {
- options.setPortHosted(0);
- } else {
- try {
- options.setPortHosted(Integer.parseInt(value));
- } catch (NumberFormatException e) {
- System.err.println("A port must be an integer or \"auto\"");
- return false;
- }
- }
- return true;
- }
- }
+public class GWTShell extends OophmHostedModeBase {
/**
* Handles the list of startup urls that can be passed at the end of the
@@ -144,7 +74,7 @@
/**
* The GWTShell argument processor.
*/
- protected static class ArgProcessor extends HostedModeBase.ArgProcessor {
+ protected static class ArgProcessor extends OophmHostedModeBase.ArgProcessor {
public ArgProcessor(ShellOptionsImpl options, boolean forceServer,
boolean noURLs) {
super(options, forceServer);
@@ -152,7 +82,6 @@
registerHandler(new ArgHandlerStartupURLsExtra(options));
}
registerHandler(new ArgHandlerOutDir(options));
- registerHandler(new ArgHandlerPortHosted(options));
}
@Override
@@ -161,20 +90,14 @@
}
}
- interface OptionPortHosted {
- int getPortHosted();
-
- void setPortHosted(int portHosted);
- }
-
/**
* Concrete class to implement all shell options.
*/
- static class ShellOptionsImpl extends HostedModeBaseOptionsImpl implements
- HostedModeBaseOptions, WorkDirs, LegacyCompilerOptions, OptionPortHosted {
+ static class ShellOptionsImpl extends OophmHostedModeBaseOptionsImpl
+ implements HostedModeBaseOptions, WorkDirs, LegacyCompilerOptions,
+ OptionPortHosted {
private int localWorkers;
private File outDir;
- private int portHosted;
public File getCompilerOutputDir(ModuleDef moduleDef) {
return new File(getOutDir(), moduleDef.getName());
@@ -188,10 +111,6 @@
return outDir;
}
- public int getPortHosted() {
- return portHosted;
- }
-
public File getShellPublicGenDir(ModuleDef moduleDef) {
return new File(getShellBaseWorkDir(moduleDef), "public");
}
@@ -208,131 +127,11 @@
public void setOutDir(File outDir) {
this.outDir = outDir;
}
-
- public void setPortHosted(int port) {
- portHosted = port;
- }
- }
-
- private class BrowserWidgetHostImpl implements BrowserWidgetHost {
- private TreeLogger logger;
- private Map<ModuleSpaceHost, ModulePanel> moduleTabs = new IdentityHashMap<ModuleSpaceHost, ModulePanel>();
-
- public BrowserWidgetHostImpl() {
- }
-
- public void compile(ModuleDef moduleDef) throws UnableToCompleteException {
- GWTShell.this.compile(getLogger(), moduleDef);
- }
-
- public void compile(String[] moduleNames) throws UnableToCompleteException {
- for (int i = 0; i < moduleNames.length; i++) {
- String moduleName = moduleNames[i];
- ModuleDef moduleDef = loadModule(moduleName, getLogger());
- compile(moduleDef);
- }
- }
-
- public ModuleSpaceHost createModuleSpaceHost(BrowserWidget widget,
- String moduleName) throws UnableToCompleteException {
- // TODO(jat): implement method createModuleSpaceHost
- return null;
- }
-
- public ModuleSpaceHost createModuleSpaceHost(TreeLogger mainLogger,
- String moduleName, String userAgent, String remoteSocket)
- throws UnableToCompleteException {
- logger = mainLogger;
- TreeLogger.Type maxLevel = TreeLogger.INFO;
- if (mainLogger instanceof AbstractTreeLogger) {
- maxLevel = ((AbstractTreeLogger) mainLogger).getMaxDetail();
- }
-
- ModulePanel tab;
- if (!isHeadless()) {
- tab = new ModulePanel(maxLevel, moduleName, userAgent, remoteSocket,
- tabs);
- logger = tab.getLogger();
-
- // Switch to a wait cursor.
- frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
- } else {
- tab = null;
- }
-
- try {
- // Try to find an existing loaded version of the module def.
- ModuleDef moduleDef = loadModule(moduleName, logger);
- assert (moduleDef != null);
-
- // Create a sandbox for the module.
- // TODO(jat): consider multiple instances of the same module open at
- // once
- TypeOracle typeOracle = moduleDef.getTypeOracle(logger);
- ShellModuleSpaceHost host = doCreateShellModuleSpaceHost(logger,
- typeOracle, moduleDef);
-
- if (tab != null) {
- moduleTabs.put(host, tab);
- }
- return host;
- } catch (RuntimeException e) {
- logger.log(TreeLogger.ERROR, "Exception initializing module", e);
- throw e;
- } finally {
- if (!isHeadless()) {
- frame.setCursor(Cursor.getDefaultCursor());
- }
- }
- }
-
- public TreeLogger getLogger() {
- return logger;
- }
-
- public String normalizeURL(String whatTheUserTyped) {
- return GWTShell.this.normalizeURL(whatTheUserTyped);
- }
-
- public BrowserWidget openNewBrowserWindow()
- throws UnableToCompleteException {
- // TODO(jat): is this ok?
- throw new UnableToCompleteException();
- }
-
- public void unloadModule(ModuleSpaceHost moduleSpaceHost) {
- ModulePanel tab = moduleTabs.remove(moduleSpaceHost);
- if (tab != null) {
- tab.disconnect();
- }
- }
-
- /**
- * Load a module.
- *
- * @param moduleName name of the module to load
- * @param logger TreeLogger to use
- * @return the loaded module
- * @throws UnableToCompleteException
- */
- private ModuleDef loadModule(String moduleName, TreeLogger logger)
- throws UnableToCompleteException {
- // TODO(jat): consider multithreading issues dealing with ModuleDefs
- boolean assumeFresh = !alreadySeenModules.contains(moduleName);
- ModuleDef moduleDef = ModuleDefLoader.loadFromClassPath(logger,
- moduleName, !assumeFresh);
- alreadySeenModules.add(moduleName);
- assert (moduleDef != null) : "Required module state is absent";
- return moduleDef;
- }
}
public static final String GWT_SHELL_PATH = ".gwt-tmp" + File.separator
+ "shell";
- private static final String PACKAGE_PATH = GWTShell.class.getPackage().getName().replace(
- '.', '/').concat("/shell/");
-
public static String checkHost(String hostUnderConsideration,
Set<String> hosts) {
hostUnderConsideration = hostUnderConsideration.toLowerCase();
@@ -394,8 +193,6 @@
}
}
- protected BrowserListener listener;
-
/**
* Hiding super field because it's actually the same object, just with a
* stronger type.
@@ -405,92 +202,11 @@
protected File outDir;
- /**
- * Cheat on the first load's refresh by assuming the module loaded by
- * {@link com.google.gwt.dev.shell.GWTShellServlet} is still fresh. This
- * prevents a double-refresh on startup. Subsequent refreshes will trigger a
- * real refresh.
- */
- private Set<String> alreadySeenModules = new HashSet<String>();
-
- private BrowserWidgetHostImpl browserHost = new BrowserWidgetHostImpl();
-
- private JFrame frame;
-
- private volatile boolean mainWindowClosed;
-
- private ShellMainWindow mainWnd;
-
- private JTabbedPane tabs;
-
- private AbstractTreeLogger topLogger;
-
- private WebServerPanel webServerLog;
-
- @Override
- public void closeAllBrowserWindows() {
- }
-
- @Override
- public TreeLogger getTopLogger() {
- return topLogger;
- }
-
- @Override
- public boolean hasBrowserWindowsOpen() {
- return false;
- }
-
public WebServerRestart hasWebServer() {
return WebServerRestart.NONE;
}
- /**
- * Launch the arguments as Urls in separate windows.
- */
@Override
- public void launchStartupUrls(final TreeLogger logger) {
- ensureOophmListener();
- String startupURL = "";
- try {
- for (String prenormalized : options.getStartupURLs()) {
- startupURL = normalizeURL(prenormalized);
- logger.log(TreeLogger.INFO, "Starting URL: " + startupURL, null);
- launchURL(startupURL);
- }
- } catch (UnableToCompleteException e) {
- logger.log(TreeLogger.ERROR,
- "Unable to open new window for startup URL: " + startupURL, null);
- }
- }
-
- public void launchURL(String url) throws UnableToCompleteException {
- /*
- * TODO(jat): properly support launching arbitrary browsers; waiting on
- * Freeland's work with BrowserScanner and the trunk merge to get it.
- */
- String separator;
- if (url.contains("?")) {
- separator = "&";
- } else {
- separator = "?";
- }
- url += separator + "gwt.hosted=" + listener.getEndpointIdentifier();
- TreeLogger branch = getTopLogger().branch(TreeLogger.INFO,
- "Launching firefox with " + url, null);
- try {
- Process browser = Runtime.getRuntime().exec("firefox " + url + "&");
- int exitCode = browser.waitFor();
- if (exitCode != 0) {
- branch.log(TreeLogger.ERROR, "Exit code " + exitCode, null);
- }
- } catch (IOException e) {
- branch.log(TreeLogger.ERROR, "Error starting browser", e);
- } catch (InterruptedException e) {
- branch.log(TreeLogger.ERROR, "Error starting browser", e);
- }
- }
-
public BrowserWidget openNewBrowserWindow() throws UnableToCompleteException {
throw new UnableToCompleteException();
}
@@ -544,14 +260,6 @@
};
}
- /**
- * Can be override to change the default log level in subclasses. JUnit does
- * this for example.
- */
- protected Type doGetDefaultLogLevel() {
- return Type.INFO;
- }
-
@Override
protected void doShutDownServer() {
// Stop the HTTP server.
@@ -560,100 +268,25 @@
}
@Override
- protected boolean doStartup() {
- if (super.doStartup()) {
- // Accept connections from OOPHM clients
- ensureOophmListener();
- return true;
- }
- return false;
- }
-
- @Override
protected int doStartUpServer() {
// TODO(bruce): make tomcat work in terms of the modular launcher
String whyFailed = EmbeddedTomcatServer.start(isHeadless() ? getTopLogger()
: webServerLog.getLogger(), getPort(), options);
- // TODO(bruce): test that we can remove this old approach in favor of
- // a better, logger-based error reporting
if (whyFailed != null) {
- System.err.println(whyFailed);
+ getTopLogger().log(TreeLogger.ERROR, "Starting Tomcat: " + whyFailed);
return -1;
}
return EmbeddedTomcatServer.getPort();
}
@Override
- protected void initializeLogger() {
- if (mainWnd != null) {
- topLogger = mainWnd.getLogger();
- } else {
- topLogger = new PrintWriterTreeLogger(new PrintWriter(System.out));
- }
- topLogger.setMaxDetail(options.getLogLevel());
+ protected ImageIcon getWebServerIcon() {
+ return loadImageIcon("tomcat24.png");
}
@Override
- protected boolean initModule(String moduleName) {
- /*
- * Not used in legacy mode due to GWTShellServlet playing this role.
- *
- * TODO: something smarter here and actually make GWTShellServlet less
- * magic?
- */
- return false;
- }
-
- @Override
- protected void loadRequiredNativeLibs() {
- // no native libraries are needed with OOPHM
- }
-
- @Override
- protected synchronized boolean notDone() {
- return !mainWindowClosed;
- }
-
- @Override
- protected void openAppWindow() {
- ImageIcon gwtIcon = loadImageIcon("icon24.png");
- frame = new JFrame("GWT Hosted Mode");
- tabs = new JTabbedPane();
- mainWnd = new ShellMainWindow(this, options.getLogLevel());
- tabs.addTab("Hosted Mode", gwtIcon, mainWnd, "GWT Hosted-mode");
- if (!options.isNoServer()) {
- ImageIcon tomcatIcon = loadImageIcon("tomcat24.png");
- webServerLog = new WebServerPanel(getPort(), options.getLogLevel());
- tabs.addTab("Tomcat", tomcatIcon, webServerLog);
- }
- frame.getContentPane().add(tabs);
- frame.setSize(950, 700);
- frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
- frame.addWindowListener(new WindowAdapter() {
- @Override
- public void windowClosed(WindowEvent e) {
- setMainWindowClosed();
- }
- });
- frame.setIconImage(loadImageIcon("icon16.png").getImage());
- frame.setVisible(true);
- }
-
- @Override
- protected void processEvents() throws Exception {
- Thread.sleep(10);
- }
-
- private void ensureOophmListener() {
- if (listener == null) {
- listener = new BrowserListener(getTopLogger(), options.getPortHosted(),
- new OophmSessionHandler(browserHost));
- listener.start();
- }
- }
-
- private synchronized void setMainWindowClosed() {
- mainWindowClosed = true;
+ protected String getWebServerName() {
+ return "Tomcat";
}
}
diff --git a/dev/oophm/overlay/com/google/gwt/dev/HostedMode.java b/dev/oophm/overlay/com/google/gwt/dev/HostedMode.java
new file mode 100644
index 0000000..6429e28
--- /dev/null
+++ b/dev/oophm/overlay/com/google/gwt/dev/HostedMode.java
@@ -0,0 +1,538 @@
+/*
+ * Copyright 2008 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;
+
+import com.google.gwt.core.ext.ServletContainer;
+import com.google.gwt.core.ext.ServletContainerLauncher;
+import com.google.gwt.core.ext.TreeLogger;
+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.Compiler.CompilerOptionsImpl;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.shell.ArtifactAcceptor;
+import com.google.gwt.dev.shell.jetty.JettyLauncher;
+import com.google.gwt.dev.util.InstalledHelpInfo;
+import com.google.gwt.dev.util.Util;
+import com.google.gwt.dev.util.arg.ArgHandlerExtraDir;
+import com.google.gwt.dev.util.arg.ArgHandlerLocalWorkers;
+import com.google.gwt.dev.util.arg.ArgHandlerModuleName;
+import com.google.gwt.dev.util.arg.ArgHandlerWarDir;
+import com.google.gwt.dev.util.arg.ArgHandlerWorkDirOptional;
+import com.google.gwt.util.tools.ArgHandlerString;
+import com.google.gwt.util.tools.Utility;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.BindException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.swing.ImageIcon;
+
+/**
+ * The main executable class for the hosted mode shell. NOTE: the public API for
+ * this class is to be determined. Consider this class as having <b>no</b>
+ * public API other than {@link #main(String[])}.
+ */
+public class HostedMode extends OophmHostedModeBase {
+
+ /**
+ * Handles the -server command line flag.
+ */
+ protected static class ArgHandlerServer extends ArgHandlerString {
+ private HostedModeOptions options;
+
+ public ArgHandlerServer(HostedModeOptions options) {
+ this.options = options;
+ }
+
+ @Override
+ public String[] getDefaultArgs() {
+ if (options.isNoServer()) {
+ return null;
+ } else {
+ return new String[] {getTag(), JettyLauncher.class.getName()};
+ }
+ }
+
+ @Override
+ public String getPurpose() {
+ return "Specify a different embedded web server to run (must implement ServletContainerLauncher)";
+ }
+
+ @Override
+ public String getTag() {
+ return "-server";
+ }
+
+ @Override
+ public String[] getTagArgs() {
+ return new String[] {"servletContainerLauncher"};
+ }
+
+ @Override
+ public boolean setString(String sclClassName) {
+ // Supercedes -noserver.
+ options.setNoServer(false);
+ Throwable t;
+ try {
+ Class<?> clazz = Class.forName(sclClassName, true,
+ Thread.currentThread().getContextClassLoader());
+ Class<? extends ServletContainerLauncher> sclClass = clazz.asSubclass(ServletContainerLauncher.class);
+ options.setServletContainerLauncher(sclClass.newInstance());
+ return true;
+ } catch (ClassCastException e) {
+ t = e;
+ } catch (ClassNotFoundException e) {
+ t = e;
+ } catch (InstantiationException e) {
+ t = e;
+ } catch (IllegalAccessException e) {
+ t = e;
+ }
+ System.err.println("Unable to load server class '" + sclClassName + "'");
+ t.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * Handles a startup url that can be passed on the command line.
+ */
+ protected static class ArgHandlerStartupURLs extends ArgHandlerString {
+ private final OptionStartupURLs options;
+
+ public ArgHandlerStartupURLs(OptionStartupURLs options) {
+ this.options = options;
+ }
+
+ @Override
+ public String getPurpose() {
+ return "Automatically launches the specified URL";
+ }
+
+ @Override
+ public String getTag() {
+ return "-startupUrl";
+ }
+
+ @Override
+ public String[] getTagArgs() {
+ return new String[] {"url"};
+ }
+
+ @Override
+ public boolean setString(String arg) {
+ options.addStartupURL(arg);
+ return true;
+ }
+ }
+
+ static class ArgProcessor extends OophmHostedModeBase.ArgProcessor {
+ public ArgProcessor(HostedModeOptions options) {
+ super(options, false);
+ registerHandler(new ArgHandlerServer(options));
+ registerHandler(new ArgHandlerStartupURLs(options));
+ registerHandler(new ArgHandlerWarDir(options));
+ registerHandler(new ArgHandlerExtraDir(options));
+ registerHandler(new ArgHandlerWorkDirOptional(options));
+ registerHandler(new ArgHandlerLocalWorkers(options));
+ registerHandler(new ArgHandlerModuleName(options) {
+ @Override
+ public String getPurpose() {
+ return super.getPurpose() + " to host";
+ }
+ });
+ }
+
+ @Override
+ protected String getName() {
+ return HostedMode.class.getName();
+ }
+ }
+
+ interface HostedModeOptions extends OophmHostedModeBaseOptions,
+ CompilerOptions {
+ ServletContainerLauncher getServletContainerLauncher();
+
+ void setServletContainerLauncher(ServletContainerLauncher scl);
+ }
+
+ /**
+ * Concrete class to implement all hosted mode options.
+ */
+ static class HostedModeOptionsImpl extends OophmHostedModeBaseOptionsImpl
+ implements HostedModeOptions {
+ private File extraDir;
+ private int localWorkers;
+ private File outDir;
+ private ServletContainerLauncher scl;
+ private File warDir;
+
+ public File getExtraDir() {
+ return extraDir;
+ }
+
+ public int getLocalWorkers() {
+ return localWorkers;
+ }
+
+ @Deprecated
+ public File getOutDir() {
+ return outDir;
+ }
+
+ public ServletContainerLauncher getServletContainerLauncher() {
+ return scl;
+ }
+
+ public File getShellBaseWorkDir(ModuleDef moduleDef) {
+ return new File(new File(getWorkDir(), moduleDef.getName()), "shell");
+ }
+
+ public File getShellPublicGenDir(ModuleDef moduleDef) {
+ return new File(getShellBaseWorkDir(moduleDef), "public");
+ }
+
+ public File getWarDir() {
+ return warDir;
+ }
+
+ public void setExtraDir(File extraDir) {
+ this.extraDir = extraDir;
+ }
+
+ public void setLocalWorkers(int localWorkers) {
+ this.localWorkers = localWorkers;
+ }
+
+ @Deprecated
+ public void setOutDir(File outDir) {
+ this.outDir = outDir;
+ }
+
+ public void setServletContainerLauncher(ServletContainerLauncher scl) {
+ this.scl = scl;
+ }
+
+ public void setWarDir(File warDir) {
+ this.warDir = warDir;
+ }
+ }
+
+ public static void main(String[] args) {
+ /*
+ * NOTE: main always exits with a call to System.exit to terminate any
+ * non-daemon threads that were started in Generators. Typically, this is to
+ * shutdown AWT related threads, since the contract for their termination is
+ * still implementation-dependent.
+ */
+ HostedMode hostedMode = new HostedMode();
+ if (new ArgProcessor(hostedMode.options).processArgs(args)) {
+ hostedMode.run();
+ // Exit w/ success code.
+ System.exit(0);
+ }
+ // Exit w/ non-success code.
+ System.exit(-1);
+ }
+
+ /**
+ * Hiding super field because it's actually the same object, just with a
+ * stronger type.
+ */
+ @SuppressWarnings("hiding")
+ protected final HostedModeOptionsImpl options = (HostedModeOptionsImpl) super.options;
+
+ /**
+ * Maps each active linker stack by module.
+ */
+ private final Map<String, StandardLinkerContext> linkerStacks = new HashMap<String, StandardLinkerContext>();
+
+ /**
+ * The set of specified modules by name; the keys represent the renamed name
+ * of each module rather than the canonical name.
+ */
+ private Map<String, ModuleDef> modulesByName = new HashMap<String, ModuleDef>();
+
+ /**
+ * The server that was started.
+ */
+ private ServletContainer server;
+
+ /**
+ * Tracks whether we created a temp workdir that we need to destroy.
+ */
+ private boolean tempWorkDir = false;
+
+ /**
+ * Default constructor for testing; no public API yet.
+ */
+ HostedMode() {
+ }
+
+ public WebServerRestart hasWebServer() {
+ return options.isNoServer() ? WebServerRestart.DISABLED
+ : WebServerRestart.ENABLED;
+ }
+
+ public void restartServer(TreeLogger logger) throws UnableToCompleteException {
+ server.refresh();
+ }
+
+ @Override
+ protected void compile(TreeLogger logger) throws UnableToCompleteException {
+ CompilerOptions newOptions = new CompilerOptionsImpl(options);
+ new Compiler(newOptions).run(logger);
+ }
+
+ @Deprecated
+ @Override
+ protected void compile(TreeLogger logger, ModuleDef moduleDef)
+ throws UnableToCompleteException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected HostedModeBaseOptions createOptions() {
+ return new HostedModeOptionsImpl();
+ }
+
+ @Override
+ protected ArtifactAcceptor doCreateArtifactAcceptor(final ModuleDef module) {
+ return new ArtifactAcceptor() {
+ public void accept(TreeLogger logger, ArtifactSet newlyGeneratedArtifacts)
+ throws UnableToCompleteException {
+ relink(logger, module, newlyGeneratedArtifacts);
+ }
+ };
+ }
+
+ @Override
+ protected void doShutDownServer() {
+ if (server != null) {
+ try {
+ server.stop();
+ } catch (UnableToCompleteException e) {
+ // Already logged.
+ }
+ server = null;
+ }
+
+ if (tempWorkDir) {
+ Util.recursiveDelete(options.getWorkDir(), false);
+ }
+ }
+
+ @Override
+ protected boolean doStartup() {
+ if (!super.doStartup()) {
+ return false;
+ }
+ tempWorkDir = options.getWorkDir() == null;
+ if (tempWorkDir) {
+ try {
+ options.setWorkDir(Utility.makeTemporaryDirectory(null, "gwtc"));
+ } catch (IOException e) {
+ System.err.println("Unable to create hosted mode work directory");
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ ServletValidator servletValidator = null;
+ File webXml = new File(options.getWarDir(), "WEB-INF/web.xml");
+ if (webXml.exists()) {
+ servletValidator = ServletValidator.create(getTopLogger(), webXml);
+ }
+
+ for (String moduleName : options.getModuleNames()) {
+ TreeLogger loadLogger = getTopLogger().branch(TreeLogger.DEBUG,
+ "Bootstrap link for command-line module '" + moduleName + "'");
+ try {
+ ModuleDef module = loadModule(loadLogger, moduleName, false);
+ validateServletTags(loadLogger, servletValidator, module, webXml);
+ link(loadLogger, module);
+ } catch (UnableToCompleteException e) {
+ // Already logged.
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ protected int doStartUpServer() {
+ try {
+ TreeLogger serverLogger = webServerLog.getLogger();
+ serverLogger.log(TreeLogger.INFO, "Starting HTTP on port " + getPort(),
+ null);
+ server = options.getServletContainerLauncher().start(serverLogger,
+ getPort(), options.getWarDir());
+ assert (server != null);
+ return server.getPort();
+ } catch (BindException e) {
+ System.err.println("Port "
+ + getPort()
+ + " is already is use; you probably still have another session active");
+ } catch (Exception e) {
+ System.err.println("Unable to start embedded HTTP server");
+ e.printStackTrace();
+ }
+ return -1;
+ }
+
+ @Override
+ protected String getHost() {
+ if (server != null) {
+ return server.getHost();
+ }
+ return super.getHost();
+ }
+
+ @Override
+ protected ImageIcon getWebServerIcon() {
+ return loadImageIcon(options.getServletContainerLauncher().getIconPath(),
+ false);
+ }
+
+ @Override
+ protected String getWebServerName() {
+ return options.getServletContainerLauncher().getName();
+ }
+
+ @Override
+ protected boolean initModule(String moduleName) {
+ ModuleDef module = modulesByName.get(moduleName);
+ if (module == null) {
+ getTopLogger().log(
+ TreeLogger.WARN,
+ "Unknown module requested '"
+ + moduleName
+ + "'; all active GWT modules must be specified in the command line arguments");
+ return false;
+ }
+ try {
+ TreeLogger logger = getTopLogger().branch(TreeLogger.DEBUG,
+ "Initializing module '" + module.getName() + "' for hosted mode");
+ boolean shouldRefreshPage = false;
+ if (module.isGwtXmlFileStale()) {
+ shouldRefreshPage = true;
+ module = loadModule(logger, module.getCanonicalName(), false);
+ }
+ link(logger, module);
+ return shouldRefreshPage;
+ } catch (UnableToCompleteException e) {
+ // Already logged.
+ return false;
+ }
+ }
+
+ /*
+ * Overridden to keep our map up to date.
+ */
+ @Override
+ protected ModuleDef loadModule(TreeLogger logger, String moduleName,
+ boolean refresh) throws UnableToCompleteException {
+ ModuleDef module = super.loadModule(logger, moduleName, refresh);
+ modulesByName.put(module.getName(), module);
+ return module;
+ }
+
+ /**
+ * Perform an initial hosted mode link, without overwriting newer or
+ * unmodified files in the output folder.
+ *
+ * @param logger the logger to use
+ * @param module the module to link
+ * @param includePublicFiles if <code>true</code>, include public files in
+ * the link, otherwise do not include them
+ * @throws UnableToCompleteException
+ */
+ private void link(TreeLogger logger, ModuleDef module)
+ throws UnableToCompleteException {
+ TreeLogger linkLogger = logger.branch(TreeLogger.DEBUG, "Linking module '"
+ + module.getName() + "'");
+
+ // TODO: move the module-specific computations to a helper function.
+ File moduleOutDir = new File(options.getWarDir(), module.getName());
+ File moduleExtraDir = (options.getExtraDir() == null) ? null : new File(
+ options.getExtraDir(), module.getName());
+
+ // Create a new active linker stack for the fresh link.
+ StandardLinkerContext linkerStack = new StandardLinkerContext(linkLogger,
+ module, options);
+ linkerStacks.put(module.getName(), linkerStack);
+
+ ArtifactSet artifacts = linkerStack.invokeLink(linkLogger);
+ linkerStack.produceOutputDirectory(linkLogger, artifacts, moduleOutDir,
+ moduleExtraDir);
+ }
+
+ /**
+ * Perform hosted mode relink when new artifacts are generated, without
+ * overwriting newer or unmodified files in the output folder.
+ *
+ * @param logger the logger to use
+ * @param module the module to link
+ * @param newlyGeneratedArtifacts the set of new artifacts
+ * @throws UnableToCompleteException
+ */
+ private void relink(TreeLogger logger, ModuleDef module,
+ ArtifactSet newlyGeneratedArtifacts) throws UnableToCompleteException {
+ TreeLogger linkLogger = logger.branch(TreeLogger.DEBUG,
+ "Relinking module '" + module.getName() + "'");
+
+ // TODO: move the module-specific computations to a helper function.
+ File moduleOutDir = new File(options.getWarDir(), module.getName());
+ File moduleExtraDir = (options.getExtraDir() == null) ? null : new File(
+ options.getExtraDir(), module.getName());
+
+ // Find the existing linker stack.
+ StandardLinkerContext linkerStack = linkerStacks.get(module.getName());
+ assert linkerStack != null;
+
+ ArtifactSet artifacts = linkerStack.invokeRelink(linkLogger,
+ newlyGeneratedArtifacts);
+ linkerStack.produceOutputDirectory(linkLogger, artifacts, moduleOutDir,
+ moduleExtraDir);
+ }
+
+ private void validateServletTags(TreeLogger logger,
+ ServletValidator servletValidator, ModuleDef module, File webXml) {
+ TreeLogger servletLogger = logger.branch(TreeLogger.DEBUG,
+ "Validating <servlet> tags for module '" + module.getName() + "'",
+ null, new InstalledHelpInfo("servletMappings.html"));
+ String[] servletPaths = module.getServletPaths();
+ if (servletValidator == null && servletPaths.length > 0) {
+ servletLogger.log(
+ TreeLogger.WARN,
+ "Module declares "
+ + servletPaths.length
+ + " <servlet> declaration(s), but a valid 'web.xml' was not found at '"
+ + webXml.getAbsolutePath() + "'");
+ } else {
+ for (String servletPath : servletPaths) {
+ String servletClass = module.findServletForPath(servletPath);
+ assert (servletClass != null);
+ // Prefix module name to convert module mapping to global mapping.
+ servletPath = "/" + module.getName() + servletPath;
+ servletValidator.validate(servletLogger, servletClass, servletPath);
+ }
+ }
+ }
+}
diff --git a/dev/oophm/overlay/com/google/gwt/dev/shell/BrowserWidgetHost.java b/dev/oophm/overlay/com/google/gwt/dev/shell/BrowserWidgetHost.java
index 703f90d..e69de29 100644
--- a/dev/oophm/overlay/com/google/gwt/dev/shell/BrowserWidgetHost.java
+++ b/dev/oophm/overlay/com/google/gwt/dev/shell/BrowserWidgetHost.java
@@ -1,39 +0,0 @@
-/*
- * Copyright 2006 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.UnableToCompleteException;
-import com.google.gwt.dev.cfg.ModuleDef;
-
-/**
- * Interface that unifies access to the <code>BrowserWidget</code>,
- * <code>ModuleSpaceHost</code>, and the compiler.
- */
-public interface BrowserWidgetHost {
- void compile(String[] modules) throws UnableToCompleteException;
-
- void compile(ModuleDef module) throws UnableToCompleteException;
-
- ModuleSpaceHost createModuleSpaceHost(TreeLogger logger, String moduleName, String userAgent,
- String remoteEndpoint) throws UnableToCompleteException;
-
- TreeLogger getLogger();
-
- String normalizeURL(String whatTheUserTyped);
-
- void unloadModule(ModuleSpaceHost moduleSpaceHost);
-}
diff --git a/dev/oophm/overlay/com/google/gwt/dev/shell/ShellMainWindow.java b/dev/oophm/overlay/com/google/gwt/dev/shell/ShellMainWindow.java
index 57ab665..97075fd 100644
--- a/dev/oophm/overlay/com/google/gwt/dev/shell/ShellMainWindow.java
+++ b/dev/oophm/overlay/com/google/gwt/dev/shell/ShellMainWindow.java
@@ -16,7 +16,6 @@
package com.google.gwt.dev.shell;
import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.dev.GWTShell;
import com.google.gwt.dev.util.log.AbstractTreeLogger;
import com.google.gwt.dev.util.log.SwingLoggerPanel;
@@ -34,7 +33,7 @@
private SwingLoggerPanel logWindow;
- public ShellMainWindow(GWTShell shell, TreeLogger.Type maxLevel) {
+ public ShellMainWindow(TreeLogger.Type maxLevel) {
super(new BorderLayout());
JPanel panel = new JPanel(new GridLayout(2, 1));
JPanel optionPanel = new JPanel();
diff --git a/dev/oophm/src/com/google/gwt/dev/OophmHostedModeBase.java b/dev/oophm/src/com/google/gwt/dev/OophmHostedModeBase.java
new file mode 100644
index 0000000..8c1d21e
--- /dev/null
+++ b/dev/oophm/src/com/google/gwt/dev/OophmHostedModeBase.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright 2008 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;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.WebServerPanel.RestartAction;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.shell.BrowserListener;
+import com.google.gwt.dev.shell.BrowserWidget;
+import com.google.gwt.dev.shell.BrowserWidgetHost;
+import com.google.gwt.dev.shell.ModuleSpaceHost;
+import com.google.gwt.dev.shell.OophmSessionHandler;
+import com.google.gwt.dev.shell.ShellMainWindow;
+import com.google.gwt.dev.shell.ShellModuleSpaceHost;
+import com.google.gwt.dev.util.log.AbstractTreeLogger;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+import com.google.gwt.util.tools.ArgHandlerString;
+
+import java.awt.Cursor;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+import javax.swing.ImageIcon;
+import javax.swing.JFrame;
+import javax.swing.JTabbedPane;
+import javax.swing.WindowConstants;
+
+/**
+ * The main executable class for hosted mode shells based on SWT.
+ */
+abstract class OophmHostedModeBase extends HostedModeBase {
+
+ /**
+ * Handles the -portHosted command line flag.
+ */
+ private static class ArgHandlerPortHosted extends ArgHandlerString {
+
+ private final OptionPortHosted options;
+
+ public ArgHandlerPortHosted(OptionPortHosted options) {
+ this.options = options;
+ }
+
+ @Override
+ public String[] getDefaultArgs() {
+ return new String[] {"-portHosted", "9997"};
+ }
+
+ @Override
+ public String getPurpose() {
+ return "Listens on the specified port for hosted mode connections";
+ }
+
+ @Override
+ public String getTag() {
+ return "-portHosted";
+ }
+
+ @Override
+ public String[] getTagArgs() {
+ return new String[] {"port-number | \"auto\""};
+ }
+
+ @Override
+ public boolean setString(String value) {
+ if (value.equals("auto")) {
+ options.setPortHosted(0);
+ } else {
+ try {
+ options.setPortHosted(Integer.parseInt(value));
+ } catch (NumberFormatException e) {
+ System.err.println("A port must be an integer or \"auto\"");
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ abstract static class ArgProcessor extends HostedModeBase.ArgProcessor {
+ public ArgProcessor(OophmHostedModeBaseOptions options, boolean forceServer) {
+ super(options, forceServer);
+ registerHandler(new ArgHandlerPortHosted(options));
+ }
+ }
+
+ interface OophmHostedModeBaseOptions extends HostedModeBaseOptions,
+ OptionPortHosted {
+ }
+
+ /**
+ * Concrete class to implement all shell options.
+ */
+ static class OophmHostedModeBaseOptionsImpl extends HostedModeBaseOptionsImpl
+ implements OophmHostedModeBaseOptions {
+ private int portHosted;
+
+ public int getPortHosted() {
+ return portHosted;
+ }
+
+ public void setPortHosted(int port) {
+ portHosted = port;
+ }
+ }
+
+ interface OptionPortHosted {
+ int getPortHosted();
+
+ void setPortHosted(int portHosted);
+ }
+
+ private class OophmBrowserWidgetHostImpl extends BrowserWidgetHostImpl {
+ private final Map<ModuleSpaceHost, ModulePanel> moduleTabs = new IdentityHashMap<ModuleSpaceHost, ModulePanel>();
+
+ public ModuleSpaceHost createModuleSpaceHost(TreeLogger logger,
+ BrowserWidget widget, String moduleName)
+ throws UnableToCompleteException {
+ throw new UnsupportedOperationException();
+ }
+
+ public ModuleSpaceHost createModuleSpaceHost(TreeLogger mainLogger,
+ String moduleName, String userAgent, String remoteSocket)
+ throws UnableToCompleteException {
+ TreeLogger logger = mainLogger;
+ TreeLogger.Type maxLevel = TreeLogger.INFO;
+ if (mainLogger instanceof AbstractTreeLogger) {
+ maxLevel = ((AbstractTreeLogger) mainLogger).getMaxDetail();
+ }
+
+ ModulePanel tab;
+ if (!isHeadless()) {
+ tab = new ModulePanel(maxLevel, moduleName, userAgent, remoteSocket,
+ tabs);
+ logger = tab.getLogger();
+
+ // Switch to a wait cursor.
+ frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ } else {
+ tab = null;
+ }
+
+ try {
+ // Try to find an existing loaded version of the module def.
+ ModuleDef moduleDef = loadModule(logger, moduleName, true);
+ assert (moduleDef != null);
+
+ // Create a sandbox for the module.
+ // TODO(jat): consider multiple instances of the same module open at
+ // once
+ TypeOracle typeOracle = moduleDef.getTypeOracle(logger);
+ ShellModuleSpaceHost host = doCreateShellModuleSpaceHost(logger,
+ typeOracle, moduleDef);
+
+ if (tab != null) {
+ moduleTabs.put(host, tab);
+ }
+ return host;
+ } catch (RuntimeException e) {
+ logger.log(TreeLogger.ERROR, "Exception initializing module", e);
+ throw e;
+ } finally {
+ if (!isHeadless()) {
+ frame.setCursor(Cursor.getDefaultCursor());
+ }
+ }
+ }
+
+ public void unloadModule(ModuleSpaceHost moduleSpaceHost) {
+ ModulePanel tab = moduleTabs.remove(moduleSpaceHost);
+ if (tab != null) {
+ tab.disconnect();
+ }
+ }
+ }
+
+ protected static final String PACKAGE_PATH = OophmHostedModeBase.class.getPackage().getName().replace(
+ '.', '/').concat("/shell/");
+
+ /**
+ * Loads an image from the classpath in this package.
+ */
+ static ImageIcon loadImageIcon(String name) {
+ return loadImageIcon(name, true);
+ }
+
+ /**
+ * Loads an image from the classpath, optionally prepending this package.
+ *
+ * @param name name of an image file.
+ * @param prependPackage true if {@link #PACKAGE_PATH} should be prepended to
+ * this name.
+ */
+ static ImageIcon loadImageIcon(String name, boolean prependPackage) {
+ ClassLoader cl = OophmHostedModeBase.class.getClassLoader();
+ if (prependPackage) {
+ name = PACKAGE_PATH + name;
+ }
+ URL url = cl.getResource(name);
+ if (url != null) {
+ ImageIcon image = new ImageIcon(url);
+ return image;
+ } else {
+ // Bad image.
+ return new ImageIcon();
+ }
+ }
+
+ protected BrowserListener listener;
+
+ /**
+ * Hiding super field because it's actually the same object, just with a
+ * stronger type.
+ */
+ @SuppressWarnings("hiding")
+ protected final OophmHostedModeBaseOptionsImpl options = (OophmHostedModeBaseOptionsImpl) super.options;
+
+ // TODO(jat): clean up access to this field
+ protected WebServerPanel webServerLog;
+
+ private BrowserWidgetHostImpl browserHost = new OophmBrowserWidgetHostImpl();
+
+ private JFrame frame;
+
+ private volatile boolean mainWindowClosed;
+
+ private ShellMainWindow mainWnd;
+
+ private JTabbedPane tabs;
+
+ private AbstractTreeLogger topLogger;
+
+ public OophmHostedModeBase() {
+ super();
+ }
+
+ @Override
+ public void closeAllBrowserWindows() {
+ }
+
+ @Override
+ public TreeLogger getTopLogger() {
+ return topLogger;
+ }
+
+ @Override
+ public boolean hasBrowserWindowsOpen() {
+ return false;
+ }
+
+ /**
+ * Launch the arguments as Urls in separate windows.
+ */
+ @Override
+ public void launchStartupUrls(final TreeLogger logger) {
+ ensureOophmListener();
+ String startupURL = "";
+ try {
+ for (String prenormalized : options.getStartupURLs()) {
+ startupURL = normalizeURL(prenormalized);
+ logger.log(TreeLogger.INFO, "Starting URL: " + startupURL, null);
+ launchURL(startupURL);
+ }
+ } catch (UnableToCompleteException e) {
+ logger.log(TreeLogger.ERROR,
+ "Unable to open new window for startup URL: " + startupURL, null);
+ }
+ }
+
+ public void launchURL(String url) throws UnableToCompleteException {
+ /*
+ * TODO(jat): properly support launching arbitrary browsers; waiting on
+ * Freeland's work with BrowserScanner and the trunk merge to get it.
+ */
+ String separator;
+ if (url.contains("?")) {
+ separator = "&";
+ } else {
+ separator = "?";
+ }
+ url += separator + "gwt.hosted=" + listener.getEndpointIdentifier();
+ TreeLogger branch = getTopLogger().branch(TreeLogger.INFO,
+ "Launching firefox with " + url, null);
+ try {
+ Process browser = Runtime.getRuntime().exec("firefox " + url + "&");
+ int exitCode = browser.waitFor();
+ if (exitCode != 0) {
+ branch.log(TreeLogger.ERROR, "Exit code " + exitCode, null);
+ }
+ } catch (IOException e) {
+ branch.log(TreeLogger.ERROR, "Error starting browser", e);
+ } catch (InterruptedException e) {
+ branch.log(TreeLogger.ERROR, "Error starting browser", e);
+ }
+ }
+
+ public BrowserWidget openNewBrowserWindow() throws UnableToCompleteException {
+ throw new UnableToCompleteException();
+ }
+
+ /**
+ * @throws UnableToCompleteException
+ */
+ @Override
+ protected void compile(TreeLogger logger) throws UnableToCompleteException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected boolean doStartup() {
+ if (super.doStartup()) {
+ // Accept connections from OOPHM clients
+ ensureOophmListener();
+ return true;
+ }
+ return false;
+ }
+
+ protected final BrowserWidgetHost getBrowserHost() {
+ return browserHost;
+ }
+
+ /**
+ * @return the icon to use for the web server tab
+ */
+ protected ImageIcon getWebServerIcon() {
+ return null;
+ }
+
+ /**
+ * @return the name of the web server tab
+ */
+ protected String getWebServerName() {
+ return "Server";
+ }
+
+ @Override
+ protected void initializeLogger() {
+ if (mainWnd != null) {
+ topLogger = mainWnd.getLogger();
+ } else {
+ topLogger = new PrintWriterTreeLogger(new PrintWriter(System.out));
+ }
+ topLogger.setMaxDetail(options.getLogLevel());
+ }
+
+ @Override
+ protected boolean initModule(String moduleName) {
+ /*
+ * Not used in legacy mode due to GWTShellServlet playing this role.
+ *
+ * TODO: something smarter here and actually make GWTShellServlet less
+ * magic?
+ */
+ return false;
+ }
+
+ @Override
+ protected void loadRequiredNativeLibs() {
+ // no native libraries are needed with OOPHM
+ }
+
+ @Override
+ protected synchronized boolean notDone() {
+ return !mainWindowClosed;
+ }
+
+ @Override
+ protected void openAppWindow() {
+ ImageIcon gwtIcon = loadImageIcon("icon24.png");
+ frame = new JFrame("GWT Hosted Mode");
+ tabs = new JTabbedPane();
+ mainWnd = new ShellMainWindow(options.getLogLevel());
+ tabs.addTab("Hosted Mode", gwtIcon, mainWnd, "GWT Hosted-mode");
+ if (!options.isNoServer()) {
+ webServerLog = new WebServerPanel(getPort(), options.getLogLevel(),
+ new RestartAction() {
+ public void restartServer(TreeLogger logger) {
+ try {
+ OophmHostedModeBase.this.restartServer(logger);
+ } catch (UnableToCompleteException e) {
+ // Already logged why it failed
+ }
+ }
+ });
+ tabs.addTab(getWebServerName(), getWebServerIcon(), webServerLog);
+ }
+ frame.getContentPane().add(tabs);
+ frame.setSize(950, 700);
+ frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ frame.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosed(WindowEvent e) {
+ setMainWindowClosed();
+ }
+ });
+ frame.setIconImage(loadImageIcon("icon16.png").getImage());
+ frame.setVisible(true);
+ }
+
+ @Override
+ protected void processEvents() throws Exception {
+ Thread.sleep(10);
+ }
+
+ protected synchronized void setMainWindowClosed() {
+ mainWindowClosed = true;
+ }
+
+ private void ensureOophmListener() {
+ if (listener == null) {
+ listener = new BrowserListener(getTopLogger(), options.getPortHosted(),
+ new OophmSessionHandler(browserHost));
+ listener.start();
+ }
+ }
+}
diff --git a/dev/oophm/src/com/google/gwt/dev/WebServerPanel.java b/dev/oophm/src/com/google/gwt/dev/WebServerPanel.java
index 0b3ec1e..3536603 100644
--- a/dev/oophm/src/com/google/gwt/dev/WebServerPanel.java
+++ b/dev/oophm/src/com/google/gwt/dev/WebServerPanel.java
@@ -19,17 +19,44 @@
import com.google.gwt.dev.util.log.SwingLoggerPanel;
import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.JButton;
import javax.swing.JPanel;
/**
*/
public class WebServerPanel extends JPanel {
+
+ /**
+ * Callback interface for when the server should be restarted.
+ */
+ public interface RestartAction {
+ void restartServer(TreeLogger logger);
+ }
+
private SwingLoggerPanel logWindow;
public WebServerPanel(int serverPort, TreeLogger.Type maxLevel) {
+ this(serverPort, maxLevel, null);
+ }
+
+ public WebServerPanel(int serverPort, TreeLogger.Type maxLevel,
+ final RestartAction restartServerAction) {
super(new BorderLayout());
logWindow = new SwingLoggerPanel(maxLevel);
+ if (restartServerAction != null) {
+ JPanel panel = new JPanel();
+ JButton restartButton = new JButton("Restart Server");
+ restartButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ restartServerAction.restartServer(getLogger());
+ }
+ });
+ panel.add(restartButton);
+ add(panel, BorderLayout.NORTH);
+ }
add(logWindow);
}
diff --git a/dev/oophm/src/com/google/gwt/dev/shell/BrowserListener.java b/dev/oophm/src/com/google/gwt/dev/shell/BrowserListener.java
index b06c46a..66d9421 100644
--- a/dev/oophm/src/com/google/gwt/dev/shell/BrowserListener.java
+++ b/dev/oophm/src/com/google/gwt/dev/shell/BrowserListener.java
@@ -16,6 +16,7 @@
package com.google.gwt.dev.shell;
import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.shell.BrowserChannel.SessionHandler;
import java.io.IOException;
@@ -88,7 +89,11 @@
}
}
- public String getEndpointIdentifier() {
+ public String getEndpointIdentifier() throws UnableToCompleteException {
+ if (listenSocket == null) {
+ // If we failed to initialize our socket, just bail here.
+ throw new UnableToCompleteException();
+ }
try {
return InetAddress.getLocalHost().getHostAddress() + ":"
+ listenSocket.getLocalPort();
diff --git a/user/src/com/google/gwt/user/tools/WebAppCreator.java b/user/src/com/google/gwt/user/tools/WebAppCreator.java
index 280372e..8896d97 100644
--- a/user/src/com/google/gwt/user/tools/WebAppCreator.java
+++ b/user/src/com/google/gwt/user/tools/WebAppCreator.java
@@ -215,6 +215,7 @@
String gwtUserPath = installPath + '/' + "gwt-user.jar";
String gwtDevPath = installPath + '/' + Utility.getDevJarName();
String gwtServletPath = installPath + '/' + "gwt-servlet.jar";
+ String gwtOophmPath = installPath + '/' + "gwt-dev-oophm.jar";
// Public builds generate a DTD reference.
String gwtModuleDtd = "";
@@ -254,6 +255,7 @@
replacements.put("@gwtSdk", installPath);
replacements.put("@gwtUserPath", gwtUserPath);
replacements.put("@gwtDevPath", gwtDevPath);
+ replacements.put("@gwtOophmPath", gwtOophmPath);
replacements.put("@gwtVersion", About.GWT_VERSION_NUM);
replacements.put("@gwtModuleDtd", gwtModuleDtd);
replacements.put("@shellClass", HostedMode.class.getName());
diff --git a/user/src/com/google/gwt/user/tools/project.ant.xmlsrc b/user/src/com/google/gwt/user/tools/project.ant.xmlsrc
index 8b3ef42..a98d4ea 100644
--- a/user/src/com/google/gwt/user/tools/project.ant.xmlsrc
+++ b/user/src/com/google/gwt/user/tools/project.ant.xmlsrc
@@ -55,6 +55,21 @@
<!-- Additional arguments like -style PRETTY or -logLevel DEBUG -->
<arg value="@moduleName"/>
</java>
+ </target>
+
+ <target name="oophm" depends="javac" description="Run OOPHNM hosted mode">
+ <java failonerror="true" fork="true" classname="@shellClass">
+ <classpath>
+ <pathelement location="${gwt.sdk}/gwt-dev-oophm.jar"/>
+ <pathelement location="src"/>
+ <path refid="project.class.path"/>
+ </classpath>
+ <jvmarg value="-Xmx256M"/>@antVmargs
+ <arg value="-startupUrl"/>
+ <arg value="@startupUrl"/>
+ <!-- Additional arguments like -style PRETTY or -logLevel DEBUG -->
+ <arg value="@moduleName"/>
+ </java>
</target>@antEclipseRule
<target name="build" depends="gwtc" description="Build this project" />