blob: e98e36fbf63700280cef4b6b9dfccb9376670c94 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2011 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.jsdt.debug.internal.crossfire.event;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.wst.jsdt.debug.core.jsdi.ThreadReference;
import org.eclipse.wst.jsdt.debug.core.jsdi.event.EventQueue;
import org.eclipse.wst.jsdt.debug.core.jsdi.event.EventSet;
import org.eclipse.wst.jsdt.debug.core.jsdi.request.EventRequestManager;
import org.eclipse.wst.jsdt.debug.core.jsdi.request.ResumeRequest;
import org.eclipse.wst.jsdt.debug.core.jsdi.request.ScriptLoadRequest;
import org.eclipse.wst.jsdt.debug.core.jsdi.request.SuspendRequest;
import org.eclipse.wst.jsdt.debug.core.jsdi.request.ThreadEnterRequest;
import org.eclipse.wst.jsdt.debug.core.jsdi.request.ThreadExitRequest;
import org.eclipse.wst.jsdt.debug.core.jsdi.request.VMDeathRequest;
import org.eclipse.wst.jsdt.debug.internal.crossfire.CFThrowable;
import org.eclipse.wst.jsdt.debug.internal.crossfire.CrossFirePlugin;
import org.eclipse.wst.jsdt.debug.internal.crossfire.Tracing;
import org.eclipse.wst.jsdt.debug.internal.crossfire.jsdi.CFLocation;
import org.eclipse.wst.jsdt.debug.internal.crossfire.jsdi.CFMirror;
import org.eclipse.wst.jsdt.debug.internal.crossfire.jsdi.CFScriptReference;
import org.eclipse.wst.jsdt.debug.internal.crossfire.jsdi.CFThreadReference;
import org.eclipse.wst.jsdt.debug.internal.crossfire.jsdi.CFVirtualMachine;
import org.eclipse.wst.jsdt.debug.internal.crossfire.transport.Attributes;
import org.eclipse.wst.jsdt.debug.internal.crossfire.transport.CFEventPacket;
import org.eclipse.wst.jsdt.debug.internal.crossfire.transport.JSON;
import org.eclipse.wst.jsdt.debug.transport.exception.DisconnectedException;
import org.eclipse.wst.jsdt.debug.transport.exception.TimeoutException;
/**
* Default {@link EventQueue} for Crossfire
*
* @since 1.0
*/
public class CFEventQueue extends CFMirror implements EventQueue {
private static boolean TRACE = false;
private EventRequestManager eventmgr = null;
private boolean disposed = false;
/**
* Constructor
*
* @param vm
* @param manager
*/
public CFEventQueue(CFVirtualMachine vm, EventRequestManager manager) {
super(vm);
this.eventmgr = manager;
}
/* (non-Javadoc)
* @see org.eclipse.wst.jsdt.debug.core.jsdi.event.EventQueue#remove()
*/
public EventSet remove() {
return remove(-1);
}
/* (non-Javadoc)
* @see org.eclipse.wst.jsdt.debug.core.jsdi.event.EventQueue#remove(int)
*/
public EventSet remove(int timeout) {
try {
while(true && !disposed) {
CFEventPacket event = crossfire().receiveEvent(timeout);
String name = event.getEvent();
CFEventSet set = new CFEventSet(crossfire());
if(CFEventPacket.CLOSED.equals(name)) {
List deaths = eventmgr.vmDeathRequests();
for (Iterator iter = deaths.iterator(); iter.hasNext();) {
VMDeathRequest request = (VMDeathRequest) iter.next();
set.add(new CFVMDeathEvent(crossfire(), request));
}
if(TRACE) {
Tracing.writeString("QUEUE [event - "+CFEventPacket.CLOSED+"] "+JSON.serialize(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
}
else if(CFEventPacket.ON_BREAK.equals(name)) {
if(TRACE) {
Tracing.writeString("QUEUE [event - "+CFEventPacket.ON_BREAK+"] "+JSON.serialize(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
String threadid = event.getContextId();
if(threadid != null) {
CFThreadReference thread = crossfire().findThread(threadid);
set.setThread(thread);
if(thread != null && !thread.isSuspended()) {
List suspends = eventmgr.suspendRequests();
for (Iterator iter = suspends.iterator(); iter.hasNext();) {
SuspendRequest request = (SuspendRequest) iter.next();
Map locaction = (Map) event.getBody().get(Attributes.LOCATION);
if(locaction == null) {
continue;
}
String url = (String) locaction.get(Attributes.URL);
Number line = (Number) locaction.get(Attributes.LINE);
CFScriptReference script = crossfire().findScript(url);
if(script != null) {
CFLocation loc = new CFLocation(crossfire(), script, null, line.intValue());
set.add(new CFSuspendEvent(crossfire(), request, thread, loc));
}
}
thread.markSuspended(true);
}
else {
return null;
}
}
else {
return null;
}
}
else if(CFEventPacket.ON_RESUME.equals(name)) {
if(TRACE) {
Tracing.writeString("QUEUE [event - "+CFEventPacket.ON_RESUME+"] "+JSON.serialize(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
String threadid = event.getContextId();
if(threadid != null) {
CFThreadReference thread = crossfire().findThread(threadid);
if(thread != null) {
set.setThread(thread);
List resumes = eventmgr.resumeRequests();
for (Iterator iter = resumes.iterator(); iter.hasNext();) {
ResumeRequest request = (ResumeRequest) iter.next();
if(request.thread().equals(thread)) {
CFLocation loc = new CFLocation(crossfire(), null, null, 0);
set.add(new CFResumeEvent(crossfire(), request, thread, loc));
}
}
thread.eventResume();
}
else {
return null;
}
}
else {
return null;
}
}
else if(CFEventPacket.ON_SCRIPT.equals(name)) {
ThreadReference thread = crossfire().findThread(event.getContextId());
if(thread != null) {
set.setThread(thread);
Map json = (Map) event.getBody().get(Attributes.SCRIPT);
if(json != null) {
CFScriptReference script = crossfire().addScript(event.getContextId(), json);
List scripts = eventmgr.scriptLoadRequests();
for (Iterator iter = scripts.iterator(); iter.hasNext();) {
ScriptLoadRequest request = (ScriptLoadRequest) iter.next();
set.add(new CFScriptLoadEvent(crossfire(), request, thread, script));
}
}
else {
return null;
}
}
else {
return null;
}
if(TRACE) {
Tracing.writeString("QUEUE [event - "+CFEventPacket.ON_SCRIPT+"] "+JSON.serialize(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
}
else if(CFEventPacket.ON_CONTEXT_SELECTED.equals(name)) {
handleContext(set, event, true);
if(TRACE) {
Tracing.writeString("QUEUE [event - "+CFEventPacket.ON_CONTEXT_SELECTED+"] "+JSON.serialize(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
}
else if(CFEventPacket.ON_CONTEXT_CREATED.equals(name)) {
handleContext(set, event, true);
if(TRACE) {
Tracing.writeString("QUEUE [event - "+CFEventPacket.ON_CONTEXT_CREATED+"] "+JSON.serialize(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
}
else if(CFEventPacket.ON_CONTEXT_LOADED.equals(name)) {
handleContext(set, event, true);
if(TRACE) {
Tracing.writeString("QUEUE [event - "+CFEventPacket.ON_CONTEXT_LOADED+"] "+JSON.serialize(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
}
else if(CFEventPacket.ON_CONTEXT_DESTROYED.equals(name)) {
ThreadReference thread = crossfire().findThread(event.getContextId());
crossfire().removeThread(event.getContextId());
crossfire().removeScriptsForContext(event.getContextId());
if(thread != null) {
List threads = eventmgr.threadExitRequests();
for (Iterator iter = threads.iterator(); iter.hasNext();) {
ThreadExitRequest request = (ThreadExitRequest) iter.next();
set.add(new CFThreadExitEvent(crossfire(), request, thread));
}
}
else {
return null;
}
if(TRACE) {
Tracing.writeString("QUEUE [event - "+CFEventPacket.ON_CONTEXT_DESTROYED+"] "+JSON.serialize(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
}
else if(CFEventPacket.ON_CONSOLE_DEBUG.equals(name)) {
Map info = (Map) event.getBody().get(Attributes.VALUE);
if(info != null) {
log(IStatus.INFO, info);
}
if(TRACE) {
Tracing.writeString("QUEUE [event - "+CFEventPacket.ON_CONSOLE_DEBUG+"] "+JSON.serialize(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
return null;
}
else if(CFEventPacket.ON_CONSOLE_ERROR.equals(name)) {
logError(event.getBody());
if(TRACE) {
Tracing.writeString("QUEUE [event - "+CFEventPacket.ON_CONSOLE_ERROR+"] "+JSON.serialize(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
return null;
}
else if(CFEventPacket.ON_CONSOLE_INFO.equals(name)) {
Map info = (Map) event.getBody().get(Attributes.VALUE);
if(info != null) {
log(IStatus.INFO, info);
}
if(TRACE) {
Tracing.writeString("QUEUE [event - "+CFEventPacket.ON_CONSOLE_INFO+"] "+JSON.serialize(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
return null;
}
else if(CFEventPacket.ON_CONSOLE_LOG.equals(name)) {
Map info = (Map) event.getBody().get(Attributes.VALUE);
if(info != null) {
log(IStatus.INFO, info);
}
if(TRACE) {
Tracing.writeString("QUEUE [event - "+CFEventPacket.ON_CONSOLE_LOG+"] "+JSON.serialize(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
return null;
}
else if(CFEventPacket.ON_CONSOLE_WARN.equals(name)) {
Map info = (Map) event.getBody().get(Attributes.VALUE);
if(info != null) {
log(IStatus.WARNING, info);
}
if(TRACE) {
Tracing.writeString("QUEUE [event - "+CFEventPacket.ON_CONSOLE_WARN+"] "+JSON.serialize(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
return null;
}
else if(CFEventPacket.ON_ERROR.equals(name)) {
Map body = event.getBody();
if(body != null) {
Throwable thro = new CFThrowable(body);
Status status = new Status(IStatus.ERROR, CrossFirePlugin.PLUGIN_ID, thro.getMessage(), thro);
CrossFirePlugin.log(status);
}
}
else if(CFEventPacket.ON_INSPECT_NODE.equals(name)) {
if(TRACE) {
Tracing.writeString("QUEUE [event - "+CFEventPacket.ON_INSPECT_NODE+"] "+JSON.serialize(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
return null;
}
else if(CFEventPacket.ON_TOGGLE_BREAKPOINT.equals(name)) {
crossfire().toggleBreakpoint(event.getBody());
if(TRACE) {
Tracing.writeString("QUEUE [event - "+CFEventPacket.ON_TOGGLE_BREAKPOINT+"] "+JSON.serialize(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
return null;
}
else {
if(TRACE) {
Tracing.writeString("QUEUE [unknown event - "+name+"] "+JSON.serialize(event)); //$NON-NLS-1$ //$NON-NLS-2$
}
}
if (set.isEmpty()) {
set.resume();
continue;
}
return set;
}
}
catch(DisconnectedException de) {
if(TRACE) {
Tracing.writeString("QUEUE [disconnect exception]: "+de.getMessage()); //$NON-NLS-1$
}
crossfire().disconnectVM();
handleException(de.getMessage(), (de.getCause() == null ? de : de.getCause()));
}
catch(TimeoutException te) {
CrossFirePlugin.log(te);
}
return null;
}
void logError(Map info) {
if(info != null) {
MultiStatus mstatus = new MultiStatus(CrossFirePlugin.PLUGIN_ID, IStatus.ERROR, "Error message logged in Firebug console", null); //$NON-NLS-1$
String message = (String) info.get(Attributes.MESSAGE);
CFThrowable thrw = new CFThrowable((Map) info.get(Attributes.STACKTRACE));
if(message != null) {
IStatus status = new Status(IStatus.ERROR, CrossFirePlugin.PLUGIN_ID, message, thrw);
mstatus.add(status);
}
if(mstatus.getChildren().length > 0) {
CrossFirePlugin.log(mstatus);
}
}
}
/**
* Logs the entry from the queue
*
* @param kind
* @param objects
*/
void log(int kind, Map objects) {
IStatus status = null;
if(objects.size() > 1) {
MultiStatus mstatus = new MultiStatus(CrossFirePlugin.PLUGIN_ID, kind, "Messages logged from Firebug console", null); //$NON-NLS-1$
Entry entry = null;
for (Iterator i = objects.entrySet().iterator(); i.hasNext();) {
entry = (Entry) i.next();
Object value = entry.getValue();
if(value instanceof Map) {
Map map = (Map) entry.getValue();
if(!map.containsKey(Attributes.HANDLE)) {
Object val = map.get(Attributes.VALUE);
if(val != null) {
mstatus.add(new Status(kind, CrossFirePlugin.PLUGIN_ID, val.toString()));
}
}
}
}
status = mstatus;
}
if(status != null) {
CrossFirePlugin.log(status);
}
}
/**
* Handles a context created, loaded, and changed event
* @param set the {@link EventSet} to add to
* @param event the {@link CFEventPacket} received
* @param lookup if we should try to lookup the {@link ThreadReference} before creating a new one
*/
void handleContext(CFEventSet set, CFEventPacket event, boolean lookup) {
List threads = eventmgr.threadEnterRequests();
CFThreadReference thread = null;
String context = event.getContextId();
if(lookup) {
thread = crossfire().findThread(context);
}
if(thread == null) {
thread = crossfire().addThread(context, (String) event.getBody().get(Attributes.URL));
}
set.setThread(thread);
for (Iterator iter = threads.iterator(); iter.hasNext();) {
ThreadEnterRequest request = (ThreadEnterRequest) iter.next();
set.add(new CFThreadEnterEvent(crossfire(), request, thread));
}
}
/**
* Flushes and cleans up the queue
*/
public void dispose() {
disposed = true;
}
/**
* Turns on / off tracing in the event queue
* @param tracing
*/
public static void setTracing(boolean tracing) {
TRACE = tracing;
}
}