Bug 406518 - migrate OT/Equinox to the standard OSGi WeavingHook
Uniformly use the transformer with a Bundle instead of a ClassLoader
Progress on when to do which piece of work
- don't try instantiate/activate before all needed classes are available
  - keep track of classes being processed
  - check all bases of a given team
- no multiple processing of the same team triggered by different bases
- remove baseTripWires when done
Add reverse imports (base->aspect)
Logging; wiring to fwLog still not working
diff --git a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBinding.java b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBinding.java
index 940327a..e9b8ac3 100644
--- a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBinding.java
+++ b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBinding.java
@@ -16,6 +16,8 @@
  **********************************************************************/
 package org.eclipse.objectteams.internal.osgi.weaving;
 
+import static org.eclipse.objectteams.otequinox.TransformerPlugin.log;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -25,8 +27,10 @@
 import java.util.Set;
 
 import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.objectteams.otequinox.ActivationKind;
+import org.osgi.framework.Bundle;
 
 /** 
  * A simple record representing the information read from an extension to org.eclipse.objectteams.otequinox.aspectBindings.
@@ -34,16 +38,25 @@
  * @since 1.3.0 (was a nested class before that) 
  */
 public class AspectBinding {
+	enum State { Initial, TeamsScanned, TeamsActivated };
+	
 	public String aspectPlugin;
 	public String basePlugin;
 	public IConfigurationElement[] forcedExports;
 	public ActivationKind[] activations = null; 
 	public String[]         teamClasses;
 	public List<String>[]   subTeamClasses;
-	public boolean          activated= false; // FIXME: consistently set?
+
+	public State 			 state = State.Initial;
+	
+	/** Dispenser for team classes indexed by base classes that should trigger activating the team. */
 	private HashMap<String, Set<String>> teamsPerBase = new HashMap<>();
+	
+	/** Lookup to find base classes affected by a team (need to be available before instantiate/activate). */
 	HashMap<String, Collection<String>> basesPerTeam = new HashMap<>();
 	
+	Set<String> teamsInProgress = new HashSet<>(); // TODO cleanup teams that are done
+	
 	public AspectBinding(String aspectId, String baseId, IConfigurationElement[] forcedExportsConfs) {
 		this.aspectPlugin= aspectId;
 		this.basePlugin= baseId;
@@ -93,8 +106,13 @@
 	}
 
 	/** Destructively read the names of teams to load for a given base class. */
-	public @Nullable Collection<String> getTeamsForBase(String baseClassName) {
-		return teamsPerBase.remove(baseClassName);		
+	public synchronized @Nullable Collection<String> getTeamsForBase(String baseClassName) {
+		Set<String> teamNames = teamsPerBase.remove(baseClassName);
+		if (teamNames != null) {
+			teamNames.removeAll(teamsInProgress);
+			teamsInProgress.addAll(teamNames);
+		}
+		return teamNames;		
 	}
 
 	public ActivationKind getActivation(String teamClassName) {
@@ -104,4 +122,20 @@
 		}
 		return ActivationKind.NONE;
 	}
+
+	/** Read OT attributes of all teams in aspectBinding and collect affected base classes. */
+	public synchronized void scanTeamClasses(Bundle bundle) {
+		ClassScanner scanner = new ClassScanner();
+		for (String teamName : getAllTeams()) {
+			try {
+				teamName = scanner.readOTAttributes(bundle, teamName);
+				Collection<String> baseClassNames = scanner.getCollectedBaseClassNames();
+				addBaseClassNames(teamName, baseClassNames);
+				log(IStatus.INFO, "Scanned team class "+teamName+", found "+baseClassNames.size()+" base classes");
+			} catch (Exception e) {
+				log(e, "Failed to scan team class "+teamName);
+			}
+		}
+		this.state = State.TeamsScanned;
+	}
 }
\ 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 96c57c7..b06206e 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
@@ -35,6 +35,7 @@
 import java.util.Set;
 
 import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.RegistryFactory;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -43,6 +44,7 @@
 import org.eclipse.objectteams.otequinox.hook.ILogger;
 import org.objectteams.ITeam;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.hooks.weaving.WovenClass;
 
 /**
  * An instance of this class holds the information loaded from extensions
@@ -72,6 +74,12 @@
 	private Set<String> selfAdaptingAspects= new HashSet<String>(); // TODO, never read / evaluated
 	
 	private HashMap<String, BaseBundleLoadTrigger> baseTripWires = new HashMap<>();
+	
+	Set<String> beingDefined = new HashSet<>(); // shared with OTWeavingHook!
+
+	public AspectBindingRegistry(Set<String> beingDefined) {
+		this.beingDefined = beingDefined;
+	}
 
 	/** Record for one team waiting for instantiation/activation. */
 	static class WaitingTeamRecord {
@@ -314,10 +322,12 @@
 	}
 
 	/** Check if the given base bundle / base class mandate any loading/instantiation/activation of teams. */
