/* * 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.ModuleHandle; import com.google.gwt.dev.shell.BrowserChannel.Value; import com.google.gwt.dev.shell.BrowserChannelServer.SessionHandlerServer; import com.google.gwt.dev.shell.JsValue.DispatchMethod; import com.google.gwt.dev.shell.JsValue.DispatchObject; import com.google.gwt.dev.util.log.speedtracer.DevModeEventType; import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger; import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event; import java.lang.reflect.Member; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * */ public class OophmSessionHandler extends SessionHandlerServer { private BrowserWidgetHost host; private Map<BrowserChannelServer, ModuleSpace> moduleMap = Collections.synchronizedMap(new HashMap<BrowserChannelServer, ModuleSpace>()); private Map<BrowserChannelServer, ModuleHandle> moduleHandleMap = Collections.synchronizedMap(new HashMap<BrowserChannelServer, ModuleHandle>()); private final TreeLogger topLogger; /** * Listens for new connections from browsers. * * @param topLogger logger to use for non-module-related messages * @param host BrowserWidgetHost instance */ public OophmSessionHandler(TreeLogger topLogger, BrowserWidgetHost host) { this.host = host; this.topLogger = topLogger; } @Override public void freeValue(BrowserChannelServer channel, int[] ids) { ServerObjectsTable localObjects = channel.getJavaObjectsExposedInBrowser(); for (int id : ids) { localObjects.free(id); } } @Override public ExceptionOrReturnValue getProperty(BrowserChannelServer channel, int refId, int dispId) { ModuleSpace moduleSpace = moduleMap.get(channel); ModuleHandle moduleHandle = moduleHandleMap.get(channel); assert moduleSpace != null && moduleHandle != null; TreeLogger logger = moduleHandle.getLogger(); ServerObjectsTable localObjects = channel.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 = channel.convertFromJsValue(localObjects, jsval); if (logger.isLoggable(TreeLogger.SPAM)) { 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 = channel.convertFromJsValue(localObjects, jsval); return new ExceptionOrReturnValue(true, retVal); } } /** * Invoke a method on a server object in from client code. */ @Override public ExceptionOrReturnValue invoke(BrowserChannelServer channel, Value thisVal, int methodDispatchId, Value[] args) { Event jsToJavaCallEvent = SpeedTracerLogger.start(channel.getDevModeSession(), DevModeEventType.JS_TO_JAVA_CALL); ServerObjectsTable localObjects = channel.getJavaObjectsExposedInBrowser(); ModuleSpace moduleSpace = moduleMap.get(channel); ModuleHandle moduleHandle = moduleHandleMap.get(channel); assert moduleSpace != null && moduleHandle != null; TreeLogger logger = moduleHandle.getLogger(); CompilingClassLoader cl = moduleSpace.getIsolatedClassLoader(); // Treat dispatch id 0 as toString() if (methodDispatchId == 0) { methodDispatchId = cl.getDispId("java.lang.Object::toString()"); } JsValueOOPHM jsThis = new JsValueOOPHM(); channel.convertToJsValue(cl, localObjects, thisVal, jsThis); if (SpeedTracerLogger.jsniCallLoggingEnabled()) { DispatchClassInfo clsInfo = cl.getClassInfoByDispId(methodDispatchId); if (clsInfo != null) { Member member = clsInfo.getMember(methodDispatchId); if (member != null) { jsToJavaCallEvent.addData("name", member.toString()); } } } TreeLogger branch = TreeLogger.NULL; if (logger.isLoggable(TreeLogger.SPAM)) { 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.SPAM, logMsg.toString(), null); } JsValueOOPHM[] jsArgs = new JsValueOOPHM[args.length]; for (int i = 0; i < args.length; ++i) { jsArgs[i] = new JsValueOOPHM(); channel.convertToJsValue(cl, localObjects, args[i], jsArgs[i]); if (logger.isLoggable(TreeLogger.SPAM)) { branch.log(TreeLogger.SPAM, " 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 = channel.convertFromJsValue(localObjects, jsRetVal); jsToJavaCallEvent.end(); return new ExceptionOrReturnValue(exception, retVal); } @Override public synchronized TreeLogger loadModule(BrowserChannelServer channel, String moduleName, String userAgent, String url, String tabKey, String sessionKey, byte[] userAgentIcon) { Event moduleInit = SpeedTracerLogger.start(channel.getDevModeSession(), DevModeEventType.MODULE_INIT, "Module Name", moduleName); ModuleHandle moduleHandle = host.createModuleLogger(moduleName, userAgent, url, tabKey, sessionKey, channel, userAgentIcon); TreeLogger logger = moduleHandle.getLogger(); moduleHandleMap.put(channel, moduleHandle); ModuleSpace moduleSpace = null; try { // Attach a new ModuleSpace to make it programmable. ModuleSpaceHost msh = host.createModuleSpaceHost(moduleHandle, moduleName); moduleSpace = new ModuleSpaceOOPHM(msh, moduleName, channel); moduleMap.put(channel, moduleSpace); moduleSpace.onLoad(logger); if (logger.isLoggable(TreeLogger.INFO)) { moduleHandle.getLogger().log(TreeLogger.INFO, "Module " + moduleName + " has been loaded"); } } 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. // moduleHandle.getLogger().log( TreeLogger.ERROR, "Failed to load module '" + moduleName + "' from user agent '" + userAgent + "' at " + channel.getRemoteEndpoint(), e); if (moduleSpace != null) { moduleSpace.dispose(); } moduleHandle.unload(); moduleMap.remove(channel); moduleHandleMap.remove(channel); return null; } finally { moduleInit.end(); } return moduleHandle.getLogger(); } @Override public ExceptionOrReturnValue setProperty(BrowserChannelServer channel, int refId, int dispId, Value newValue) { ModuleSpace moduleSpace = moduleMap.get(channel); ModuleHandle moduleHandle = moduleHandleMap.get(channel); assert moduleSpace != null && moduleHandle != null; TreeLogger logger = moduleHandle.getLogger(); ServerObjectsTable localObjects = channel.getJavaObjectsExposedInBrowser(); try { JsValueOOPHM obj = new JsValueOOPHM(); DispatchObject dispObj; obj.setWrappedJavaObject(moduleSpace.getIsolatedClassLoader(), localObjects.get(refId)); dispObj = obj.getJavaObjectWrapper(); if (logger.isLoggable(TreeLogger.SPAM)) { logger.log(TreeLogger.SPAM, "Client special invoke of setProperty(id=" + dispId + ", newValue=" + newValue + ") on " + obj.toString(), null); } JsValueOOPHM jsval = new JsValueOOPHM(); channel.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 = channel.convertFromJsValue(localObjects, jsval); return new ExceptionOrReturnValue(true, retVal); } } @Override public void unloadModule(BrowserChannelServer channel, String moduleName) { ModuleHandle moduleHandle = moduleHandleMap.get(channel); ModuleSpace moduleSpace = moduleMap.get(channel); if (moduleSpace == null || moduleHandle == null) { topLogger.log(TreeLogger.ERROR, "Unload request without a module loaded", null); return; } moduleHandle.getLogger().log( TreeLogger.INFO, "Unloading module " + moduleSpace.getModuleName() + " (" + moduleName + ")", null); moduleSpace.dispose(); moduleHandle.unload(); moduleMap.remove(channel); moduleHandleMap.remove(channel); } }