Bug 406518 - migrate OT/Equinox to the standard OSGi WeavingHook
- register also as a WovenClassListener to instantiate deferred teams
diff --git a/plugins/org.eclipse.objectteams.osgi.weaving/META-INF/MANIFEST.MF b/plugins/org.eclipse.objectteams.osgi.weaving/META-INF/MANIFEST.MF
index 46c0025..6657733 100644
--- a/plugins/org.eclipse.objectteams.osgi.weaving/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.objectteams.osgi.weaving/META-INF/MANIFEST.MF
@@ -9,6 +9,7 @@
  org.eclipse.objectteams.runtime;bundle-version="2.1.0",
  org.eclipse.osgi,
  org.eclipse.equinox.ds;bundle-version="1.4.100"
+Import-Package: org.osgi.service.component;version="1.2.0"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
 Bundle-ActivationPolicy: lazy
 Service-Component: OSGI-INF/weavinghook.xml
diff --git a/plugins/org.eclipse.objectteams.osgi.weaving/OSGI-INF/weavinghook.xml b/plugins/org.eclipse.objectteams.osgi.weaving/OSGI-INF/weavinghook.xml
index 31f3c9d..9253e26 100644
--- a/plugins/org.eclipse.objectteams.osgi.weaving/OSGI-INF/weavinghook.xml
+++ b/plugins/org.eclipse.objectteams.osgi.weaving/OSGI-INF/weavinghook.xml
@@ -3,5 +3,6 @@
 	<implementation class="org.eclipse.objectteams.internal.osgi.weaving.OTWeavingHook"/>
 	<service>
 		<provide interface="org.osgi.framework.hooks.weaving.WeavingHook"/>
+		<provide interface="org.osgi.framework.hooks.weaving.WovenClassListener"/>
 	</service>
 </component>
\ No newline at end of file
diff --git a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBindingRegistry.java b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBindingRegistry.java
index 3cb24f9..295bca9 100644
--- a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBindingRegistry.java
+++ b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBindingRegistry.java
@@ -110,8 +110,6 @@
 	}
 	// records of teams that have been deferred due to unresolved class dependencies:
 	private List<WaitingTeamRecord> deferredTeams = new ArrayList<>();
-	// records of teams whose class dependencies should/could be unblocked by now:
-	private List<WaitingTeamRecord> scheduledTeams = new ArrayList<>();
 
 	public static boolean IS_OTDT = false;
 	
@@ -323,37 +321,32 @@
 	}
 
 	/** Record the given team classes as waiting for instantiation/activation. */
