All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openbp.server.engine.debugger.DebuggerImpl Maven / Gradle / Ivy

There is a newer version: 0.9.11
Show newest version
/*
 *   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 org.openbp.server.engine.debugger;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;

import org.openbp.common.logger.LogUtil;
import org.openbp.common.setting.SettingUtil;
import org.openbp.common.util.observer.EventObserver;
import org.openbp.common.util.observer.ObserverEvent;
import org.openbp.core.CoreConstants;
import org.openbp.core.OpenBPException;
import org.openbp.core.engine.debugger.Breakpoint;
import org.openbp.core.engine.debugger.CallStackInfo;
import org.openbp.core.engine.debugger.DebuggerEvent;
import org.openbp.core.engine.debugger.DebuggerService;
import org.openbp.core.engine.debugger.ObjectMemberInfo;
import org.openbp.core.model.ModelQualifier;
import org.openbp.core.model.item.process.InitialNode;
import org.openbp.core.model.item.process.NodeParam;
import org.openbp.core.model.item.process.NodeSocket;
import org.openbp.core.model.item.process.WorkflowNode;
import org.openbp.server.ServerConstants;
import org.openbp.server.context.CallStack;
import org.openbp.server.context.CallStackItem;
import org.openbp.server.context.SessionRegistry;
import org.openbp.server.context.TokenContext;
import org.openbp.server.context.TokenContextUtil;
import org.openbp.server.engine.EngineEventObserverMgr;
import org.openbp.server.engine.EngineTraceEvent;
import org.openbp.server.engine.EngineTraceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * The OpenBP debugger is an engine trace client used to visually debug OpenBP processes.
 * This class implements the server-side of the debugger.
 * The debugger registers with the {@link EngineEventObserverMgr} as engine trace client
 * and handles all events needed for debugging a process.
 *
 * The client side of the debugger controls the server-side by using the {@link DebuggerService}
 * which in turn will forward client request to this debugger class.
 *
 * So this class will react to respond to events generated by the engine trace manager
 * of the engine as well as control requests (like single step, run, print expression etc.)
 * from the debugger client.
 *
 * @author Heiko Erhardt
 */
