Bug 511696: [otdre] debugger needs to refresh internal cache after
redefineClasses
diff --git a/plugins/org.eclipse.objectteams.otdt.debug.adaptor/META-INF/MANIFEST.MF b/plugins/org.eclipse.objectteams.otdt.debug.adaptor/META-INF/MANIFEST.MF
index 390f579..e6e5c37 100644
--- a/plugins/org.eclipse.objectteams.otdt.debug.adaptor/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.objectteams.otdt.debug.adaptor/META-INF/MANIFEST.MF
@@ -28,6 +28,7 @@
 Bundle-Activator: org.eclipse.objectteams.otdt.internal.debug.adaptor.OTDebugAdaptorPlugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
 Bundle-ActivationPolicy: lazy
-Export-Package: org.eclipse.objectteams.otdt.internal.debug.adaptor;x-internal:=true;ot-aspect-host="org.eclipse.objectteams.otdt.debug.adaptor",
- org.eclipse.objectteams.otdt.internal.debug.adaptor.actions;x-internal:=true;ot-aspect-host="org.eclipse.objectteams.otdt.debug.adaptor",
- org.eclipse.objectteams.otdt.internal.debug.adaptor.launching;x-internal:=true;ot-aspect-host="org.eclipse.objectteams.otdt.debug.adaptor"
+Export-Package: org.eclipse.objectteams.otdt.internal.debug.adaptor;ot-aspect-host="org.eclipse.objectteams.otdt.debug.adaptor";x-internal:=true,
+ org.eclipse.objectteams.otdt.internal.debug.adaptor.actions;ot-aspect-host="org.eclipse.objectteams.otdt.debug.adaptor";x-internal:=true,
+ org.eclipse.objectteams.otdt.internal.debug.adaptor.dynamic;ot-aspect-host="org.eclipse.objectteams.otdt.debug.adaptor";x-internal:=true,
+ org.eclipse.objectteams.otdt.internal.debug.adaptor.launching;ot-aspect-host="org.eclipse.objectteams.otdt.debug.adaptor";x-internal:=true
diff --git a/plugins/org.eclipse.objectteams.otdt.debug.adaptor/plugin.xml b/plugins/org.eclipse.objectteams.otdt.debug.adaptor/plugin.xml
index 29ac957..200ce84 100644
--- a/plugins/org.eclipse.objectteams.otdt.debug.adaptor/plugin.xml
+++ b/plugins/org.eclipse.objectteams.otdt.debug.adaptor/plugin.xml
@@ -74,6 +74,11 @@
                class="org.eclipse.objectteams.otdt.internal.debug.adaptor.SourceLookupAdaptor"
                icon="platform:/plugin/org.eclipse.objectteams.otdt.ui/icons/ot/team_obj.gif">
          </team>
+         <team
+               activation="NONE"
+               class="org.eclipse.objectteams.otdt.internal.debug.adaptor.dynamic.RedefineClassesBPListener"
+               icon="platform:/plugin/org.eclipse.objectteams.otdt.ui/icons/ot/team_obj.gif">
+         </team>
       </aspectBinding>
      <aspectBinding
            icon="platform:/plugin/org.eclipse.objectteams.otdt.ui/icons/ot/calloutbinding_obj.gif">
diff --git a/plugins/org.eclipse.objectteams.otdt.debug.adaptor/src/org/eclipse/objectteams/otdt/internal/debug/adaptor/OTDebugAdaptorPlugin.java b/plugins/org.eclipse.objectteams.otdt.debug.adaptor/src/org/eclipse/objectteams/otdt/internal/debug/adaptor/OTDebugAdaptorPlugin.java
index 2eb31bf..a0956c3 100644
--- a/plugins/org.eclipse.objectteams.otdt.debug.adaptor/src/org/eclipse/objectteams/otdt/internal/debug/adaptor/OTDebugAdaptorPlugin.java
+++ b/plugins/org.eclipse.objectteams.otdt.debug.adaptor/src/org/eclipse/objectteams/otdt/internal/debug/adaptor/OTDebugAdaptorPlugin.java
@@ -42,4 +42,9 @@
 	public static void logError(String msg) {
 		instance.getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, msg));
 	}
+	
+	
+	public static void logException(String msg, Exception ex) {
+		instance.getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, msg, ex));
+	}
 }
