/* * Copyright 2010 Red Hat, Inc. and/or its affiliates. * * 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.drools.eclipse.debug.core; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.drools.eclipse.DroolsEclipsePlugin; import org.eclipse.core.resources.IMarkerDelta; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.model.IThread; import org.eclipse.jdi.TimeoutException; import org.eclipse.jdt.debug.core.IJavaBreakpoint; import org.eclipse.jdt.internal.debug.core.IJDIEventListener; import org.eclipse.jdt.internal.debug.core.breakpoints.JavaBreakpoint; import org.eclipse.jdt.internal.debug.core.model.JDIDebugModelMessages; import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget; import org.eclipse.jdt.internal.debug.core.model.JDIObjectValue; import org.eclipse.jdt.internal.debug.core.model.JDIThread; import com.sun.jdi.ClassType; import com.sun.jdi.IntegerValue; import com.sun.jdi.Location; import com.sun.jdi.Method; import com.sun.jdi.ObjectCollectedException; import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.StringReference; import com.sun.jdi.ThreadReference; import com.sun.jdi.VMDisconnectedException; import com.sun.jdi.Value; import com.sun.jdi.VirtualMachine; import com.sun.jdi.event.BreakpointEvent; import com.sun.jdi.event.ClassPrepareEvent; import com.sun.jdi.event.Event; import com.sun.jdi.event.EventSet; import com.sun.jdi.event.ThreadDeathEvent; import com.sun.jdi.event.ThreadStartEvent; import com.sun.jdi.event.VMStartEvent; import com.sun.jdi.request.BreakpointRequest; import com.sun.jdi.request.ClassPrepareRequest; import com.sun.jdi.request.EventRequest; import com.sun.jdi.request.EventRequestManager; public class DroolsDebugTarget extends JDIDebugTarget { private ArrayList<JDIThread> fThreads; private ThreadStartHandler fThreadStartHandler = null; private boolean fSuspended = true; public DroolsDebugTarget(ILaunch launch, VirtualMachine jvm, String name, boolean supportTerminate, boolean supportDisconnect, IProcess process, boolean resume) { super( launch, jvm, name, supportTerminate, supportDisconnect, process, resume ); } public void breakpointAdded(IBreakpoint breakpoint) { try { if ( breakpoint instanceof DroolsLineBreakpoint ) { ((DroolsLineBreakpoint) breakpoint).setJavaBreakpointProperties(); final DroolsLineBreakpoint d = (DroolsLineBreakpoint) breakpoint; if ( d.getDialectName().equals( "mvel" ) ) { //getBreakpoints().add( breakpoint ); //super.breakpointAdded(breakpoint); Iterator<ReferenceType> handleriter = getVM().classesByName( "org.drools.core.base.mvel.MVELDebugHandler" ).iterator(); if ( !handleriter.hasNext() ) { // Create class prepare request to add breakpoint after MVELDebugHanlder is loaded ClassPrepareRequest req = getEventRequestManager().createClassPrepareRequest(); req.addClassFilter( "org.drools.core.base.mvel.MVELDebugHandler" ); req.setSuspendPolicy( EventRequest.SUSPEND_ALL ); addJDIEventListener(new IJDIEventListener() { public boolean handleEvent(Event event, JDIDebugTarget target) { addRemoteBreakpoint(d); return true; } public void wonSuspendVote(Event event, JDIDebugTarget target) { } public void eventSetComplete(Event event, JDIDebugTarget target, boolean suspend, EventSet eventSet) { wonSuspendVote(event, target); } public boolean handleEvent(Event event, JDIDebugTarget target, boolean suspendVote, EventSet eventSet) { return handleEvent(event, target); } }, req); req.enable(); return; } addRemoteBreakpoint( d ); } else { // only add breakpoint if setting Java properties of DRL // breakpoint does not generate an error super.breakpointAdded( breakpoint ); } } else { super.breakpointAdded( breakpoint ); } } catch ( Throwable t ) { // Exception will be thrown when trying to use breakpoint // on drl that is incorrect (cannot be parsed or compiled) DroolsEclipsePlugin.log( t ); } } protected synchronized void initialize() { setThreadList( new ArrayList<JDIThread>( 5 ) ); super.initialize(); } protected JDIThread createThread(ThreadReference thread) { JDIThread jdiThread = null; try { jdiThread = new DroolsThread( this, thread ); } catch ( ObjectCollectedException exception ) { // ObjectCollectionException can be thrown if the thread has already // completed (exited) in the VM. return null; } if ( isDisconnected() ) { return null; } synchronized ( fThreads ) { fThreads.add( jdiThread ); } jdiThread.fireCreationEvent(); return jdiThread; } @SuppressWarnings("unchecked") private Iterator<JDIThread> getThreadIterator() { List<JDIThread> threadList; synchronized ( fThreads ) { threadList = (List<JDIThread>) fThreads.clone(); } return threadList.iterator(); } private boolean hasSuspendedThreads() { Iterator<JDIThread> it = getThreadIterator(); while ( it.hasNext() ) { IThread thread = it.next(); if ( thread.isSuspended() ) return true; } return false; } public boolean canResume() { return (isSuspended() || hasSuspendedThreads()) && isAvailable() && !isPerformingHotCodeReplace(); } protected void resume(boolean fireNotification) throws DebugException { if ( (!isSuspended() && !hasSuspendedThreads()) || !isAvailable() ) { return; } try { setSuspended( false ); resumeThreads(); VirtualMachine vm = getVM(); if ( vm != null ) { vm.resume(); } if ( fireNotification ) { fireResumeEvent( DebugEvent.CLIENT_REQUEST ); } } catch ( VMDisconnectedException e ) { disconnected(); return; } catch ( RuntimeException e ) { setSuspended( true ); fireSuspendEvent( DebugEvent.CLIENT_REQUEST ); targetRequestFailed(MessageFormat.format(JDIDebugModelMessages.JDIDebugTarget_exception_resume, e.toString()), e); } } private void setSuspended(boolean suspended) { fSuspended = suspended; } public boolean isSuspended() { return fSuspended; } private void setThreadList(ArrayList<JDIThread> threads) { fThreads = threads; } public IThread[] getThreads() { synchronized ( fThreads ) { return fThreads.toArray( new IThread[0] ); } } protected void removeAllThreads() { Iterator<JDIThread> itr = getThreadIterator(); while ( itr.hasNext() ) { DroolsThread child = (DroolsThread) itr.next(); child.terminated(); } synchronized ( fThreads ) { fThreads.clear(); } } protected void initializeRequests() { setThreadStartHandler( new ThreadStartHandler() ); new ThreadDeathHandler(); new MVELTraceHandler(); } class ThreadDeathHandler implements IJDIEventListener { protected ThreadDeathHandler() { createRequest(); } /** * Creates and registers a request to listen to thread * death events. */ protected void createRequest() { EventRequestManager manager = getEventRequestManager(); if ( manager != null ) { try { EventRequest req = manager.createThreadDeathRequest(); req.setSuspendPolicy( EventRequest.SUSPEND_NONE ); req.enable(); addJDIEventListener( this, req ); } catch ( RuntimeException e ) { logError( e ); } } } /** * Locates the model thread associated with the underlying JDI thread * that has terminated, and removes it from the collection of * threads belonging to this debug target. A terminate event is * fired for the model thread. * * @param event a thread death event * @param target the target in which the thread died * @return <code>true</code> - the thread should be resumed */ public boolean handleEvent(Event event, JDIDebugTarget target) { ThreadReference ref = ((ThreadDeathEvent) event).thread(); DroolsThread thread = (DroolsThread) findThread( ref ); if ( thread != null ) { synchronized ( fThreads ) { fThreads.remove( thread ); } thread.terminated(); } return true; } public void wonSuspendVote(Event event, JDIDebugTarget target) { // do nothing } public void eventSetComplete(Event event, JDIDebugTarget target, boolean suspend, EventSet eventSet) { wonSuspendVote(event, target); } public boolean handleEvent(Event event, JDIDebugTarget target, boolean suspendVote, EventSet eventSet) { return handleEvent(event, target); } } class ThreadStartHandler implements IJDIEventListener { protected EventRequest fRequest; protected ThreadStartHandler() { createRequest(); } /** * Creates and registers a request to handle all thread start * events */ protected void createRequest() { EventRequestManager manager = getEventRequestManager(); if ( manager != null ) { try { EventRequest req = manager.createThreadStartRequest(); req.setSuspendPolicy( EventRequest.SUSPEND_NONE ); req.enable(); addJDIEventListener( this, req ); setRequest( req ); } catch ( RuntimeException e ) { logError( e ); } } } /** * Creates a model thread for the underlying JDI thread * and adds it to the collection of threads for this * debug target. As a side effect of creating the thread, * a create event is fired for the model thread. * The event is ignored if the underlying thread is already * marked as collected. * * @param event a thread start event * @param target the target in which the thread started * @return <code>true</code> - the thread should be resumed */ public boolean handleEvent(Event event, JDIDebugTarget target) { ThreadReference thread = ((ThreadStartEvent) event).thread(); try { if ( thread.isCollected() ) { return false; } } catch ( VMDisconnectedException exception ) { return false; } catch ( ObjectCollectedException e ) { return false; } catch ( TimeoutException e ) { // continue - attempt to create the thread } DroolsThread jdiThread = (DroolsThread) findThread( thread ); if ( jdiThread == null ) { jdiThread = (DroolsThread) createThread( thread ); if ( jdiThread == null ) { return false; } } else { jdiThread.disposeStackFrames(); jdiThread.fireChangeEvent( DebugEvent.CONTENT ); } return !jdiThread.isSuspended(); } /* (non-Javadoc) * @see org.eclipse.jdt.internal.debug.core.IJDIEventListener#wonSuspendVote(com.sun.jdi.event.Event, org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget) */ public void wonSuspendVote(Event event, JDIDebugTarget target) { // do nothing } /** * Deregisters this event listener. */ protected void deleteRequest() { if ( getRequest() != null ) { removeJDIEventListener( this, getRequest() ); setRequest( null ); } } protected EventRequest getRequest() { return fRequest; } protected void setRequest(EventRequest request) { fRequest = request; } public void eventSetComplete(Event event, JDIDebugTarget target, boolean suspend, EventSet eventSet) { wonSuspendVote(event, target); } public boolean handleEvent(Event event, JDIDebugTarget target, boolean suspendVote, EventSet eventSet) { return handleEvent(event, target); } } private void disposeThreadHandler() { ThreadStartHandler handler = getThreadStartHandler2(); if ( handler != null ) { handler.deleteRequest(); } } public boolean hasThreads() { return fThreads.size() > 0; } protected ThreadStartHandler getThreadStartHandler2() { return fThreadStartHandler; } protected void setThreadStartHandler(ThreadStartHandler threadStartHandler) { fThreadStartHandler = threadStartHandler; } public boolean isOutOfSynch() throws DebugException { Iterator<JDIThread> threads = getThreadIterator(); while ( threads.hasNext() ) { JDIThread thread = threads.next(); if ( thread.isOutOfSynch() ) { return true; } } return false; } public boolean mayBeOutOfSynch() { Iterator<JDIThread> threads = getThreadIterator(); while ( threads.hasNext() ) { JDIThread thread = threads.next(); if ( thread.mayBeOutOfSynch() ) { return true; } } return false; } public JDIThread findThread(ThreadReference tr) { Iterator<JDIThread> iter = getThreadIterator(); while ( iter.hasNext() ) { JDIThread thread = iter.next(); if ( thread.getUnderlyingThread().equals( tr ) ) return thread; } return null; } public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) { if ( !isAvailable() ) { return; } if ( supportsBreakpoint( breakpoint ) ) { try { if ( breakpoint instanceof DroolsLineBreakpoint ) { ((DroolsLineBreakpoint) breakpoint).setJavaBreakpointProperties(); final DroolsLineBreakpoint d = (DroolsLineBreakpoint) breakpoint; if ( d.getDialectName().equals( "mvel" ) ) { removeRemoteBreakpoint( (DroolsLineBreakpoint) breakpoint, delta ); } } ((JavaBreakpoint) breakpoint).removeFromTarget( this ); getBreakpoints().remove( breakpoint ); Iterator<JDIThread> threads = getThreadIterator(); while ( threads.hasNext() ) { ((DroolsThread) threads.next()).removeCurrentBreakpoint( breakpoint ); } } catch ( CoreException e ) { logError( e ); } } } protected void suspendThreads() { Iterator<JDIThread> threads = getThreadIterator(); while ( threads.hasNext() ) { ((DroolsThread) threads.next()).suspendedByVM(); } } protected void resumeThreads() throws DebugException { Iterator<JDIThread> threads = getThreadIterator(); while ( threads.hasNext() ) { ((DroolsThread) threads.next()).resumedByVM(); } } public void disconnect() throws DebugException { if ( !isAvailable() ) { // already done return; } if ( !canDisconnect() ) { notSupported( JDIDebugModelMessages.JDIDebugTarget_does_not_support_disconnect ); } try { disposeThreadHandler(); VirtualMachine vm = getVM(); if ( vm != null ) { vm.dispose(); } } catch ( VMDisconnectedException e ) { // if the VM disconnects while disconnecting, perform // normal disconnect handling disconnected(); } catch ( RuntimeException e ) { targetRequestFailed(MessageFormat.format(JDIDebugModelMessages.JDIDebugTarget_exception_disconnecting, e.toString()), e); } } public void terminate() throws DebugException { if ( !isAvailable() ) { return; } if ( !supportsTerminate() ) { notSupported( JDIDebugModelMessages.JDIDebugTarget_does_not_support_termination ); } try { setTerminating( true ); disposeThreadHandler(); VirtualMachine vm = getVM(); if ( vm != null ) { vm.exit( 1 ); } IProcess process = getProcess(); if ( process != null ) { process.terminate(); } } catch ( VMDisconnectedException e ) { // if the VM disconnects while exiting, perform // normal termination processing terminated(); } catch ( TimeoutException exception ) { // if there is a timeout see if the associated process is terminated IProcess process = getProcess(); if ( process != null && process.isTerminated() ) { terminated(); } else { // All we can do is disconnect disconnected(); } } catch ( RuntimeException e ) { targetRequestFailed(MessageFormat.format(JDIDebugModelMessages.JDIDebugTarget_exception_terminating, e.toString()), e); } } public void handleVMStart(VMStartEvent event) { if ( isResumeOnStartup() ) { try { setSuspended( true ); resume(); } catch ( DebugException e ) { logError( e ); } } // If any threads have resumed since thread collection was initialized, // update their status (avoid concurrent modification - use #getThreads()) IThread[] threads = getThreads(); for ( int i = 0; i < threads.length; i++ ) { DroolsThread thread = (DroolsThread) threads[i]; if ( thread.isSuspended() ) { try { boolean suspended = thread.getUnderlyingThread().isSuspended(); if ( !suspended ) { thread.setRunning( true ); thread.fireResumeEvent( DebugEvent.CLIENT_REQUEST ); } } catch ( VMDisconnectedException e ) { } catch ( ObjectCollectedException e ) { } catch ( RuntimeException e ) { logError( e ); } } } } protected void initializeState() { List<ThreadReference> threads = null; VirtualMachine vm = getVM(); if ( vm != null ) { try { threads = vm.allThreads(); } catch ( RuntimeException e ) { internalError( e ); } if ( threads != null ) { Iterator<ThreadReference> initialThreads = threads.iterator(); while ( initialThreads.hasNext() ) { createThread(initialThreads.next()); } } } if ( isResumeOnStartup() ) { setSuspended( false ); } } public void suspend() throws DebugException { if ( isSuspended() ) { return; } try { VirtualMachine vm = getVM(); if ( vm != null ) { vm.suspend(); } suspendThreads(); setSuspended( true ); fireSuspendEvent( DebugEvent.CLIENT_REQUEST ); } catch ( RuntimeException e ) { setSuspended( false ); fireResumeEvent( DebugEvent.CLIENT_REQUEST ); targetRequestFailed(MessageFormat.format(JDIDebugModelMessages.JDIDebugTarget_exception_suspend, e.toString()), e); } } public void prepareToSuspendByBreakpoint(JavaBreakpoint breakpoint) { setSuspended( true ); suspendThreads(); } protected void cancelSuspendByBreakpoint(JavaBreakpoint breakpoint) throws DebugException { setSuspended( false ); resumeThreads(); } class MVELTraceHandler implements IJDIEventListener { protected MVELTraceHandler() { createRequest(); } protected void createRequest() { EventRequestManager manager = getEventRequestManager(); if ( manager != null ) { try { ClassPrepareRequest req = manager.createClassPrepareRequest(); req.addClassFilter( "org.drools.core.base.mvel.MVELDebugHandler" ); req.setSuspendPolicy( EventRequest.SUSPEND_ALL ); addJDIEventListener( MVELTraceHandler.this, req ); req.enable(); } catch ( RuntimeException e ) { logError( e ); } } } /** * Locates the model thread associated with the underlying JDI thread * that has terminated, and removes it from the collection of * threads belonging to this debug target. A terminate event is * fired for the model thread. * * @param event a thread death event * @param target the target in which the thread died * @return <code>true</code> - the thread should be resumed */ public boolean handleEvent(Event event, JDIDebugTarget target) { ClassType classType = ( ClassType ) ((ClassPrepareEvent) event).referenceType(); String name = classType.name(); // change this to create a breakpoint, as the method enter was too slow BreakpointRequest req = null; List<Method> list = classType.methodsByName( "onBreak" ); if ( list.size() == 0 ) { throw new IllegalStateException( "MVELDebugHandler.onBreak cannot be found by JDI" ); } Method method = list.get( 0 ); if (method != null && !method.isNative()) { Location location = method.location(); if (location != null && location.codeIndex() != -1) { req = getEventRequestManager().createBreakpointRequest(location); req.addThreadFilter( ((ClassPrepareEvent) event).thread() ); req.setSuspendPolicy( EventRequest.SUSPEND_ALL ); } else { throw new IllegalStateException( "MVELDebugHandler.onBreak location cannot be found by JDI" ); } } else { throw new IllegalStateException( "MVELDebugHandler.onBreak cannot be found by JDI" ); } //breakpointCatched /*field= type.fieldByName(getFieldName()); Field field; EventRequest req= manager.createModificationWatchpointRequest(field); */ //req.setSuspendPolicy( EventRequest.SUSPEND_EVENT_THREAD ); addJDIEventListener(new IJDIEventListener() { public boolean handleEvent(Event event, JDIDebugTarget target) { BreakpointEvent entryEvent = (BreakpointEvent) event; //System.out.println( entryEvent + ":" + entryEvent.location() ); try { IThread[] tharr = getThreads(); ThreadReference t = null; DroolsThread t2 = null; for (int i = 0; i < tharr.length; i++) { DroolsThread th2 = (DroolsThread) tharr[i]; ThreadReference th2real = ((DroolsThread) tharr[i]).getUnderlyingThread(); if (th2real.suspendCount() == 1 && th2.getName().equals("main")) { t = th2real; t2 = (DroolsThread) th2; th2real.suspend(); th2.setRunning(false); th2.fireSuspendEvent(DebugEvent.CLIENT_REQUEST); return true; } } } catch (Exception t) { logError(t); } return true; } public void wonSuspendVote(Event event, JDIDebugTarget target) { // do nothing } public void eventSetComplete(Event event, JDIDebugTarget target, boolean suspend, EventSet eventSet) { wonSuspendVote(event, target); } public boolean handleEvent(Event event, JDIDebugTarget target, boolean suspendVote, EventSet eventSet) { return handleEvent(event, target); } }, req); req.enable(); // now remove the ClassPrepareEvent removeJDIEventListener(MVELTraceHandler.this, ((ClassPrepareEvent) event).request() ); return true; } /* (non-Javadoc) * @see org.eclipse.jdt.internal.debug.core.IJDIEventListener#wonSuspendVote(com.sun.jdi.event.Event, org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget) */ public void wonSuspendVote(Event event, JDIDebugTarget target) { // do nothing } public void eventSetComplete(Event event, JDIDebugTarget target, boolean suspend, EventSet eventSet) { wonSuspendVote(event, target); } public boolean handleEvent(Event event, JDIDebugTarget target, boolean suspendVote, EventSet eventSet) { return handleEvent(event, target); } } /** * Tries to find a match for the provided breakpoint information from the list of registered breakpoints. * For stepping and possibly other purposes it returns also a breakpoint for cases where exactly the same line was not found. * * If breakpoint is not found for <code>line</code> at <code>source</code> then it takes the first line that is above the * specified line at the same file. * * @param source * @param line * @return */ public DroolsLineBreakpoint getDroolsBreakpoint(String source) { if ( source == null ) { return null; } Iterator<IBreakpoint> iterator = getBreakpoints().iterator(); while ( iterator.hasNext() ) { IJavaBreakpoint element = (IJavaBreakpoint) iterator.next(); if ( element instanceof DroolsLineBreakpoint && ((DroolsLineBreakpoint) element).getDialectName().equals( "mvel" ) ) { DroolsLineBreakpoint l = (DroolsLineBreakpoint) element; try { int matchLine = l.getLineNumber(); String matchSource = l.getRuleName(); if ( source.equals( matchSource ) || l.getFileRuleMappings().containsKey( source ) ) { return l; } } catch ( CoreException e ) { logError( e ); } } } return null; } private void addRemoteBreakpoint(DroolsLineBreakpoint d) { try { if ( !d.isEnabled() ) { return; // No need to install disabled breakpoints } } catch ( CoreException e2 ) { logError( e2 ); return; // No need to install breakpoints that are this much broken } Iterator<ReferenceType> handleriter = getVM().classesByName( "org.drools.core.base.mvel.MVELDebugHandler" ).iterator(); ReferenceType debugHandlerClass = handleriter.next(); int line; String sourceName; try { line = d.getLineNumber(); sourceName = d.getTypeName(); } catch ( CoreException e1 ) { logError( e1 ); return; } ReferenceType refType = debugHandlerClass; Method m = (Method) refType.methodsByName( "registerBreakpoint" ).iterator().next(); List<Value> args = new ArrayList<Value>(); IntegerValue lineVal = getVM().mirrorOf( line ); StringReference nameVal = getVM().mirrorOf( sourceName ); JDIObjectValue val = (JDIObjectValue) newValue( sourceName ); ObjectReference realVal = val.getUnderlyingObject(); args.add( nameVal ); args.add( lineVal ); try { ClassType tt = (ClassType) debugHandlerClass; IThread[] tharr = getThreads(); ThreadReference t = null; DroolsThread t2 = null; for ( int i = 0; i < tharr.length; i++ ) { IThread th2 = tharr[i]; ThreadReference th2real = ((DroolsThread) tharr[i]).getUnderlyingThread(); if ( th2real.suspendCount() == 1 && th2.getName().equals( "main" ) ) { t = th2real; t2 = (DroolsThread) th2; } } tt.invokeMethod( t, m, args, ObjectReference.INVOKE_SINGLE_THREADED ); //t2.computeNewStackFrames(); super.breakpointAdded( d ); } catch ( Exception e ) { logError( e ); } } private void removeRemoteBreakpoint(DroolsLineBreakpoint d, IMarkerDelta delta) { Iterator<ReferenceType> handleriter = getVM().classesByName( "org.drools.core.base.mvel.MVELDebugHandler" ).iterator(); ReferenceType debugHandlerClass = handleriter.next(); int line; String sourceName; try { line = d.getLineNumber(); sourceName = d.getTypeName(); } catch ( CoreException e1 ) { logError( e1 ); return; } ReferenceType refType = debugHandlerClass; Method m = (Method) refType.methodsByName( "removeBreakpoint" ).iterator().next(); List<Value> args = new ArrayList<Value>(); IntegerValue lineVal = getVM().mirrorOf( line ); StringReference nameVal = getVM().mirrorOf( sourceName ); JDIObjectValue val = (JDIObjectValue) newValue( sourceName ); ObjectReference realVal = val.getUnderlyingObject(); args.add( nameVal ); args.add( lineVal ); try { ClassType tt = (ClassType) debugHandlerClass; IThread[] tharr = getThreads(); ThreadReference t = null; DroolsThread t2 = null; for ( int i = 0; i < tharr.length; i++ ) { IThread th2 = tharr[i]; ThreadReference th2real = ((DroolsThread) tharr[i]).getUnderlyingThread(); if ( th2real.suspendCount() == 1 && th2.getName().equals( "main" ) ) { t = th2real; t2 = (DroolsThread) th2; } } tt.invokeMethod( t, m, args, ObjectReference.INVOKE_SINGLE_THREADED ); } catch ( Exception e ) { logError( e ); } } }