-	public void triggerLoadingHooks(@Nullable String bundleName, @Nullable String className) {
+	public void triggerLoadingHooks(@Nullable String bundleName, @Nullable WovenClass baseClass) {
 		BaseBundleLoadTrigger activation = baseTripWires.get(bundleName);
-		if (activation != null)
-			activation.fire(className);
+		if (activation != null) {
+			if (activation.fire(baseClass, beingDefined))
+				baseTripWires.remove(bundleName);
+		}
 	}
 
 	/** Record the given team classes as waiting for instantiation/activation. */
@@ -345,8 +355,9 @@
 		}
 		if (scheduledTeams == null) return;
 		for(WaitingTeamRecord record : scheduledTeams) {
+			log(IStatus.INFO, "Consider for instantiation/activation: team "+record.getTeamName());
 			try {
-				new TeamLoader(deferredTeams).instantiateWaitingTeam(record); // may re-insert to deferredTeams
+				new TeamLoader(deferredTeams, beingDefined).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/BaseBundleLoadTrigger.java b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/BaseBundleLoadTrigger.java
index d775f07..958f310 100644
--- a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/BaseBundleLoadTrigger.java
+++ b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/BaseBundleLoadTrigger.java
@@ -15,13 +15,16 @@
  **********************************************************************/
 package org.eclipse.objectteams.internal.osgi.weaving;
 
-import static org.eclipse.objectteams.otequinox.Activator.log;
+import static org.eclipse.objectteams.otequinox.TransformerPlugin.log;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 import org.eclipse.core.runtime.IStatus;
+import org.eclipse.objectteams.internal.osgi.weaving.AspectBinding.State;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.hooks.weaving.WovenClass;
 
 /**
  * Each instance of this class represents the fact that a given base bundle has aspect bindings,
@@ -33,10 +36,7 @@
 	@SuppressWarnings("deprecation")
 	private org.osgi.service.packageadmin.PackageAdmin admin;
 
-	private String baseBundleName;
-	
-	private boolean teamsScanned = false;
-	
+	private String baseBundleName;	
 
 	public BaseBundleLoadTrigger(String bundleSymbolicName, AspectBindingRegistry aspectBindingRegistry, 
 			@SuppressWarnings("deprecation") org.osgi.service.packageadmin.PackageAdmin admin) 
@@ -46,13 +46,20 @@
 		this.admin = admin;
 	}
 	
-	/** Signal that the given class is being loaded and trigger any necessary loading/instantiation/activation. */
-	public void fire(String className) {
+	/**
+	 * Signal that the given class is being loaded and trigger any necessary steps:
+	 * - scan team & add reverse imports (now)
+	 * - load & instantiate & activate (now or later).
+	 */
+	public boolean fire(WovenClass baseClass, Set<String> beingDefined) {
 		List<AspectBindingRegistry.WaitingTeamRecord> deferredTeamClasses = new ArrayList<>();
 		List<AspectBinding> aspectBindings = aspectBindingRegistry.getAdaptingAspectBindings(baseBundleName);
+		boolean allDone = true;
 		if (aspectBindings != null) {
 			for (AspectBinding aspectBinding : aspectBindings) {
-				if (aspectBinding.activated)
+				if (aspectBinding.state == State.Initial)
+					log(IStatus.INFO, "Preparing aspect binding for base bundle "+baseBundleName);
+				if (aspectBinding.state == State.TeamsActivated)
 					continue;
 				@SuppressWarnings("deprecation")
 				Bundle[] aspectBundles = admin.getBundles(aspectBinding.aspectPlugin, null);
@@ -61,35 +68,17 @@
 					continue;
 				}
 				Bundle aspectBundle = aspectBundles[0];
-				if (shouldScan())
-					scanTeamClasses(aspectBundle, aspectBinding);
-				TeamLoader loading = new TeamLoader(deferredTeamClasses);
-				if (loading.loadTeams(aspectBundle, aspectBinding, className))
-//					aspectBinding.activated = true; // FIXME(SH): this still spoils team activation, the given class may not be the trigger
-					;
+				if (aspectBinding.state != State.TeamsScanned)
+					aspectBinding.scanTeamClasses(aspectBundle);
+				TeamLoader loading = new TeamLoader(deferredTeamClasses, beingDefined);
+				if (!loading.loadTeamsForBase(aspectBundle, aspectBinding, baseClass))
+					allDone = false;
 			}
-			if (!deferredTeamClasses.isEmpty())
+			if (!deferredTeamClasses.isEmpty()) {
 				aspectBindingRegistry.addDeferredTeamClasses(deferredTeamClasses);
-		}
-	}
-
-	private synchronized boolean shouldScan() {
-		boolean shouldScan = !teamsScanned;
-		teamsScanned = true;
-		return shouldScan;
-	}
-
-	/** Read OT attributes of all teams in aspectBinding and collect affected base classes. */
-	private void scanTeamClasses(Bundle bundle, AspectBinding aspectBinding) { 
-		List<String> allTeams = aspectBinding.getAllTeams();
-		ClassScanner scanner = new ClassScanner();
-		for (String teamName : allTeams) {
-			try {
-				teamName = scanner.readOTAttributes(bundle, teamName);
-				aspectBinding.addBaseClassNames(teamName, scanner.getCollectedBaseClassNames());
-			} catch (Exception e) {
-				log(e, "Failed to load team class "+teamName);
+				return false;
 			}
 		}
+		return allDone;
 	}
 }
diff --git a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/Logger.java b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/Logger.java
new file mode 100644
index 0000000..7ee6979
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/Logger.java
@@ -0,0 +1,80 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2007, 2013 Fraunhofer Gesellschaft, Munich, Germany,
+ * for its Fraunhofer Institute for Computer Architecture and Software
+ * Technology (FIRST), Berlin, Germany and Technical University Berlin,
+ * Germany, 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
+ * 
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ * 
+ * Contributors:
+ * Fraunhofer FIRST - Initial API and implementation
+ * Technical University Berlin - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.internal.osgi.weaving;
+
+import org.eclipse.objectteams.otequinox.hook.HookConfigurator;
+import org.eclipse.objectteams.otequinox.hook.ILogger;
+import org.eclipse.osgi.framework.log.FrameworkLog;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+
+
+/**
+ * Log either to console or - as soon as it is initialized - via the TransformerPlugin.
+ * 
+ * @author stephan
+ * @since OTDT 1.1.4
+ */
+public class Logger implements ILogger
+{
+	private FrameworkLog fwLog;
+	
+	public Logger(FrameworkLog fwLog) {
+		this.fwLog = fwLog;
+	}
+
+	public void log(Throwable t, String msg) {
+		log(HookConfigurator.class.getPackage().getName(), t, msg);
+	}
+	public void log(String pluginID, Throwable t, String msg) {
+		if (this.fwLog != null) {
+			this.fwLog.log(new FrameworkLogEntry(pluginID, FrameworkLogEntry.ERROR, 0, msg, 0, t, null));
+			return;
+		} else {			
+			// no success logging, print to console instead:
+			System.err.println("OT/Equinox: "+msg); //$NON-NLS-1$
+			t.printStackTrace();
+		}
+	}
+	
+	public void log(int status, String msg) {
+		if (status >= Util.WARN_LEVEL)		
+			doLog(HookConfigurator.class.getPackage().getName(), status, msg);
+	}
+	public void log(String pluginID, int status, String msg) {
+		if (status >= Util.WARN_LEVEL)
+			doLog(pluginID, status, msg);
+	}
+	
+	public void doLog(int status, String msg) {
+		doLog(HookConfigurator.class.getPackage().getName(), status, msg);
+	}
+	public void doLog(String pluginID, int status, String msg) {
+		if (this.fwLog != null) {
+			this.fwLog.log(new FrameworkLogEntry(pluginID, status, 0, msg, 0, null, null));
+		} else {
+			// no success logging, print to console instead:
+			msg = "OT/Equinox: "+msg; //$NON-NLS-1$
+			if ((status & ERROR) != 0)
+				System.err.println(msg);
+			else
+				System.out.println(msg);
+		}
+	}
+}
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 6b3f7a8..656530c 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
@@ -20,7 +20,6 @@
 import java.lang.instrument.IllegalClassFormatException;
 import java.security.ProtectionDomain;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -58,12 +57,13 @@
 public class OTWeavingHook implements WeavingHook, WovenClassListener {
 
 	private AspectBindingRegistry aspectBindingRegistry;
-	private ObjectTeamsTransformer objectTeamsTransformer;
+	
+	@NonNull Set<String> beingDefined = new HashSet<>(); // shared with AspectBindingRegistry!
+	
 	
 	/** Call-back from DS framework. */
 	public void activate(ComponentContext context) {
 		this.aspectBindingRegistry = loadAspectBindingRegistry(context.getBundleContext());
-		this.objectTeamsTransformer = new ObjectTeamsTransformer();
 		TransformerPlugin.getDefault().registerAspectBindingRegistry(this.aspectBindingRegistry);
 	}
 
@@ -77,46 +77,39 @@
 		else
 			log(ILogger.ERROR, "Failed to load PackageAdmin service. Will not be able to handle fragments.");
 
-		AspectBindingRegistry aspectBindingRegistry = new AspectBindingRegistry();
+		AspectBindingRegistry aspectBindingRegistry = new AspectBindingRegistry(this.beingDefined);
 		aspectBindingRegistry.loadAspectBindings(packageAdmin);
 		return aspectBindingRegistry;
 	}
 
 	@Override
 	public void weave(WovenClass wovenClass) {
+		beingDefined.add(wovenClass.getClassName());
+
 		try {
 			BundleWiring bundleWiring = wovenClass.getBundleWiring();
 			String bundleName = bundleWiring.getBundle().getSymbolicName();
 			String className = wovenClass.getClassName();
 			
 			// do whatever is needed *before* loading this class:
-			aspectBindingRegistry.triggerLoadingHooks(bundleName, className);
+			aspectBindingRegistry.triggerLoadingHooks(bundleName, wovenClass);
 			
 			if (requiresWeaving(bundleWiring)) {
+				ObjectTeamsTransformer transformer = new ObjectTeamsTransformer();
 				Class<?> classBeingRedefined = null; // TODO
 				ProtectionDomain protectionDomain = wovenClass.getProtectionDomain();
 				byte[] bytes = wovenClass.getBytes();
 				try {
-					log(IStatus.INFO, "About to transform class "+wovenClass);
-					byte[] newBytes = objectTeamsTransformer.transform(bundleWiring.getClassLoader(),
+					log(IStatus.OK, "About to transform class "+className);
+					byte[] newBytes = transformer.transform(bundleWiring.getBundle(),
 										className, classBeingRedefined, protectionDomain, bytes);
 					if (newBytes != bytes && !Arrays.equals(newBytes, bytes)) {
+						log(IStatus.INFO, "Transformation performed on "+className);
 						wovenClass.setBytes(newBytes);
 						if (otreAdded.add(bundleWiring.getBundle())) {
+							log(IStatus.INFO, "Adding OTRE import to "+bundleName);
 							List<String> imports = wovenClass.getDynamicImports();
 							imports.add("org.objectteams");
-							List<AspectBinding> aspects = aspectBindingRegistry.getAdaptingAspectBindings(bundleName);
-							if (aspects != null) {
-								for (AspectBinding aspect : aspects) {
-									Collection<String> teamsForBase = aspect.getTeamsForBase(className);
-									if (teamsForBase != null) {
-										for (String t : teamsForBase) {
-											int dot = t.lastIndexOf('.');
-											imports.add(t.substring(0, dot));
-										}
-									}
-								}
-							}
 						}
 					}
 				} catch (IllegalClassFormatException e) {
@@ -132,6 +125,7 @@
 	@Override
 	public void modified(WovenClass wovenClass) {
 		if (wovenClass.getState() == WovenClass.DEFINED) {
+			beingDefined.remove(wovenClass.getClassName());
 			@SuppressWarnings("null") @NonNull String className = wovenClass.getClassName();
 			aspectBindingRegistry.instantiateScheduledTeams(className);
 		}
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 0a60d80..bb840e9 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
@@ -21,7 +21,9 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 
+import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.objectteams.internal.osgi.weaving.AspectBindingRegistry.WaitingTeamRecord;
 import org.eclipse.objectteams.otequinox.ActivationKind;
@@ -30,6 +32,7 @@
 import org.objectteams.ITeam;
 import org.objectteams.Team;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.hooks.weaving.WovenClass;
 
 /**
  * This class triggers the actual loading/instantiation/activation of teams.
@@ -48,17 +51,32 @@
 	private List<WaitingTeamRecord> deferredTeams;
 	
 	/** did we record the fact that a team needs deferring? */
-	boolean needDeferring; 
+	boolean needDeferring;
+
+	private Set<String> beingDefined; 
 	
-	public TeamLoader(List<WaitingTeamRecord> deferredTeams) {
+	public TeamLoader(List<WaitingTeamRecord> deferredTeams, Set<String> beingDefined) {
 		this.deferredTeams = deferredTeams;
+		this.beingDefined = beingDefined;
 	}
 
-	/** Team loading, 1st attempt (trying to do all three phases load/instantiate/activate). */
-	public boolean loadTeams(Bundle aspectBundle, AspectBinding aspectBinding, String className) {
-		Collection<String> teamsForBase = aspectBinding.getTeamsForBase(className);
-		if (teamsForBase == null) return true;
+	/**
+	 * Team loading, 1st attempt before the base class is even loaded
+	 * Trying to do these phases load/instantiate/activate,
+	 * and also adds a reverse import to the base.
+	 */
+	public boolean loadTeamsForBase(Bundle aspectBundle, AspectBinding aspectBinding, WovenClass baseClass) {
+		Collection<String> teamsForBase = aspectBinding.getTeamsForBase(baseClass.getClassName());
+		if (teamsForBase == null) return false;
+		List<String> imports = baseClass.getDynamicImports();
 		for (String teamForBase : teamsForBase) {
+			// Add dependency:
+			String packageOfTeam = "";
+			int dot = teamForBase.lastIndexOf('.');
+			if (dot != -1)
+				packageOfTeam = teamForBase.substring(0, dot);
+			imports.add(packageOfTeam);
+			log(IStatus.INFO, "Added dependency from base "+baseClass.getClassName()+" to package '"+packageOfTeam+"'");
 			// Load:
 			Class<? extends ITeam> teamClass;
 			teamClass = findTeamClass(teamForBase, aspectBundle);
@@ -76,7 +94,7 @@
 			// Activate?
 			activateTeam(aspectBinding, teamForBase, teamInstance, activationKind);
 		}
-		return !needDeferring; // TODO, need to figure out whether we're done with aspectBinding.
+		return true;
 	}
 
 	/** Team loading, subsequent attempts. */
@@ -155,6 +173,10 @@
 	}
 
 	private @Nullable ITeam instantiateTeam(AspectBinding aspectBinding, Class<? extends ITeam> teamClass, String teamName) {
+		// don't try to instantiate before all base classes successfully loaded.
+		if (!isReadyToLoad(aspectBinding, teamClass, null, teamName))
+			return null;
+
 		try {
 			ITeam instance = teamClass.newInstance();
 			TransformerPlugin.registerTeamInstance(instance);
@@ -174,26 +196,19 @@
 
 	private void activateTeam(AspectBinding aspectBinding, String teamName, ITeam teamInstance, ActivationKind activationKind)
 	{
-		// don't try to activate before all base classes successfully load.
-		ClassLoader loader = teamInstance.getClass().getClassLoader();
-		for (String baseclass : aspectBinding.basesPerTeam.get(teamName)) {
-			try {
-				loader.loadClass(baseclass);
-			} catch (ClassNotFoundException cnfe) {
-				synchronized (deferredTeams) {
-					deferredTeams.add(new WaitingTeamRecord(teamInstance, aspectBinding, baseclass)); // TODO(SH): synchronization, deadlock?
-				}
-				return;
-			}
-		}
+		// don't try to activate before all base classes successfully loaded.
+		if (!isReadyToLoad(aspectBinding, teamInstance.getClass(), teamInstance, teamName))
+			return;
 		// good to go, so go:
 		try {
 			switch (activationKind) {
 			case ALL_THREADS:
 				teamInstance.activate(Team.ALL_THREADS);
+				log(IStatus.INFO, "Activated team "+teamName);
 				break;
 			case THREAD:
 				teamInstance.activate();
+				log(IStatus.INFO, "Activated team "+teamName);
 				break;
 			//$CASES-OMITTED$
 			default:
@@ -204,4 +219,19 @@
 			log(t, "Failed to activate team "+teamName);
 		}
 	}
+	boolean isReadyToLoad(AspectBinding aspectBinding, Class<? extends ITeam> teamClass, ITeam teamInstance, String teamName) {
+		for (String baseclass : aspectBinding.basesPerTeam.get(teamName)) {
+			if (this.beingDefined.contains(baseclass)) {
+				synchronized (deferredTeams) {
+					WaitingTeamRecord record = teamInstance != null
+							? new WaitingTeamRecord(teamInstance, aspectBinding, baseclass)
+							: new WaitingTeamRecord(teamClass, aspectBinding, baseclass);
+					deferredTeams.add(record); // TODO(SH): synchronization, deadlock?
+				}
+				log(IStatus.INFO, "Defer instantation/activation of team "+teamName);
+				return false;
+			}
+		}
+		return true;
+	}
 }
diff --git a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/Util.java b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/Util.java
new file mode 100644
index 0000000..ff85099
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/Util.java
@@ -0,0 +1,123 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2004, 2006 Fraunhofer Gesellschaft, Munich, Germany,
+ * for its Fraunhofer Institute for Computer Architecture and Software
+ * Technology (FIRST), Berlin, Germany and Technical University Berlin,
+ * Germany.
+ * 
+ * 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
+ * $Id: Util.java 23461 2010-02-04 22:10:39Z stephan $
+ * 
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ * 
+ * Contributors:
+ * Fraunhofer FIRST - Initial API and implementation
+ * Technical University Berlin - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.internal.osgi.weaving;
+
+import java.util.HashSet;
+import static org.eclipse.core.runtime.IStatus.*;
+
+public class Util 
+{
+
+	// configure OT/Equinox debugging:
+	public static int WARN_LEVEL = INFO;
+	public static boolean PROFILE= false;
+	static {
+		String level = System.getProperty("otequinox.debug"); //$NON-NLS-1$
+		if (level != null) {
+			level = level.toUpperCase();
+			if (level.equals("OK"))            //$NON-NLS-1$
+				WARN_LEVEL = OK;
+			else if (level.equals("INFO"))     //$NON-NLS-1$
+				WARN_LEVEL = INFO;
+			else if (level.startsWith("WARN")) //$NON-NLS-1$
+				WARN_LEVEL = WARNING;
+			else if (level.startsWith("ERR"))  //$NON-NLS-1$
+				WARN_LEVEL = ERROR;
+			else
+				WARN_LEVEL = OK;
+		}
+		PROFILE= (System.getProperty("otequinox.profile") != null); //$NON-NLS-1$
+	}
+
+	/** Profiling data: */
+	enum ProfileKind { BaseTransformation, AspectTransformation, SuperClassFetching }
+	private static long[] profileTimes= new long[ProfileKind.values().length];
+	private static long systemStartTime= System.nanoTime();
+
+	static HashSet<String> PLATFORM_BUNDLES = null;
+	
+	@SuppressWarnings("nls")
+	private static void checkInit() {
+		if (PLATFORM_BUNDLES == null) {
+			PLATFORM_BUNDLES = new HashSet<String>();
+			// FIXME: reconsider:
+			for (String bundle : new String[] { "org.eclipse.equinox.common",
+												"org.eclipse.update.configurator",
+												"org.eclipse.core.runtime",
+												"org.eclipse.equinox.registry",
+												"org.eclipse.equinox.app",
+												"org.eclipse.equinox.ds",
+												"org.eclipse.equinox.event",
+												"org.eclipse.equinox.util",
+												"org.eclipse.osgi.services",
+												"org.eclipse.core.runtime.compatibility.auth",
+												"org.eclipse.equinox.preferences",
+												"org.eclipse.equinox.simpleconfigurator",
+												"org.eclipse.core.jobs",
+												"org.eclipse.core.runtime.compatibility",
+												"org.eclipse.equinox.p2.core",
+												"org.eclipse.equinox.p2.reconciler.dropins",
+												"org.eclipse.equinox.p2.directorywatcher",
+												"org.eclipse.ecf",
+												"org.eclipse.ecf.identity",
+												"org.eclipse.ecf.filetransfer",
+												"org.eclipse.ecf.provider.filetransfer",
+												"org.eclipse.ecf.provider.filetransfer.httpclient",
+												"org.apache.commons.httpclient",
+												// for Eclipse 4.x:
+												"org.eclipse.swt",
+												"org.eclipse.e4.ui.css.core",
+												"org.eclipse.e4.ui.css.swt.theme",
+												"org.eclipse.core.contenttype"
+				})
+				PLATFORM_BUNDLES.add(bundle);
+		}
+	}
+
+	static boolean isPlatformBundle(String bundleName) {
+		checkInit();
+		return PLATFORM_BUNDLES.contains(bundleName);
+	}
+
+	@SuppressWarnings("nls")
+	public static void profile(long startTime, ProfileKind kind, String msg, Logger logger) 
+	{
+		long now= System.nanoTime();
+		long delta= (now-startTime) / getActiveCount();
+		long total= (profileTimes[kind.ordinal()]+= delta);
+		msg = msg.substring(msg.lastIndexOf('.')+1);
+		logger.doLog(INFO, "Profile "+kind.name()+": "+m(delta)+"("+m(total)+"/"+m(now-systemStartTime)+") ["+msg+"]");
+	}
+	// nano-to milli conversion
+	private static double m(long l) {
+		return (l/1000000.0);
+	}
+
+	private static int getActiveCount() {
+		ThreadGroup group= Thread.currentThread().getThreadGroup();
+		ThreadGroup parent= group.getParent();
+		while (parent != null) {
+			group= parent;
+			parent= group.getParent();
+		}
+		return group.activeCount();
+	}
+}
diff --git a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/otequinox/TransformerPlugin.java b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/otequinox/TransformerPlugin.java
index d26aff9..cb6eeea 100644
--- a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/otequinox/TransformerPlugin.java
+++ b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/otequinox/TransformerPlugin.java
@@ -128,7 +128,11 @@
 	 * {@link IAspectRegistry#getAdaptingAspectPlugins(Bundle)} 
 	 */
 	public @NonNull String[] getAdaptingAspectPlugins(Bundle basePlugin) {
-		List<AspectBinding> aspectBindings = this.aspectBindingRegistry.getAdaptingAspectBindings(basePlugin.getSymbolicName());
+		return getAdaptingAspectPlugins(basePlugin.getSymbolicName());
+	}
+
+	public @NonNull String[] getAdaptingAspectPlugins(String id) {
+		List<AspectBinding> aspectBindings = this.aspectBindingRegistry.getAdaptingAspectBindings(id);
 		if (aspectBindings == null)
 			return new String[0];
 		String[] result = new String[aspectBindings.size()];
diff --git a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/BaseCallRedirection.java b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/BaseCallRedirection.java
index 639a870..c1357e3 100644
--- a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/BaseCallRedirection.java
+++ b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/BaseCallRedirection.java
@@ -58,7 +58,7 @@
 		public InstructionHandle snd() {return _ih2; }
 	}
 
-    public BaseCallRedirection(ClassLoader loader) {
+    public BaseCallRedirection(Object loader) {
     	super(loader);
     }
     
diff --git a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/BaseMethodTransformation.java b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/BaseMethodTransformation.java
index 2118419..ad2d6b4 100644
--- a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/BaseMethodTransformation.java
+++ b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/BaseMethodTransformation.java
@@ -142,7 +142,7 @@
 
 	public boolean useReflection = false;
 
-	public BaseMethodTransformation(ClassLoader loader) {
+	public BaseMethodTransformation(Object loader) {
 		super(loader);
 	}
 	/**
diff --git a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/Decapsulation.java b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/Decapsulation.java
index c91e352..c9c8ae1 100644
--- a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/Decapsulation.java
+++ b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/Decapsulation.java
@@ -53,7 +53,7 @@
 	private HashSet<String/*callout accessed fields*/> generatedFieldCalloutAccessors = new HashSet<String>();
 	private HashSet<String/*super-accessed methods (sign)*/> generatedSuperAccessors  = new HashSet<String>();
 	
-    public Decapsulation(ClassLoader loader) {
+    public Decapsulation(Object loader) {
     	super(loader);
     }
 
diff --git a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/LiftingParticipantTransformation.java b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/LiftingParticipantTransformation.java
index b62e874..14c402c 100644
--- a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/LiftingParticipantTransformation.java
+++ b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/LiftingParticipantTransformation.java
@@ -69,7 +69,7 @@
 	private static final String CREATE_ROLE_METHOD = "createRole";
 	private static final String LIFTING_PARTICIPANT_FIELD = "_OT$liftingParticipant";
 	
-	public LiftingParticipantTransformation(ClassLoader loader) { super(loader); }
+	public LiftingParticipantTransformation(Object loader) { super(loader); }
 
 	public void doTransformCode(ClassGen cg) 
 	{
diff --git a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/StaticSliceBaseTransformation.java b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/StaticSliceBaseTransformation.java
index 7d75263..ab76fc1 100644
--- a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/StaticSliceBaseTransformation.java
+++ b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/StaticSliceBaseTransformation.java
@@ -50,7 +50,7 @@
 	static final String _OT_ACTIVE_TEAMS=    "_OT$activeTeams";   //$NON-NLS-1$
 	static final String _OT_ACTIVE_TEAM_IDS= "_OT$activeTeamIDs"; //$NON-NLS-1$
 
-	public StaticSliceBaseTransformation(ClassLoader loader) { super(loader); }
+	public StaticSliceBaseTransformation(Object loader) { super(loader); }
 	
 	private HashMap<String,String> rejectReasons = new HashMap<String, String>();
 
diff --git a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/SubBoundBaseMethodRedefinition.java b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/SubBoundBaseMethodRedefinition.java
index 92e2656..9dc5691 100644
--- a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/SubBoundBaseMethodRedefinition.java
+++ b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/SubBoundBaseMethodRedefinition.java
@@ -38,7 +38,7 @@
 public class SubBoundBaseMethodRedefinition 
 	extends ObjectTeamsTransformation {
 
-	public SubBoundBaseMethodRedefinition(ClassLoader loader) { super(loader); }
+	public SubBoundBaseMethodRedefinition(Object loader) { super(loader); }
 
 	/**
 	 * Main entry for this transformer.
diff --git a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/TeamInterfaceImplementation.java b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/TeamInterfaceImplementation.java
index ce29d45..6bacee9 100644
--- a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/TeamInterfaceImplementation.java
+++ b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/TeamInterfaceImplementation.java
@@ -46,7 +46,7 @@
 	extends ObjectTeamsTransformation {
 
 
-	public TeamInterfaceImplementation(ClassLoader loader) {
+	public TeamInterfaceImplementation(Object loader) {
 		super(loader);
 	}
 
diff --git a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/jplis/JPLISEnhancer.java b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/jplis/JPLISEnhancer.java
index e752986..29d0c11 100644
--- a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/jplis/JPLISEnhancer.java
+++ b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/jplis/JPLISEnhancer.java
@@ -21,9 +21,9 @@
 import java.io.InputStream;
 
 import org.eclipse.objectteams.otre.ClassEnhancer;
+import org.eclipse.objectteams.otre.ClassLoaderAccess;
 import org.eclipse.objectteams.otre.OTREInternalError;
 import org.eclipse.objectteams.otre.ObjectTeamsTransformation;
-
 import org.apache.bcel.Constants;
 import org.apache.bcel.classfile.ClassParser;
 import org.apache.bcel.classfile.Field;
@@ -44,9 +44,9 @@
 
 public class JPLISEnhancer implements ClassEnhancer {
 
-	private ClassLoader loader;
+	private Object loader;
 	
-	public JPLISEnhancer(ClassGen cg, ClassLoader loader) {
+	public JPLISEnhancer(ClassGen cg, Object loader) {
 		this.loader = loader;
 	}
 	
@@ -109,7 +109,7 @@
 		InputStream is = null;
 		try {
 			String binaryName = className.replace('.', '/');
-			is = loader.getResourceAsStream(binaryName+".class");
+			is = ClassLoaderAccess.getResourceAsStream(this.loader, binaryName+".class");
 			if (is != null) {
 				ClassGen cg = new ClassGen(new ClassParser(is, className).parse());
 				client.checkReadClassAttributes(this, cg, className, cg.getConstantPool());
diff --git a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/jplis/ObjectTeamsTransformer.java b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/jplis/ObjectTeamsTransformer.java
index 2e90dac..9d70991 100644
--- a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/jplis/ObjectTeamsTransformer.java
+++ b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otre/jplis/ObjectTeamsTransformer.java
@@ -65,6 +65,7 @@
 	};
 
 	static boolean warmedUp = false;
+
 	/*
 	 * (non-Javadoc)
 	 * 
@@ -72,7 +73,13 @@
 	 *      java.lang.String, java.lang.Class, java.security.ProtectionDomain,
 	 *      byte[])
 	 */
-	public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
+	public byte[] transform(ClassLoader loader, String className,
+			Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
+			byte[] classfileBuffer) throws IllegalClassFormatException {
+		return transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
+	}
+	
+	public byte[] transform(Object loader, String className, Class<?> classBeingRedefined,
 			ProtectionDomain protectionDomain, byte[] classfileBuffer)
 			throws IllegalClassFormatException
 	{
@@ -86,7 +93,7 @@
 			}		
 		}
 	}
-	public byte[] internalTransform(ClassLoader loader, String className, Class<?> classBeingRedefined,
+	public byte[] internalTransform(Object loader, String className, Class<?> classBeingRedefined,
 			ProtectionDomain protectionDomain, byte[] classfileBuffer)
 			throws IllegalClassFormatException
 	{
@@ -125,7 +132,7 @@
 		StaticSliceBaseTransformation 		staticSliceBaseTransformation 		= new StaticSliceBaseTransformation(loader);
 		SubBoundBaseMethodRedefinition 		subBoundBaseMethodRedefinition 		= new SubBoundBaseMethodRedefinition(loader);
 		TeamInterfaceImplementation 		teamInterfaceImplementation 		= new TeamInterfaceImplementation(loader);
-		ThreadActivation 					threadActivation					= new ThreadActivation();
+//		ThreadActivation 					threadActivation					= new ThreadActivation();
 				
 		// tell Repository about the class loader for improved lookupClass()
 		DietClassLoaderRepository prevRepository = RepositoryAccess.setClassLoader(loader);
@@ -179,7 +186,8 @@
 //			baseMethodTransformation.doTransformInterface(jpe, cg);
 //			staticSliceBaseTransformation.doTransformInterface(jpe, cg);
 //			teamInterfaceImplementation.doTransformInterface(jpe, cg);
-			threadActivation.doTransformInterface(jpe, cg);
+// FIXME:
+//			threadActivation.doTransformInterface(jpe, cg);
 
 			
 //			baseCallRedirection.doTransformCode(cg); // empty method
@@ -187,7 +195,8 @@
 			liftingParticipantTransformation.doTransformCode(cg);
 			staticSliceBaseTransformation.doTransformCode(cg);
 			teamInterfaceImplementation.doTransformCode(cg);
-			threadActivation.doTransformCode(cg);
+// FIXME:
+//			threadActivation.doTransformCode(cg);
 			
 			JavaClass new_java_class = cg.getJavaClass(); 
 			if (dumping) {