diff --git a/plugins/org.eclipse.objectteams.otdt.debug.adaptor/src/org/eclipse/objectteams/otdt/internal/debug/adaptor/dynamic/RedefineClassesBPListener.java b/plugins/org.eclipse.objectteams.otdt.debug.adaptor/src/org/eclipse/objectteams/otdt/internal/debug/adaptor/dynamic/RedefineClassesBPListener.java
new file mode 100644
index 0000000..7d8f654
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otdt.debug.adaptor/src/org/eclipse/objectteams/otdt/internal/debug/adaptor/dynamic/RedefineClassesBPListener.java
@@ -0,0 +1,170 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2017 GK Software AG
+ *  
+ * 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
+ * 
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ * 
+ * Contributors:
+ * 	Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otdt.internal.debug.adaptor.dynamic;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IBreakpointManager;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.IStackFrame;
+import org.eclipse.debug.core.model.IValue;
+import org.eclipse.debug.core.model.IVariable;
+import org.eclipse.jdi.internal.ReferenceTypeImpl;
+import org.eclipse.jdi.internal.ValueCache;
+import org.eclipse.jdi.internal.jdwp.JdwpReferenceTypeID;
+import org.eclipse.jdt.core.dom.Message;
+import org.eclipse.jdt.debug.core.IJavaArray;
+import org.eclipse.jdt.debug.core.IJavaBreakpoint;
+import org.eclipse.jdt.debug.core.IJavaBreakpointListener;
+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.core.IJavaType;
+import org.eclipse.jdt.debug.core.IJavaValue;
+import org.eclipse.jdt.debug.core.JDIDebugModel;
+import org.eclipse.objectteams.otdt.core.ext.WeavingScheme;
+import org.eclipse.objectteams.otdt.debug.internal.breakpoints.ClassRedefinitionBreakpoint;
+import org.eclipse.objectteams.otdt.internal.debug.adaptor.OTDebugAdaptorPlugin;
+
+import com.sun.jdi.VirtualMachine;
+
+import base org.eclipse.jdi.internal.VirtualMachineImpl;
+
+/**
+ * Listen to breakpoint hits on InstrumentationImpl.redefineClasses in order to
+ * <ul>
+ * <li>create obsolete {@link ReferenceTypeImpl} from internal cache
+ * <li>refresh breakpoints in the affected type
+ * </ul>
+ * Implemented as a team only to gain access to the cache inside {@link VirtualMachineImpl}.
+ */
+@SuppressWarnings("restriction")
+public team class RedefineClassesBPListener implements IJavaBreakpointListener {
+
+	/**
+	 * Conditionally get a breakpoint listener for class redefinition events (only for OTDRE). 
+	 */
+	public static IJavaBreakpointListener get(WeavingScheme scheme) {
+    	if (scheme == WeavingScheme.OTDRE)
+    		return new RedefineClassesBPListener();
+		return null;
+	}
+
+	@Override
+	public int breakpointHit(IJavaThread thread, IJavaBreakpoint breakpoint) {
+	    if (!breakpoint.getMarker().exists())
+	    	return DONT_CARE;
+	    try {
+		    if (ClassRedefinitionBreakpoint.isRedefineClassesBreakpoint(breakpoint)) {
+		    	handleClassRedefinition(thread);
+				return IJavaBreakpointListener.DONT_SUSPEND;
+		    }
+	    }
+	    catch (Exception ex) {
+	 		OTDebugAdaptorPlugin.logException("RedefineClassesBPListener can't read infos from debugTarget anymore. Disconnected?", ex); //$NON-NLS-1$
+			// if something fails, let the debugger go on
+			return IJavaBreakpointListener.DONT_SUSPEND;
+		}
+
+	    return IJavaBreakpointListener.DONT_CARE;
+	}
+
+	private void handleClassRedefinition(IJavaThread thread) throws DebugException {
+		IStackFrame frame = thread.getTopStackFrame();
+		IVariable[] variables = frame.getVariables(); 
+		IValue values = variables[2].getValue();
+		if (values instanceof IJavaArray) {
+			IJavaValue[] arrayValues = ((IJavaArray) values).getValues();
+			for (IJavaValue value : arrayValues) {
+				if (value instanceof IJavaObject) {
+					IJavaValue clazz = ((IJavaObject) value).sendMessage("getDefinitionClass", "()Ljava/lang/Class;", null, thread, false);
+					IJavaValue name = ((IJavaObject) clazz).sendMessage("getName", "()Ljava/lang/String;", null, thread, false);
+					String className = name.getValueString();
+					VirtualMachine vm = ((org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget) thread.getDebugTarget()).getVM();
+					removeTypeFromCache((org.eclipse.jdi.internal.VirtualMachineImpl) vm, className);
+					updateBreakpoints(thread.getDebugTarget(), className);
+				}
+			}
+		}
+	}
+
+	private void removeTypeFromCache(VirtualMachineImpl as VM vm, String typeName) {
+		vm.removeTypeFromCache(typeName);
+	}
+
+	/**
+	 * Gateway to inaccessible cache of ReferenceTypeImpl
+	 */
+	protected class VM playedBy VirtualMachineImpl {
+		@SuppressWarnings("decapsulation")
+		ValueCache getCachedReftypes() -> get ValueCache fCachedReftypes;
+		
+		protected void removeTypeFromCache(String typeName) {
+			List<JdwpReferenceTypeID> found = new ArrayList<>();
+			ValueCache cache = getCachedReftypes();
+			for (Object value : cache.values()) {
+				if (value instanceof ReferenceTypeImpl) { 
+					ReferenceTypeImpl refType = (ReferenceTypeImpl) value;
+					if (refType.name().equals(typeName))
+						found.add(refType.getRefTypeID());
+				}
+			}
+			/* alternative to above loop (but involves a JDWP request:
+			   List<ReferenceType> classes = target.jdiClassesByName(name);
+			 */
+			for (JdwpReferenceTypeID id : found) {
+				cache.remove(id);
+			}
+		}
+	}
+
+	private void updateBreakpoints(IDebugTarget debugTarget, String className) {
+		// cf. JavaHotCodeReplaceManager.redefineTypesJDK() -> target.reinstallBreakpointsIn()
+        IBreakpointManager breakpointManager = DebugPlugin.getDefault().getBreakpointManager();
+        IBreakpoint[] breakpoints = breakpointManager.getBreakpoints(JDIDebugModel.getPluginIdentifier());
+        for (IBreakpoint breakpoint : breakpoints) {
+			if (breakpoint instanceof IJavaLineBreakpoint) {
+				IJavaLineBreakpoint lineBreakpoint = (IJavaLineBreakpoint) breakpoint;
+				try {
+					if (lineBreakpoint.getTypeName().equals(className)) {
+						debugTarget.breakpointRemoved(lineBreakpoint, null);
+						debugTarget.breakpointAdded(lineBreakpoint);
+					}
+				} catch (CoreException e) {
+					OTDebugAdaptorPlugin.logException("Failed to update breakpoint", e);
+				}
+			}
+		}
+	}
+
+	// --- empty implementation of unused hooks: ---
+	@Override
+	public int installingBreakpoint(IJavaDebugTarget target, IJavaBreakpoint breakpoint, IJavaType type) {
+		return IJavaBreakpointListener.DONT_CARE;
+	}
+
+	@Override public void addingBreakpoint(IJavaDebugTarget target, IJavaBreakpoint breakpoint) { }
+	@Override public void breakpointInstalled(IJavaDebugTarget target, IJavaBreakpoint breakpoint) { }
+	@Override public void breakpointRemoved(IJavaDebugTarget target, IJavaBreakpoint breakpoint) { }
+	@Override public void breakpointHasRuntimeException(IJavaLineBreakpoint breakpoint, DebugException exception) { }
+	@Override public void breakpointHasCompilationErrors(IJavaLineBreakpoint breakpoint, Message[] errors) { }
+}
diff --git a/plugins/org.eclipse.objectteams.otdt.debug.adaptor/src/org/eclipse/objectteams/otdt/internal/debug/adaptor/launching/JDTLaunchingAdaptor.java b/plugins/org.eclipse.objectteams.otdt.debug.adaptor/src/org/eclipse/objectteams/otdt/internal/debug/adaptor/launching/JDTLaunchingAdaptor.java
index 29edddf..1172980 100644
--- a/plugins/org.eclipse.objectteams.otdt.debug.adaptor/src/org/eclipse/objectteams/otdt/internal/debug/adaptor/launching/JDTLaunchingAdaptor.java
+++ b/plugins/org.eclipse.objectteams.otdt.debug.adaptor/src/org/eclipse/objectteams/otdt/internal/debug/adaptor/launching/JDTLaunchingAdaptor.java
@@ -34,6 +34,7 @@
 import org.eclipse.objectteams.otdt.debug.OTDebugPlugin;
 import org.eclipse.objectteams.otdt.debug.OTVMRunnerAdaptor;
 import org.eclipse.objectteams.otdt.debug.TeamBreakpointInstaller;