-	public synchronized void addDeferredTeamClasses(List<WaitingTeamRecord> teamClasses) {
-		deferredTeams.addAll(teamClasses);		
-	}
-
-	/**
-	 * Check if the given class has been recorded as not-found before,
-	 * If so, unblock the team class(es) that depend on this class
-	 */
-	public synchronized void scheduleTeamClassesFor(@Nullable String className) {
-		List<WaitingTeamRecord> currentList = deferredTeams;
-		deferredTeams = new ArrayList<>();
-		for (WaitingTeamRecord record : currentList) {
-			if (record.notFoundClass.equals(className))
-				scheduledTeams.add(record);
-			else
-				deferredTeams.add(record);
+	public void addDeferredTeamClasses(List<WaitingTeamRecord> teamClasses) {
+		synchronized (deferredTeams) {
+			deferredTeams.addAll(teamClasses);
 		}
 	}
 
 	/**
-	 * Try to instantiate/activate any deferred teams that have been unblocked by now.
+	 * Try to instantiate/activate any deferred teams that may be unblocked
+	 * by the definition of the given trigger class.
 	 */
-	public void instantiateScheduledTeams() {
-		List<WaitingTeamRecord> currentList;
-		synchronized (this) {			
-			currentList = scheduledTeams;
-			scheduledTeams = new ArrayList<>();
+	public void instantiateScheduledTeams(String triggerClassName) {
+		List<WaitingTeamRecord> scheduledTeams = null;
+		synchronized(deferredTeams) {
+			for (WaitingTeamRecord record : new ArrayList<>(deferredTeams)) {
+				if (record.notFoundClass.equals(triggerClassName)) {
+					if (scheduledTeams == null)
+						scheduledTeams = new ArrayList<>();
+					if (deferredTeams.remove(record))
+						scheduledTeams.add(record);
+				}
+			}
 		}
-		for(WaitingTeamRecord record : currentList) {
+		if (scheduledTeams == null) return;
+		for(WaitingTeamRecord record : scheduledTeams) {
 			try {
-				new TeamLoader(deferredTeams).instantiateWaitingTeam(record);
+				new TeamLoader(deferredTeams).instantiateWaitingTeam(record); // may re-insert to deferredTeams
 			} catch (Exception e) {
 				log(e, "Failed to instantiate team "+record.getTeamName());
 				continue;
diff --git a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/OTWeavingHook.java b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/OTWeavingHook.java
index 3371dfc..18dde18 100644
--- a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/OTWeavingHook.java
+++ b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/OTWeavingHook.java
@@ -23,32 +23,62 @@
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.objectteams.osgi.weaving.Activator;
+import org.eclipse.objectteams.otequinox.hook.ILogger;
 import org.eclipse.objectteams.otre.jplis.ObjectTeamsTransformer;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
 import org.osgi.framework.hooks.weaving.WeavingHook;
 import org.osgi.framework.hooks.weaving.WovenClass;
+import org.osgi.framework.hooks.weaving.WovenClassListener;
 import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.service.component.ComponentContext;
+
 
 /**
  * This class integrates the OT/J weaver into OSGi using the standard API {@link WeavingHook}.
+ * <p>
+ * Additionally, we listen to events of woven classes changing to state {@link WovenClass#DEFINED}:
+ * </p>
+ * <ul>
+ * <li>Given that {@link AspectBindingRegistry#addDeferredTeamClasses} was used to record
+ * teams that could not be instantiated due to some required class being reported
+ * as {@link NoClassDefFoundError}.</li>
+ * <li>Assuming further that this error happened because the required class was in the process
+ * of being loaded further down the call stack.</li>
+ * <li>If later one of the not-found classes has been defined we use that trigger to
+ * re-attempt instantiating the dependent team(s).</li>
+ * </ul>
  */
-public class OTWeavingHook implements WeavingHook {
+public class OTWeavingHook implements WeavingHook, WovenClassListener {
 
 	private AspectBindingRegistry aspectBindingRegistry;
 	private ObjectTeamsTransformer objectTeamsTransformer;
 	
-	public OTWeavingHook() {
-		this.aspectBindingRegistry = Activator.loadAspectBindingRegistry();
+	/** Call-back from DS framework. */
+	public void activate(ComponentContext context) {
+		this.aspectBindingRegistry = loadAspectBindingRegistry(context.getBundleContext());
 		this.objectTeamsTransformer = new ObjectTeamsTransformer();
 	}
 
+	@SuppressWarnings("deprecation")
+	private AspectBindingRegistry loadAspectBindingRegistry(BundleContext context) {
+		org.osgi.service.packageadmin.PackageAdmin packageAdmin = null;;
+		
+		ServiceReference<?> ref= context.getServiceReference(org.osgi.service.packageadmin.PackageAdmin.class.getName());
+		if (ref!=null)
+			packageAdmin = (org.osgi.service.packageadmin.PackageAdmin)context.getService(ref);
+		else
+			log(ILogger.ERROR, "Failed to load PackageAdmin service. Will not be able to handle fragments.");
+
+		AspectBindingRegistry aspectBindingRegistry = new AspectBindingRegistry();
+		aspectBindingRegistry.loadAspectBindings(packageAdmin);
+		return aspectBindingRegistry;
+	}
+
 	@Override
 	public void weave(WovenClass wovenClass) {
 		try {
-			// TODO(SH): ideally this trigger would be inserted into the previous woven class
-			// do whatever left-overs we find from previous invocations:
-			aspectBindingRegistry.instantiateScheduledTeams();
-			
 			BundleWiring bundleWiring = wovenClass.getBundleWiring();
 			String bundleName = bundleWiring.getBundle().getSymbolicName();
 			String className = wovenClass.getClassName();
@@ -70,12 +100,18 @@
 					log(e, "Failed to transform class "+className);
 				}
 			}
-			// unblock any waiting teams depending on this class:
-			aspectBindingRegistry.scheduleTeamClassesFor(className);
 		} catch (ClassCircularityError cce) {
 			log(cce, "Weaver encountered a circular class dependency");
 		}
 	}
+	
+	@Override
+	public void modified(WovenClass wovenClass) {
+		if (wovenClass.getState() == WovenClass.DEFINED) {
+			@SuppressWarnings("null") @NonNull String className = wovenClass.getClassName();
+			aspectBindingRegistry.instantiateScheduledTeams(className);
+		}
+	}
 
 	private boolean requiresWeaving(BundleWiring bundleWiring) {
 		@SuppressWarnings("null")@NonNull
diff --git a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/TeamLoader.java b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/TeamLoader.java
index 794d728..fc21590 100644
--- a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/TeamLoader.java
+++ b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/TeamLoader.java
@@ -101,7 +101,9 @@
 			return instance;
 		} catch (NoClassDefFoundError ncdfe) {
 			needDeferring = true;
-			deferredTeams.add(new WaitingTeamRecord(teamClass, aspectBinding, ncdfe.getMessage().replace('/','.')));
+			synchronized(deferredTeams) {
+				deferredTeams.add(new WaitingTeamRecord(teamClass, aspectBinding, ncdfe.getMessage().replace('/','.')));
+			}
 		} catch (Throwable e) {
 			// application error during constructor execution?
 			log(e, "Failed to instantiate team "+teamName);
@@ -124,7 +126,9 @@
 				break;
 			}
 		} catch (NoClassDefFoundError e) {
-			deferredTeams.add(new WaitingTeamRecord(teamInstance, aspectBinding, e.getMessage().replace('/','.'))); // TODO(SH): synchronization
+			synchronized (deferredTeams) {
+				deferredTeams.add(new WaitingTeamRecord(teamInstance, aspectBinding, e.getMessage().replace('/','.'))); // TODO(SH): synchronization
+			}
 		} catch (Throwable t) {
 			// application errors during activation
 			log(t, "Failed to activate team "+teamName);
diff --git a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/osgi/weaving/Activator.java b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/osgi/weaving/Activator.java
index bd22246..2dd0029 100644
--- a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/osgi/weaving/Activator.java
+++ b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/osgi/weaving/Activator.java
@@ -35,21 +35,6 @@
 		OTREInit();
 	}
 
-	@SuppressWarnings("deprecation")
-	public static AspectBindingRegistry loadAspectBindingRegistry() {
-		org.osgi.service.packageadmin.PackageAdmin packageAdmin = null;;
-		
-		ServiceReference<?> ref= context.getServiceReference(org.osgi.service.packageadmin.PackageAdmin.class.getName());
-		if (ref!=null)
-			packageAdmin = (org.osgi.service.packageadmin.PackageAdmin)context.getService(ref);
-		else
-			log(ILogger.ERROR, "Failed to load PackageAdmin service. Will not be able to handle fragments.");
-
-		AspectBindingRegistry aspectBindingRegistry = new AspectBindingRegistry();
-		aspectBindingRegistry.loadAspectBindings(packageAdmin);
-		return aspectBindingRegistry;
-	}
-
 	@SuppressWarnings("restriction")
 	private void acquireLog(BundleContext bundleContext) {
 		try {