Revision: 6285 Author: [email protected] Date: Fri Oct 2 08:37:20 2009 Log: Checkpoint functional UI refactor. Todo:
- something funky in BatchingStrategy, maybe a merge issue - JUnitShell needs work - need to test GWTShell - event handling needs to be improved - figure out how to pass icons to UI implementation http://code.google.com/p/google-web-toolkit/source/detail?r=6285 Deleted: /changes/jat/abstractui/dev/oophm/src/com/google/gwt/dev/OophmHostedModeBase.java Modified: /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/HostedModeBase.java /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/SwingUI.java /changes/jat/abstractui/dev/oophm/overlay/com/google/gwt/dev/GWTShell.java /changes/jat/abstractui/dev/oophm/overlay/com/google/gwt/dev/HostedMode.java /changes/jat/abstractui/dev/oophm/src/com/google/gwt/dev/ModuleTabPanel.java /changes/jat/abstractui/user/src/com/google/gwt/junit/BatchingStrategy.java /changes/jat/abstractui/user/src/com/google/gwt/junit/JUnitShell.java ======================================= --- /changes/jat/abstractui/dev/oophm/src/com/google/gwt/dev/OophmHostedModeBase.java Thu Oct 1 17:01:51 2009 +++ /dev/null @@ -1,259 +0,0 @@ -/* - * 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.cfg.ModuleDef; -import com.google.gwt.dev.shell.BrowserListener; -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.BrowserInfo; -import com.google.gwt.dev.util.collect.HashMap; -import com.google.gwt.dev.util.log.AbstractTreeLogger; -import com.google.gwt.util.tools.ArgHandlerString; - -import java.awt.Cursor; -import java.io.File; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.IdentityHashMap; -import java.util.Map; -import java.util.Random; - -import javax.swing.ImageIcon; -import javax.swing.JFrame; -import javax.swing.JTabbedPane; - -/** - * Base class for OOPHM hosted mode shells. - */ -abstract class OophmHostedModeBase extends HostedModeBase { - - /** - * Interface to group activities related to adding and deleting tabs. - */ - public interface TabPanelCollection { - - /** - * Add a new tab containing a ModuleTabPanel. - * - * @param tabPanel - * @param icon - * @param title - * @param tooltip - */ - void addTab(ModuleTabPanel tabPanel, ImageIcon icon, String title, - String tooltip); - - /** - * Remove the tab containing a ModuleTabpanel. - * - * @param tabPanel - */ - void removeTab(ModuleTabPanel tabPanel); - } - - - private class OophmBrowserWidgetHostImpl extends BrowserWidgetHostImpl { - private final Map<ModuleSpaceHost, ModulePanel> moduleTabs = new IdentityHashMap<ModuleSpaceHost, ModulePanel>(); - private final Map<DevelModeTabKey, ModuleTabPanel> tabPanels = new HashMap<DevelModeTabKey, ModuleTabPanel>(); - - public ModuleSpaceHost createModuleSpaceHost(TreeLogger mainLogger, - String moduleName, String userAgent, String url, String tabKey, - String sessionKey, String remoteSocket) - throws UnableToCompleteException { - if (sessionKey == null) { - // if we don't have a unique session key, make one up - sessionKey = randomString(); - } - TreeLogger logger = mainLogger; - TreeLogger.Type maxLevel = TreeLogger.INFO; - if (mainLogger instanceof AbstractTreeLogger) { - maxLevel = ((AbstractTreeLogger) mainLogger).getMaxDetail(); - } - ModuleTabPanel tabPanel = null; - ModulePanel tab = null; - if (!isHeadless()) { - tabPanel = findModuleTab(userAgent, remoteSocket, url, tabKey, - moduleName); - String agentTag = BrowserInfo.getShortName(userAgent).toLowerCase(); - tab = tabPanel.addModuleSession(maxLevel, moduleName, sessionKey, - options.getLogFile(String.format("%s-%s-%d.log", moduleName, - agentTag, getNextSessionCounter(options.getLogDir())))); - logger = tab.getLogger(); - TreeLogger branch = logger.branch(TreeLogger.INFO, "Loading module " - + moduleName); - if (url != null) { - branch.log(TreeLogger.INFO, "Top URL: " + url); - } - branch.log(TreeLogger.INFO, "User agent: " + userAgent); - branch.log(TreeLogger.TRACE, "Remote socket: " + remoteSocket); - if (tabKey != null) { - branch.log(TreeLogger.DEBUG, "Tab key: " + tabKey); - } - if (sessionKey != null) { - branch.log(TreeLogger.DEBUG, "Session key: " + sessionKey); - } - - // Switch to a wait cursor. - frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - } - - 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) { - Disconnectable tab = moduleTabs.remove(moduleSpaceHost); - if (tab != null) { - tab.disconnect(); - } - } - - private BrowserWidgetHostImpl browserHost = new OophmBrowserWidgetHostImpl(); - - private JFrame frame; - - private volatile boolean mainWindowClosed; - - private ShellMainWindow mainWnd; - - private JTabbedPane tabs; - - private AbstractTreeLogger topLogger; - - /** - * 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. - */ - try { - URL parsedUrl = new URL(url); - String path = parsedUrl.getPath(); - String query = parsedUrl.getQuery(); - String hash = parsedUrl.getRef(); - String hostedParam = "gwt.hosted=" + listener.getEndpointIdentifier(); - if (query == null) { - query = hostedParam; - } else { - query += '&' + hostedParam; - } - path += '?' + query; - if (hash != null) { - path += '#' + hash; - } - url = new URL(parsedUrl.getProtocol(), parsedUrl.getHost(), - parsedUrl.getPort(), path).toExternalForm(); - } catch (MalformedURLException e) { - getTopLogger().log(TreeLogger.ERROR, "Invalid URL " + url, e); - throw new UnableToCompleteException(); - } - System.err.println( - "Using a browser with the GWT Development Plugin, please browse to"); - System.err.println("the following URL:"); - System.err.println(" " + url); - getTopLogger().log(TreeLogger.INFO, - "Waiting for browser connection to " + url, null); - } - - /** - * @throws UnableToCompleteException - */ - @Override - protected void compile(TreeLogger logger) throws UnableToCompleteException { - throw new UnsupportedOperationException(); - } - - protected final BrowserWidgetHost getBrowserHost() { - return browserHost; - } - - @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; - } - - protected synchronized void setMainWindowClosed() { - mainWindowClosed = true; - } - - private void ensureOophmListener() { - if (listener == null) { - codeServerPort = options.getPortHosted(); - listener = new BrowserListener(getTopLogger(), codeServerPort, - new OophmSessionHandler(browserHost)); - listener.start(); - try { - // save the port we actually used if it was auto - codeServerPort = listener.getSocketPort(); - } catch (UnableToCompleteException e) { - // ignore errors listening, we will catch them later - } - } - } -} ======================================= --- /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/HostedModeBase.java Thu Oct 1 17:01:51 2009 +++ /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/HostedModeBase.java Fri Oct 2 08:37:20 2009 @@ -19,6 +19,7 @@ import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.TreeLogger.Type; import com.google.gwt.core.ext.typeinfo.TypeOracle; +import com.google.gwt.dev.HostedModeBase.DevelopmentModeUI.Callback; import com.google.gwt.dev.HostedModeBase.DevelopmentModeUI.ModuleHandle; import com.google.gwt.dev.Precompile.PrecompileOptionsImpl; import com.google.gwt.dev.cfg.ModuleDef; @@ -50,6 +51,8 @@ import java.io.File; import java.io.PrintWriter; +import java.net.MalformedURLException; +import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -59,22 +62,147 @@ import java.util.Map; import java.util.Random; import java.util.Set; -import java.util.concurrent.Semaphore; /** * The main executable class for the hosted mode shell. This class must not have * any GUI dependencies. - * - * TODO: remove BrowserWidget references (which reference SWT via inheritance, - * though it doesn't appear to cause any harm currently. */ abstract class HostedModeBase { - private final Map<ModuleSpaceHost, ModuleHandle> loadedModules = new IdentityHashMap<ModuleSpaceHost, ModuleHandle>(); - - public class UiBrowserWidgetHostImpl extends BrowserWidgetHostImpl { - - private ModuleHandle module; + /** + * Defines the interaction between DevelopmentMode and the UI, so that + * alternate UIs can be implemented. + */ + public abstract static class DevelopmentModeUI { + + /** + * Callback interface for events from the UI. + */ + public interface Callback { + + /** + * UI-initiated callback for some event + * + * @param event event name (TODO: make this a type-safe event in some + * way, need to consider alternate UI implementations though) + * @param callbackData arbitrary data, defined for the event type + */ + public void callback(String event, Object callbackData); + } + + /** + * Opaque handle to a module - it is returned from loadModule and can + * only be passed to unloadModule, and the client can get a logger for + * messages about that module. + */ + public interface ModuleHandle { + + /** + * @return the logger for this module. + */ + TreeLogger getLogger(); + } + + // TODO: typesafe callbacks rather than strings + + /** + * Event used to indicate the UI is ready to exit. + * + * <p>Argument is ignored. + */ + public static final String DONE = "done"; + + /** + * Event used to request a restart of the server. + * + * <p>Argument is a TreeLogger instance. Registering a callback for this + * event instructs the UI to provide some control for restarting the server. + */ + public static final String RESTART_SERVER = "restart-server"; + + private final Map<String, Callback> callbacks = new HashMap<String, Callback>(); + + public abstract TreeLogger getTopLogger(TreeLogger.Type logLevel); + + /** + * Create the web server portion of the UI if not already created, and + * return its TreeLogger instance. + * + * @param serverName short name of the web server or null if only the icon + * should be used + * @param iconName name of the icon as a classpath resource + * @return TreeLogger instance + */ + public abstract TreeLogger getWebServerLogger(String serverName, + String iconName); + + /** + * Initialize the UI. + */ + public abstract void initialize(); + + /** + * Load a module + * + * @param userAgent full user agent name + * @param remoteSocket name of remote socket endpoint in host:port format + * @param url URL of top-level window + * @param tabKey stable browser tab identifier, or the empty string if no + * such identifier is available + * @param moduleName the name of the module loaded + * @param sessionKey a unique session key + * @param agentTag short-form user agent identifier, suitable for use in + * a label for this connection + * @param logLevel logging detail requested + * @return a handle to the module + */ + public abstract ModuleHandle loadModule(String userAgent, + String remoteSocket, String url, String tabKey, String moduleName, + String sessionKey, String agentTag, Type logLevel); + + /** + * Set a callback for the UI to report events. + * + * @param event + * @param callback + */ + public void setCallback(String event, Callback callback) { + callbacks.put(event, callback); + } + + /** + * Unload a previously loaded module. + * + * @param module ModuleHandle instance returned from loadModule on this UI + * instance + */ + public abstract void unloadModule(ModuleHandle module); + + /** + * Call callbacks for a given event. + * + * @param event + * @param callbackData arbitrary object, depending on the event type + */ + protected void callback(String event, Object callbackData) { + Callback callback = callbacks.get(event); + if (callback != null) { + callback(event, callbackData); + } + } + + /** + * Returns true if a callback has been registered for an event. + * + * @param event + * @return true if a callback has been registered for event + */ + protected boolean hasCallback(String event) { + return callbacks.get(event) != null; + } + } + + public class UiBrowserWidgetHostImpl extends BrowserWidgetHostImpl { public ModuleSpaceHost createModuleSpaceHost(TreeLogger mainLogger, String moduleName, String userAgent, String url, String tabKey, @@ -87,8 +215,8 @@ TreeLogger logger = mainLogger; TreeLogger.Type maxLevel = options.getLogLevel(); String agentTag = BrowserInfo.getShortName(userAgent).toLowerCase(); - module = ui.loadModule(userAgent, remoteSocket, url, tabKey, moduleName, - sessionKey, agentTag, maxLevel); + ModuleHandle module = ui.loadModule(userAgent, remoteSocket, url, tabKey, + moduleName, sessionKey, agentTag, maxLevel); logger = module.getLogger(); try { // Try to find an existing loaded version of the module def. @@ -117,75 +245,8 @@ ui.unloadModule(module); } } - } - private static final Random RNG = new Random(); - - /** - * Produce a random string that has low probability of collisions. - * - * <p>In this case, we use 16 characters, each drawn from a pool of 94, - * so the number of possible values is 94^16, leading to an expected number - * of values used before a collision occurs as sqrt(pi/2) * 94^8 (treated the - * same as a birthday attack), or a little under 10^16. - * - * <p>This algorithm is also implemented in hosted.html, though it is not - * technically important that they match. - * - * @return a random string - */ - protected static String randomString() { - StringBuilder buf = new StringBuilder(16); - for (int i = 0; i < 16; ++i) { - buf.append((char) RNG.nextInt('~' - '!' + 1) + '!'); - } - return buf.toString(); - } - - public abstract static class DevelopmentModeUI { - - public static final String DONE = "done"; - public static final String RESTART_SERVER = "restart-server"; - - public interface ModuleHandle { - - TreeLogger getLogger(); - } - - public interface Callback { - // TODO: typesafe callbacks - public void callback(String event, Object callbackData); - } - - private final Map<String, Callback> callbacks = new HashMap<String, Callback>(); - - public abstract void initialize(); - - public void setCallback(String event, Callback callback) { - callbacks.put(event, callback); - } - - protected void callback(String event, Object callbackData) { - Callback callback = callbacks.get(event); - if (callback != null) { - callback(event, callbackData); - } - } - - public abstract TreeLogger getTopLogger(TreeLogger.Type logLevel); - - // TODO: How to pass icon? - public abstract TreeLogger getWebServerLogger(String serverName, - String iconName); - - public abstract ModuleHandle loadModule(String userAgent, - String remoteSocket, String url, String tabKey, String moduleName, - String sessionKey, String agentTag, Type logLevel); - - public abstract void unloadModule(ModuleHandle module); - } - /** * Handles the -blacklist command line argument. */ @@ -241,7 +302,7 @@ return true; } } - + /** * Handles the -noserver command line flag. */ @@ -315,6 +376,53 @@ return true; } } + + /** + * 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; + } + } /** * Handles the -whitelist command line flag. @@ -379,60 +487,6 @@ return HostedModeBase.this.normalizeURL(whatTheUserTyped); } } - - - /** - * 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; - } - } - - protected interface OptionPortHosted { - int getPortHosted(); - - void setPortHosted(int portHosted); - } protected interface HostedModeBaseOptions extends JJSOptions, OptionLogDir, OptionLogLevel, OptionGenDir, OptionNoServer, OptionPort, @@ -440,6 +494,9 @@ /** * The base shell work directory. + * + * @param moduleDef + * @return working directory base */ File getShellBaseWorkDir(ModuleDef moduleDef); } @@ -453,16 +510,9 @@ private boolean isNoServer; private File logDir; private int port; - private final List<String> startupURLs = new ArrayList<String>(); private int portHosted; - - public int getPortHosted() { - return portHosted; - } - - public void setPortHosted(int port) { - portHosted = port; - } + private final List<String> startupURLs = new ArrayList<String>(); + public void addStartupURL(String url) { startupURLs.add(url); } @@ -474,6 +524,7 @@ public File getLogDir() { return logDir; } + public File getLogFile(String sublog) { if (logDir == null) { return null; @@ -484,6 +535,10 @@ public int getPort() { return port; } + + public int getPortHosted() { + return portHosted; + } public File getShellBaseWorkDir(ModuleDef moduleDef) { return new File(new File(getWorkDir(), moduleDef.getName()), "shell"); @@ -508,6 +563,10 @@ public void setPort(int port) { this.port = port; } + + public void setPortHosted(int port) { + portHosted = port; + } } /** @@ -523,6 +582,7 @@ void setLogFile(String filename); } + /** * Controls whether to run a server or not. @@ -533,7 +593,7 @@ void setNoServer(boolean isNoServer); } - + /** * Controls what port to use. * @@ -543,6 +603,12 @@ void setPort(int port); } + + protected interface OptionPortHosted { + int getPortHosted(); + + void setPortHosted(int portHosted); + } /** * Controls the startup URLs. @@ -571,8 +637,12 @@ registerHandler(new ArgHandlerDisableCastChecking(options)); registerHandler(new ArgHandlerDraftCompile(options)); registerHandler(new ArgHandlerPortHosted(options)); + // TODO(rdayal): implement + // registerHandler(new ArgHandlerRemoteUI(options)); } } + + private static final Random RNG = new Random(); public static String normalizeURL(String unknownUrlText, int port, String host) { if (unknownUrlText.indexOf(":") != -1) { @@ -594,9 +664,36 @@ return "http://" + host + "/" + unknownUrlText; } } + + /** + * Produce a random string that has low probability of collisions. + * + * <p>In this case, we use 16 characters, each drawn from a pool of 94, + * so the number of possible values is 94^16, leading to an expected number + * of values used before a collision occurs as sqrt(pi/2) * 94^8 (treated the + * same as a birthday attack), or a little under 10^16. + * + * <p>This algorithm is also implemented in hosted.html, though it is not + * technically important that they match. + * + * @return a random string + */ + protected static String randomString() { + StringBuilder buf = new StringBuilder(16); + for (int i = 0; i < 16; ++i) { + buf.append((char) RNG.nextInt('~' - '!' + 1) + '!'); + } + return buf.toString(); + } + + protected int codeServerPort; + + protected BrowserListener listener; protected final HostedModeBaseOptions options; + protected DevelopmentModeUI ui = null; + /** * Cheat on the first load's refresh by assuming the module loaded by * {...@link com.google.gwt.dev.shell.GWTShellServlet} is still fresh. This @@ -605,13 +702,17 @@ */ private Set<String> alreadySeenModules = new HashSet<String>(); + private final Object blockUntilDone = new Object(); + + private BrowserWidgetHostImpl browserHost = new UiBrowserWidgetHostImpl(); + private boolean headlessMode = false; - private TreeLogger topLogger; + private final Map<ModuleSpaceHost, ModuleHandle> loadedModules = new IdentityHashMap<ModuleSpaceHost, ModuleHandle>(); private boolean started; - protected DevelopmentModeUI ui; + private TreeLogger topLogger; public HostedModeBase() { // Set any platform specific system properties. @@ -631,22 +732,34 @@ public TreeLogger getTopLogger() { return topLogger; } - + /** * Launch the arguments as Urls in separate windows. + * + * @param logger TreeLogger instance to use */ - public abstract void launchStartupUrls(final TreeLogger logger); + 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 final String normalizeURL(String unknownUrlText) { return normalizeURL(unknownUrlText, getPort(), getHost()); } - - private final Semaphore blockUntilDone = new Semaphore(0); - + /** * Sets up all the major aspects of running the shell graphically, including - * creating the main window and optionally starting the embedded Tomcat - * server. + * creating the main window and optionally starting an embedded web server. */ public final void run() { if (ui == null) { @@ -661,33 +774,15 @@ launchStartupUrls(getTopLogger()); } - blockUntilDone.acquire(); + synchronized (blockUntilDone) { + blockUntilDone.wait(); + } } catch (Exception e) { e.printStackTrace(); } finally { shutDown(); } } - - protected final void setDone() { - blockUntilDone.release(); - } - - protected abstract void restartServer(); - - public class Callback implements DevelopmentModeUI.Callback { - - public void callback(String event, Object callbackData) { - if (DevelopmentModeUI.DONE.equals(event)) { - setDone(); - return; - } else if (DevelopmentModeUI.RESTART_SERVER.equals(event)) { - restartServer(); - return; - } - // TODO: unexpected event? - } - } public final void setPort(int port) { options.setPort(port); @@ -748,6 +843,13 @@ // Create the main app window. ui.initialize(); + // Set done callback + ui.setCallback(DevelopmentModeUI.DONE, new Callback() { + public void callback(String doneEvent, Object ignored) { + setDone(); + } + }); + // Initialize the logger. initializeLogger(); @@ -773,11 +875,7 @@ return true; } - protected int codeServerPort; - - protected BrowserListener listener; - - private BrowserWidgetHostImpl browserHost = new UiBrowserWidgetHostImpl(); + protected abstract int doStartUpServer(); protected void ensureOophmListener() { if (listener == null) { @@ -793,24 +891,10 @@ } } } - - protected abstract int doStartUpServer(); protected String getHost() { return "localhost"; } - - private void initializeLogger() { - Type logLevel = options.getLogLevel(); - if (headlessMode) { - PrintWriterTreeLogger logger = new PrintWriterTreeLogger( - new PrintWriter(System.out)); - logger.setMaxDetail(logLevel); - topLogger = logger; - } else { - topLogger = ui.getTopLogger(logLevel); - } - } /** * Called from a selection script as it begins to load in hosted mode. This @@ -840,6 +924,40 @@ protected final boolean isHeadless() { return headlessMode; } + + protected 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. + */ + try { + URL parsedUrl = new URL(url); + String path = parsedUrl.getPath(); + String query = parsedUrl.getQuery(); + String hash = parsedUrl.getRef(); + String hostedParam = "gwt.hosted=" + listener.getEndpointIdentifier(); + if (query == null) { + query = hostedParam; + } else { + query += '&' + hostedParam; + } + path += '?' + query; + if (hash != null) { + path += '#' + hash; + } + url = new URL(parsedUrl.getProtocol(), parsedUrl.getHost(), + parsedUrl.getPort(), path).toExternalForm(); + } catch (MalformedURLException e) { + getTopLogger().log(TreeLogger.ERROR, "Invalid URL " + url, e); + throw new UnableToCompleteException(); + } + System.err.println( + "Using a browser with the GWT Development Plugin, please browse to"); + System.err.println("the following URL:"); + System.err.println(" " + url); + getTopLogger().log(TreeLogger.INFO, + "Waiting for browser connection to " + url, null); + } /** * Load a module. @@ -859,6 +977,12 @@ assert (moduleDef != null) : "Required module state is absent"; return moduleDef; } + + protected final void setDone() { + synchronized (blockUntilDone) { + blockUntilDone.notifyAll(); + } + } protected final void setHeadless(boolean headlessMode) { this.headlessMode = headlessMode; @@ -894,4 +1018,16 @@ return true; } -} + + private void initializeLogger() { + Type logLevel = options.getLogLevel(); + if (headlessMode) { + PrintWriterTreeLogger logger = new PrintWriterTreeLogger( + new PrintWriter(System.out)); + logger.setMaxDetail(logLevel); + topLogger = logger; + } else { + topLogger = ui.getTopLogger(logLevel); + } + } +} ======================================= --- /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/SwingUI.java Thu Oct 1 17:01:51 2009 +++ /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/SwingUI.java Fri Oct 2 08:37:20 2009 @@ -18,16 +18,15 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.TreeLogger.Type; import com.google.gwt.dev.HostedModeBase.DevelopmentModeUI; -import com.google.gwt.dev.OophmHostedModeBase.TabPanelCollection; import com.google.gwt.dev.WebServerPanel.RestartAction; import com.google.gwt.dev.shell.ShellMainWindow; import com.google.gwt.dev.util.collect.HashMap; +import com.google.gwt.dev.util.log.PrintWriterTreeLogger; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.net.URL; -import java.util.IdentityHashMap; import java.util.Map; import javax.swing.Icon; @@ -41,58 +40,57 @@ */ public class SwingUI extends DevelopmentModeUI { - private final Map<ModuleHandle, ModulePanel> moduleTabs = new IdentityHashMap<ModuleHandle, ModulePanel>(); - private final Map<DevelModeTabKey, ModuleTabPanel> tabPanels = new HashMap<DevelModeTabKey, ModuleTabPanel>(); - + /** + * Module handle for Swing UI. + */ public class SwingModuleHandle implements ModuleHandle { - public TreeLogger getLogger() { - // TODO(jat) Auto-generated method stub - return null; + private final TreeLogger logger; + private final ModulePanel tab; + + public SwingModuleHandle(TreeLogger logger, ModulePanel tab) { + this.logger = logger; + this.tab = tab; } - } - - private final HostedModeBase hostedModeBase; - private JFrame frame; - private JTabbedPane tabs; - private ShellMainWindow mainWnd; - private WebServerPanel webServerLog; - - public SwingUI(HostedModeBase hostedModeBase) { - this.hostedModeBase = hostedModeBase; - } - - @Override - public void initialize() { - if (hostedModeBase.isHeadless()) { - return; - } - ImageIcon gwtIcon = loadImageIcon("icon24.png"); - frame = new JFrame("GWT Development Mode"); - tabs = new JTabbedPane(); - if (hostedModeBase.options.alsoLogToFile()) { - hostedModeBase.options.getLogDir().mkdirs(); - } - mainWnd = new ShellMainWindow(hostedModeBase.options.getLogLevel(), - hostedModeBase.options.getLogFile("main.log")); - tabs.addTab("Development Mode", gwtIcon, mainWnd, "GWT Development mode"); - frame.getContentPane().add(tabs); - frame.setSize(950, 700); - frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - frame.addWindowListener(new WindowAdapter() { - @Override - public void windowClosed(WindowEvent e) { - callback(DONE, null); - } - }); - frame.setIconImage(loadImageIcon("icon16.png").getImage()); - frame.setVisible(true); + public TreeLogger getLogger() { + return logger; + } + + public ModulePanel getTab() { + return tab; + } + } + + /** + * Interface to group activities related to adding and deleting tabs. + */ + public interface TabPanelCollection { + + /** + * Add a new tab containing a ModuleTabPanel. + * + * @param tabPanel + * @param icon + * @param title + * @param tooltip + */ + void addTab(ModuleTabPanel tabPanel, ImageIcon icon, String title, + String tooltip); + + /** + * Remove the tab containing a ModuleTabpanel. + * + * @param tabPanel + */ + void removeTab(ModuleTabPanel tabPanel); } protected static final String PACKAGE_PATH = SwingUI.class.getPackage( ).getName().replace('.', '/').concat("/shell/"); + private static int sessionCounter = 0; + /** * Loads an image from the classpath in this package. */ @@ -108,7 +106,7 @@ * this name. */ static ImageIcon loadImageIcon(String name, boolean prependPackage) { - ClassLoader cl = OophmHostedModeBase.class.getClassLoader(); + ClassLoader cl = SwingUI.class.getClassLoader(); if (prependPackage) { name = PACKAGE_PATH + name; } @@ -122,6 +120,21 @@ return new ImageIcon(); } } + + private final HostedModeBase hostedModeBase; + private final Map<DevelModeTabKey, ModuleTabPanel> tabPanels = new HashMap< + DevelModeTabKey, ModuleTabPanel>(); + + private ShellMainWindow mainWnd; + private JFrame frame; + private JTabbedPane tabs; + private WebServerPanel webServerLog; + + private TreeLogger topLogger; + + public SwingUI(HostedModeBase hostedModeBase) { + this.hostedModeBase = hostedModeBase; + } @Override public TreeLogger getTopLogger(Type logLevel) { @@ -131,19 +144,51 @@ @Override public TreeLogger getWebServerLogger(String serverName, String iconName) { if (webServerLog == null) { + RestartAction restartAction = null; + if (hasCallback(RESTART_SERVER)) { + restartAction = new RestartAction() { + public void restartServer(TreeLogger logger) { + callback(RESTART_SERVER, logger); + } + }; + } webServerLog = new WebServerPanel(hostedModeBase.getPort(), hostedModeBase.options.getLogLevel(), - hostedModeBase.options.getLogFile("webserver.log"), - new RestartAction() { - public void restartServer(TreeLogger logger) { - callback(RESTART_SERVER, logger); - } - }); + hostedModeBase.options.getLogFile("webserver.log"), restartAction); Icon serverIcon = null; tabs.insertTab(serverName, serverIcon, webServerLog, null, 1); } return webServerLog.getLogger(); } + + @Override + public void initialize() { + if (hostedModeBase.isHeadless()) { + topLogger = new PrintWriterTreeLogger(); + return; + } + ImageIcon gwtIcon = loadImageIcon("icon24.png"); + frame = new JFrame("GWT Development Mode"); + tabs = new JTabbedPane(); + if (hostedModeBase.options.alsoLogToFile()) { + hostedModeBase.options.getLogDir().mkdirs(); + } + mainWnd = new ShellMainWindow(hostedModeBase.options.getLogLevel(), + hostedModeBase.options.getLogFile("main.log")); + topLogger = mainWnd.getLogger(); + tabs.addTab("Development Mode", gwtIcon, mainWnd, "GWT Development mode"); + frame.getContentPane().add(tabs); + frame.setSize(950, 700); + frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosed(WindowEvent e) { + callback(DONE, null); + } + }); + frame.setIconImage(loadImageIcon("icon16.png").getImage()); + frame.setVisible(true); + } @Override public ModuleHandle loadModule(String userAgent, String remoteSocket, @@ -151,14 +196,15 @@ String agentTag, TreeLogger.Type logLevel) { ModuleTabPanel tabPanel = null; ModulePanel tab = null; + TreeLogger logger; if (!hostedModeBase.isHeadless()) { tabPanel = findModuleTab(userAgent, remoteSocket, url, tabKey, moduleName); tab = tabPanel.addModuleSession(logLevel, moduleName, sessionKey, - hostedModeBase.options.getLogFile(String.format("%s-%s-%d.log", moduleName, - agentTag, getNextSessionCounter( - hostedModeBase.options.getLogDir())))); - TreeLogger logger = tab.getLogger(); + hostedModeBase.options.getLogFile(String.format("%s-%s-%d.log", + moduleName, agentTag, + getNextSessionCounter(hostedModeBase.options.getLogDir())))); + logger = tab.getLogger(); TreeLogger branch = logger.branch(TreeLogger.INFO, "Loading module " + moduleName); if (url != null) { @@ -172,16 +218,21 @@ if (sessionKey != null) { branch.log(TreeLogger.DEBUG, "Session key: " + sessionKey); } - // TODO: Switch to a wait cursor? - - SwingModuleHandle module = new SwingModuleHandle(); - moduleTabs.put(module, tab); - } - return null; - } - - private static int sessionCounter = 0; + } else { + logger = topLogger.branch(logLevel, "Module " + moduleName); + } + return new SwingModuleHandle(logger, tab); + } + + @Override + public void unloadModule(ModuleHandle module) { + SwingModuleHandle handle = (SwingModuleHandle) module; + Disconnectable tab = handle.getTab(); + if (tab != null) { + tab.disconnect(); + } + } protected int getNextSessionCounter(File logdir) { if (sessionCounter == 0 && logdir != null) { @@ -199,15 +250,6 @@ } return ++sessionCounter; } - - @Override - public void unloadModule(ModuleHandle module) { - SwingModuleHandle handle = (SwingModuleHandle) module; - Disconnectable tab = moduleTabs.remove(handle); - if (tab != null) { - tab.disconnect(); - } - } private ModuleTabPanel findModuleTab(String userAgent, String remoteSocket, String url, String tabKey, String moduleName) { ======================================= --- /changes/jat/abstractui/dev/oophm/overlay/com/google/gwt/dev/GWTShell.java Thu Oct 1 17:01:51 2009 +++ /changes/jat/abstractui/dev/oophm/overlay/com/google/gwt/dev/GWTShell.java Fri Oct 2 08:37:20 2009 @@ -31,8 +31,6 @@ import java.io.File; import java.util.Set; -import javax.swing.ImageIcon; - /** * The main executable class for the hosted mode shell. */ @@ -72,7 +70,7 @@ /** * The GWTShell argument processor. */ - protected static class ArgProcessor extends OophmHostedModeBase.ArgProcessor { + protected static class ArgProcessor extends HostedModeBase.ArgProcessor { public ArgProcessor(ShellOptionsImpl options, boolean forceServer, boolean noURLs) { super(options, forceServer); @@ -91,7 +89,7 @@ /** * Concrete class to implement all shell options. */ - protected static class ShellOptionsImpl extends OophmHostedModeBaseOptionsImpl + protected static class ShellOptionsImpl extends HostedModeBaseOptionsImpl implements HostedModeBaseOptions, WorkDirs, LegacyCompilerOptions, OptionPortHosted { private int localWorkers; @@ -185,10 +183,6 @@ protected File outDir; - public WebServerRestart hasWebServer() { - return WebServerRestart.NONE; - } - public void restartServer(TreeLogger logger) throws UnableToCompleteException { // Unimplemented. } ======================================= --- /changes/jat/abstractui/dev/oophm/overlay/com/google/gwt/dev/HostedMode.java Thu Oct 1 17:01:51 2009 +++ /changes/jat/abstractui/dev/oophm/overlay/com/google/gwt/dev/HostedMode.java Fri Oct 2 08:37:20 2009 @@ -22,6 +22,7 @@ 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.HostedModeBase.DevelopmentModeUI.Callback; import com.google.gwt.dev.cfg.ModuleDef; import com.google.gwt.dev.shell.ArtifactAcceptor; import com.google.gwt.dev.shell.jetty.JettyLauncher; @@ -140,7 +141,7 @@ } } - static class ArgProcessor extends OophmHostedModeBase.ArgProcessor { + static class ArgProcessor extends HostedModeBase.ArgProcessor { public ArgProcessor(HostedModeOptions options) { super(options, false); registerHandler(new ArgHandlerServer(options)); @@ -381,6 +382,15 @@ server = options.getServletContainerLauncher().start(serverLogger, getPort(), options.getWarDir()); assert (server != null); + ui.setCallback(DevelopmentModeUI.RESTART_SERVER, new Callback() { + public void callback(String event, Object callbackData) { + try { + server.refresh(); + } catch (UnableToCompleteException e) { + // ingore, problem already logged + } + } + }); return server.getPort(); } catch (BindException e) { System.err.println("Port " ======================================= --- /changes/jat/abstractui/dev/oophm/src/com/google/gwt/dev/ModuleTabPanel.java Thu Sep 17 15:13:42 2009 +++ /changes/jat/abstractui/dev/oophm/src/com/google/gwt/dev/ModuleTabPanel.java Fri Oct 2 08:37:20 2009 @@ -16,7 +16,7 @@ package com.google.gwt.dev; import com.google.gwt.core.ext.TreeLogger.Type; -import com.google.gwt.dev.OophmHostedModeBase.TabPanelCollection; +import com.google.gwt.dev.SwingUI.TabPanelCollection; import com.google.gwt.dev.util.BrowserInfo; import java.awt.BorderLayout; ======================================= --- /changes/jat/abstractui/user/src/com/google/gwt/junit/BatchingStrategy.java Wed Sep 30 17:53:07 2009 +++ /changes/jat/abstractui/user/src/com/google/gwt/junit/BatchingStrategy.java Fri Oct 2 08:37:20 2009 @@ -48,11 +48,12 @@ protected final Set<TestInfo> getTestsForModule(String syntheticModuleName) { Set<TestInfo> toExecute = GWTTestCase.getTestsForModule(syntheticModuleName).getTests(); Set<TestInfo> toRemove = new HashSet<TestInfo>(); - for (TestInfo info : toExecute) { - if (JUnitShell.mustNotExecuteTest(info)) { - toRemove.add(info); - } - } + // TODO(jat): merge problem? +// for (TestInfo info : toExecute) { +// if (JUnitShell.mustNotExecuteTest(info.)) { +// toRemove.add(info); +// } +// } toExecute.removeAll(toRemove); return toExecute; } ======================================= --- /changes/jat/abstractui/user/src/com/google/gwt/junit/JUnitShell.java Thu Oct 1 17:01:51 2009 +++ /changes/jat/abstractui/user/src/com/google/gwt/junit/JUnitShell.java Fri Oct 2 08:37:20 2009 @@ -86,6 +86,26 @@ */ @SuppressWarnings("deprecation") public class JUnitShell extends GWTShell { + + /** + * Thread to process the message queue until we are done. + */ + public class MessageQueueThread extends Thread { + @Override + public void run() { + while (true) { + boolean stillGoing = notDone(); + if (!stillGoing) { + // ui.callback(DevelopmentModeUI.DONE, null); + break; + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + } + } + } /** * A strategy for running the test. @@ -685,6 +705,7 @@ + (developmentMode ? "development" : "production") + " mode"); return false; } + new MessageQueueThread().start(); return true; } @@ -692,7 +713,6 @@ * Overrides {...@link GWTShell#notDone()} to wait for the currently-running test * to complete. */ - @Override protected boolean notDone() { int activeClients = messageQueue.getNumClientsRetrievedTest(currentTestInfo); if (firstLaunch && runStyle instanceof RunStyleManual) { --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---
