Revision: 5921
Author: [email protected]
Date: Fri Aug  7 15:17:40 2009
Log: Merging in OOPHM wire protocol change  
(http://gwt-code-reviews.appspot.com/51835),
c5911 and followup c5912

svn merge -r5910:5912 https://google-web-toolkit.googlecode.com/svn/trunk .


http://code.google.com/p/google-web-toolkit/source/detail?r=5921

Added:
   
/branches/snapshot-2009.08.04-ihm-r5888/dev/core/src/com/google/gwt/dev/util/TemporaryBufferStream.java
  /branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/test
Modified:
  /branches/snapshot-2009.08.04-ihm-r5888/branch-info.txt
   
/branches/snapshot-2009.08.04-ihm-r5888/dev/core/src/com/google/gwt/core/ext/linker/impl/hosted.html
   
/branches/snapshot-2009.08.04-ihm-r5888/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java
   
/branches/snapshot-2009.08.04-ihm-r5888/dev/core/src/com/google/gwt/dev/shell/BrowserWidget.java
   
/branches/snapshot-2009.08.04-ihm-r5888/dev/core/src/com/google/gwt/dev/shell/BrowserWidgetHost.java
  /branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/build.xml
   
/branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/src/com/google/gwt/dev/OophmHostedModeBase.java
   
/branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannel.java
   
/branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannelServer.java
   
/branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/src/com/google/gwt/dev/shell/OophmSessionHandler.java
Replaced:
  /branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/test/com
  /branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/test/com/google
  /branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/test/com/google/gwt
  /branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/test/com/google/gwt/dev
   
/branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/test/com/google/gwt/dev/shell
   
/branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/test/com/google/gwt/dev/shell/BrowserChannelTest.java

=======================================
--- /dev/null
+++  
/branches/snapshot-2009.08.04-ihm-r5888/dev/core/src/com/google/gwt/dev/util/TemporaryBufferStream.java
  
Fri Aug  7 15:17:40 2009
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2009 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.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+
+/**
+ * A utility for tests that allow writing to a temporary buffer and reading
+ * from the same buffer to verify that serialization/deserialization works.
+ */
+public class TemporaryBufferStream {
+
+  private ArrayList<Byte> buf = new ArrayList<Byte>();
+
+  private InputStream inputStream = new InputStream() {
+    @Override
+    public int read() throws IOException {
+      try {
+        return buf.remove(0) & 255;
+      } catch (IndexOutOfBoundsException e) {
+        return -1;
+      }
+    }
+  };
+
+  private OutputStream outputStream = new OutputStream() {
+    @Override
+    public void write(int b) throws IOException {
+      buf.add(Byte.valueOf((byte) b));
+    }
+  };
+
+  public InputStream getInputStream() {
+    return inputStream;
+  }
+
+  public OutputStream getOutputStream() {
+    return outputStream;
+  }
+}
=======================================
--- /branches/snapshot-2009.08.04-ihm-r5888/branch-info.txt     Fri Aug  7  
14:47:13 2009
+++ /branches/snapshot-2009.08.04-ihm-r5888/branch-info.txt     Fri Aug  7  
15:17:40 2009
@@ -1,4 +1,4 @@
-branch-info.txt for the snapshot-2009.08.04-r5888 branch.
+branch-info.txt for the snapshot-2009.08.04-ihm-r5888 branch.
  Tracks interactions between this branch and other branches.
  See: http://code.google.com/p/google-web-toolkit/wiki/ManagingMerges

@@ -7,3 +7,6 @@
  /branches/snapshot-2009.08.04-r5888

  Merges:
+/trunk r5910:5912 was merged into this branch
+  OOPHM wire protocol change (http://gwt-code-reviews.appspot.com/51835)
+  svn merge -r5910:5912  
https://google-web-toolkit.googlecode.com/svn/trunk .
=======================================
---  
/branches/snapshot-2009.08.04-ihm-r5888/dev/core/src/com/google/gwt/core/ext/linker/impl/hosted.html
     
Wed Jul  8 14:29:31 2009
+++  
/branches/snapshot-2009.08.04-ihm-r5888/dev/core/src/com/google/gwt/core/ext/linker/impl/hosted.html
     
Fri Aug  7 15:17:40 2009
@@ -11,6 +11,7 @@
    var moduleName = moduleFunc ? moduleFunc.moduleName : "unknown";
     
$stats({moduleName:moduleName,subSystem:'startup',evtGroup:'moduleStartup',millis:(new
  
Date()).getTime(),type:'moduleEvalStart'});
  }
+var $hostedHtmlVersion="2.0";

  var gwtOnLoad;
  var $hosted = "localhost:9997";
@@ -23,11 +24,30 @@
    }
  } catch(e) {
  }
