Bug 406518 - migrate OT/Equinox to the standard OSGi WeavingHook
cleanup: documentation/copyright & refactoring
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 9836ec3..8f425ea 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
@@ -1,7 +1,7 @@
 /**********************************************************************
  * This file is part of "Object Teams Development Tooling"-Software
  * 
- * Copyright 2009 Germany and Technical University Berlin, Germany.
+ * Copyright 2009, 2013 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
@@ -11,7 +11,8 @@
  * Please visit http://www.eclipse.org/objectteams for updates and contact.
  * 
  * Contributors:
- * Technical University Berlin - Initial API and implementation
+ * 		Technical University Berlin - Initial API and implementation
+ * 		Stephan Herrmann - Initial API and implementation
  **********************************************************************/
 package org.eclipse.objectteams.internal.osgi.weaving;
 
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 bc366f9..3cb24f9 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
@@ -1,3 +1,19 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2008, 2013 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.objectteams.org for updates and contact.
+ * 
+ * Contributors:
+ *	Technical University Berlin - Initial API and implementation
+ * 	Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
 package org.eclipse.objectteams.internal.osgi.weaving;
 
 import static org.eclipse.objectteams.osgi.weaving.Activator.log;
@@ -6,13 +22,13 @@
 import static org.eclipse.objectteams.osgi.weaving.Constants.BASE_PLUGIN;
 import static org.eclipse.objectteams.osgi.weaving.Constants.CLASS;
 import static org.eclipse.objectteams.osgi.weaving.Constants.ID;
+import static org.eclipse.objectteams.osgi.weaving.Constants.REQUIRED_FRAGMENT;
 import static org.eclipse.objectteams.osgi.weaving.Constants.SELF;
 import static org.eclipse.objectteams.osgi.weaving.Constants.SUPERCLASS;
 import static org.eclipse.objectteams.osgi.weaving.Constants.TEAM;
 import static org.eclipse.objectteams.osgi.weaving.Constants.TRANSFORMER_PLUGIN_ID;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -24,12 +40,19 @@
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.objectteams.osgi.weaving.Constants;
-import org.eclipse.objectteams.otequinox.hook.IAspectRegistry;
 import org.eclipse.objectteams.otequinox.hook.ILogger;
 import org.objectteams.ITeam;
 import org.osgi.framework.Bundle;
-import org.osgi.service.packageadmin.PackageAdmin;
 
