| /* |
| * Copyright 2008 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.gwt.dev.shell; |
| |
| import com.google.gwt.core.ext.TreeLogger; |
| import com.google.gwt.dev.shell.BrowserChannel.SessionHandler; |
| import com.google.gwt.dev.shell.BrowserChannel.Value; |
| import com.google.gwt.dev.shell.JsValue.DispatchMethod; |
| import com.google.gwt.dev.shell.JsValue.DispatchObject; |
| |
| import java.lang.reflect.Member; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| /** |
| * |
| */ |
| public class OophmSessionHandler extends SessionHandler { |
| |
| private BrowserWidgetHost host; |
| |
| private TreeLogger logger; |
| |
| private Map<BrowserChannelServer, ModuleSpace> moduleMap = Collections.synchronizedMap(new HashMap<BrowserChannelServer, ModuleSpace>()); |
| |
| /** |
| * Listens for new connections from browsers. |
| */ |
| public OophmSessionHandler(BrowserWidgetHost host) { |
| this.host = host; |
| logger = host.getLogger(); |
| } |
| |
| @Override |
| public void freeValue(BrowserChannel channel, int[] ids) { |
| BrowserChannelServer serverChannel = (BrowserChannelServer) channel; |
| ServerObjectsTable localObjects = serverChannel.getJavaObjectsExposedInBrowser(); |
| for (int id : ids) { |
| localObjects.free(id); |
| } |
| } |
| |
| @Override |
| public ExceptionOrReturnValue getProperty(BrowserChannel channel, int refId, |
| int dispId) { |
| BrowserChannelServer serverChannel = (BrowserChannelServer) channel; |
| ModuleSpace moduleSpace = moduleMap.get(serverChannel); |
| assert moduleSpace != null; |
| ServerObjectsTable localObjects = serverChannel.getJavaObjectsExposedInBrowser(); |
| try { |
| JsValueOOPHM obj = new JsValueOOPHM(); |
| DispatchObject dispObj; |
| CompilingClassLoader ccl = moduleSpace.getIsolatedClassLoader(); |
| obj.setWrappedJavaObject(ccl, localObjects.get(refId)); |
| dispObj = obj.getJavaObjectWrapper(); |
| TreeLogger branch = logger.branch(TreeLogger.SPAM, |
| "Client special invoke of getProperty(" + dispId + " [" |
| + ccl.getClassInfoByDispId(dispId).getMember(dispId) + "]) on " |
| + obj.toString(), null); |
| JsValueOOPHM jsval = (JsValueOOPHM) dispObj.getField(dispId); |
| Value retVal = serverChannel.convertFromJsValue(localObjects, jsval); |
| branch.log(TreeLogger.SPAM, "result is " + retVal, null); |
| return new ExceptionOrReturnValue(false, retVal); |
| } catch (Throwable t) { |
| JsValueOOPHM jsval = new JsValueOOPHM(); |
| JsValueGlue.set(jsval, moduleSpace.getIsolatedClassLoader(), |
| t.getClass(), t); |
| Value retVal = serverChannel.convertFromJsValue(localObjects, jsval); |
| return new ExceptionOrReturnValue(true, retVal); |
| } |
| } |
| |
| /** |
| * Invoke a method on a server object in from client code. |
| */ |
| @Override |
| public ExceptionOrReturnValue invoke(BrowserChannel channel, Value thisVal, |
| int methodDispatchId, Value[] args) { |
| BrowserChannelServer serverChannel = (BrowserChannelServer) channel; |
| ServerObjectsTable localObjects = serverChannel.getJavaObjectsExposedInBrowser(); |
| ModuleSpace moduleSpace = moduleMap.get(serverChannel); |
| assert moduleSpace != null; |
| CompilingClassLoader cl = moduleSpace.getIsolatedClassLoader(); |
| |
| // Treat dispatch id 0 as toString() |
| if (methodDispatchId == 0) { |
| methodDispatchId = cl.getDispId("java.lang.Object::toString()"); |
| } |
| |
| JsValueOOPHM jsThis = new JsValueOOPHM(); |
| serverChannel.convertToJsValue(cl, localObjects, thisVal, jsThis); |
| |
| TreeLogger branch = TreeLogger.NULL; |
| if (logger.isLoggable(TreeLogger.DEBUG)) { |
| StringBuffer logMsg = new StringBuffer(); |
| logMsg.append("Client invoke of "); |
| logMsg.append(methodDispatchId); |
| DispatchClassInfo classInfo = cl.getClassInfoByDispId(methodDispatchId); |
| if (classInfo != null) { |
| Member member = classInfo.getMember(methodDispatchId); |
| if (member != null) { |
| logMsg.append(" ("); |
| logMsg.append(member.getName()); |
| logMsg.append(")"); |
| } |
| } |
| logMsg.append(" on "); |
| logMsg.append(jsThis.toString()); |
| branch = logger.branch(TreeLogger.DEBUG, logMsg.toString(), null); |
| } |
| JsValueOOPHM[] jsArgs = new JsValueOOPHM[args.length]; |
| for (int i = 0; i < args.length; ++i) { |
| jsArgs[i] = new JsValueOOPHM(); |
| serverChannel.convertToJsValue(cl, localObjects, args[i], jsArgs[i]); |
| branch.log(TreeLogger.DEBUG, " arg " + i + " = " + jsArgs[i].toString(), |
| null); |
| } |
| JsValueOOPHM jsRetVal = new JsValueOOPHM(); |
| JsValueOOPHM jsMethod; |
| DispatchObject dispObj; |
| if (jsThis.isWrappedJavaObject()) { |
| // If this is a wrapped object, get get the method off it. |
| dispObj = jsThis.getJavaObjectWrapper(); |
| } else { |
| // Look it up on the static dispatcher. |
| dispObj = (DispatchObject) moduleSpace.getStaticDispatcher(); |
| } |
| jsMethod = (JsValueOOPHM) dispObj.getField(methodDispatchId); |
| DispatchMethod dispMethod = jsMethod.getWrappedJavaFunction(); |
| boolean exception; |
| try { |
| exception = dispMethod.invoke(jsThis, jsArgs, jsRetVal); |
| } catch (Throwable t) { |
| exception = true; |
| JsValueGlue.set(jsRetVal, moduleSpace.getIsolatedClassLoader(), |
| t.getClass(), t); |
| } |
| Value retVal = serverChannel.convertFromJsValue(localObjects, jsRetVal); |
| return new ExceptionOrReturnValue(exception, retVal); |
| } |
| |
| @Override |
| public TreeLogger loadModule(TreeLogger loadModuleLogger, |
| BrowserChannel channel, String moduleName, String userAgent, String url, |
| String tabKey, String sessionKey) { |
| logger = loadModuleLogger; |
| try { |
| // Attach a new ModuleSpace to make it programmable. |
| // |
| // TODO(jat): pass serverChannel to createModuleSpaceHost instead |
| // of the remote endpoint when we remove SWT |
| BrowserChannelServer serverChannel = (BrowserChannelServer) channel; |
| ModuleSpaceHost msh = host.createModuleSpaceHost(loadModuleLogger, |
| moduleName, userAgent, url, tabKey, sessionKey, |
| channel.getRemoteEndpoint()); |
| logger = msh.getLogger(); |
| ModuleSpace moduleSpace = new ModuleSpaceOOPHM(msh, moduleName, |
| serverChannel); |
| moduleMap.put(serverChannel, moduleSpace); |
| moduleSpace.onLoad(loadModuleLogger); |
| } catch (Throwable e) { |
| // We do catch Throwable intentionally because there are a ton of things |
| // that can go wrong trying to load a module, including Error-derived |
| // things like NoClassDefFoundError. |
| // |
| this.logger.log(TreeLogger.ERROR, "Failed to load module '" + moduleName |
| + "' from user agent '" + userAgent + "' at " |
| + channel.getRemoteEndpoint(), e); |
| } |
| return this.logger; |
| } |
| |
| @Override |
| public ExceptionOrReturnValue setProperty(BrowserChannel channel, int refId, |
| int dispId, Value newValue) { |
| BrowserChannelServer serverChannel = (BrowserChannelServer) channel; |
| ModuleSpace moduleSpace = moduleMap.get(serverChannel); |
| assert moduleSpace != null; |
| ServerObjectsTable localObjects = serverChannel.getJavaObjectsExposedInBrowser(); |
| try { |
| JsValueOOPHM obj = new JsValueOOPHM(); |
| DispatchObject dispObj; |
| obj.setWrappedJavaObject(moduleSpace.getIsolatedClassLoader(), |
| localObjects.get(refId)); |
| dispObj = obj.getJavaObjectWrapper(); |
| logger.log(TreeLogger.SPAM, "Client special invoke of setProperty(id=" |
| + dispId + ", newValue=" + newValue + ") on " + obj.toString(), null); |
| JsValueOOPHM jsval = new JsValueOOPHM(); |
| serverChannel.convertToJsValue(moduleSpace.getIsolatedClassLoader(), |
| localObjects, newValue, jsval); |
| dispObj.setField(dispId, jsval); |
| return new ExceptionOrReturnValue(false, newValue); |
| } catch (Throwable t) { |
| JsValueOOPHM jsval = new JsValueOOPHM(); |
| JsValueGlue.set(jsval, moduleSpace.getIsolatedClassLoader(), |
| t.getClass(), t); |
| Value retVal = serverChannel.convertFromJsValue(localObjects, jsval); |
| return new ExceptionOrReturnValue(true, retVal); |
| } |
| } |
| |
| @Override |
| public void unloadModule(BrowserChannel channel, String moduleName) { |
| BrowserChannelServer serverChannel = (BrowserChannelServer) channel; |
| ModuleSpace moduleSpace = moduleMap.get(serverChannel); |
| if (moduleSpace == null) { |
| logger.log(TreeLogger.ERROR, "Unload request without a module loaded", |
| null); |
| return; |
| } |
| logger.log(TreeLogger.INFO, "Unloading module " |
| + moduleSpace.getModuleName() + " (" + moduleName + ")", null); |
| host.unloadModule(moduleSpace.host); |
| moduleSpace.dispose(); |
| moduleMap.remove(serverChannel); |
| } |
| } |