Bug 290285 - API for custom JDWP commands and model state refresh
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java
index ddc3675..b1e5b50 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java
@@ -98,6 +98,7 @@
 import org.eclipse.jdt.debug.tests.sourcelookup.SourceLocationTests;
 import org.eclipse.jdt.debug.tests.sourcelookup.SourceLookupTests;
 import org.eclipse.jdt.debug.tests.sourcelookup.TypeResolutionTests;
+import org.eclipse.jdt.debug.tests.state.RefreshStateTests;
 import org.eclipse.jdt.debug.tests.ui.DetailPaneManagerTests;
 import org.eclipse.jdt.debug.tests.ui.ViewMangementTests;
 import org.eclipse.jdt.debug.tests.variables.TestLogicalStructures;
@@ -250,6 +251,8 @@
 		
 	// JDWP tests
 		addTest(new TestSuite(JDWPTests.class));
+	// Refresh state tests
+		addTest(new TestSuite(RefreshStateTests.class));		
 		
 	// HCR tests are last - they modify resources
 		addTest(new TestSuite(HcrTests.class));
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ProjectCreationDecorator.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ProjectCreationDecorator.java
index da0cc7a..367c91c 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ProjectCreationDecorator.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ProjectCreationDecorator.java
@@ -167,6 +167,7 @@
         createLaunchConfiguration("ThrowsNPE");
         createLaunchConfiguration("ThrowsException");
         createLaunchConfiguration("org.eclipse.debug.tests.targets.Watchpoint");
+        createLaunchConfiguration("org.eclipse.debug.tests.targets.CallLoop");
         createLaunchConfiguration("A");
         createLaunchConfiguration("HitCountLooper");
         createLaunchConfiguration("CompileError");
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/JDWPTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/JDWPTests.java
index 5d2c234..0ac88e9 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/JDWPTests.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/JDWPTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007 IBM Corporation and others.
+ * Copyright (c) 2007, 2009 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
@@ -19,6 +19,7 @@
 import org.eclipse.jdi.internal.jdwp.JdwpPacket;
 import org.eclipse.jdi.internal.jdwp.JdwpReplyPacket;
 import org.eclipse.jdi.internal.jdwp.JdwpString;
+import org.eclipse.jdt.debug.core.IJavaDebugTarget;
 import org.eclipse.jdt.debug.core.IJavaThread;
 import org.eclipse.jdt.debug.tests.AbstractDebugTest;
 import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
@@ -52,9 +53,9 @@
 			thread= launchToLineBreakpoint(typeName, bp);
 			IDebugTarget target = thread.getDebugTarget();
 			assertTrue("Wrong target", target instanceof JDIDebugTarget);
-			JDIDebugTarget jdiTarget = (JDIDebugTarget) target;
+			IJavaDebugTarget jdiTarget = (IJavaDebugTarget) target;
 			// VM capabilities
-			byte[] reply = jdiTarget.sendJDWPCommand((byte)1, (byte)12, null);
+			byte[] reply = jdiTarget.sendCommand((byte)1, (byte)12, null);
 			JdwpReplyPacket packet = (JdwpReplyPacket) JdwpPacket.build(reply);
 			
 			assertEquals("Unexpected error code in reply packet", 0, packet.errorCode());
@@ -84,12 +85,12 @@
 			thread= launchToLineBreakpoint(typeName, bp);
 			IDebugTarget target = thread.getDebugTarget();
 			assertTrue("Wrong target", target instanceof JDIDebugTarget);
-			JDIDebugTarget jdiTarget = (JDIDebugTarget) target;
+			IJavaDebugTarget jdiTarget = (IJavaDebugTarget) target;
 			// VM capabilities
 			ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
 			DataOutputStream outData = new DataOutputStream(outBytes);
 			JdwpString.write("LBreakpoints;", outData);