+/**
+ * An instance of this class holds the information loaded from extensions
+ * to the <code>aspectBindings</code> extension point.
+ * <p>
+ * Additionally it maintains dynamic data during the process of loading,
+ * instantiating and activating teams.
+ * </p>
+ */
+// parts of this class are moved from org.eclipse.objectteams.otequinox.TransformerPlugin
 @NonNullByDefault
 public class AspectBindingRegistry {
 	
@@ -46,17 +69,14 @@
 			   new HashMap<String, ArrayList<AspectBinding>>();
 	private static HashMap<String, ArrayList<AspectBinding>> aspectBindingsByAspectPlugin = 
 		       new HashMap<String, ArrayList<AspectBinding>>();
-	// map base bundle name to list of adapted base class names
-	private static HashMap<String, ArrayList<String>> adaptedBaseClassNames =
-		       new HashMap<String, ArrayList<String>>();
-	// set of aspect plug-ins which have internal teams:
-	private Set<String> selfAdaptingAspects= new HashSet<String>();
+	private Set<String> selfAdaptingAspects= new HashSet<String>(); // TODO, never read / evaluated
 	
-	private HashMap<String, BaseBundleActivation> baseTripWires = new HashMap<>();
+	private HashMap<String, BaseBundleLoadTrigger> baseTripWires = new HashMap<>();
 
+	/** Record for one team waiting for instantiation/activation. */
 	static class WaitingTeamRecord {
-		@Nullable Class<? extends ITeam> teamClass;
-		@Nullable ITeam teamInstance;
+		@Nullable Class<? extends ITeam> teamClass; // ... either this is set
+		@Nullable ITeam teamInstance;	// ... or this
 		AspectBinding aspectBinding;
 		String notFoundClass;
 		
@@ -100,7 +120,7 @@
 	}
 
 	/* Load extensions for org.eclipse.objectteams.otequinox.aspectBindings and check aspect permissions. */
-	public void loadAspectBindings(@Nullable PackageAdmin packageAdmin) {
+	public void loadAspectBindings(@SuppressWarnings("deprecation") @Nullable org.osgi.service.packageadmin.PackageAdmin packageAdmin) {
 		IConfigurationElement[] aspectBindingConfigs = RegistryFactory.getRegistry().getConfigurationElementsFor(
 				TRANSFORMER_PLUGIN_ID, ASPECT_BINDING_EXTPOINT_ID);
 		
@@ -111,6 +131,7 @@
 			@SuppressWarnings("null")@NonNull String aspectBundleId= currentBindingConfig.getContributor().getName();
 			IS_OTDT |= KNOWN_OTDT_ASPECTS.contains(aspectBundleId);
 			if (packageAdmin != null) {
+				@SuppressWarnings("deprecation")
 				Bundle[] aspectBundles = packageAdmin.getBundles(aspectBundleId, null);
 				if (aspectBundles == null || aspectBundles.length == 0 || (aspectBundles[0].getState() < Bundle.RESOLVED)) {
 					log(ILogger.ERROR, "aspect bundle "+aspectBundleId+" is not resolved - not loading aspectBindings.");
@@ -125,12 +146,16 @@
 				continue;
 			}
 			String baseBundleId = basePlugins[0].getAttribute(ID);
-			
-// FIXME(SH):
-//			//base fragments?
-//			IConfigurationElement[] fragments = basePlugins[0].getChildren(REQUIRED_FRAGMENT);
-//			if (fragments != null && !checkRequiredFragments(aspectBundleId, baseBundleId, fragments)) // reported inside
-//				continue;
+			if (baseBundleId == null) {
+				log(ILogger.ERROR, "aspectBinding of "+aspectBundleId+" must specify the id of a basePlugin");
+				continue;
+			}
+				
+ 			//base fragments?
+			IConfigurationElement[] fragments = basePlugins[0].getChildren(REQUIRED_FRAGMENT);
+			if (fragments != null 
+					&& !checkRequiredFragments(aspectBundleId, baseBundleId, fragments, packageAdmin)) // reported inside
+				continue;
 			
 			AspectBinding binding = new AspectBinding(aspectBundleId, baseBundleId, basePlugins[0].getChildren(Constants.FORCED_EXPORTS_ELEMENT));
 			// TODO(SH): maybe enforce that every bundle id is given only once?
@@ -150,7 +175,7 @@
 				addBindingForBaseBundle(realBaseBundleId, binding);
 				addBindingForAspectBundle(aspectBundleId, binding);
 				if (!baseTripWires.containsKey(realBaseBundleId))
-					baseTripWires.put(realBaseBundleId, new BaseBundleActivation(realBaseBundleId, this, packageAdmin));
+					baseTripWires.put(realBaseBundleId, new BaseBundleLoadTrigger(realBaseBundleId, this, packageAdmin));
 
 				
 				// now that binding.teamClasses is filled connect to super team, if requested:
@@ -166,6 +191,55 @@
 		}
 	}
 	
+	@SuppressWarnings("deprecation") // multiple uses of deprecated but still recommended class PackageAdmin
+	private boolean checkRequiredFragments(String aspectBundleId, String baseBundleId, IConfigurationElement[] fragments, 
+			@Nullable org.osgi.service.packageadmin.PackageAdmin packageAdmin) 
+	{
+		// checking only, no real action needed.
+		boolean hasError = false;
+		for (IConfigurationElement fragment : fragments) {
+			String fragId = fragment.getAttribute(ID);
+			if (fragId == null) {
+				log(ILogger.ERROR, "Mandatory attribute \"id\" missing from element \"requiredFragment\" of aspect binding in "+aspectBundleId);
+				return false;
+			} 
+			if (packageAdmin == null) {
+				log(ILogger.ERROR, "Not checking required fragment "+fragId+" in aspect binding of "+aspectBundleId+", package admin service not present");
+				return false; // report only once.
+			}
+			
+			Bundle[] fragmentBundles = packageAdmin.getBundles(fragId, null);
+			if (fragmentBundles == null || fragmentBundles.length == 0) {
+				log(ILogger.ERROR, "Required fragment "+fragId+" not found in aspect binding of "+aspectBundleId);
+				hasError = true;
+				continue;
+			}
+			Bundle fragmentBundle = fragmentBundles[0];
+			String aspectBindingHint = " (aspect binding of "+aspectBundleId+")";
+			if (packageAdmin.getBundleType(fragmentBundle) != org.osgi.service.packageadmin.PackageAdmin.BUNDLE_TYPE_FRAGMENT) {
+				log(ILogger.ERROR, "Required fragment " + fragId + " is not a fragment" + aspectBindingHint);
+				hasError = true;
+				continue;
+			}
+			Bundle[] hosts = packageAdmin.getHosts(fragmentBundle);
+			if (hosts == null || hosts.length == 0) {
+				if (fragmentBundle.getState() < Bundle.RESOLVED) {
+					log(ILogger.ERROR, "Required fragment " + fragId + " is not resolved" + aspectBindingHint);
+					hasError = true;
+					continue;
+				}
+				log(ILogger.ERROR, "Required fragment "+fragId+" has no host bundle"+aspectBindingHint);
+				hasError = true;					
+				continue;
+			}
+			Bundle host = hosts[0];
+			if (!host.getSymbolicName().equals(baseBundleId)) {
+				log(ILogger.ERROR, "Required fragment "+fragId+" has wrong host "+host.getSymbolicName()+aspectBindingHint);
+				hasError = true;
+			}
+		}
+		return !hasError;
+	}
 
 	private static void addBindingForBaseBundle(String baseBundleId, AspectBinding binding) {
 		ArrayList<AspectBinding> bindingList = aspectBindingsByBasePlugin.get(baseBundleId);
@@ -214,10 +288,9 @@
 		}		
 	}
 
-
 	/**
-	 * Internal API for TransformerHook:
-	 * see {@link IAspectRegistry#getAdaptedBasePlugins(Bundle)}
+	 * Given a potential aspect bundle, answer the symbolic names of all base bundles
+	 * adapted by the aspect bundle.
 	 */
 	public @Nullable String[] getAdaptedBasePlugins(Bundle aspectBundle) {
 		ArrayList<AspectBinding> bindings = aspectBindingsByAspectPlugin.get(aspectBundle.getSymbolicName());
@@ -234,75 +307,17 @@
 		ArrayList<AspectBinding> list = aspectBindingsByBasePlugin.get(symbolicName);
 		return list != null && !list.isEmpty();
 	}
-	
-	/**
-	 * public API:
-	 * {@link IAspectRegistry#getAdaptingAspectPlugins(Bundle)} 
-	 */
-	public String[] getAdaptingAspectPlugins(Bundle basePlugin) {
-		return getAdaptingAspectPlugins(basePlugin.getSymbolicName());
-	}
-	/**
-	 * public API:
-  	 * Get the names of aspect plugins adapting a given base plugin.
-	 * @param basePluginName symbolic name of a base plugin.
-	 * @return non-null array of symbolic names of aspect plugins.
-	 */
-	public String[] getAdaptingAspectPlugins(@Nullable String basePluginName) {
-		ArrayList<AspectBinding> list = aspectBindingsByBasePlugin.get(basePluginName);
-		
-		if (list == null)
-			return new String[0];
-		
-		String[] aspects = new String[list.size()];
-		for (int i=0; i< list.size(); i++) 
-			aspects[i] = list.get(i).aspectPlugin;
-		
-		return aspects;
-	}
-	
+
 	/**
 	 * Get the list of aspect bindings affecting the given base plugin.
 	 */
 	public @Nullable List<AspectBinding> getAdaptingAspectBindings(@Nullable String basePluginName) {
 		return aspectBindingsByBasePlugin.get(basePluginName);
 	}
-	
-	/**
-     * Recored the names of base classes adapted by a given team from a given aspect bundle.
-	 */
-	public void storeAdaptedBaseClassNames(String aspectBundleName, String teamName, Collection<String> baseClassNames) 
-	{
-		// search the base plugin being adapted by the given team:
-		String basePlugin = null;
-		bindings:
-		for (AspectBinding aspectBinding : aspectBindingsByAspectPlugin.get(aspectBundleName)) {
-			for (String aspectTeamClass : aspectBinding.teamClasses)
-				if (aspectTeamClass.equals(teamName)) {
-					basePlugin = aspectBinding.basePlugin;
-					break bindings;
-				}
-		}
-		if (basePlugin == null && selfAdaptingAspects.contains(aspectBundleName))
-			basePlugin = aspectBundleName;
-		if (basePlugin == null) {
-			log(ILogger.ERROR, "Base plugin for team "+teamName+" from "+aspectBundleName+" not found!");
-			return;
-		}
-		// merge base class names into existing:
-		synchronized (adaptedBaseClassNames) {
-			ArrayList<String> baseBundleClassNames = adaptedBaseClassNames.get(basePlugin);
-			if (baseBundleClassNames == null) {
-				baseBundleClassNames = new ArrayList<String>();
-				adaptedBaseClassNames.put(basePlugin, baseBundleClassNames);
-			}
-			baseBundleClassNames.addAll(baseClassNames);
-		}
-	}
 
 	/** Check if the given base bundle / base class mandate any loading/instantiation/activation of teams. */
 	public void triggerLoadingHooks(@Nullable String bundleName, @Nullable String className) {
-		BaseBundleActivation activation = baseTripWires.get(bundleName);
+		BaseBundleLoadTrigger activation = baseTripWires.get(bundleName);
 		if (activation != null)
 			activation.fire(className);
 	}
@@ -338,7 +353,7 @@
 		}
 		for(WaitingTeamRecord record : currentList) {
 			try {
-				BaseBundleActivation.instantiateWaitingTeam(record, deferredTeams);
+				new TeamLoader(deferredTeams).instantiateWaitingTeam(record);
 			} 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/BaseBundleActivation.java b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/BaseBundleActivation.java
deleted file mode 100644
index c35d734..0000000
--- a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/BaseBundleActivation.java
+++ /dev/null
@@ -1,174 +0,0 @@
-package org.eclipse.objectteams.internal.osgi.weaving;
-
-import static org.eclipse.objectteams.osgi.weaving.Activator.log;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-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.osgi.weaving.ActivationKind;
-import org.eclipse.objectteams.otequinox.hook.ILogger;
-import org.objectteams.ITeam;
-import org.objectteams.Team;
-import org.osgi.framework.Bundle;
-
-/**
- * Each instance of this class represents the fact that a given base bundle has aspect bindings,
- * which require to load / instantiate / activate one or more teams at a suitable point in time.
- */
-public class BaseBundleActivation {
-
-	private AspectBindingRegistry aspectBindingRegistry;
-	@SuppressWarnings("deprecation")
-	private org.osgi.service.packageadmin.PackageAdmin admin;
-
-	private String baseBundleName;
-	
-	private boolean teamsScanned = false;
-	
-
-	public BaseBundleActivation(String bundleSymbolicName, AspectBindingRegistry aspectBindingRegistry, 
-			@SuppressWarnings("deprecation") org.osgi.service.packageadmin.PackageAdmin admin) 
-	{
-		this.baseBundleName = bundleSymbolicName;
-		this.aspectBindingRegistry = aspectBindingRegistry;
-		this.admin = admin;
-	}
-	
-	/** Signal that the given class is being loaded and trigger any necessary loading/instantiation/activation. */
-	public void fire(String className) {
-		List<AspectBindingRegistry.WaitingTeamRecord> deferredTeamClasses = new ArrayList<>();
-		List<AspectBinding> aspectBindings = aspectBindingRegistry.getAdaptingAspectBindings(baseBundleName);
-		if (aspectBindings != null) {
-			for (AspectBinding aspectBinding : aspectBindings) {
-				if (aspectBinding.activated)
-					continue;
-				Bundle[] aspectBundles = admin.getBundles(aspectBinding.aspectPlugin, null);
-				if (aspectBundles == null || aspectBundles.length == 0) {
-					log(IStatus.ERROR, "Cannot find aspect bundle "+aspectBinding.aspectPlugin);
-					continue;
-				}
-				Bundle aspectBundle = aspectBundles[0];
-				if (shouldScan())
-					scanTeamClasses(aspectBundle, aspectBinding);
-				if (loadTeams(aspectBundle, aspectBinding, className, deferredTeamClasses))
-//					aspectBinding.activated = true; // FIXME(SH): this still spoils team activation, the given class may not be the trigger
-					;
-			}
-			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 {
-				scanner.readOTAttributes(bundle, teamName);
-				aspectBinding.addBaseClassNames(teamName, scanner.getCollectedBaseClassNames());
-			} catch (Exception e) {
-				log(e, "Failed to load team class "+teamName);
-			}
-		}
-	}
-
-	/** Team loading, 1st attempt (trying to do all three phases load/instantiate/activate). */
-	private boolean loadTeams(Bundle aspectBundle, AspectBinding aspectBinding, String className, List<WaitingTeamRecord> deferredTeamClasses) {
-		Collection<String> teamsForBase = aspectBinding.getTeamsForBase(className);
-		if (teamsForBase == null) return true;
-		TeamLoading delegate = new TeamLoading(deferredTeamClasses);
-		for (String teamForBase : teamsForBase) {
-			// Load:
-			Class<? extends ITeam> teamClass;
-			try {
-				teamClass = (Class<? extends ITeam>) aspectBundle.loadClass(teamForBase);
-			} catch (ClassNotFoundException e) {
-				log(e, "Failed to load team "+teamForBase);
-				continue;
-			}
-			// Instantiate?
-			ActivationKind activationKind = aspectBinding.getActivation(teamForBase);
-			if (activationKind == ActivationKind.NONE)
-				continue;
-			ITeam teamInstance = delegate.instantiateTeam(aspectBinding, teamClass, teamForBase);
-			if (teamInstance == null)
-				continue;
-			// Activate?
-			delegate.activateTeam(aspectBinding, teamForBase, teamInstance, activationKind);
-		}
-		return !delegate.needDeferring; // TODO, need to figure out whether we're done with aspectBinding.
-	}
-
-	/** Team loading, subsequent attempts. */
-	public static void instantiateWaitingTeam(WaitingTeamRecord record, List<WaitingTeamRecord> deferredTeams)
-			throws InstantiationException, IllegalAccessException 
-	{
-		ITeam teamInstance = record.teamInstance;
-		String teamName = record.getTeamName();
-		TeamLoading delegate = new TeamLoading(deferredTeams);
-		if (teamInstance == null) {
-			// Instantiate (we only get here if activationKind != NONE)
-			teamInstance = delegate.instantiateTeam(record.aspectBinding, record.teamClass, teamName);
-			if (teamInstance == null)
-				return;
-		}
-		// Activate?
-		ActivationKind activationKind = record.aspectBinding.getActivation(teamName);
-		delegate.activateTeam(record.aspectBinding, teamName, teamInstance, activationKind);
-	}
-
-	/* Common parts for both first and subsequent loading attempts. */
-	private static class TeamLoading {
-		List<WaitingTeamRecord> deferredTeams;
-		boolean needDeferring; // did we record the fact that a team needs deferring?
-		
-		public TeamLoading(List<WaitingTeamRecord> deferredTeams) {
-			this.deferredTeams = deferredTeams;
-		}
-
-		@Nullable ITeam instantiateTeam(AspectBinding aspectBinding, Class<? extends ITeam> teamClass, String teamName) {
-			try {
-				ITeam instance = teamClass.newInstance();
-				log(ILogger.INFO, "Instantiated team "+teamName);
-				return instance;
-			} catch (NoClassDefFoundError ncdfe) {
-				needDeferring = true;
-				deferredTeams.add(new WaitingTeamRecord(teamClass, aspectBinding, ncdfe.getMessage().replace('/','.')));
-			} catch (Throwable e) {
-				// application error during constructor execution?
-				log(e, "Failed to instantiate team "+teamName);
-			}
-			return null;
-		}
-		void activateTeam(AspectBinding aspectBinding, String teamName, ITeam teamInstance, ActivationKind activationKind)
-		{
-			try {
-				switch (activationKind) {
-				case ALL_THREADS:
-					teamInstance.activate(Team.ALL_THREADS);
-					break;
-				case THREAD:
-					teamInstance.activate();
-					break;
-					//$CASES-OMITTED$
-				}
-			} catch (NoClassDefFoundError e) {
-				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/internal/osgi/weaving/BaseBundleLoadTrigger.java b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/BaseBundleLoadTrigger.java
new file mode 100644
index 0000000..cd8587e
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/BaseBundleLoadTrigger.java
@@ -0,0 +1,95 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2013 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.objectteams.org for updates and contact.
+ * 
+ * Contributors:
+ * 	Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.internal.osgi.weaving;
+
+import static org.eclipse.objectteams.osgi.weaving.Activator.log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.osgi.framework.Bundle;
+
+/**
+ * Each instance of this class represents the fact that a given base bundle has aspect bindings,
+ * which require to load / instantiate / activate one or more teams at a suitable point in time.
+ */
+public class BaseBundleLoadTrigger {
+
+	private AspectBindingRegistry aspectBindingRegistry;
+	@SuppressWarnings("deprecation")
+	private org.osgi.service.packageadmin.PackageAdmin admin;
+
+	private String baseBundleName;
+	
+	private boolean teamsScanned = false;
+	
+
+	public BaseBundleLoadTrigger(String bundleSymbolicName, AspectBindingRegistry aspectBindingRegistry, 
+			@SuppressWarnings("deprecation") org.osgi.service.packageadmin.PackageAdmin admin) 
+	{
+		this.baseBundleName = bundleSymbolicName;
+		this.aspectBindingRegistry = aspectBindingRegistry;
+		this.admin = admin;
+	}
+	
+	/** Signal that the given class is being loaded and trigger any necessary loading/instantiation/activation. */
+	public void fire(String className) {
+		List<AspectBindingRegistry.WaitingTeamRecord> deferredTeamClasses = new ArrayList<>();
+		List<AspectBinding> aspectBindings = aspectBindingRegistry.getAdaptingAspectBindings(baseBundleName);
+		if (aspectBindings != null) {
+			for (AspectBinding aspectBinding : aspectBindings) {
+				if (aspectBinding.activated)
+					continue;
+				@SuppressWarnings("deprecation")
+				Bundle[] aspectBundles = admin.getBundles(aspectBinding.aspectPlugin, null);
+				if (aspectBundles == null || aspectBundles.length == 0) {
+					log(IStatus.ERROR, "Cannot find aspect bundle "+aspectBinding.aspectPlugin);
+					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 (!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 {
+				scanner.readOTAttributes(bundle, teamName);
+				aspectBinding.addBaseClassNames(teamName, scanner.getCollectedBaseClassNames());
+			} catch (Exception e) {
+				log(e, "Failed to load team class "+teamName);
+			}
+		}
+	}
+}
diff --git a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/ClassScanner.java b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/ClassScanner.java
index 377fe1d..ebc053e 100644
--- a/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/ClassScanner.java
+++ b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/ClassScanner.java
@@ -1,18 +1,18 @@
 /**********************************************************************
  * This file is part of "Object Teams Development Tooling"-Software
  * 
- * Copyright 2008, 2010 Technical University Berlin, Germany.
+ * Copyright 2008, 2013 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
- * $Id: ClassScanner.java 23461 2010-02-04 22:10:39Z stephan $
  * 
  * Please visit http://www.eclipse.org/objectteams for updates and contact.
  * 
  * Contributors:
- * Technical University Berlin - Initial API and implementation
+ * 		Technical University Berlin - Initial API and implementation
+ * 		Stephan Herrmann - Initial API and implementation
  **********************************************************************/
 package org.eclipse.objectteams.internal.osgi.weaving;
 
@@ -32,8 +32,8 @@
 import org.osgi.framework.Bundle;
 
 /**
- * Bridge for the TransformerPlugin by which it can access the ObjectTeamsTransformer
- * without accessing that OTRE class.
+ * Simple facility for scanning class files for OT Attributes without
+ * actually doing any weaving.
  * 
  * @author stephan
  * @since 1.2.0
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 3c51b4f..3371dfc 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
@@ -1,3 +1,18 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2013 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.objectteams.org for updates and contact.
+ * 
+ * Contributors:
+ * 	Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
 package org.eclipse.objectteams.internal.osgi.weaving;
 
 import static org.eclipse.objectteams.osgi.weaving.Activator.log;
@@ -14,6 +29,9 @@
 import org.osgi.framework.hooks.weaving.WovenClass;
 import org.osgi.framework.wiring.BundleWiring;
 
+/**
+ * This class integrates the OT/J weaver into OSGi using the standard API {@link WeavingHook}.
+ */
 public class OTWeavingHook implements WeavingHook {
 
 	private AspectBindingRegistry aspectBindingRegistry;
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
new file mode 100644
index 0000000..794d728
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.osgi.weaving/src/org/eclipse/objectteams/internal/osgi/weaving/TeamLoader.java
@@ -0,0 +1,133 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2013 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.objectteams.org for updates and contact.
+ * 
+ * Contributors:
+ * 	Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.internal.osgi.weaving;
+
+import static org.eclipse.objectteams.osgi.weaving.Activator.log;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.objectteams.internal.osgi.weaving.AspectBindingRegistry.WaitingTeamRecord;
+import org.eclipse.objectteams.osgi.weaving.ActivationKind;
+import org.eclipse.objectteams.otequinox.hook.ILogger;
+import org.objectteams.ITeam;
+import org.objectteams.Team;
+import org.osgi.framework.Bundle;
+
+/**
+ * This class triggers the actual loading/instantiation/activation of teams.
+ * <p>
+ * It implements a strategy of deferring those teams where instantiation/activation
+ * failed (NoClassDefFoundError), which assumably happens, if one required class
+ * cannot be loaded, because its loading is already in progress further down in
+ * our call stack.
+ * </p><p>
+ * Which teams participate in deferred instantiation is communicated via the list
+ * {@link #deferredTeams}.
+ * </p>
+ */
+public class TeamLoader {
+
+	private List<WaitingTeamRecord> deferredTeams;
+	
+	/** did we record the fact that a team needs deferring? */
+	boolean needDeferring; 
+	
+	public TeamLoader(List<WaitingTeamRecord> deferredTeams) {
+		this.deferredTeams = deferredTeams;
+	}
+
+	/** 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;
+		for (String teamForBase : teamsForBase) {
+			// Load:
+			Class<? extends ITeam> teamClass;
+			try {
+				teamClass = (Class<? extends ITeam>) aspectBundle.loadClass(teamForBase);
+			} catch (ClassNotFoundException e) {
+				log(e, "Failed to load team "+teamForBase);
+				continue;
+			}
+			// Instantiate?
+			ActivationKind activationKind = aspectBinding.getActivation(teamForBase);
+			if (activationKind == ActivationKind.NONE)
+				continue;
+			ITeam teamInstance = instantiateTeam(aspectBinding, teamClass, teamForBase);
+			if (teamInstance == null)
+				continue;
+			// Activate?
+			activateTeam(aspectBinding, teamForBase, teamInstance, activationKind);
+		}
+		return !needDeferring; // TODO, need to figure out whether we're done with aspectBinding.
+	}
+
+	/** Team loading, subsequent attempts. */
+	public void instantiateWaitingTeam(WaitingTeamRecord record)
+			throws InstantiationException, IllegalAccessException 
+	{
+		ITeam teamInstance = record.teamInstance;
+		String teamName = record.getTeamName();
+		if (teamInstance == null) {
+			// Instantiate (we only get here if activationKind != NONE)
+			teamInstance = instantiateTeam(record.aspectBinding, record.teamClass, teamName);
+			if (teamInstance == null)
+				return;
+		}
+		// Activate?
+		ActivationKind activationKind = record.aspectBinding.getActivation(teamName);
+		activateTeam(record.aspectBinding, teamName, teamInstance, activationKind);
+	}
+
+	private @Nullable ITeam instantiateTeam(AspectBinding aspectBinding, Class<? extends ITeam> teamClass, String teamName) {
+		try {
+			ITeam instance = teamClass.newInstance();
+			log(ILogger.INFO, "Instantiated team "+teamName);
+			return instance;
+		} catch (NoClassDefFoundError ncdfe) {
+			needDeferring = true;
+			deferredTeams.add(new WaitingTeamRecord(teamClass, aspectBinding, ncdfe.getMessage().replace('/','.')));
+		} catch (Throwable e) {
+			// application error during constructor execution?
+			log(e, "Failed to instantiate team "+teamName);
+		}
+		return null;
+	}
+
+	private void activateTeam(AspectBinding aspectBinding, String teamName, ITeam teamInstance, ActivationKind activationKind)
+	{
+		try {
+			switch (activationKind) {
+			case ALL_THREADS:
+				teamInstance.activate(Team.ALL_THREADS);
+				break;
+			case THREAD:
+				teamInstance.activate();
+				break;
+			//$CASES-OMITTED$
+			default:
+				break;
+			}
+		} catch (NoClassDefFoundError e) {
+			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);
+		}
+	}
+}