+import org.eclipse.objectteams.otdt.internal.debug.adaptor.dynamic.RedefineClassesBPListener;
 import org.eclipse.pde.internal.ui.IPDEUIConstants;
 
 import base org.eclipse.jdt.launching.AbstractJavaLaunchConfigurationDelegate;
@@ -147,11 +148,14 @@
 				this.fAdaptor = null;
 				return;
 			}
-			this.fAdaptor = new OTVMRunnerAdaptor(getJavaProject(config));
+			IJavaProject javaProject = getJavaProject(config);
+			this.fAdaptor = new OTVMRunnerAdaptor(javaProject);
 			this.fAdaptor.setAdaptationArgs(config, mode, launch);
 			// install OT-breakpoints
-			if (ILaunchManager.DEBUG_MODE.equals(mode))
-				TeamBreakpointInstaller.installTeamBreakpoints(getJavaProject(config));
+			if (ILaunchManager.DEBUG_MODE.equals(mode)) {
+				TeamBreakpointInstaller.installTeamBreakpoints(javaProject,
+						RedefineClassesBPListener.get(this.fAdaptor.getWeavingScheme()));
+			}
 		}
 
 		// --- VM Arguments: ---
diff --git a/plugins/org.eclipse.objectteams.otdt.debug.adaptor/src/org/eclipse/objectteams/otdt/internal/debug/adaptor/launching/PDELaunchingAdaptor.java b/plugins/org.eclipse.objectteams.otdt.debug.adaptor/src/org/eclipse/objectteams/otdt/internal/debug/adaptor/launching/PDELaunchingAdaptor.java
index e008838..0c35cbd 100644
--- a/plugins/org.eclipse.objectteams.otdt.debug.adaptor/src/org/eclipse/objectteams/otdt/internal/debug/adaptor/launching/PDELaunchingAdaptor.java
+++ b/plugins/org.eclipse.objectteams.otdt.debug.adaptor/src/org/eclipse/objectteams/otdt/internal/debug/adaptor/launching/PDELaunchingAdaptor.java
@@ -25,6 +25,7 @@
 import org.eclipse.debug.core.ILaunchConfiguration;
 import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
 import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.internal.core.util.Util;
 import org.eclipse.objectteams.otdt.core.ext.OTREContainer;
