Revision: 5911 Author: [email protected] Date: Thu Aug 6 18:57:54 2009 Log: Update the OOPHM wire protocol for the following: - support future changes such as clients supporting multiple versions of the wire protocol simultaneously, switching to alternate transport mechanisms such as shared memory, and being able to detect an out-of-date hosted.html file for developers running an external server (-noserver) - collect additional data for upcoming OOPHM UI changes (in particular, the top-level URL and a session key) - split plugin initialization from connection to differentiate between failures in those steps - add tests for wire protocol messages - refactor for upcoming HTMLUnit hosted mode integration
Backwards compatibility with v1 plugins is retained by keeping thd old LoadModule message, defining a new tag for the new LoadModule message, and checking for v1 clients during the initialization of the protocol. Patch by: jat Review by: amitmanjhi http://code.google.com/p/google-web-toolkit/source/detail?r=5911 Added: /trunk/dev/core/src/com/google/gwt/dev/util/TemporaryBufferStream.java /trunk/dev/oophm/test /trunk/dev/oophm/test/com /trunk/dev/oophm/test/com/google /trunk/dev/oophm/test/com/google/gwt /trunk/dev/oophm/test/com/google/gwt/dev /trunk/dev/oophm/test/com/google/gwt/dev/shell /trunk/dev/oophm/test/com/google/gwt/dev/shell/BrowserChannelTest.java Modified: /trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/hosted.html /trunk/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java /trunk/dev/core/src/com/google/gwt/dev/shell/BrowserWidgetHost.java /trunk/dev/oophm/build.xml /trunk/dev/oophm/src/com/google/gwt/dev/OophmHostedModeBase.java /trunk/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannel.java /trunk/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannelServer.java /trunk/dev/oophm/src/com/google/gwt/dev/shell/OophmSessionHandler.java ======================================= --- /dev/null +++ /trunk/dev/core/src/com/google/gwt/dev/util/TemporaryBufferStream.java Thu Aug 6 18:57:54 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; + } +} ======================================= --- /dev/null +++ /trunk/dev/oophm/test/com/google/gwt/dev/shell/BrowserChannelTest.java Thu Aug 6 18:57:54 2009 @@ -0,0 +1,298 @@ +/* + * 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.shell; + +import com.google.gwt.dev.util.TemporaryBufferStream; +import com.google.gwt.dev.shell.BrowserChannel.MessageType; +import com.google.gwt.dev.shell.BrowserChannel.Value; +import com.google.gwt.dev.shell.BrowserChannel.SessionHandler.SpecialDispatchId; +import com.google.gwt.dev.shell.BrowserChannel.Value.ValueType; +import com.google.gwt.dev.shell.BrowserChannel.CheckVersionsMessage; +import com.google.gwt.dev.shell.BrowserChannel.ChooseTransportMessage; +import com.google.gwt.dev.shell.BrowserChannel.FatalErrorMessage; +import com.google.gwt.dev.shell.BrowserChannel.FreeMessage; +import com.google.gwt.dev.shell.BrowserChannel.InvokeOnClientMessage; +import com.google.gwt.dev.shell.BrowserChannel.InvokeOnServerMessage; +import com.google.gwt.dev.shell.BrowserChannel.InvokeSpecialMessage; +import com.google.gwt.dev.shell.BrowserChannel.JavaObjectRef; +import com.google.gwt.dev.shell.BrowserChannel.LoadJsniMessage; +import com.google.gwt.dev.shell.BrowserChannel.LoadModuleMessage; +import com.google.gwt.dev.shell.BrowserChannel.OldLoadModuleMessage; +import com.google.gwt.dev.shell.BrowserChannel.ProtocolVersionMessage; +import com.google.gwt.dev.shell.BrowserChannel.QuitMessage; +import com.google.gwt.dev.shell.BrowserChannel.ReturnMessage; +import com.google.gwt.dev.shell.BrowserChannel.SwitchTransportMessage; + +import junit.framework.TestCase; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; + +/** + * Test for {...@link BrowserChannel}. + */ +public class BrowserChannelTest extends TestCase { + + private TemporaryBufferStream bufferStream = new TemporaryBufferStream(); + + private class TestBrowserChannel extends BrowserChannel { + public TestBrowserChannel(InputStream inputStream, + OutputStream outputStream) throws IOException { + super(inputStream, outputStream); + } + + public MessageType readMessageType() throws IOException, + BrowserChannelException { + getStreamToOtherSide().flush(); + return Message.readMessageType(getStreamFromOtherSide()); + } + } + + private DataInputStream iStr = new DataInputStream( + bufferStream.getInputStream()); + private DataOutputStream oStr = new DataOutputStream( + bufferStream.getOutputStream()); + private TestBrowserChannel channel; + + @Override + protected void setUp() throws Exception { + channel = new TestBrowserChannel(bufferStream.getInputStream(), + bufferStream.getOutputStream()); + } + + public void testBooleanValue() throws IOException { + Value val = new Value(); + val.setBoolean(true); + BrowserChannel.writeValue(oStr, val); + val = BrowserChannel.readValue(iStr); + assertEquals(ValueType.BOOLEAN, val.getType()); + assertEquals(true, val.getBoolean()); + val.setBoolean(false); + BrowserChannel.writeValue(oStr, val); + val = BrowserChannel.readValue(iStr); + assertEquals(ValueType.BOOLEAN, val.getType()); + assertEquals(false, val.getBoolean()); + } + + // TODO(jat): add more tests for Value types + + public void testCheckVersions() throws IOException, BrowserChannelException { + int minVersion = 1; + int maxVersion = 2; + String hostedHtmlVersion = "2.0"; + new CheckVersionsMessage(channel, minVersion, maxVersion, + hostedHtmlVersion).send(); + MessageType type = channel.readMessageType(); + assertEquals(MessageType.CHECK_VERSIONS, type); + CheckVersionsMessage message = CheckVersionsMessage.receive(channel); + assertEquals(minVersion, message.getMinVersion()); + assertEquals(maxVersion, message.getMaxVersion()); + assertEquals(hostedHtmlVersion, message.getHostedHtmlVersion()); + } + + public void testChooseTransport() throws IOException, + BrowserChannelException { + String[] transports = new String[] { "shm" }; + new ChooseTransportMessage(channel, transports).send(); + MessageType type = channel.readMessageType(); + assertEquals(MessageType.CHOOSE_TRANSPORT, type); + ChooseTransportMessage message = ChooseTransportMessage.receive(channel); + String[] transportsRecv = message.getTransports(); + assertTrue(Arrays.equals(transports, transportsRecv)); + } + + public void testFatalErrorMessage() throws IOException, + BrowserChannelException { + String error = "Fatal error"; + new FatalErrorMessage(channel, error).send(); + MessageType type = channel.readMessageType(); + assertEquals(MessageType.FATAL_ERROR, type); + FatalErrorMessage message = FatalErrorMessage.receive(channel); + assertEquals(error, message.getError()); + } + + public void testFreeMessage() throws IOException, BrowserChannelException { + int[] ids = new int[] { 42, 1024 }; + new FreeMessage(channel, ids).send(); + MessageType type = channel.readMessageType(); + assertEquals(MessageType.FREE_VALUE, type); + FreeMessage message = FreeMessage.receive(channel); + int[] idsRecv = message.getIds(); + assertTrue(Arrays.equals(ids, idsRecv)); + } + + public void testInvokeOnClientMessage() throws IOException, + BrowserChannelException { + String methodName = "fooMethod"; + Value thisRef = new Value(); + thisRef.setJavaObject(new JavaObjectRef(42)); + Value[] args = new Value[] { + new Value(), new Value(), + }; + args[0].setInt(0); + args[1].setInt(1); + new InvokeOnClientMessage(channel, methodName, thisRef, args).send(); + MessageType type = channel.readMessageType(); + assertEquals(MessageType.INVOKE, type); + InvokeOnClientMessage message = InvokeOnClientMessage.receive(channel); + assertEquals(methodName, message.getMethodName()); + Value thisRefRecv = message.getThis(); + assertEquals(ValueType.JAVA_OBJECT, thisRefRecv.getType()); + assertEquals(42, thisRefRecv.getJavaObject().getRefid()); + Value[] argsRecv = message.getArgs(); + assertEquals(2, argsRecv.length); + for (int i = 0; i < 2; ++i) { + assertEquals(ValueType.INT, argsRecv[i].getType()); + assertEquals(i, argsRecv[i].getInt()); + } + } + + public void testInvokeOnServerMessage() throws IOException, + BrowserChannelException { + int methodId = -1; + Value thisRef = new Value(); + thisRef.setJavaObject(new JavaObjectRef(42)); + Value[] args = new Value[] { + new Value(), new Value(), + }; + args[0].setInt(0); + args[1].setInt(1); + new InvokeOnServerMessage(channel, methodId, thisRef, args).send(); + MessageType type = channel.readMessageType(); + assertEquals(MessageType.INVOKE, type); + InvokeOnServerMessage message = InvokeOnServerMessage.receive(channel); + assertEquals(methodId, message.getMethodDispatchId()); + Value thisRefRecv = message.getThis(); + assertEquals(ValueType.JAVA_OBJECT, thisRefRecv.getType()); + assertEquals(42, thisRefRecv.getJavaObject().getRefid()); + Value[] argsRecv = message.getArgs(); + assertEquals(2, argsRecv.length); + for (int i = 0; i < 2; ++i) { + assertEquals(ValueType.INT, argsRecv[i].getType()); + assertEquals(i, argsRecv[i].getInt()); + } + } + + public void testInvokeSpecialMessage() throws IOException, + BrowserChannelException { + Value[] args = new Value[] { + new Value(), new Value(), + }; + args[0].setInt(0); + args[1].setInt(1); + new InvokeSpecialMessage(channel, SpecialDispatchId.HasMethod, args).send(); + MessageType type = channel.readMessageType(); + assertEquals(MessageType.INVOKE_SPECIAL, type); + InvokeSpecialMessage message = InvokeSpecialMessage.receive(channel); + assertEquals(SpecialDispatchId.HasMethod, message.getDispatchId()); + Value[] argsRecv = message.getArgs(); + assertEquals(2, argsRecv.length); + for (int i = 0; i < 2; ++i) { + assertEquals(ValueType.INT, argsRecv[i].getType()); + assertEquals(i, argsRecv[i].getInt()); + } + } + + public void testLoadJsniMessage() throws IOException, + BrowserChannelException { + String jsni = "function foo() { }"; + new LoadJsniMessage(channel, jsni).send(); + MessageType type = channel.readMessageType(); + assertEquals(MessageType.LOAD_JSNI, type); + LoadJsniMessage message = LoadJsniMessage.receive(channel); + assertEquals(jsni, message.getJsni()); + } + + public void testLoadModuleMessage() throws IOException, + BrowserChannelException { + String url = "http://www.google.com"; + String sessionKey = "asdkfjklAI*23ja"; + String moduleName = "org.example.Hello"; + String userAgent = "Firefox"; + new LoadModuleMessage(channel, url, sessionKey, moduleName, + userAgent).send(); + MessageType type = channel.readMessageType(); + assertEquals(MessageType.LOAD_MODULE, type); + LoadModuleMessage message = LoadModuleMessage.receive(channel); + assertEquals(url, message.getUrl()); + assertEquals(sessionKey, message.getSessionKey()); + assertEquals(moduleName, message.getModuleName()); + assertEquals(userAgent, message.getUserAgent()); + } + + public void testOldLoadModuleMessage() throws IOException, + BrowserChannelException { + int protoVersion = 42; + String moduleName = "org.example.Hello"; + String userAgent = "Firefox"; + new OldLoadModuleMessage(channel, protoVersion, moduleName, + userAgent).send(); + MessageType type = channel.readMessageType(); + assertEquals(MessageType.OLD_LOAD_MODULE, type); + OldLoadModuleMessage message = OldLoadModuleMessage.receive(channel); + assertEquals(protoVersion, message.getProtoVersion()); + assertEquals(moduleName, message.getModuleName()); + assertEquals(userAgent, message.getUserAgent()); + } + + public void testProtocolVersionMessage() throws IOException, + BrowserChannelException { + int protoVersion = 42; + new ProtocolVersionMessage(channel, protoVersion).send(); + MessageType type = channel.readMessageType(); + assertEquals(MessageType.PROTOCOL_VERSION, type); + ProtocolVersionMessage message = ProtocolVersionMessage.receive(channel); + assertEquals(protoVersion, message.getProtocolVersion()); + } + + public void testQuitMessage() throws IOException, + BrowserChannelException { + new QuitMessage(channel).send(); + MessageType type = channel.readMessageType(); + assertEquals(MessageType.QUIT, type); + QuitMessage message = QuitMessage.receive(channel); + } + + public void testReturnMessage() throws IOException, + BrowserChannelException { + Value val = new Value(); + val.setInt(42); + new ReturnMessage(channel, false, val).send(); + MessageType type = channel.readMessageType(); + assertEquals(MessageType.RETURN, type); + ReturnMessage message = ReturnMessage.receive(channel); + assertFalse(message.isException()); + Value valRecv = message.getReturnValue(); + assertEquals(ValueType.INT, valRecv.getType()); + assertEquals(42, valRecv.getInt()); + } + + public void testSwitchTransportMessage() throws IOException, + BrowserChannelException { + String transport = "shm"; + String transportArgs = "17021"; + new SwitchTransportMessage(channel, transport, transportArgs).send(); + MessageType type = channel.readMessageType(); + assertEquals(MessageType.SWITCH_TRANSPORT, type); + SwitchTransportMessage message = SwitchTransportMessage.receive(channel); + assertEquals(transport, message.getTransport()); + assertEquals(transportArgs, message.getTransportArgs()); + } +} ======================================= --- /trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/hosted.html Wed Jul 8 14:29:31 2009 +++ /trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/hosted.html Thu Aug 6 18:57:54 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"); + } + } } } ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java Tue Mar 17 22:05:42 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java Thu Aug 6 18:57:54 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(); } ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/shell/BrowserWidgetHost.java Tue Mar 17 22:05:42 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/shell/BrowserWidgetHost.java Thu Aug 6 18:57:54 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(); ======================================= --- /trunk/dev/oophm/build.xml Wed Mar 11 12:33:27 2009 +++ /trunk/dev/oophm/build.xml Thu Aug 6 18:57:54 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> ======================================= --- /trunk/dev/oophm/src/com/google/gwt/dev/OophmHostedModeBase.java Tue Jun 23 12:43:56 2009 +++ /trunk/dev/oophm/src/com/google/gwt/dev/OophmHostedModeBase.java Thu Aug 6 18:57:54 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, ======================================= --- /trunk/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannel.java Mon May 18 11:47:32 2009 +++ /trunk/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannel.java Thu Aug 6 18:57:54 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: ======================================= --- /trunk/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannelServer.java Mon May 18 11:47:32 2009 +++ /trunk/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannelServer.java Thu Aug 6 18:57:54 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; + } +} ======================================= --- /trunk/dev/oophm/src/com/google/gwt/dev/shell/OophmSessionHandler.java Mon May 18 11:47:32 2009 +++ /trunk/dev/oophm/src/com/google/gwt/dev/shell/OophmSessionHandler.java Thu Aug 6 18:57:54 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 -~----------~----~----~----~------~----~------~--~---