-			byte[] reply = jdiTarget.sendJDWPCommand((byte)1, (byte)2, outBytes.toByteArray());
+			byte[] reply = jdiTarget.sendCommand((byte)1, (byte)2, outBytes.toByteArray());
 			JdwpReplyPacket packet = (JdwpReplyPacket) JdwpPacket.build(reply);
 			assertEquals("Unexpected error code in reply packet", 0, packet.errorCode());
 			DataInputStream replyData = packet.dataInStream();
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/state/RefreshStateTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/state/RefreshStateTests.java
new file mode 100644
index 0000000..5be4b7f
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/state/RefreshStateTests.java
@@ -0,0 +1,312 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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.jdt.debug.tests.state;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.model.ILineBreakpoint;
+import org.eclipse.debug.core.model.IThread;
+import org.eclipse.jdi.internal.jdwp.JdwpPacket;
+import org.eclipse.jdi.internal.jdwp.JdwpReplyPacket;
+import org.eclipse.jdt.debug.core.IJavaBreakpoint;
+import org.eclipse.jdt.debug.core.IJavaDebugTarget;
+import org.eclipse.jdt.debug.core.IJavaLineBreakpoint;
+import org.eclipse.jdt.debug.core.IJavaObject;
+import org.eclipse.jdt.debug.core.IJavaThread;
+import org.eclipse.jdt.debug.testplugin.DebugElementEventWaiter;
+import org.eclipse.jdt.debug.tests.AbstractDebugTest;
+
+/**
+ * Tests that state refresh works after modifying the target state
+ * with a custom JDWP command.
+ * 
+ * @since 3.6
+ */
+public class RefreshStateTests extends AbstractDebugTest {
+
+	public RefreshStateTests(String name) {
+		super(name);
+	}
+	
+	/**
+	 * Resume a thread behind the scenes and ensure model state updates appropriately.
+	 * 
+	 * @throws CoreException
+	 */
+	public void testThreadHasResumed() throws Exception {
+		String typeName = "org.eclipse.debug.tests.targets.CallLoop";
+		ILineBreakpoint bp = createLineBreakpoint(16, "org.eclipse.debug.tests.targets.Looper");
+		
+		IJavaThread thread = null;
+		try {
+			thread= launchToLineBreakpoint(typeName, bp);
+			IJavaDebugTarget jdiTarget = (IJavaDebugTarget) thread.getDebugTarget();
+			// Resume thread (set 11, command 3)
+			IJavaObject reference = thread.getThreadObject();
+			ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
+			DataOutputStream outData = new DataOutputStream(outBytes);
+			outData.writeLong(reference.getUniqueId());
+			byte[] reply = jdiTarget.sendCommand((byte)11, (byte)3, outBytes.toByteArray());
+			JdwpReplyPacket packet = (JdwpReplyPacket) JdwpPacket.build(reply);
+			
+			assertEquals("Unexpected error code in reply packet", 0, packet.errorCode());
+			
+			// model should still be suspended
+			assertTrue("Model should be in suspended state", thread.isSuspended());
+			
+			// refresh the model, expect a resume event
+			DebugElementEventWaiter waiter = new DebugElementEventWaiter(DebugEvent.RESUME, thread);
+			jdiTarget.refreshState();
+			Object source = waiter.waitForEvent();
+			assertNotNull("Thread never sent resume event", source);
+			assertEquals("Wrong thread resumed", thread, source);
+			// model should now be in running state
+			assertFalse("Model should now be running", thread.isSuspended());
+		} finally {
+			terminateAndRemove(thread);
+			removeAllBreakpoints();
+		}	
+	}
+	
+	/**
+	 * Resume all threads behind the scenes and ensure model state updates appropriately.
+	 * 
+	 * @throws CoreException
+	 */
+	public void testAllThreadsResumed() throws Exception {
+		String typeName = "org.eclipse.debug.tests.targets.CallLoop";
+		IJavaLineBreakpoint bp = createLineBreakpoint(16, "org.eclipse.debug.tests.targets.Looper");
+		bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_VM);
+		
+		IJavaThread thread = null;
+		try {
+			thread= launchToLineBreakpoint(typeName, bp);
+			IJavaDebugTarget jdiTarget = (IJavaDebugTarget) thread.getDebugTarget();
+			// Resume each thread (set 11, command 3)
+			IThread[] threads = jdiTarget.getThreads();
+			for (int i = 0; i < threads.length; i++) {
+				IJavaThread jThread = (IJavaThread) threads[i];
+				IJavaObject reference = jThread.getThreadObject();
+				ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
+				DataOutputStream outData = new DataOutputStream(outBytes);
+				outData.writeLong(reference.getUniqueId());
+				byte[] reply = jdiTarget.sendCommand((byte)11, (byte)3, outBytes.toByteArray());
+				JdwpReplyPacket packet = (JdwpReplyPacket) JdwpPacket.build(reply);
+				assertEquals("Unexpected error code in reply packet", 0, packet.errorCode());
+			}
+			
+			// model should still be suspended
+			assertTrue("Model should be in suspended state", jdiTarget.isSuspended());
+			
+			// refresh the model, expect a resume event
+			DebugElementEventWaiter waiter = new DebugElementEventWaiter(DebugEvent.RESUME, jdiTarget);
+			jdiTarget.refreshState();
+			Object source = waiter.waitForEvent();
+			assertNotNull("Thread never sent resume event", source);
+			// model should now be in running state
+			assertFalse("Model should now be running", jdiTarget.isSuspended());
+		} finally {
+			terminateAndRemove(thread);
+			removeAllBreakpoints();
+		}	
+	}
+	
+	/**
+	 * Suspend a thread behind the scenes and ensure model state updates appropriately.
+	 * 
+	 * @throws CoreException
+	 */
+	public void testThreadHasSuspended() throws Exception {
+		String typeName = "org.eclipse.debug.tests.targets.CallLoop";
+		ILineBreakpoint bp = createLineBreakpoint(16, "org.eclipse.debug.tests.targets.Looper");
+		
+		IJavaThread thread = null;
+		try {
+			thread= launchToLineBreakpoint(typeName, bp);
+			IJavaDebugTarget jdiTarget = (IJavaDebugTarget) thread.getDebugTarget();
+			// resume the thread to get into running state
+			DebugElementEventWaiter waiter = new DebugElementEventWaiter(DebugEvent.RESUME, thread);
+			thread.resume();
+			waiter.waitForEvent();
+			// model should now be in running state
+			assertFalse("Model should now be running", thread.isSuspended());
+			
+			// suspend the thread with a JDWP command (set 11, command 2)
+			IJavaObject reference = thread.getThreadObject();
+			ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
+			DataOutputStream outData = new DataOutputStream(outBytes);
+			outData.writeLong(reference.getUniqueId());
+			byte[] reply = jdiTarget.sendCommand((byte)11, (byte)2, outBytes.toByteArray());
+			JdwpReplyPacket packet = (JdwpReplyPacket) JdwpPacket.build(reply);
+			
+			assertEquals("Unexpected error code in reply packet", 0, packet.errorCode());
+			
+			// model should still be running
+			assertFalse("Model should be in running state", thread.isSuspended());
+			
+			// refresh the model, expect a suspend event
+			waiter = new DebugElementEventWaiter(DebugEvent.SUSPEND, thread);
+			jdiTarget.refreshState();
+			Object source = waiter.waitForEvent();
+			assertNotNull("Thread never sent suspend event", source);
+			assertEquals("Wrong thread suspended", thread, source);
+			
+			// model should be suspended now
+			assertTrue("Model should be in suspended state", thread.isSuspended());
+			
+		} finally {
+			terminateAndRemove(thread);
+			removeAllBreakpoints();
+		}	
+	}	
+	
+	/**
+	 * Suspend all threads behind the scenes and ensure model state updates appropriately.
+	 * 
+	 * @throws CoreException
+	 */
+	public void testAllThreadsSuspended() throws Exception {
+		String typeName = "org.eclipse.debug.tests.targets.CallLoop";
+		ILineBreakpoint bp = createLineBreakpoint(16, "org.eclipse.debug.tests.targets.Looper");
+		
+		IJavaThread thread = null;
+		try {
+			thread= launchToLineBreakpoint(typeName, bp);
+			IJavaDebugTarget jdiTarget = (IJavaDebugTarget) thread.getDebugTarget();
+			// resume the thread to get into running state
+			DebugElementEventWaiter waiter = new DebugElementEventWaiter(DebugEvent.RESUME, thread);
+			thread.resume();
+			waiter.waitForEvent();
+			// model should now be in running state
+			assertFalse("Model should now be running", thread.isSuspended());
+			
+			IThread[] threads = jdiTarget.getThreads();
+			for (int i = 0; i < threads.length; i++) {
+				IJavaThread jThread = (IJavaThread) threads[i];
+				// suspend each thread with a JDWP command (set 11, command 2)
+				IJavaObject reference = jThread.getThreadObject();
+				ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
+				DataOutputStream outData = new DataOutputStream(outBytes);
+				outData.writeLong(reference.getUniqueId());
+				byte[] reply = jdiTarget.sendCommand((byte)11, (byte)2, outBytes.toByteArray());
+				JdwpReplyPacket packet = (JdwpReplyPacket) JdwpPacket.build(reply);
+				assertEquals("Unexpected error code in reply packet", 0, packet.errorCode());
+			}
+			
+			
+			// model should still be running
+			assertFalse("Model should be in running state", jdiTarget.isSuspended());
+			
+			// refresh the model, expect a suspend event
+			waiter = new DebugElementEventWaiter(DebugEvent.SUSPEND, jdiTarget);
+			jdiTarget.refreshState();
+			Object source = waiter.waitForEvent();
+			assertNotNull("Target never sent suspend event", source);
+			
+			// model should be suspended now
+			assertTrue("Model should be in suspended state", jdiTarget.isSuspended());
+			
+		} finally {
+			terminateAndRemove(thread);
+			removeAllBreakpoints();
+		}	
+	}		
+
+	/**
+	 * Suspend the entire target behind the scenes and ensure model state updates appropriately.
+	 * 
+	 * @throws CoreException
+	 */
+	public void testTargetHasSuspended() throws Exception {
+		String typeName = "org.eclipse.debug.tests.targets.CallLoop";
+		ILineBreakpoint bp = createLineBreakpoint(16, "org.eclipse.debug.tests.targets.Looper");
+		
+		IJavaThread thread = null;
+		try {
+			thread= launchToLineBreakpoint(typeName, bp);
+			IJavaDebugTarget jdiTarget = (IJavaDebugTarget) thread.getDebugTarget();
+			// resume the thread to get into running state
+			DebugElementEventWaiter waiter = new DebugElementEventWaiter(DebugEvent.RESUME, thread);
+			thread.resume();
+			waiter.waitForEvent();
+			// model should now be in running state
+			assertFalse("Model should now be running", thread.isSuspended());
+			
+			// suspend the target with a JDWP command (set 1, command 8)
+			IJavaObject reference = thread.getThreadObject();
+			ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
+			DataOutputStream outData = new DataOutputStream(outBytes);
+			outData.writeLong(reference.getUniqueId());
+			byte[] reply = jdiTarget.sendCommand((byte)1, (byte)8, outBytes.toByteArray());
+			JdwpReplyPacket packet = (JdwpReplyPacket) JdwpPacket.build(reply);
+			
+			assertEquals("Unexpected error code in reply packet", 0, packet.errorCode());
+			
+			// model should still be running
+			assertFalse("Model should be in running state", jdiTarget.isSuspended());
+			
+			// refresh the model, expect a suspend event
+			waiter = new DebugElementEventWaiter(DebugEvent.SUSPEND, jdiTarget);
+			jdiTarget.refreshState();
+			Object source = waiter.waitForEvent();
+			assertNotNull("Target never sent suspend event", source);
+			
+			// model should be suspended now
+			assertTrue("Model should be in suspended state", jdiTarget.isSuspended());
+			
+		} finally {
+			terminateAndRemove(thread);
+			removeAllBreakpoints();
+		}	
+	}		
+	
+	/**
+	 * Resume a target behind the scenes and ensure model state updates appropriately.
+	 * 
+	 * @throws CoreException
+	 */
+	public void testTargetHasResumed() throws Exception {
+		String typeName = "org.eclipse.debug.tests.targets.CallLoop";
+		IJavaLineBreakpoint bp = createLineBreakpoint(16, "org.eclipse.debug.tests.targets.Looper");
+		bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_VM);
+		
+		IJavaThread thread = null;
+		try {
+			thread= launchToLineBreakpoint(typeName, bp);
+			IJavaDebugTarget jdiTarget = (IJavaDebugTarget) thread.getDebugTarget();
+			// Resume target (set 1, command 9)
+			IJavaObject reference = thread.getThreadObject();
+			ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
+			DataOutputStream outData = new DataOutputStream(outBytes);
+			outData.writeLong(reference.getUniqueId());
+			byte[] reply = jdiTarget.sendCommand((byte)1, (byte)9, outBytes.toByteArray());
+			JdwpReplyPacket packet = (JdwpReplyPacket) JdwpPacket.build(reply);
+			
+			assertEquals("Unexpected error code in reply packet", 0, packet.errorCode());
+			
+			// model should still be suspended
+			assertTrue("Model should be in suspended state", jdiTarget.isSuspended());
+			
+			// refresh the model, expect a resume event
+			DebugElementEventWaiter waiter = new DebugElementEventWaiter(DebugEvent.RESUME, jdiTarget);
+			jdiTarget.refreshState();
+			Object source = waiter.waitForEvent();
+			assertNotNull("Target never sent resume event", source);
+			// model should now be in running state
+			assertFalse("Model should now be running", jdiTarget.isSuspended());
+		} finally {
+			terminateAndRemove(thread);
+			removeAllBreakpoints();
+		}	
+	}	
+}
diff --git a/org.eclipse.jdt.debug/META-INF/MANIFEST.MF b/org.eclipse.jdt.debug/META-INF/MANIFEST.MF
index d06fbef..a614e07 100644
--- a/org.eclipse.jdt.debug/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.debug/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.jdt.debug; singleton:=true
-Bundle-Version: 3.5.100.qualifier
+Bundle-Version: 3.6.0.qualifier
 Bundle-ClassPath: jdi.jar,
  jdimodel.jar,
  tools.jar
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaDebugTarget.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaDebugTarget.java
index 7b4a1ec..2f7eada 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaDebugTarget.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaDebugTarget.java
@@ -450,4 +450,34 @@
 	 */
 	public String getVersion() throws DebugException;	
 	