@@ -33,6 +34,7 @@
 import org.eclipse.objectteams.otdt.debug.TeamBreakpointInstaller;
 import org.eclipse.objectteams.otdt.internal.debug.adaptor.DebugMessages;
 import org.eclipse.objectteams.otdt.internal.debug.adaptor.OTDebugAdaptorPlugin;
+import org.eclipse.objectteams.otdt.internal.debug.adaptor.dynamic.RedefineClassesBPListener;
 import org.eclipse.objectteams.otequinox.TransformerPlugin;
 import org.eclipse.osgi.util.NLS;
 import org.eclipse.pde.core.plugin.IPluginModelBase;
@@ -125,13 +127,15 @@
 	 * Installs breakpoints needed for the TeamMonitor into org.objectteams.Team.
 	 * Needs to find a project with OTJavaNature to do this.
 	 */
-	static void installOOTBreakpoints(IProject[] projects) throws CoreException
+	static void installOOTBreakpoints(IProject[] projects, String weavingMode) throws CoreException
 	{
 		if (projects != null)
 			for (IProject project : projects) 
 				// find org.objectteams.Team in any OT/J Project:
 				if (project.getNature(JavaCore.OTJ_NATURE_ID) != null) {
-					TeamBreakpointInstaller.installTeamBreakpoints(JavaCore.create(project));
+					IJavaProject javaProject = JavaCore.create(project);
+					TeamBreakpointInstaller.installTeamBreakpoints(javaProject,
+							RedefineClassesBPListener.get(WeavingScheme.valueOf(weavingMode)));
 					return; // good, done.
 				}
 		logException(null, Status.WARNING, DebugMessages.OTLaunching_no_OTJ_project_found);
@@ -166,9 +170,9 @@
 			if (isOTLaunch(configuration))
 				try {
 					IProject[] projects = getProjectsForProblemSearch(configuration, mode);
-					if (ILaunchManager.DEBUG_MODE.equals(mode))
-						PDELaunchingAdaptor.installOOTBreakpoints(projects);
 					determineWeavingMode(projects);
+					if (ILaunchManager.DEBUG_MODE.equals(mode))
+						PDELaunchingAdaptor.installOOTBreakpoints(projects, weavingMode);
 				} catch (DebugException dex) {
 					throw dex;
 				} catch (CoreException ex) {