blob: 64b696329214193febde4be9fbe1cb411e9b568b [file] [log] [blame]
/*
* 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);
}
}