Bug 516043 - Ignore "disconnected" exceptions during disconnect

Introduced "fDisconnecting" flag, with similar implementation/behavior
on disconnect() and disconnected() as "fTerminating" and
terminate()/terminated(). isAvailable() checks this flag now.

Change-Id: Iab361cd22799971253ee756b4a52d1594faa87c8
Signed-off-by: Andrey Loskutov <loskutov@gmx.de>
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/JavaDebugTargetTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/JavaDebugTargetTests.java
index e45d6e0..82681a9 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/JavaDebugTargetTests.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/JavaDebugTargetTests.java
@@ -10,9 +10,12 @@
  *******************************************************************************/
 package org.eclipse.jdt.debug.tests.core;
 
+import java.lang.reflect.Method;
+
 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;
 
 /**
  * Tests IJavaDebugTarget API
@@ -61,4 +64,92 @@
 		}
 	}
 
+	public void testIsAvailable() throws Exception {
+		String typeName = "Breakpoints";
+		createLineBreakpoint(52, typeName);
+
+		IJavaThread thread = null;
+		try {
+			// do not register launch - see bug 130911
+			thread = launchToBreakpoint(typeName, false);
+			assertNotNull("Breakpoint not hit within timeout period", thread);
+			JDIDebugTarget target = (JDIDebugTarget) thread.getDebugTarget();
+			assertTrue(target.isAvailable());
+			JDIDebugTargetProxy proxy = new JDIDebugTargetProxy(target);
+			assertTrue(proxy.isAvailable());
+			assertFalse(proxy.isDisconnecting());
+			assertFalse(proxy.isTerminating());
+
+			proxy.setDisconnecting(true);
+			assertFalse(proxy.isAvailable());
+			assertTrue(proxy.isDisconnecting());
+			assertFalse(proxy.isTerminating());
+
+			proxy.setDisconnecting(false);
+			assertTrue(proxy.isAvailable());
+			assertFalse(proxy.isDisconnecting());
+			assertFalse(proxy.isTerminating());
+
+			proxy.setTerminating(true);
+			assertFalse(proxy.isAvailable());
+			assertFalse(proxy.isDisconnecting());
+			assertTrue(proxy.isTerminating());
+
+			proxy.setTerminating(false);
+			assertTrue(proxy.isAvailable());
+			assertFalse(proxy.isDisconnecting());
+			assertFalse(proxy.isTerminating());
+
+			terminateAndRemove(thread);
+			assertFalse(proxy.isAvailable());
+			assertFalse(proxy.isDisconnecting());
+			assertFalse(proxy.isTerminating());
+		}
+		finally {
+			terminateAndRemove(thread);
+			removeAllBreakpoints();
+		}
+	}
+
+	static private class JDIDebugTargetProxy {
+
+		private JDIDebugTarget target;
+
+		public JDIDebugTargetProxy(JDIDebugTarget target) {
+			this.target = target;
+		}
+
+		public boolean isAvailable() {
+			return target.isAvailable();
+		}
+
+		public boolean isTerminating() throws Exception {
+			return callBooleanGetMethod("isTerminating");
+		}
+
+		public boolean isDisconnecting() throws Exception {
+			return callBooleanGetMethod("isDisconnecting");
+		}
+
+		public void setTerminating(boolean terminating) throws Exception {
+			callBooleanSetMethod("setTerminating", terminating);
+		}
+
+		public void setDisconnecting(boolean disconnecting) throws Exception {
+			callBooleanSetMethod("setDisconnecting", disconnecting);
+		}
+
+		private boolean callBooleanGetMethod(String name) throws Exception {
+			Method method = JDIDebugTarget.class.getDeclaredMethod(name);
+			method.setAccessible(true);
+			return (Boolean) method.invoke(target);
+		}
+
+		private void callBooleanSetMethod(String name, boolean arg) throws Exception {
+			Method method = JDIDebugTarget.class.getDeclaredMethod(name, boolean.class);
+			method.setAccessible(true);
+			method.invoke(target, Boolean.valueOf(arg));
+		}
+	}
+
 }
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 519f877..5c49532 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
@@ -160,6 +160,12 @@
 	 * Whether in the process of terminating
 	 */
 	private boolean fTerminating;
+
+	/**
+	 * Whether in the process of disconnecting
+	 */
+	private boolean fDisconnecting;
+
 	/**
 	 * Whether disconnected
 	 */
@@ -349,6 +355,7 @@
 		setTerminated(false);
 		setTerminating(false);
 		setDisconnected(false);
+		setDisconnecting(false);
 		setName(name);
 		prepareBreakpointsSearchScope();
 		setBreakpoints(new ArrayList<IBreakpoint>(5));
@@ -862,6 +869,7 @@
 		}
 
 		try {
+			setDisconnecting(true);
 			disposeThreadHandler();
 			VirtualMachine vm = getVM();
 			if (vm != null) {
@@ -1136,7 +1144,7 @@
 	 * Returns whether this target is available to handle VM requests
 	 */
 	public boolean isAvailable() {
-		return !(isTerminated() || isTerminating() || isDisconnected());
+		return !(isTerminated() || isTerminating() || isDisconnected() || isDisconnecting());
 	}
 
 	/**
@@ -1839,6 +1847,7 @@
 	 */
 	@Override
 	protected void disconnected() {
+		setDisconnecting(false);
 		if (!isDisconnected()) {
 			setDisconnected(true);
 			cleanup();
@@ -2262,6 +2271,14 @@
 		fTerminating = terminating;
 	}
 
+	protected boolean isDisconnecting() {
+		return fDisconnecting;
+	}
+
+	protected void setDisconnecting(boolean disconnecting) {
+		fDisconnecting = disconnecting;
+	}
+
 	/**
 	 * An event handler for thread start events. When a thread starts in the
 	 * target VM, a model thread is created.