+
+function getTopWindow() {
+  var topWin = window.top;
+  while (topWin.opener) {
+    topWin = topWin.opener.top;
+  }
+  return topWin;
+}
+
+function loadIframe(url) {
+  var iframe = $doc.createElement('iframe');
+  iframe.src = "javascript:''";
+  iframe.style.width = "100%";
+  iframe.style.height = "100%";
+  iframe.style.borderWidth = "0px";
+  $doc.body.insertBefore(iframe, $doc.body.firstChild);
+  iframe.contentWindow.location.replace(url);
+}
+
  if ($legacyHosted) {
    gwtOnLoad = function(errFn, modName, modBase) {
      $moduleName = modName;
      $moduleBase = modBase;
-    if (!external.gwtOnLoad(window, modName, "1.6")) {
+    if (!external.gwtOnLoad(window, modName, $hostedHtmlVersion)) {
        if (errFn) {
          errFn(modName);
        }
@@ -35,7 +55,7 @@
    }

    window.onunload = function() {
-    external.gwtOnLoad(window, null, "1.6");
+    external.gwtOnLoad(window, null, $hostedHtmlVersion);
    };
  } else {
    // install eval wrapper on FF to avoid EvalError problem
@@ -175,29 +195,51 @@
        findPluginObject,
        findPluginEmbed,
      ];
-    var found = false;
+    var topWin = getTopWindow();
+    var url = topWin.location.href;
+    if (!topWin.__gwt_SessionID) {
+      var ASCII_EXCLAMATION = 33;
+      var ASCII_TILDE = 126;
+      var chars = [];
+      for (var i = 0; i < 16; ++i) {
+        chars.push(Math.floor(ASCII_EXCLAMATION
+            + Math.random() * (ASCII_TILDE - ASCII_EXCLAMATION + 1)));
+      }
+      topWin.__gwt_SessionID = String.fromCharCode.apply(null, chars);
+    }
+    var plugin = null;
      for (var i = 0; i < pluginFinders.length; ++i) {
        try {
-        var plugin = pluginFinders[i]();
-        if (plugin != null) {
-          // TODO: split connect into init/connect so we can tell plugin
-          // failures from connection failures.
-          if (plugin.connect($hosted, $moduleName, window)) {
-            found = true;
-            break;
-          } else {
-            if (errFn) {
-              errFn(modName);
-            } else {
-              alert("failed to connect to hosted mode server at " +  
$hosted);
-            }
-          }
+        plugin = pluginFinders[i]();
+        if (plugin != null && plugin.init(window)) {
+          break;
          }
        } catch (e) {
        }
      }
-    if (!found) {
-      alert("No GWT plugin found or hosted-mode connection failed");
+    if (!plugin) {
+      // try searching for a v1 plugin for backwards compatibility
+      var found = false;
+      for (var i = 0; i < pluginFinders.length; ++i) {
+        try {
+          plugin = pluginFinders[i]();
+          if (plugin != null && plugin.connect($hosted, $moduleName,  
window)) {
+            return;
+          }
+        } catch (e) {
+        }
+      }
+       
loadIframe("http://google-web-toolkit.googlecode.com/svn/trunk/plugins/MissingBrowserPlugin.html";);
+    } else {
+      if (!plugin.connect(url, topWin.__gwt_SessionID, $hosted,  
$moduleName,
+          $hostedHtmlVersion)) {
+        if (errFn) {
+          errFn(modName);
+        } else {
+          alert("Plugin failed to connect to hosted mode server at " +  
$hosted);
+           
loadIframe("http://code.google.com/p/google-web-toolkit/wiki/TroubleshootingOOPHM";);
+        }
+      }
      }
    }

=======================================
---  
/branches/snapshot-2009.08.04-ihm-r5888/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java
   
Tue Mar 17 22:05:42 2009
+++  
/branches/snapshot-2009.08.04-ihm-r5888/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java
   
Fri Aug  7 15:17:40 2009
@@ -76,8 +76,8 @@
      }

      public ModuleSpaceHost createModuleSpaceHost(TreeLogger logger,
-        String moduleName, String userAgent, String remoteEndpoint)
-        throws UnableToCompleteException {
+        String moduleName, String userAgent, String url, String sessionKey,
+        String remoteEndpoint) throws UnableToCompleteException {
        throw new UnsupportedOperationException();
      }

=======================================
---  
/branches/snapshot-2009.08.04-ihm-r5888/dev/core/src/com/google/gwt/dev/shell/BrowserWidget.java
         
Wed Mar 11 16:11:41 2009
+++  
/branches/snapshot-2009.08.04-ihm-r5888/dev/core/src/com/google/gwt/dev/shell/BrowserWidget.java
         
Fri Aug  7 15:17:40 2009
@@ -172,7 +172,7 @@
     * The version number that should be passed into gwtOnLoad. Must match  
the
     * version in hosted.html.
     */
-  private static final String EXPECTED_GWT_ONLOAD_VERSION = "1.6";
+  private static final String EXPECTED_GWT_ONLOAD_VERSION = "2.0";

    public static void launchExternalBrowser(TreeLogger logger, String  
location) {
      String browserCmd = System.getenv("GWT_EXTERNAL_BROWSER");
=======================================
---  
/branches/snapshot-2009.08.04-ihm-r5888/dev/core/src/com/google/gwt/dev/shell/BrowserWidgetHost.java
     
Tue Mar 17 22:05:42 2009
+++  
/branches/snapshot-2009.08.04-ihm-r5888/dev/core/src/com/google/gwt/dev/shell/BrowserWidgetHost.java
     
Fri Aug  7 15:17:40 2009
@@ -49,9 +49,17 @@

    /**
     * For OOPHM.
+   *
+   * @param logger
+   * @param moduleName
+   * @param userAgent
+   * @param url URL of top-level window (may be null for old browser  
plugins)
+   * @param sessionKey unique session key (may be null for old browser  
plugins)
+   * @param remoteEndpoint
     */
    ModuleSpaceHost createModuleSpaceHost(TreeLogger logger, String  
moduleName,
-      String userAgent, String remoteEndpoint) throws  
UnableToCompleteException;
+      String userAgent, String url, String sessionKey,
+      String remoteEndpoint) throws UnableToCompleteException;

    TreeLogger getLogger();

=======================================
--- /branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/build.xml Wed Mar 11  
12:33:27 2009
+++ /branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/build.xml Fri Aug  7  
15:17:40 2009
@@ -6,6 +6,23 @@
    <property.ensure name="gwt.core.root" location="../core" />
    <property.ensure name="gwt.core.build"  
location="${project.build}/../core" />

+  <fileset id="default.tests" dir="${javac.junit.out}">
+    <include name="**/com/google/**/*Test.class" />
+  </fileset>
+
+  <target name="compile.tests" depends="build" description="Compiles the  
test code for this project">
+    <mkdir dir="${javac.junit.out}" />
+    <gwt.javac srcdir="test" destdir="${javac.junit.out}">
+      <classpath>
+        <pathelement location="${javac.out}" />
+        <pathelement location="${gwt.core.build}/bin" />
+        <pathelement location="${gwt.core.build}/bin-tests" />
+        <pathelement location="${alldeps.jar}" />
+        <pathelement location="${gwt.tools.lib}/junit/junit-3.8.1.jar" />
+      </classpath>
+    </gwt.javac>
+  </target>
+
    <target name="compile" description="Compile all java files">
      <mkdir dir="${javac.out}" />
      <gwt.javac>
@@ -35,4 +52,41 @@
      <delete file="${project.lib}" failonerror="false" />
    </target>

+  <target name="test" depends="build, compile.tests" description="Run unit  
tests for this project.">
+    <!-- TODO: refactor gwt.junit so it can be reused here -->
+    <taskdef name="junit"  
classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTask">
+      <classpath>
+        <pathelement location="${gwt.tools.lib}/junit/junit-3.8.1.jar" />
+        <pathelement location="${gwt.tools.antlib}/ant-junit-1.6.5.jar" />
+      </classpath>
+    </taskdef>
+
+    <echo message="Writing test results to ${junit.out}/reports for  
${test.cases}" />
+    <mkdir dir="${junit.out}/reports" />
+
+    <echo message="${javac.out} ${javac.junit.out}" />
+    <junit dir="${junit.out}" fork="yes" printsummary="yes"  
haltonfailure="true">
+      <classpath>
+        <pathelement location="test" />
+        <pathelement location="${javac.junit.out}" />
+        <pathelement location="${javac.out}" />
+        <pathelement location="${gwt.core.build}/bin" />
+        <pathelement location="${gwt.core.build}/bin-tests" />
+        <pathelement location="${alldeps.jar}" />
+        <pathelement location="${gwt.tools.lib}/junit/junit-3.8.1.jar" />
+        <!-- Pull in gwt-dev and gwt-user sources for .gwt.xml files -->
+        <pathelement location="${gwt.root}/user/src/" />
+        <pathelement location="${gwt.root}/user/super/" />
+        <pathelement location="${gwt.root}/dev/core/super" />
+      </classpath>
+
+      <formatter type="plain" />
+      <formatter type="xml" />
+
+      <batchtest todir="${junit.out}/reports">
+        <fileset refid="default.tests" />
+      </batchtest>
+    </junit>
+  </target>
+
  </project>
=======================================
---  
/branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/src/com/google/gwt/dev/OophmHostedModeBase.java
        
Tue Jun 23 12:43:56 2009
+++  
/branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/src/com/google/gwt/dev/OophmHostedModeBase.java
        
Fri Aug  7 15:17:40 2009
@@ -142,14 +142,15 @@
      }

      public ModuleSpaceHost createModuleSpaceHost(TreeLogger mainLogger,
-        String moduleName, String userAgent, String remoteSocket)
-        throws UnableToCompleteException {
+        String moduleName, String userAgent, String url, String sessionKey,
+        String remoteSocket) throws UnableToCompleteException {
        TreeLogger logger = mainLogger;
        TreeLogger.Type maxLevel = TreeLogger.INFO;
        if (mainLogger instanceof AbstractTreeLogger) {
          maxLevel = ((AbstractTreeLogger) mainLogger).getMaxDetail();
        }
-
+      // TODO(jat): collect different sessions into the same tab, with a
+      //    dropdown to select individual module's logs
        ModulePanel tab;
        if (!isHeadless()) {
          tab = new ModulePanel(maxLevel, moduleName, userAgent,  
remoteSocket,
=======================================
---  
/branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannel.java
       
Mon May 18 11:47:32 2009
+++  
/branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannel.java
       
Fri Aug  7 15:17:40 2009
@@ -26,6 +26,8 @@
  import java.io.DataInputStream;
  import java.io.DataOutputStream;
  import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
  import java.io.UnsupportedEncodingException;
  import java.lang.ref.Reference;
  import java.lang.ref.ReferenceQueue;
@@ -102,9 +104,14 @@

    /**
     * Enumeration of message type ids.
+   *
+   * NOTE: order is important as this defines the ordinals used in the wire
+   * protocol.
     */
    public enum MessageType {
-    Invoke, Return, LoadModule, Quit, LoadJsni, InvokeSpecial, FreeValue;
+    INVOKE, RETURN, OLD_LOAD_MODULE, QUIT, LOAD_JSNI, INVOKE_SPECIAL,  
FREE_VALUE,
+    FATAL_ERROR, CHECK_VERSIONS, PROTOCOL_VERSION, CHOOSE_TRANSPORT,
+    SWITCH_TRANSPORT, LOAD_MODULE;
    }

    /**
@@ -152,7 +159,8 @@
          Value thisObj, int dispId, Value[] args);

      public abstract TreeLogger loadModule(TreeLogger logger,
-        BrowserChannel channel, String moduleName, String userAgent);
+        BrowserChannel channel, String moduleName, String userAgent,  
String url,
+        String sessionKey);

      public abstract ExceptionOrReturnValue setProperty(BrowserChannel  
channel,
          int refId, int dispId, Value newValue);
@@ -512,6 +520,132 @@
        return type + ": " + value;
      }
    }
+
+  /**
+   * The initial request from the client, supplies a range of supported  
versions
+   * and the version from hosted.html (so stale copies on an external  
server
+   * can be detected).
+   */
+  protected static class CheckVersionsMessage extends Message {
+
+    public static CheckVersionsMessage receive(BrowserChannel channel)
+        throws IOException {
+      DataInputStream stream = channel.getStreamFromOtherSide();
+      int minVersion = stream.readInt();
+      int maxVersion = stream.readInt();
+      String hostedHtmlVersion = readUtf8String(stream);
+      return new CheckVersionsMessage(channel, minVersion, maxVersion,
+          hostedHtmlVersion);
+    }
+
+    private final int minVersion;
+
+    private final int maxVersion;
+
+    private final String hostedHtmlVersion;
+
+    public CheckVersionsMessage(BrowserChannel channel, int minVersion,
+        int maxVersion, String hostedHtmlVersion) {
+      super(channel);
+      this.minVersion = minVersion;
+      this.maxVersion = maxVersion;
+      this.hostedHtmlVersion = hostedHtmlVersion;
+    }
+
+    public String getHostedHtmlVersion() {
+      return hostedHtmlVersion;
+    }
+
+    public int getMaxVersion() {
+      return maxVersion;
+    }
+
+    public int getMinVersion() {
+      return minVersion;
+    }
+
+    @Override
+    public void send() throws IOException {
+      DataOutputStream stream = getBrowserChannel().getStreamToOtherSide();
+      stream.writeByte(MessageType.CHECK_VERSIONS.ordinal());
+      stream.writeInt(minVersion);
+      stream.writeInt(maxVersion);
+      writeUntaggedString(stream, hostedHtmlVersion);
+      stream.flush();
+    }
+  }
+
+  /**
+   * A message from the client giving a list of supported connection  
methods
+   * and requesting the server choose one of them to switch protocol  
traffic to.
+   */
+  protected static class ChooseTransportMessage extends Message {
+
+    public static ChooseTransportMessage receive(BrowserChannel channel)
+        throws IOException {
+      DataInputStream stream = channel.getStreamFromOtherSide();
+      int n = stream.readInt();
+      String[] transports = new String[n];
+      for (int i = 0; i < n; ++i) {
+        transports[i] = readUtf8String(stream);
+      }
+      return new ChooseTransportMessage(channel, transports);
+    }
+
+    private final String[] transports;
+
+    public ChooseTransportMessage(BrowserChannel channel,
+        String[] transports) {
+      super(channel);
+      this.transports = transports;
+    }
+
+    public String[] getTransports() {
+      return transports;
+    }
+
+    @Override
+    public void send() throws IOException {
+      DataOutputStream stream = getBrowserChannel().getStreamToOtherSide();
+      stream.writeByte(MessageType.CHOOSE_TRANSPORT.ordinal());
+      stream.writeInt(transports.length);
+      for (String transport : transports) {
+        writeUntaggedString(stream, transport);
+      }
+    }
+  }
+
+  /**
+   * A message reporting a connection error to the client.
+   */
+  protected static class FatalErrorMessage extends Message {
+
+    public static FatalErrorMessage receive(BrowserChannel channel)
+        throws IOException {
+      DataInputStream stream = channel.getStreamFromOtherSide();
+      // NOTE: Tag has already been read.
+      String error = readUtf8String(stream);
+      return new FatalErrorMessage(channel, error);
+    }
+
+    private final String error;
+
+    public FatalErrorMessage(BrowserChannel channel, String error) {
+      super(channel);
+      this.error = error;
+    }
+
+    public String getError() {
+      return error;
+    }
+
+    @Override
+    public void send() throws IOException {
+      DataOutputStream stream = getBrowserChannel().getStreamToOtherSide();
+      stream.writeByte(MessageType.FATAL_ERROR.ordinal());
+      writeUntaggedString(stream, error);
+    }
+  }

    /**
     * A message asking the other side to free object references. Note that  
there
@@ -534,7 +668,7 @@
      public static void send(BrowserChannel channel, int[] ids)
          throws IOException {
        DataOutputStream stream = channel.getStreamToOtherSide();
-      stream.writeByte(MessageType.FreeValue.ordinal());
+      stream.writeByte(MessageType.FREE_VALUE.ordinal());
        stream.writeInt(ids.length);
        for (int id : ids) {
          stream.writeInt(id);
@@ -565,44 +699,101 @@
    }

    /**
-   * A request from the to invoke a function on the other side.
+   * A request from the server to invoke a function on the client.
+   *
+   * Note that MessageType.INVOKE can refer to either this class
+   * or {...@link InvokeOnServerMessage} depending on the direction, as the
+   * protocol is asymmetric (Java needs a dispatch ID, Javascript needs a
+   * name).
     */
-  protected static class InvokeMessage extends Message {
-    public static InvokeMessage receive(BrowserChannel channel)
+  protected static class InvokeOnClientMessage extends Message {
+    public static InvokeOnClientMessage receive(BrowserChannel channel)
          throws IOException {
-      final DataInputStream stream = channel.getStreamFromOtherSide();
+      DataInputStream stream = channel.getStreamFromOtherSide();
        // NOTE: Tag has already been read.
-      final int methodDispatchId = stream.readInt();
-      final Value thisRef = readValue(stream);
-      final int argLen = stream.readInt();
-      final Value[] args = new Value[argLen];
+      String methodName = readUtf8String(stream);
+      Value thisRef = readValue(stream);
+      int argLen = stream.readInt();
+      Value[] args = new Value[argLen];
        for (int i = 0; i < argLen; i++) {
          args[i] = readValue(stream);
        }
-      return new InvokeMessage(channel, methodDispatchId, thisRef, args);
+      return new InvokeOnClientMessage(channel, methodName, thisRef, args);
      }

-    private final int methodDispatchId;
      private final String methodName;

      private final Value thisRef;
      private final Value[] args;

-    public InvokeMessage(BrowserChannel channel, int methodDispatchId,
+    public InvokeOnClientMessage(BrowserChannel channel, String methodName,
          Value thisRef, Value[] args) {
        super(channel);
        this.thisRef = thisRef;
-      this.methodName = null;
-      this.methodDispatchId = methodDispatchId;
+      this.methodName = methodName;
        this.args = args;
      }

-    public InvokeMessage(BrowserChannel channel, String methodName,
+    public Value[] getArgs() {
+      return args;
+    }
+
+    public String getMethodName() {
+      return methodName;
+    }
+
+    public Value getThis() {
+      return thisRef;
+    }
+
+    @Override
+    public void send() throws IOException {
+      final DataOutputStream stream =  
getBrowserChannel().getStreamToOtherSide();
+
+      stream.writeByte(MessageType.INVOKE.ordinal());
+      writeUntaggedString(stream, methodName);
+      writeValue(stream, thisRef);
+      stream.writeInt(args.length);
+      for (int i = 0; i < args.length; i++) {
+        writeValue(stream, args[i]);
+      }
+      stream.flush();
+    }
+  }
+
+  /**
+   * A request from the client to invoke a function on the server.
+   *
+   * Note that MessageType.INVOKE can refer to either this class
+   * or {...@link InvokeOnClientMessage} depending on the direction, as the
+   * protocol is asymmetric (Java needs a dispatch ID, Javascript needs a
+   * name).
+   */
+  protected static class InvokeOnServerMessage extends Message {
+    public static InvokeOnServerMessage receive(BrowserChannel channel)
+        throws IOException {
+      DataInputStream stream = channel.getStreamFromOtherSide();
+      // NOTE: Tag has already been read.
+      int methodDispatchId = stream.readInt();
+      Value thisRef = readValue(stream);
+      int argLen = stream.readInt();
+      Value[] args = new Value[argLen];
+      for (int i = 0; i < argLen; i++) {
+        args[i] = readValue(stream);
+      }
+      return new InvokeOnServerMessage(channel, methodDispatchId, thisRef,
+          args);
+    }
+
+    private final int methodDispatchId;
+    private final Value thisRef;
+    private final Value[] args;
+
+    public InvokeOnServerMessage(BrowserChannel channel, int  
methodDispatchId,
          Value thisRef, Value[] args) {
        super(channel);
        this.thisRef = thisRef;
-      this.methodName = methodName;
-      this.methodDispatchId = -1;
+      this.methodDispatchId = methodDispatchId;
        this.args = args;
      }

@@ -613,10 +804,6 @@
      public int getMethodDispatchId() {
        return methodDispatchId;
      }
-
-    public String getMethodName() {
-      return methodName;
-    }

      public Value getThis() {
        return thisRef;
@@ -626,8 +813,8 @@
      public void send() throws IOException {
        final DataOutputStream stream =  
getBrowserChannel().getStreamToOtherSide();

-      stream.writeByte(MessageType.Invoke.ordinal());
-      writeUntaggedString(stream, methodName);
+      stream.writeByte(MessageType.INVOKE.ordinal());
+      stream.writeInt(methodDispatchId);
        writeValue(stream, thisRef);
        stream.writeInt(args.length);
        for (int i = 0; i < args.length; i++) {
@@ -682,7 +869,7 @@
      public void send() throws IOException {
        final DataOutputStream stream =  
getBrowserChannel().getStreamToOtherSide();

-      stream.writeByte(MessageType.InvokeSpecial.ordinal());
+      stream.writeByte(MessageType.INVOKE_SPECIAL.ordinal());
        stream.writeByte(dispatchId.ordinal());
        stream.writeInt(args.length);
        for (int i = 0; i < args.length; i++) {
@@ -701,14 +888,14 @@
      public static LoadJsniMessage receive(BrowserChannel channel)
          throws IOException {
        DataInputStream stream = channel.getStreamFromOtherSide();
-      String js = stream.readUTF();
+      String js = readUtf8String(stream);
        return new LoadJsniMessage(channel, js);
      }

      public static void send(BrowserChannel channel, String js)
          throws IOException {
        DataOutputStream stream = channel.getStreamToOtherSide();
-      stream.write(MessageType.LoadJsni.ordinal());
+      stream.write(MessageType.LOAD_JSNI.ordinal());
        writeUntaggedString(stream, js);
        stream.flush();
      }
@@ -719,6 +906,10 @@
        super(channel);
        this.js = js;
      }
+
+    public String getJsni() {
+      return js;
+    }

      @Override
      public boolean isAsynchronous() {
@@ -737,44 +928,43 @@
     */
    protected static class LoadModuleMessage extends Message {
      public static LoadModuleMessage receive(BrowserChannel channel)
-        throws IOException, BrowserChannelException {
-      final DataInputStream stream = channel.getStreamFromOtherSide();
-      final int version = stream.readInt();
-      checkProtocolVersion(version);
-      final String moduleName = readUtf8String(stream);
-      final String userAgent = readUtf8String(stream);
-      return new LoadModuleMessage(channel, version, moduleName,  
userAgent);
-    }
-
-    private static void checkProtocolVersion(int version)
-        throws BrowserChannelException {
-      if (version != BROWSERCHANNEL_PROTOCOL_VERSION) {
-        throw new BrowserChannelException(
-            "Incompatible client version: server="
-                + BROWSERCHANNEL_PROTOCOL_VERSION + ", client=" + version);
-      }
+        throws IOException {
+      DataInputStream stream = channel.getStreamFromOtherSide();
+      String url = readUtf8String(stream);
+      String sessionKey = readUtf8String(stream);
+      String moduleName = readUtf8String(stream);
+      String userAgent = readUtf8String(stream);
+      return new LoadModuleMessage(channel, url, sessionKey, moduleName,
+          userAgent);
      }

      private final String moduleName;

      private final String userAgent;

-    private final int protocolVersion;
-
-    public LoadModuleMessage(BrowserChannel channel, int protocolVersion,
-        String moduleName, String userAgent) {
+    private final String url;
+
+    private final String sessionKey;
+
+    public LoadModuleMessage(BrowserChannel channel, String url,
+        String sessionKey, String moduleName, String userAgent) {
        super(channel);
+      this.url = url;
+      this.sessionKey = sessionKey;
        this.moduleName = moduleName;
        this.userAgent = userAgent;
-      this.protocolVersion = protocolVersion;
      }

      public String getModuleName() {
        return moduleName;
      }

-    public int getProtocolVersion() {
-      return protocolVersion;
+    public String getSessionKey() {
+      return sessionKey;
+    }
+
+    public String getUrl() {
+      return url;
      }

      public String getUserAgent() {
@@ -783,9 +973,10 @@

      @Override
      public void send() throws IOException {
-      final DataOutputStream stream =  
getBrowserChannel().getStreamToOtherSide();
-      stream.writeByte(MessageType.LoadModule.ordinal());
-      stream.writeInt(protocolVersion);
+      DataOutputStream stream = getBrowserChannel().getStreamToOtherSide();
+      stream.writeByte(MessageType.LOAD_MODULE.ordinal());
+      writeUntaggedString(stream, url);
+      writeUntaggedString(stream, sessionKey);
        writeUntaggedString(stream, moduleName);
        writeUntaggedString(stream, userAgent);
        stream.flush();
@@ -832,6 +1023,90 @@
            + " is a message format that can only be received.");
      }
    }
+
+  /**
+   * A request from the client that the server load and initialize a given
+   * module (original v1 version).
+   */
+  protected static class OldLoadModuleMessage extends Message {
+    public static OldLoadModuleMessage receive(BrowserChannel channel)
+        throws IOException {
+      DataInputStream stream = channel.getStreamFromOtherSide();
+      int protoVersion = stream.readInt();
+      String moduleName = readUtf8String(stream);
+      String userAgent = readUtf8String(stream);
+      return new OldLoadModuleMessage(channel, protoVersion, moduleName,
+          userAgent);
+    }
+
+    private final String moduleName;
+
+    private final String userAgent;
+
+    private final int protoVersion;
+
+    public OldLoadModuleMessage(BrowserChannel channel, int protoVersion,
+        String moduleName, String userAgent) {
+      super(channel);
+      this.protoVersion = protoVersion;
+      this.moduleName = moduleName;
+      this.userAgent = userAgent;
+    }
+
+    public String getModuleName() {
+      return moduleName;
+    }
+
+    public int getProtoVersion() {
+      return protoVersion;
+    }
+
+    public String getUserAgent() {
+      return userAgent;
+    }
+
+    @Override
+    public void send() throws IOException {
+      DataOutputStream stream = getBrowserChannel().getStreamToOtherSide();
+      stream.writeByte(MessageType.OLD_LOAD_MODULE.ordinal());
+      stream.writeInt(protoVersion);
+      writeUntaggedString(stream, moduleName);
+      writeUntaggedString(stream, userAgent);
+      stream.flush();
+    }
+  }
+
+  /**
+   * Reports the selected protocol version.
+   */
+  protected static class ProtocolVersionMessage extends Message {
+
+    public static ProtocolVersionMessage receive(BrowserChannel channel)
+        throws IOException {
+      DataInputStream stream = channel.getStreamFromOtherSide();
+      int protocolVersion = stream.readInt();
+      return new ProtocolVersionMessage(channel, protocolVersion);
+    }
+
+    private final int protocolVersion;
+
+    public ProtocolVersionMessage(BrowserChannel channel, int  
protocolVersion) {
+      super(channel);
+      this.protocolVersion = protocolVersion;
+    }
+
+    public int getProtocolVersion() {
+      return protocolVersion;
+    }
+
+    @Override
+    public void send() throws IOException {
+      DataOutputStream stream = getBrowserChannel().getStreamToOtherSide();
+      stream.writeByte(MessageType.PROTOCOL_VERSION.ordinal());
+      stream.writeInt(protocolVersion);
+      stream.flush();
+    }
+  }

    /**
     * A message signifying a soft close of the communications channel.
@@ -843,7 +1118,7 @@

      public static void send(BrowserChannel channel) throws IOException {
        final DataOutputStream stream = channel.getStreamToOtherSide();
-      stream.writeByte(MessageType.Quit.ordinal());
+      stream.writeByte(MessageType.QUIT.ordinal());
        stream.flush();
      }

@@ -872,7 +1147,7 @@
      public static void send(BrowserChannel channel, boolean isException,
          Value returnValue) throws IOException {
        final DataOutputStream stream = channel.getStreamToOtherSide();
-      stream.writeByte(MessageType.Return.ordinal());
+      stream.writeByte(MessageType.RETURN.ordinal());
        stream.writeBoolean(isException);
        writeValue(stream, returnValue);
        stream.flush();
@@ -908,7 +1183,56 @@
      }
    }

-  public static final int BROWSERCHANNEL_PROTOCOL_VERSION = 1;
+  /**
+   * A response to ChooseTransport telling the client which transport  
should
+   * be used for the remainder of the protocol.
+   */
+  protected static class SwitchTransportMessage extends Message {
+
+    public static SwitchTransportMessage receive(BrowserChannel channel)
+        throws IOException {
+      DataInputStream stream = channel.getStreamFromOtherSide();
+      String transport = readUtf8String(stream);
+      String transportArgs = readUtf8String(stream);
+      return new SwitchTransportMessage(channel, transport, transportArgs);
+    }
+
+    private final String transport;
+
+    private final String transportArgs;
+
+    public SwitchTransportMessage(BrowserChannel channel,
+        String transport, String transportArgs) {
+      super(channel);
+      // Change nulls to empty strings
+      if (transport == null) {
+        transport = "";
+      }
+      if (transportArgs == null) {
+        transportArgs = "";
+      }
+      this.transport = transport;
+      this.transportArgs = transportArgs;
+    }
+
+    public String getTransport() {
+      return transport;
+    }
+
+    public String getTransportArgs() {
+      return transportArgs;
+    }
+
+    @Override
+    public void send() throws IOException {
+      DataOutputStream stream = getBrowserChannel().getStreamToOtherSide();
+      stream.writeByte(MessageType.SWITCH_TRANSPORT.ordinal());
+      writeUntaggedString(stream, transport);
+      writeUntaggedString(stream, transportArgs);
+    }
+  }
+
+  public static final int BROWSERCHANNEL_PROTOCOL_VERSION = 2;

    public static final int SPECIAL_CLIENTMETHODS_OBJECT = 0;

@@ -1161,12 +1485,17 @@
    private Socket socket;

    public BrowserChannel(Socket socket) throws IOException {
-    streamFromOtherSide = new DataInputStream(new BufferedInputStream(
-        socket.getInputStream()));
-    streamToOtherSide = new DataOutputStream(new BufferedOutputStream(
-        socket.getOutputStream()));
+    this(new BufferedInputStream(socket.getInputStream()),
+        new BufferedOutputStream(socket.getOutputStream()));
      this.socket = socket;
    }
+
+  protected BrowserChannel(InputStream inputStream, OutputStream  
outputStream)
+      throws IOException {
+    streamFromOtherSide = new DataInputStream(inputStream);
+    streamToOtherSide = new DataOutputStream(outputStream);
+    socket = null;
+  }

    public void endSession() {
      Utility.close(streamFromOtherSide);
@@ -1209,13 +1538,16 @@
    }

    public String getRemoteEndpoint() {
+    if (socket == null) {
+      return "";
+    }
      return socket.getInetAddress().getCanonicalHostName() + ":"
          + socket.getPort();
    }

    public Value invoke(String methodName, Value vthis, Value[] vargs,
        SessionHandler handler) throws IOException, BrowserChannelException {
-    new InvokeMessage(this, methodName, vthis, vargs).send();
+    new InvokeOnClientMessage(this, methodName, vthis, vargs).send();
      final ReturnMessage msg =  
reactToMessagesWhileWaitingForReturn(handler);
      return msg.returnValue;
    }
@@ -1226,19 +1558,19 @@
        getStreamToOtherSide().flush();
        MessageType messageType =  
Message.readMessageType(getStreamFromOtherSide());
        switch (messageType) {
-        case FreeValue:
+        case FREE_VALUE:
            final FreeMessage freeMsg = FreeMessage.receive(this);
            handler.freeValue(this, freeMsg.getIds());
            break;
-        case Invoke:
-          final InvokeMessage imsg = InvokeMessage.receive(this);
+        case INVOKE:
+          final InvokeOnServerMessage imsg =  
InvokeOnServerMessage.receive(this);
            ReturnMessage.send(this, handler.invoke(this, imsg.getThis(),
                imsg.getMethodDispatchId(), imsg.getArgs()));
            break;
-        case InvokeSpecial:
+        case INVOKE_SPECIAL:
            handleInvokeSpecial(handler);
            break;
-        case Quit:
+        case QUIT:
            return;
          default:
            throw new BrowserChannelException("Invalid message type "
@@ -1253,18 +1585,18 @@
        getStreamToOtherSide().flush();
        MessageType messageType =  
Message.readMessageType(getStreamFromOtherSide());
        switch (messageType) {
-        case FreeValue:
+        case FREE_VALUE:
            final FreeMessage freeMsg = FreeMessage.receive(this);
            handler.freeValue(this, freeMsg.getIds());
            break;
-        case Return:
+        case RETURN:
            return ReturnMessage.receive(this);
-        case Invoke:
-          final InvokeMessage imsg = InvokeMessage.receive(this);
+        case INVOKE:
+          final InvokeOnServerMessage imsg =  
InvokeOnServerMessage.receive(this);
            ReturnMessage.send(this, handler.invoke(this, imsg.getThis(),
                imsg.getMethodDispatchId(), imsg.getArgs()));
            break;
-        case InvokeSpecial:
+        case INVOKE_SPECIAL:
            handleInvokeSpecial(handler);
            break;
          default:
=======================================
---  
/branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannelServer.java
         
Mon May 18 11:47:32 2009
+++  
/branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannelServer.java
         
Fri Aug  7 15:17:40 2009
@@ -82,8 +82,8 @@
        vargs[i] = convertFromJsValue(remoteObjects, args[i]);
      }
      try {
-      InvokeMessage invokeMessage = new InvokeMessage(this, methodName,  
vthis,
-          vargs);
+      InvokeOnClientMessage invokeMessage = new InvokeOnClientMessage(this,
+          methodName, vthis, vargs);
        invokeMessage.send();
        final ReturnMessage msg =  
reactToMessagesWhileWaitingForReturn(handler);
        Value returnValue = msg.getReturnValue();
@@ -139,21 +139,7 @@

    public void run() {
      try {
-      MessageType type = Message.readMessageType(getStreamFromOtherSide());
-      assert type == MessageType.LoadModule;
-      LoadModuleMessage message = LoadModuleMessage.receive(this);
-      moduleName = message.getModuleName();
-      userAgent = message.getUserAgent();
-      Thread.currentThread().setName(
-          "Hosting " + moduleName + " for " + userAgent);
-      logger = handler.loadModule(logger, this, moduleName, userAgent);
-      try {
-        // send LoadModule response
-        ReturnMessage.send(this, false, new Value());
-        reactToMessages(handler);
-      } finally {
-        handler.unloadModule(this, moduleName);
-      }
+      processConnection();
      } catch (IOException e) {
        logger.log(TreeLogger.WARN, "Client connection lost", e);
      } catch (BrowserChannelException e) {
@@ -265,4 +251,113 @@
          break;
      }
    }
-}
+
+  /**
+   * Create the requested transport and return the appropriate information  
so
+   * the client can connect to the same transport.
+   *
+   * @param transport transport name to create
+   * @return transport-specific arguments for the client to use in  
attaching
+   *     to this transport
+   */
+  private String createTransport(String transport) {
+    // TODO(jat): implement support for additional transports
+    throw new UnsupportedOperationException(
+        "No alternate transports supported");
+  }
+
+  private void processConnection() throws IOException,  
BrowserChannelException {
+    MessageType type = Message.readMessageType(getStreamFromOtherSide());
+    // TODO(jat): add support for getting the a shim plugin downloading the
+    //    real plugin via a GetRealPlugin message before CheckVersions
+    String url = null;
+    String sessionKey = null;
+    switch (type) {
+      case OLD_LOAD_MODULE:
+        // v1 client
+        OldLoadModuleMessage oldLoadModule =  
OldLoadModuleMessage.receive(this);
+        if (oldLoadModule.getProtoVersion() != 1) {
+          // This message type was only used in v1, so something is really
+          // broken here.
+          throw new BrowserChannelException(
+              "Old LoadModule message used, but not v1 protocol");
+        }
+        moduleName = oldLoadModule.getModuleName();
+        userAgent = oldLoadModule.getUserAgent();
+        break;
+      case CHECK_VERSIONS:
+        String connectError = null;
+        CheckVersionsMessage hello = CheckVersionsMessage.receive(this);
+        int minVersion = hello.getMinVersion();
+        int maxVersion = hello.getMaxVersion();
+        String hostedHtmlVersion = hello.getHostedHtmlVersion();
+        if (minVersion > BROWSERCHANNEL_PROTOCOL_VERSION
+            || maxVersion < BROWSERCHANNEL_PROTOCOL_VERSION) {
+          connectError = "No supported protocol version in range " +  
minVersion
+          + " - " + maxVersion;
+        }
+        // TODO(jat): verify hosted.html version
+        if (connectError != null) {
+          logger.log(TreeLogger.ERROR, "Connection error " + connectError,  
null);
+          new FatalErrorMessage(this, connectError).send();
+          return;
+        }
+        new ProtocolVersionMessage(this,  
BROWSERCHANNEL_PROTOCOL_VERSION).send();
+        type = Message.readMessageType(getStreamFromOtherSide());
+
+        // Optionally allow client to request switch of transports.   
Inband is
+        // always supported, so a return of an empty transport string  
requires
+        // the client to stay in this channel.
+        if (type == MessageType.CHOOSE_TRANSPORT) {
+          ChooseTransportMessage chooseTransport =  
ChooseTransportMessage.receive(this);
+          String transport =  
selectTransport(chooseTransport.getTransports());
+          String transportArgs = null;
+          if (transport != null) {
+            transportArgs = createTransport(transport);
+          }
+          new SwitchTransportMessage(this, transport,  
transportArgs).send();
+          type = Message.readMessageType(getStreamFromOtherSide());
+        }
+
+        // Now we expect a LoadModule message to load a GWT module.
+        if (type != MessageType.LOAD_MODULE) {
+          logger.log(TreeLogger.ERROR, "Unexpected message type " + type
+              + "; expecting LoadModule");
+          return;
+        }
+        LoadModuleMessage loadModule = LoadModuleMessage.receive(this);
+        url = loadModule.getUrl();
+        sessionKey = loadModule.getSessionKey();
+        moduleName = loadModule.getModuleName();
+        userAgent = loadModule.getUserAgent();
+        break;
+      default:
+        logger.log(TreeLogger.ERROR, "Unexpected message type " + type
+            + "; expecting CheckVersions");
+        return;
+    }
+    Thread.currentThread().setName(
+        "Hosting " + moduleName + " for " + userAgent + " on " + url + "  
@ "
+        + sessionKey);
+    logger = handler.loadModule(logger, this, moduleName, userAgent, url,
+        sessionKey);
+    try {
+      // send LoadModule response
+      ReturnMessage.send(this, false, new Value());
+      reactToMessages(handler);
+    } finally {
+      handler.unloadModule(this, moduleName);
+    }
+  }
+
+  /**
+   * Select a transport from those provided by the client.
+   *
+   * @param transports array of supported transports
+   * @return null to continue in-band, or a transport type
+   */
+  private String selectTransport(String[] transports) {
+    // TODO(jat): add support for shared memory, others
+    return null;
+  }
+}
=======================================
---  
/branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/src/com/google/gwt/dev/shell/OophmSessionHandler.java
  
Mon May 18 11:47:32 2009
+++  
/branches/snapshot-2009.08.04-ihm-r5888/dev/oophm/src/com/google/gwt/dev/shell/OophmSessionHandler.java
  
Fri Aug  7 15:17:40 2009
@@ -155,13 +155,13 @@

    @Override
    public TreeLogger loadModule(TreeLogger logger, BrowserChannel channel,
-      String moduleName, String userAgent) {
+      String moduleName, String userAgent, String url, String sessionKey) {
      try {
        // Attach a new ModuleSpace to make it programmable.
        //
        BrowserChannelServer serverChannel = (BrowserChannelServer) channel;
        ModuleSpaceHost msh = host.createModuleSpaceHost(logger, moduleName,
-          userAgent, channel.getRemoteEndpoint());
+          userAgent, url, sessionKey, channel.getRemoteEndpoint());
        this.logger = logger = msh.getLogger();
        ModuleSpace moduleSpace = new ModuleSpaceOOPHM(msh, moduleName,
            serverChannel);

--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---

Reply via email to