@Service("debugger")
public class DebuggerImpl
	implements EventObserver, Debugger
{
	/** Thread for timer */
	Thread timerThread;

	/**
	 * Table of debugger clients.
	 * Maps client ids (Strings) to clients ({@link DebuggerClient} objects)
	 */
	Hashtable clients;

	/** Client id generator */
	private int lastClientId = 0;

	/** Global debugger control: Stop at first event */
	private boolean stopAtFirstEvent;

	/** Global debugger control: Stop at any event */
	private boolean stopAtAnyEvent;

	/** Global debugger control: Wait for a matching debugger instance */
	private boolean waitForDebugger;

	@Autowired
	private EngineEventObserverMgr observerMgr;

	@Autowired(required=false)
	private SessionRegistry sessionRegistry;

	/**
	 * Private constructor.
	 */
	public DebuggerImpl()
	{
		// Initialize clients hashtable.
		clients = new Hashtable();
	}

	/**
	 * The initialization method retrieves some debugger options and possibly registers the debugger as engine observer.
     * This method will also be called from the IoC container that supports the JSR 250 @PostConstruct annotation.
	 */
	public void initialize()
	{
		String debuggerControl = SettingUtil.getStringSetting(ServerConstants.SYSPROP_DEBUGGER_CONTROL);
		if (debuggerControl != null)
		{
			if ("stopAtFirstEvent".equalsIgnoreCase(debuggerControl))
			{
				stopAtFirstEvent = true;
			}
			else if ("waitAtFirstEvent".equalsIgnoreCase(debuggerControl))
			{
				stopAtFirstEvent = true;
				waitForDebugger = true;
			}
			else if ("stopAtAnyEvent".equalsIgnoreCase(debuggerControl))
			{
				stopAtAnyEvent = true;
			}
			else if ("waitAtAnyEvent".equalsIgnoreCase(debuggerControl))
			{
				stopAtAnyEvent = true;
				waitForDebugger = true;
			}

			if (waitForDebugger)
			{
				registerObserver();
			}
		}
	}

	//////////////////////////////////////////////////
	// @@ Debugger client registration
	//////////////////////////////////////////////////

	/**
	 * Registers a debugger client.
	 * The debugger client will be automatically unregistered when the timeout expires.
	 *
	 * @param clientId The client id or null in order to generate one
	 * @param timeout Timeout in seconds or 0 for no automatic unregistering
	 * @return The client id (must be passed in subsequent calls to the debugger)
	 *
	 * @throws OpenBPException If a client with this id has already registered
	 */
	public String registerClient(String clientId, int timeout)
	{
		String id;
		if (clientId != null)
			id = clientId;
		else
			id = "Deb" + ++lastClientId;

		for (Iterator it = clients.values().iterator(); it.hasNext();)
		{
			DebuggerClient client = (DebuggerClient) it.next();
			if (client.getClientId().equals(id))
			{
				if (clientId == null)
				{
					// Auto-generated id, unregister current debugger
					unregisterClient(id);
				}
				else
				{
					if (client.getTimeout() != 0)
						throw new OpenBPException("Engine.Debugger", "Debugger with id '" + id + "' already registered, timeout is in "
							+ (timeout / 60) + " min.");
					throw new OpenBPException("Engine.Debugger", "Debugger with id '" + id + "' already registered");
				}
			}
		}

		// Create a new client and add it to the clients
		DebuggerClient client = new DebuggerClient(id);
		client.setTimestamp(System.currentTimeMillis());
		client.setTimeout(timeout);
		clients.put(id, client);

		// Register the debugger as event observer all trace event types
		registerObserver();

		if (timeout != 0)
		{
			if (timerThread == null)
			{
				// Start the timer thread
				timerThread = new DebuggerClientCleanupThread();
				timerThread.start();
			}
		}

		return id;
	}

	/**
	 * Checks if a debugger client has been registered.
	 *
	 * @param clientId The client id
	 *
	 * @return true of this client has been registered
	 */
	public synchronized boolean isClientRegistered(String clientId)
	{
		return getClient(clientId) != null;
	}

	/**
	 * Removes the client from the table of registered clients.
	 * Also removes (cleanup) all breakpoints set by this client.
	 *
	 * @param clientId Id of the client returned by {@link #registerClient}
	 * @throws OpenBPException If the client id is invalid or unregistering failed
	 */
	public synchronized void unregisterClient(String clientId)
	{
		unregisterClient(clientId, true);
	}

	/**
	 * Removes the client from the table of registered clients.
	 * Also removes (cleanup) all breakpoints set by this client.
	 *
	 * @param clientId Id of the client
	 * @param removeClient
	 *		true	Removes the client from the clients table.
* false Does not yet remove the client. * @throws OpenBPException If the client id is invalid or unregistering failed */ private void unregisterClient(String clientId, boolean removeClient) { DebuggerClient client = determineClient(clientId); // Retrieves the current halted process if there exists one if (client.getHaltInfo() != null) { // There is a halted process, terminate it try { stop(clientId); } catch (OpenBPException e) { } } clearBreakpoints(clientId, null); // TODO Fix 5 Remove this client id from all token contexts // Remove client from the client list clients.remove(clientId); // Check if there are still clients registered if (clients.isEmpty()) { unregisterObserver(); // Stop the timer thread if (timerThread != null) timerThread.interrupt(); // Reset client id generator lastClientId = 0; } else { if (timerThread != null) { // Check clients if there is a at least one timeout set, if not interrupt timer boolean needTimer = false; for (Iterator it = clients.values().iterator(); it.hasNext();) { DebuggerClient c = (DebuggerClient) it.next(); if (c.getTimeout() != 0) needTimer = true; } if (! needTimer) timerThread.interrupt(); } } } /** * Removes all clients from the table of registered clients. * Also removes all breakpoints. * @throws OpenBPException If unregistering failed */ public synchronized void unregisterAllClients() { if (! clients.isEmpty()) { for (Iterator it = clients.values().iterator(); it.hasNext();) { DebuggerClient c = (DebuggerClient) it.next(); unregisterClient(c.getClientId(), false); } clients.clear(); } unregisterObserver(); // Stop the timer thread if (timerThread != null) timerThread.interrupt(); // Reset client id generator lastClientId = 0; } /** * Kills all halted processes. * Will cause an exception for all currently stopped processes. */ public void killAllHaltedProcesses() { for (Iterator it = clients.values().iterator(); it.hasNext();) { DebuggerClient client = (DebuggerClient) it.next(); if (client.getHaltInfo() != null) { try { setCommand(client, CMD_STOP); } catch (OpenBPException e) { // Never happens } } } } /** * Gets a client by its id. * @param clientId Id of the client * @return The Debugger client or null if no such client exists */ public synchronized DebuggerClient getClient(String clientId) { // Check if there exists a client for the given id return (DebuggerClient) clients.get(clientId); } /** * Gets a client by its id. * @param clientId Id of the client * @return The Debugger client * @throws OpenBPException If the client id is invalid */ private synchronized DebuggerClient determineClient(String clientId) { // Check if there exists a client for the given id DebuggerClient client = (DebuggerClient) clients.get(clientId); if (client != null) { // There is a sign of life of the client, update its timestamp long timestamp = System.currentTimeMillis(); client.setTimestamp(timestamp); return client; } throw new OpenBPException("Engine.Debugger", "Debugger: Client '" + clientId + "' is not a registered debugger client"); } ////////////////////////////////////////////////// // @@ Process engine trace ////////////////////////////////////////////////// /** * Aborts the execution of a process. * * @param clientId Id of the client * @return The event * @throws OpenBPException If the client id is invalid */ public synchronized DebuggerEvent getEvent(String clientId) { DebuggerClient client = determineClient(clientId); DebuggerClient.HaltInfo haltInfo = client.getHaltInfo(); if (haltInfo == null) // Client does not have a halted process return null; EngineTraceEvent event = haltInfo.getEvent(); if (event == null) // We already communicated this event to the client, so nothing to do return null; // Reset the event, so the next call to getEvent won't return the same event haltInfo.setEvent(null); return event.createDebuggerEvent(haltInfo.getPosition()); } /** * Kills all processes that run under control of this client. * * @param clientId Id of the client * @throws OpenBPException If the client id is invalid */ public void killProcesses(String clientId) { // Set the termination request flag of all registered processes that are debugged by the specified client if (sessionRegistry != null) { sessionRegistry.requestSessionAbort(clientId); } // If there is a client stopped by this debugger, signalize it to stop DebuggerClient client = determineClient(clientId); if (client.getHaltInfo() != null) { try { setCommand(client, CMD_STOP); } catch (OpenBPException e) { // Never happens } } } /** * Kills a halted processes - if any - of a client. * * @param clientId Id of the client * @throws OpenBPException If the client id is invalid */ public void killHaltedProcess(String clientId) { DebuggerClient client = determineClient(clientId); if (client.getHaltInfo() != null) { try { setCommand(client, CMD_STOP); } catch (OpenBPException e) { // Never happens } } } /** * Aborts the execution of a process. * * @param clientId Id of the client * @throws OpenBPException If the client id is invalid */ public synchronized void stop(String clientId) { setCommand(clientId, CMD_STOP); } /** * Steps until the next event. * Stops before the next socket. * * @param clientId Id of the client * @throws OpenBPException If the client id is invalid */ public synchronized void stepNext(String clientId) { setCommand(clientId, CMD_STEP_NEXT); } /** * Steps into call or jump nodes or steps just one socket (stepNext) * * @param clientId Id of the client * @throws OpenBPException If the client id is invalid */ public synchronized void stepInto(String clientId) { setCommand(clientId, CMD_STEP_INTO); } /** * Steps over call or jump nodes or step just one socket (stepNext) * * @param clientId Id of the client * @throws OpenBPException If the client id is invalid */ public synchronized void stepOver(String clientId) { DebuggerClient client = determineClient(clientId); client.saveCallDepth(); setCommand(clientId, CMD_STEP_OVER); } /** * Continues execution of a process until it's end (step out). * * @param clientId Id of the client * @throws OpenBPException If the client id is invalid */ public synchronized void stepOut(String clientId) { DebuggerClient client = determineClient(clientId); client.saveCallDepth(); setCommand(clientId, CMD_STEP_OUT); } /** * Steps until a particular position. * The client actually should already have reserved the process to * debug because of {@link #setBreakpoint} * * @param clientId Id of the client * @param position Reference to a process object * @throws OpenBPException If the client id or the position is invalid */ public synchronized void stepUntil(String clientId, String position) { DebuggerClient client = determineClient(clientId); client.setStepUntilPosition(position); setCommand(client, CMD_STEP_UNTIL); } /** * Continues a halted process. * * @param clientId Id of the client * @throws OpenBPException If the client id is invalid */ public synchronized void run(String clientId) { // Notify engine to continue the process setCommand(clientId, CMD_RUN); } ////////////////////////////////////////////////// // @@ Breakpoint control ////////////////////////////////////////////////// /** * Sets the operation mode of the debugger. * * @param clientId Id of the client * @param debuggerMode {@link Debugger#MODE_SKIP_SYSTEM_MODEL}|{@link Debugger#MODE_BREAK_ON_EXCEPTION}|{@link Debugger#MODE_BREAK_ON_TOP_LEVEL}|{@link Debugger#MODE_BREAK_ON_WORKFLOW} * @throws OpenBPException If the client id is invalid */ public void setDebuggerMode(String clientId, int debuggerMode) { DebuggerClient client = determineClient(clientId); client.setDebuggerMode(debuggerMode); } /** * Gets the operation mode of the debugger. * * @param clientId Id of the client * @return {@link Debugger#MODE_SKIP_SYSTEM_MODEL}|{@link Debugger#MODE_BREAK_ON_EXCEPTION}|{@link Debugger#MODE_BREAK_ON_TOP_LEVEL}|{@link Debugger#MODE_BREAK_ON_WORKFLOW} * @throws OpenBPException If the client id is invalid */ public int getDebuggerMode(String clientId) { DebuggerClient client = determineClient(clientId); return client.getDebuggerMode(); } /** * Adds or updates a breakpoint. * @param clientId Id of the client * @param qualifier Reference to a node or a node socket * @param state Any combination of {@link Breakpoint#STATE_DISABLED} | {@link Breakpoint#STATE_GLOBAL} | {@link Breakpoint#STATE_TEMPORARY} or 0 * @throws OpenBPException If the client id is invalid * or if the process has been halted by another client */ public synchronized void setBreakpoint(String clientId, ModelQualifier qualifier, int state) { DebuggerClient client = determineClient(clientId); client.setBreakpoint(qualifier, state); } /** * Clears a breakpoint. * @param clientId Id of the client * @param qualifier Reference to a node or a node socket * @throws OpenBPException If the client id is invalid */ public synchronized void clearBreakpoint(String clientId, ModelQualifier qualifier) { DebuggerClient client = determineClient(clientId); client.clearBreakpoint(qualifier); } /** * Gets a list of all breakpoints of a client. * @param clientId Id of the client * @return A list of {@link Breakpoint} objects or null if there are not breakpoints * defined for this client * @throws OpenBPException If the client id is invalid * or if the process has been halted by another client */ public synchronized List getBreakpoints(String clientId) { DebuggerClient client = determineClient(clientId); return client.getBreakpoints(); } /** * Changes the state of all breakpoints of the specified process. * @param clientId Id of the client * @param qualifiedProcessName Reference to the process * or null to clear all breakpoints of the client. * @param state Any combination of {@link Breakpoint#STATE_DISABLED} | {@link Breakpoint#STATE_GLOBAL} * @param set * true Sets the specified state flags.
* false Clears the specified state flags. * @throws OpenBPException If the client id is invalid * or if the process has been halted by another client */ public synchronized void updateBreakpoints(String clientId, String qualifiedProcessName, int state, boolean set) { DebuggerClient client = determineClient(clientId); client.updateBreakpoints(qualifiedProcessName, state, set); } /** * Clears all breakpoints of the specified process. * @param clientId Id of the client * @param qualifiedProcessName Reference to the process * or null to clear all breakpoints of the client. * @throws OpenBPException If the client id is invalid */ public synchronized void clearBreakpoints(String clientId, String qualifiedProcessName) { DebuggerClient client = determineClient(clientId); client.clearBreakpoints(qualifiedProcessName); } ////////////////////////////////////////////////// // @@ Context inspection ////////////////////////////////////////////////// /** * Retrieves information about a parameter of the token context or a member * of a particular parameter object within the object hierarchy of the parameter. * * @param clientId Id of the client * * @param contextPath Path of the context object we are refering to.
* The path must specify an existing context parameter. * * @param expression This expression may refer to a member of the parameter * (e. g. contextPath = "CreateClient.Out.Client", expression = "User.Profile" * will return the 'Profile' member of the 'User' object of the created client. * * @return The object member information or null if the request could not be resolved * @throws OpenBPException If the client id or the expression are invalid */ public ObjectMemberInfo getObjectValue(String clientId, String contextPath, String expression) { DebuggerClient client = determineClient(clientId); ContextInspector inspector = client.getInspector(); return inspector.getObjectValue(contextPath, expression); } /** * Retrieves information about parameters of the token context or the members * of a particular parameter object within the object hierarchy of the parameter. * * @param clientId Id of the client * * @param contextPath Path of the context object we are refering to:
* If the path is null, all parameters of the context will be returned. * If the path specifies the full path of a context object (e. g. "node.socket.param"), * the method returns all members of the object.
* Otherwise, all parameters beginning with the specified path will be returned. * * @param expression if the 'contextPath' referes to a particular parameter, this expression * may refer to a member of this parameter (e. g. contextPath = "CreateClient.Out.Client", * expression = "User.Profile" will return all members of the 'Profile' member of the * 'User' object of the created client. * * @return A list of {@link ObjectMemberInfo} objects or null if the request could not be resolved * @throws OpenBPException If the client id or the expression are invalid */ public List getObjectMembers(String clientId, String contextPath, String expression) { DebuggerClient client = determineClient(clientId); ContextInspector inspector = client.getInspector(); return inspector.getObjectMembers(contextPath, expression); } /** * Retrieves information about the current state of the call stack. * * @param clientId Id of the client * @return A list of {@link CallStackInfo} objects or null if the call stack is empty * @throws OpenBPException If the client id or the expression are invalid */ public List getCallStackElements(String clientId) { DebuggerClient client = determineClient(clientId); ContextInspector inspector = client.getInspector(); CallStack callStack = inspector.getContext().getCallStack(); if (callStack.getCallDepth() == 0) // Empty call stack return null; List elements = new ArrayList(); // Iterate the call stack and create the list of stack info objects for (Iterator it = callStack.iterator(); it.hasNext();) { CallStackItem stackItem = (CallStackItem) it.next(); CallStackInfo stackInfo = new CallStackInfo(); stackInfo.setType(stackItem.getType()); // TODO Fix 5 No difference between current pos. and saved pos. of call stack info NodeSocket nodeSocket = stackItem.getNodeSocket(); stackInfo.setCurrentPosition(nodeSocket.getQualifier()); stackInfo.setSavedPosition(nodeSocket.getQualifier()); elements.add(stackInfo); } return elements; } ////////////////////////////////////////////////// // @@ Synchronization with the engine ////////////////////////////////////////////////// /** * Sets the command that should be executed by the process engine * and notifies the engine. * * @param clientId Id of the client that issues the command * @param command Next command * @throws OpenBPException If the client id is invalid */ private void setCommand(String clientId, int command) { // Check and get the client object, throw an Exception if not existent DebuggerClient client = determineClient(clientId); setCommand(client, command); } /** * Sets the command that should be executed by the process engine * and notifies the engine. * * @param client Client that issues the command * @param command Next command * @throws OpenBPException If the client id is invalid */ private void setCommand(DebuggerClient client, int command) { // Get the current process if (client.getHaltInfo() == null) return; // Save the command to execute in the client object and notify the // engine using the client object synchronization client.setNextCommand(command); // This call will wake up the client and continue execution after the call // to {@link #waitForCommand} in the {@link #handleEvent} method client.callNotify(); } /** * Waits until the next client command is available. * * @param client Client that is used for synchronization * @param haltInfo Halt information object that identifies the halting point we are refering to */ private void waitForCommand(DebuggerClient client, DebuggerClient.HaltInfo haltInfo) { for (;;) { // Check if the client currently deals with the halted process we are waiting for DebuggerClient.HaltInfo currentHaltInfo = client.getHaltInfo(); if (currentHaltInfo == haltInfo) { // Yes, this is our current halting point break; } // The command referred to another halting process, so let's wait a little try { // Sleep a for 100 ms Thread.sleep(100); } catch (InterruptedException ie) { } } // Wait for the next client command; // The synchronization object is the DebuggerClient object itself try { client.callWait(); } catch (InterruptedException ie) { // The client has the next command available } } ////////////////////////////////////////////////// // @@ EventObserver implementation ////////////////////////////////////////////////// /** * Template method that will be called whenever an event occurs the observer is interested in. * @param e Event * @throws EngineTraceException To abort the running process */ public void observeEvent(ObserverEvent e) { EngineTraceEvent event = (EngineTraceEvent) e; // // First, check if there is a breakpoint defined for the position // associated with this event // ModelQualifier pos1 = null; ModelQualifier pos2 = null; String eventType = event.getEventType(); if (eventType.equals(EngineTraceEvent.NODE_ENTRY)) { pos1 = event.getNode().getQualifier(); pos2 = event.getNodeSocket().getQualifier(); } else if (eventType.equals(EngineTraceEvent.NODE_EXIT)) { pos1 = event.getNodeSocket().getQualifier(); } else if (eventType.equals(EngineTraceEvent.CONTROL_FLOW)) { // Nothing to do here } else if (eventType.equals(EngineTraceEvent.DATA_FLOW)) { if (event.getTargetParam() instanceof NodeParam) pos1 = event.getTargetParam().getQualifier(); else if (event.getSourceParam() instanceof NodeParam) pos1 = event.getSourceParam().getQualifier(); } else if (eventType.equals(EngineTraceEvent.PROCESS_EXCEPTION)) { // Nothing to do here } DebuggerClient breakClient = null; // Check if this token context already runs under control of a debugger client TokenContext context = event.getTokenContext(); String clientId = context.getDebuggerId(); if (clientId != null) { DebuggerClient client = getClient(clientId); if (client == null) { // There is a client id referenced in the token context, but no such client if (waitForDebugger) { client = waitForDebuggerClient(clientId); } else { // Remove the id from the context and continue the process context.setDebuggerId(null); return; } } int mode = client.getDebuggerMode(); if (eventType.equals(EngineTraceEvent.PROCESS_EXCEPTION)) { // An exeception occurred during process execution if ((mode & MODE_BREAK_ON_EXCEPTION) != 0) { breakClient = client; } } if (breakClient == null) { if (stopAtFirstEvent) { stopAtFirstEvent = false; breakClient = client; } if (stopAtAnyEvent) { breakClient = client; } // First, check if we ran onto a breakpoint set by this client if (pos1 != null) { Breakpoint bp = client.getActiveBreakpoint(pos1); if (bp != null) { breakClient = client; if ((bp.getState() & Breakpoint.STATE_TEMPORARY) != 0) { // Clear temporary breakpoint client.clearBreakpoint(pos1); } } } if (breakClient == null && pos2 != null) { Breakpoint bp = client.getActiveBreakpoint(pos2); if (bp != null) { breakClient = client; if ((bp.getState() & Breakpoint.STATE_TEMPORARY) != 0) { // Clear temporary breakpoint client.clearBreakpoint(pos2); } } } // Check if we enter a top-level process if (breakClient == null && (mode & MODE_BREAK_ON_TOP_LEVEL) != 0 && eventType.equals(EngineTraceEvent.NODE_EXIT) && event.getNode() instanceof InitialNode && context.getCallStack().getCallDepth() == 0) { // Yes, we are at the exit socket of an initial node of a top level process // Check if there is a 'break on top level' client breakClient = client; } // Check if we accepted a workflow task if (breakClient == null && (mode & MODE_BREAK_ON_WORKFLOW) != 0 && eventType.equals(EngineTraceEvent.NODE_EXIT) && event.getNode() instanceof WorkflowNode && event.getNodeSocket().isDefaultSocket()) { // Yes, we are leaving a worklfow node at the default socket // (meaning we accepted a workflow task) breakClient = client; } if (breakClient == null) { // We have no breakpoint set by this client, now check if there is // a command pending we need to follow // Check if the current command applies to the current position int command = client.getNextCommand(); if (command == CMD_STEP_NEXT) { // The step next command halts for any events breakClient = client; } else if (command == CMD_STEP_INTO) { // The step into command if (eventType.equals(EngineTraceEvent.NODE_ENTRY)) breakClient = client; } else if (command == CMD_STEP_OVER) { // The step over command // Check if the current call level <= call level at time when the command was issued if (eventType.equals(EngineTraceEvent.NODE_ENTRY)) { int depth = context.getCallStack().getCallDepth(); int commandDepth = client.getCallDepth(); if (depth <= commandDepth) { breakClient = client; } } } else if (command == CMD_STEP_OUT) { // The step out command // Check if the current call level < call level at time when the command was issued if (eventType.equals(EngineTraceEvent.NODE_ENTRY)) { int depth = context.getCallStack().getCallDepth(); int commandDepth = client.getCallDepth(); if (depth <= commandDepth) { breakClient = client; } } } else if (command == CMD_STEP_UNTIL) { // The step until command halts at the position specified with the command String stepUntilPosition = client.getStepUntilPosition(); if (pos1 != null && pos1.equals(stepUntilPosition) || pos2 != null && pos2.equals(stepUntilPosition)) { breakClient = client; } } if (command != CMD_STEP_UNTIL) { // Check if we should skip System model processes if (breakClient != null && (mode & MODE_SKIP_SYSTEM_MODEL) != 0) { if (pos1 != null && CoreConstants.SYSTEM_MODEL_NAME.equals(pos1.getModel())) { // Process belongs to the System model, skip breakClient = null; } } } } } } else { // No client is associated with this context // check if any client has set a global breakpoint for the current position if (pos1 != null) { breakClient = determineGlobalBreakpointClient(pos1); } if (breakClient == null && pos2 != null) { breakClient = determineGlobalBreakpointClient(pos2); } } if (breakClient != null) { // There is a debugger client associated with this event, stop the process // Determine current position ModelQualifier pos = null; if (event.getTargetParam() instanceof NodeParam) pos = event.getTargetParam().getQualifier(); else if (event.getSourceParam() instanceof NodeParam) pos = event.getSourceParam().getQualifier(); else if (event.getNodeSocket() != null) pos = event.getNodeSocket().getQualifier(); else if (event.getNode() != null) pos = event.getNode().getQualifier(); if (pos == null) pos = event.getProcess().getQualifier(); // Save the halt status information in the client object DebuggerClient.HaltInfo haltInfo = breakClient.addHaltInfo(pos, event, context); // When stopping a process, reset the step until target breakClient.setStepUntilPosition(null); // Connect the token context of this process to the client context.setDebuggerId(breakClient.getClientId()); // Wait for a command from the client waitForCommand(breakClient, haltInfo); // Reset the client information concerning the halted process breakClient.resetHaltInfo(); // Check if stop command is set and inform PE if yes if (breakClient.getNextCommand() == CMD_STOP) { // Reset all status information of the client. breakClient.resetHaltInfo(); breakClient.setNextCommand(CMD_NONE); breakClient.setStepUntilPosition(null); breakClient.resetCallDepth(); // On a stop command, we throw an exception that tells the engine to stop the process. TokenContextUtil.resetTerminationRequest(context); throw new EngineTraceException("Process aborted by debugger"); } } } ////////////////////////////////////////////////// // @@ Helpers ////////////////////////////////////////////////// /** * Checks if any client has defined a global breakpoint for this position. * @param qualifier Reference to a node, a socket etc * @return The client or null if no client has such a breakpoint */ private DebuggerClient determineGlobalBreakpointClient(ModelQualifier qualifier) { for (Iterator it = clients.values().iterator(); it.hasNext();) { DebuggerClient client = (DebuggerClient) it.next(); Breakpoint bp = client.getActiveGlobalBreakpoint(qualifier); if (bp != null) return client; } return null; } private DebuggerClient waitForDebuggerClient(String clientId) { LogUtil.warn(getClass(), "Process waiting for debugger (debugger id $0)...", clientId); for (;;) { DebuggerClient client = getClient(clientId); if (client != null) return client; try { Thread.sleep(100); } catch (InterruptedException e) { } } } private void registerObserver() { // Register the debugger as event observer for all trace event types if (observerMgr != null) { observerMgr.registerObserver(this, EngineTraceEvent.getSupportedEventTypes()); } } private void unregisterObserver() { if (observerMgr != null) { observerMgr.unregisterObserver(this); } } ///////////////////////////////////////////////////////////////////////////// // @@ Runnable implementation: Timeout thread ///////////////////////////////////////////////////////////////////////////// /** * Thread that checks for debugging client timeouts. */ private class DebuggerClientCleanupThread extends Thread { /** * The constructor. */ public DebuggerClientCleanupThread() { super("Debugger client cleanup thread"); // This thread must not prevent VM shutdown. setDaemon(true); } /** * Run method of the thread that checks Debugger client timeouts. * * Checks the registered clients for timeout and unregisters the client * if the timeout is reached. */ public void run() { for (;;) { boolean haveTimeoutClient = false; try { // Sleep a while to prevent this thread from blocking CPU resources Thread.sleep(30000); } catch (InterruptedException ie) { } if (clients.isEmpty()) { // Stop timer if there are no clients left timerThread.interrupt(); } if (Thread.interrupted()) { // Return from the run method and null out the timer thread timerThread = null; break; } // Get current timestamp long currentTime = System.currentTimeMillis(); for (Iterator it = clients.values().iterator(); it.hasNext();) { DebuggerClient client = (DebuggerClient) it.next(); long timeout = client.getTimeout(); long timestamp = client.getTimestamp(); if (timeout > 0) { haveTimeoutClient = true; // Check if timout is expired long diff = (currentTime - timestamp) / 1000; if (diff > timeout) { try { // Yes, unregister client unregisterClient(client.getClientId()); // Break to prevent concurrent modification exception. break; } catch (OpenBPException e) { } } } } // Stop timer if no timed clients remain if (! haveTimeoutClient) timerThread.interrupt(); } } } /** * Gets the observer mgr. */ public EngineEventObserverMgr getObserverMgr() { return observerMgr; } /** * Sets the observer mgr. */ public void setObserverMgr(EngineEventObserverMgr observerMgr) { this.observerMgr = observerMgr; } /** * Gets the session registry. */ public SessionRegistry getSessionRegistry() { return sessionRegistry; } /** * Sets the session registry. */ public void setSessionRegistry(SessionRegistry sessionRegistry) { this.sessionRegistry = sessionRegistry; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy