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
-~----------~----~----~----~------~----~------~--~---

Reply via email to