+	/**
+	 * Refreshes the state of the Java debug model elements (client) with the current
+	 * state of the debug target.
+	 * <p>
+	 * For example, a {@link IJavaThread} may currently have a suspended state, but was
+	 * somehow resumed on the target. Calling this method will causes all threads to update
+	 * their state based on the current state of the target. Elements will fire debug events
+	 * associated with any state changes. For example, a thread would fire a resume event
+	 * if it discovered it was in a running state when it thought it was suspended.
+	 * </p>
+	 * @throws DebugException if an exception occurs
+	 * @since 3.6
+	 */
+	public void refreshState() throws DebugException;
+	
+	/**
+	 * Sends a JDWP command to the back end and returns the JDWP reply packet as bytes.
+	 * This method creates an appropriate command header and packet id, before sending
+	 * to the back end.
+	 * 
+	 * @param commandSet command set identifier as defined by JDWP
+	 * @param commandId command identifier as defined by JDWP
+	 * @param data any bytes required for the command that follow the command header
+	 * 	or <code>null</code> for commands that have no data
+	 * @return raw reply packet as bytes defined by JDWP
+	 * @exception DebugException if an error occurs sending the packet or receiving the reply
+	 * @since 3.6
+	 */
+	public byte[] sendCommand(byte commandSet, byte commandId, byte[] data) throws DebugException;	
+	
 }
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaThread.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaThread.java
index 8336d94..048386f 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaThread.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaThread.java
@@ -271,5 +271,14 @@
 	 * @since 3.3
 	 */
 	public int getFrameCount() throws DebugException;
+	
+	/**
+	 * Returns the object reference associated with this thread.
+	 * 
+	 * @return thread object reference 
+	 * @throws DebugException if unable to retrieve an object reference
+	 * @since 3.6
+	 */
+	public IJavaObject getThreadObject() throws DebugException;
 
 }
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIDebugTarget.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIDebugTarget.java
index c04a51f..6230881 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIDebugTarget.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIDebugTarget.java
@@ -22,6 +22,7 @@
 
 import org.eclipse.core.resources.IMarkerDelta;
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.OperationCanceledException;
@@ -36,9 +37,14 @@
 import org.eclipse.debug.core.ILaunch;
 import org.eclipse.debug.core.ILaunchListener;
 import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.core.model.IDebugElement;
 import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.IDisconnect;
 import org.eclipse.debug.core.model.IMemoryBlock;
+import org.eclipse.debug.core.model.IMemoryBlockRetrieval;
 import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.core.model.ISuspendResume;
+import org.eclipse.debug.core.model.ITerminate;
 import org.eclipse.debug.core.model.IThread;
 import org.eclipse.jdi.TimeoutException;
 import org.eclipse.jdi.internal.VirtualMachineImpl;
@@ -61,6 +67,7 @@
 import org.eclipse.jdt.internal.debug.core.breakpoints.JavaLineBreakpoint;
 
 import com.ibm.icu.text.MessageFormat;
+import com.sun.jdi.InternalException;
 import com.sun.jdi.ObjectCollectedException;
 import com.sun.jdi.ReferenceType;
 import com.sun.jdi.ThreadGroupReference;
@@ -2526,5 +2533,122 @@
 			// #targetRequestFailed will throw an exception				
 			return null;
 		}
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.debug.core.IJavaDebugTarget#refreshState()
+	 */
+	public void refreshState() throws DebugException {
+		if (isTerminated() || isDisconnected()) {
+			return;
+		}
+		boolean prevSuspend = isSuspended();
+		int running = 0;
+		int suspended = 0;
+		List toSuspend = new ArrayList();
+		List toResume = new ArrayList();
+		List toRefresh = new ArrayList();
+		Iterator iterator = getThreadIterator();
+		while (iterator.hasNext()) {
+			JDIThread thread = (JDIThread) iterator.next();
+			boolean modelSuspended = thread.isSuspended(); 
+			ThreadReference reference = thread.getUnderlyingThread();
+			try {
+				boolean realSuspended = reference.isSuspended();
+				if (realSuspended) {
+					suspended++;
+					if (modelSuspended) {
+						// Even if the model is suspended, it might be in a different location so refresh
+						toRefresh.add(thread);
+					} else {
+						// The thread is actually suspended, refresh frames and fire suspend event.
+						toSuspend.add(thread);
+					}
+				} else {
+					running++;
+					if (modelSuspended) {
+						// thread is actually running, model is suspended, resume model 
+						toResume.add(thread);
+					}
+					// else both are running - OK
+				}
+			} catch (InternalException e) {
+				requestFailed(e.getMessage(), e);
+			}
+		}
+		// if the entire target changed state/fire events at target level, else fire thread events
+		boolean targetLevelEvent = false;
+		if (prevSuspend) {
+			if (running > 0) {
+				// was suspended, but now a thread is running
+				targetLevelEvent = true;
+			}
+		} else {
+			if (running == 0) {
+				// was running, but now all threads are suspended
+				targetLevelEvent = true;
+			}
+		}
+		if (targetLevelEvent) {
+			iterator = toSuspend.iterator();
+			while (iterator.hasNext()) {
+				JDIThread thread = (JDIThread) iterator.next();
+				thread.suspendedByVM();
+			}
+			iterator = toResume.iterator();
+			while (iterator.hasNext()) {
+				JDIThread thread = (JDIThread) iterator.next();
+				thread.resumedByVM();
+			}
+			iterator = toRefresh.iterator();
+			while (iterator.hasNext()) {
+				JDIThread thread = (JDIThread) iterator.next();
+				thread.preserveStackFrames();
+			}
+			if (running == 0) {
+				synchronized (this) {
+					setSuspended(true);
+				}
+				fireSuspendEvent(DebugEvent.CLIENT_REQUEST);
+			} else {
+				synchronized (this) {
+					setSuspended(false);
+				}
+				fireResumeEvent(DebugEvent.CLIENT_REQUEST);
+			}
+		} else {
+			iterator = toSuspend.iterator();
+			while (iterator.hasNext()) {
+				JDIThread thread = (JDIThread) iterator.next();
+				thread.preserveStackFrames();
+				thread.setRunning(false);
+				thread.fireSuspendEvent(DebugEvent.CLIENT_REQUEST);
+			}
+			iterator = toResume.iterator();
+			while (iterator.hasNext()) {
+				JDIThread thread = (JDIThread) iterator.next();
+				thread.setRunning(true);
+				thread.fireResumeEvent(DebugEvent.CLIENT_REQUEST);
+			}
+			iterator = toRefresh.iterator();
+			while (iterator.hasNext()) {
+				JDIThread thread = (JDIThread) iterator.next();
+				thread.preserveStackFrames();
+				thread.fireSuspendEvent(DebugEvent.CLIENT_REQUEST);
+			}
+		}
+			 
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.debug.core.IJavaDebugTarget#sendCommand(byte, byte, byte[])
+	 */
+	public byte[] sendCommand(byte commandSet, byte commandId, byte[] data) throws DebugException {
+		try {
+			return sendJDWPCommand(commandSet, commandId, data);
+		} catch (IOException e) {
+			requestFailed(e.getMessage(), e);
+		}
+		return null;
 	}	
 }
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIThread.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIThread.java
index 389e18c..e674801 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIThread.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIThread.java
@@ -19,6 +19,7 @@
 import java.util.Vector;
 
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.MultiStatus;
@@ -31,7 +32,10 @@
 import org.eclipse.debug.core.IStatusHandler;
 import org.eclipse.debug.core.model.IBreakpoint;
 import org.eclipse.debug.core.model.IStackFrame;
+import org.eclipse.debug.core.model.IStep;
+import org.eclipse.debug.core.model.ISuspendResume;
 import org.eclipse.debug.core.model.ITerminate;
+import org.eclipse.debug.core.model.IThread;
 import org.eclipse.jdt.core.Signature;
 import org.eclipse.jdt.debug.core.IEvaluationRunnable;
 import org.eclipse.jdt.debug.core.IJavaBreakpoint;
@@ -2891,4 +2895,12 @@
 	public synchronized boolean isSuspendVoteInProgress() {
 		return fSuspendVoteInProgress;
 	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.debug.core.IJavaThread#getThreadObject()
+	 */
+	public IJavaObject getThreadObject() throws DebugException {
+		return (IJavaObject) JDIValue.createValue(getJavaDebugTarget(), fThread);
+	}
+	
 }
\ No newline at end of file