initial commit in accordance with CQ 3784
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/.classpath b/plugins/org.eclipse.objectteams.otequinox.hook/.classpath
new file mode 100644
index 0000000..18ffe95
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="lib" path="/org.eclipse.objectteams.otequinox.runtime"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/.project b/plugins/org.eclipse.objectteams.otequinox.hook/.project
new file mode 100644
index 0000000..7362304
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.objectteams.otequinox.hook</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/.settings/org.eclipse.jdt.core.prefs b/plugins/org.eclipse.objectteams.otequinox.hook/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..6629a10
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,65 @@
+#Sat Oct 18 18:45:16 CEST 2008
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/META-INF/MANIFEST.MF b/plugins/org.eclipse.objectteams.otequinox.hook/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..79a0607
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/META-INF/MANIFEST.MF
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Transformer Hook Fragment
+Bundle-SymbolicName: org.eclipse.objectteams.otequinox.hook
+Bundle-Version: 1.4.0.qualifier
+Bundle-Vendor: objectteams.org
+Fragment-Host: org.eclipse.osgi;bundle-version="[3.6.0.v20090928,4.0.0)"
+Export-Package: org.eclipse.objectteams.otequinox.hook
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/build.properties b/plugins/org.eclipse.objectteams.otequinox.hook/build.properties
new file mode 100644
index 0000000..61c53ff
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/build.properties
@@ -0,0 +1,10 @@
+output.. = bin/
+bin.includes = .,\
+               META-INF/
+src.includes = .classpath,\
+               .project,\
+               build.properties,\
+               documentation/
+jars.compile.order = .
+source.. = src/
+jars.extra.classpath = lib/otre.jar
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/AspectPermission.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/AspectPermission.java
new file mode 100644
index 0000000..b50bd80
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/AspectPermission.java
@@ -0,0 +1,33 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2009 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$
+ * 
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ * 
+ * Contributors:
+ * Technical University Berlin - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otequinox.hook;
+
+/**
+ * Possible values while negotiating aspect access (aspectBinding and forcedExport).
+ * Note that order is relevant in this enum: higher index means higher priority.
+ * 
+ * @author stephan
+ * @since 1.2.6
+ */
+public enum AspectPermission {
+	/** Not influencing negotiation between other parties. */
+	UNDEFINED,
+	/** A permission is granted unless someone else denies it. */
+	GRANT, 
+	/** A permission is explicitly denied. Cannot be overridden. */
+	DENY;
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/ClassScanner.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/ClassScanner.java
new file mode 100644
index 0000000..0627ee6
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/ClassScanner.java
@@ -0,0 +1,141 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2008 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: 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
+ **********************************************************************/
+package org.eclipse.objectteams.otequinox.hook;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+
+import org.eclipse.objectteams.otequinox.internal.hook.Util;
+import org.eclipse.objectteams.otre.jplis.ObjectTeamsTransformer;
+import org.eclipse.objectteams.otre.util.CallinBindingManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * Bridge for the TransformerPlugin by which it can access the ObjectTeamsTransformer
+ * without accessing that OTRE class.
+ * 
+ * @author stephan
+ * @since 1.2.0
+ */
+@SuppressWarnings("nls")
+public class ClassScanner 
+{
+	// default is "on", leave this switch for trouble shooting and profiling:
+	public static final boolean REPOSITORY_USE_RESOURCE_LOADER = !"off".equals(System.getProperty("otequinox.repository.hook"));
+
+	ObjectTeamsTransformer transformer = new ObjectTeamsTransformer();
+	
+	// collect class names recorded by readOTAttributes:
+	
+	//   * this version used by the MasterTeamLoader.loadTeams:
+	HashMap<String,ArrayList<String>> baseClassNamesByTeam = new HashMap<String, ArrayList<String>>();
+	//   * these fields used by TransformerHook.processClass (collected over multiple readOTAttributes):
+	ArrayList<String> allBaseClassNames = new ArrayList<String>();
+	ArrayList<String> roleClassNames = new ArrayList<String>();
+
+
+	/** 
+	 * Read all OT byte code attributes for the specified class.
+	 * While doing so the names of roles and adapted base classes are collected.
+	 * 
+	 * @param bundle    where to look
+	 * @param className the class to investigate (team or role)
+	 * @param loader    the loader (could be null) to use for further classFile lookup
+	 * @throws ClassFormatError
+	 * @throws IOException
+	 * @throws ClassNotFoundException the team or role class was not found
+	 */
+	public void readOTAttributes(Bundle bundle, String className, ClassLoader loader)
+			throws ClassFormatError, IOException, ClassNotFoundException 
+	{
+		if (!REPOSITORY_USE_RESOURCE_LOADER)
+			loader = null;
+		URL classFile = bundle.getResource(className.replace('.', '/')+".class");
+		if (classFile == null) 
+			throw new ClassNotFoundException(className);
+		this.transformer.readOTAttributes(classFile.openStream(), classFile.getFile(), loader);
+		Collection<String> currentBaseNames = this.transformer.fetchAdaptedBases(); // destructive read
+		if (currentBaseNames != null) {
+			// store per team:
+			ArrayList<String> basesPerTeam = this.baseClassNamesByTeam.get(className);
+			if (basesPerTeam == null) {
+				basesPerTeam = new ArrayList<String>();
+				this.baseClassNamesByTeam.put(className, basesPerTeam);
+			}
+			basesPerTeam.addAll(currentBaseNames);
+			// accumulated store:
+			allBaseClassNames.addAll(currentBaseNames);
+		}
+		readMemberTypeAttributes(bundle, className, loader);
+	}
+	
+	/** 
+	 * Get the names of the base classes adapted by the given team and 
+	 * encountered while reading the byte code attributes.
+	 * (Destructive read). 
+	 */
+	public Collection<String> getCollectedBaseClassNames(String teamName) {
+		return this.baseClassNamesByTeam.remove(teamName);
+	}
+
+	/** 
+	 * Get the names of all adapted base classes encountered while reading the byte code attributes.
+	 * (Destructive read). 
+	 */
+	public Collection<String> getCollectedBaseClassNames() {
+		try {
+			return this.allBaseClassNames;
+		} finally {
+			this.allBaseClassNames = new ArrayList<String>();
+		}
+	}
+	
+	/** 
+	 * Get the names of all member roles encountered while reading the byte code attributes. 
+	 * (Destructive read).
+	 */
+	public Collection<String> getCollectedRoleClassNames() {
+		return this.roleClassNames;
+	}
+	
+	/*
+	 * Recurse into member types scanning OT attributes.
+	 */
+	private void readMemberTypeAttributes(Bundle                 bundle,
+										  String                 className, 
+										  ClassLoader			 resourceLoader)
+	{
+		List<String> roles = CallinBindingManager.getRolePerTeam(className);
+		if (roles != null) {
+			ILogger logger = HookConfigurator.getLogger();
+			for (String roleName: roles) {
+				logger.log(Util.OK, "scanning role "+roleName);
+				try {
+					this.roleClassNames.add(roleName);
+					readOTAttributes(bundle, roleName, resourceLoader);					
+				} catch (Throwable t) {
+					logger.log(t, "Failed to read OT-Attributes of role "+roleName);
+				}
+				readMemberTypeAttributes(bundle, roleName, resourceLoader);
+			}
+		}
+	}
+};
\ No newline at end of file
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/HookConfigurator.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/HookConfigurator.java
new file mode 100644
index 0000000..f90e60a
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/HookConfigurator.java
@@ -0,0 +1,127 @@
+/**********************************************************************
+ * 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: HookConfigurator.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.otequinox.hook;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import org.eclipse.core.runtime.internal.adaptor.EclipseStorageHook;
+import org.eclipse.objectteams.otequinox.internal.hook.Logger;
+import org.eclipse.objectteams.otequinox.internal.hook.OTEquinoxServiceWatcher;
+import org.eclipse.objectteams.otequinox.internal.hook.OTStorageHook;
+import org.eclipse.objectteams.otequinox.internal.hook.TransformerHook;
+import org.eclipse.osgi.baseadaptor.hooks.StorageHook;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+
+/**
+ * This class installs the necessary hooks for using OT/J in eclipse plugins.
+ * As the first OTDT class in the load process it also updates the eclipse buildId
+ * to contain the OTDT version number, too (for logging).
+ * 
+ * @author stephan
+ * @version $Id: HookConfigurator.java 23461 2010-02-04 22:10:39Z stephan $
+ */
+public class HookConfigurator implements org.eclipse.osgi.baseadaptor.HookConfigurator {
+
+	
+	private static final String OTDT_BUILD_ID = "OT-1.4.0M2"; //$NON-NLS-1$
+	private static final String ECLIPSE_BUILD_ID = "eclipse.buildId"; //$NON-NLS-1$
+	/** Has OT/Equinox been enabled (using system property ot.equinox set to anything but "false")? */
+	public static final boolean OT_EQUINOX_ENABLED;
+
+	static {
+		String otequinoxProperty = System.getProperty("ot.equinox"); //$NON-NLS-1$
+		if (otequinoxProperty == null)
+			OT_EQUINOX_ENABLED = false;
+		else
+			OT_EQUINOX_ENABLED = !"false".equals(otequinoxProperty); //$NON-NLS-1$
+	}
+	
+	// singleton:
+	static HookConfigurator instance;
+	// logging (console or via FrameworkLog):
+	private Logger logger;
+	public HookConfigurator() {
+		instance = this;
+	}
+	public static ILogger getLogger() {
+		if (instance == null)
+			throw new RuntimeException("TransformerHook not initialized, perhaps OT/Equinox is not enabled?"); //$NON-NLS-1$
+		return instance.logger;
+	}
+	
+	private void updateBuildID() {
+		String eclipseBuild = System.getProperty(ECLIPSE_BUILD_ID);
+		if (eclipseBuild == null) {
+			eclipseBuild = OTDT_BUILD_ID;
+		} else {
+			int pos = eclipseBuild.indexOf('-', 8);
+			if (pos > 0)
+				eclipseBuild = eclipseBuild.substring(0, pos);
+			eclipseBuild = eclipseBuild+"."+OTDT_BUILD_ID; //$NON-NLS-1$
+		}
+		System.setProperty(ECLIPSE_BUILD_ID, eclipseBuild);
+	}
+	
+	public void addHooks(org.eclipse.osgi.baseadaptor.HookRegistry hookRegistry) {
+		if (!OT_EQUINOX_ENABLED)
+			return;
+		this.logger = new Logger(hookRegistry.getAdaptor().getFrameworkLog());
+		updateBuildID();
+		TransformerHook transformerHook = new TransformerHook(hookRegistry.getAdaptor());
+		// this instance observes
+		hookRegistry.addClassLoadingHook(transformerHook);
+		hookRegistry.addClassLoaderDelegateHook(transformerHook);
+		hookRegistry.addWatcher(transformerHook);
+		hookRegistry.addAdaptorHook(new OTEquinoxServiceWatcher(transformerHook));
+		synchronized (hookRegistry) {
+			insertStorageHook(hookRegistry);
+		}
+	}
+	/* Insert an OTStorageHook before EclipseStorageHook as to intercept getManifest(): */
+	private void insertStorageHook(org.eclipse.osgi.baseadaptor.HookRegistry hookRegistry) 
+	{
+		hookRegistry.addStorageHook(null); // grow array, fill in below:
+		StorageHook[] allStorageHooks= hookRegistry.getStorageHooks();
+
+		OTStorageHook storageHook= null;
+		for (int i=allStorageHooks.length-1; i>0; i--) {
+			allStorageHooks[i]= allStorageHooks[i-1];
+			if (allStorageHooks[i] instanceof EclipseStorageHook) 
+				storageHook= new OTStorageHook(null, (EclipseStorageHook)allStorageHooks[i]);
+		}
+		if (storageHook != null) // only install if validly wired
+			allStorageHooks[0]= storageHook; 
+		else
+			hookRegistry.getAdaptor().getFrameworkLog().log(new FrameworkLogEntry(
+					HookConfigurator.class.getName(),
+					"EclipseStorageHook not found", //$NON-NLS-1$
+					FrameworkLogEntry.ERROR, null, null));
+	}
+	/** Bridge to the OTStorageHook. */
+	public static void parseForcedExportsFile(File configFile, AspectPermission perm) {
+		OTStorageHook.parseForcedExportsFile(configFile, perm);
+	}
+	/** Bridge to the OTStorageHook. */
+	public static ArrayList<String[]> getForcedExportsByAspect(String aspectBundleId, AspectPermission perm) {
+		return OTStorageHook.getForcedExportsByAspect(aspectBundleId, perm);
+	}
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/IAspectRegistry.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/IAspectRegistry.java
new file mode 100644
index 0000000..a580f42
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/IAspectRegistry.java
@@ -0,0 +1,66 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2007, 2009 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: MasterTeamLoader.java 15426 2007-02-25 12:52:19Z 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.otequinox.hook;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * This slice of the IOTEquinoxService provides features to query the
+ * registry of aspectBindings.
+ * 
+ * @author stephan
+ * @since OTDT 1.1.4
+ */
+public interface IAspectRegistry {
+
+	/** Are we running within the OTDT? */
+	public boolean isOTDT();
+	
+	/** Is `symbolicName' the name of a base plugin for which an adapting team is registered? */
+	public boolean isAdaptedBasePlugin(String baseBundleName);
+
+	/**
+  	 * Get the names of aspect plugins adapting a given base plugin.
+	 * @param basePlugin base plugin.
+	 * @return non-null array of symbolic names of aspect plugins.
+ 	 */
+	public String[] getAdaptingAspectPlugins(Bundle baseBundle);
+
+	/**
+	 *  Get the plugin IDs of all base plugins adapted by this aspect plugin.
+	 *  If this plugin is not an aspect plugin return null.
+	 *  @param aspectBundle potential aspect plugin
+	 *  @return array of base plugin IDs or null.
+	 */
+	public String[] getAdaptedBasePlugins(Bundle aspectBundle);
+
+	/**
+	 * Does `bundle' have internal teams, i.e., teams that adapt classes from their
+	 * enclosing plug-in only?
+	 * @param bundle
+	 * @return
+	 */
+	public boolean hasInternalTeams(Bundle bundle);
+
+	/**
+	 * Does the symbolic name refer to an aspect bundle for which some permission was denied?
+	 * @param symbolicName
+	 * @return
+	 */
+	public boolean isDeniedAspectPlugin(String symbolicName);
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/IByteCodeAnalyzer.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/IByteCodeAnalyzer.java
new file mode 100644
index 0000000..8aa6039
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/IByteCodeAnalyzer.java
@@ -0,0 +1,32 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2008 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: IByteCodeAnalyzer.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
+ **********************************************************************/
+package org.eclipse.objectteams.otequinox.hook;
+
+import java.io.InputStream;
+
+/**
+ * Interface for byte code analyzers that peek specific information
+ * from a class' byte code without parsing the whole structure.
+ * 
+ * @author stephan
+ * @since 1.2.3
+ */
+public interface IByteCodeAnalyzer {
+
+	String getSuperclass(InputStream is, String className);
+	String getSuperclass(byte[] bytes, String className);
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/ILogger.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/ILogger.java
new file mode 100644
index 0000000..891586a
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/ILogger.java
@@ -0,0 +1,39 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2008, 2009 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$
+ * 
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ * 
+ * Contributors:
+ * Technical University Berlin - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otequinox.hook;
+
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+
+/**
+ * Our own variant of a logger interface, just slightly tailored variants of log methods.
+ * @author stephan
+ * @since 1.2.0
+ */
+public interface ILogger {
+	
+	int ERROR   = FrameworkLogEntry.ERROR;
+	int WARNING = FrameworkLogEntry.WARNING;
+	int INFO    = FrameworkLogEntry.INFO;
+	int OK      = FrameworkLogEntry.OK;
+	
+	public void log(Throwable t, String msg);
+	public void log(int status, String msg);
+	/** Same as above but do not filter by warnlevel. */
+	public void doLog(int status, String msg);
+	public void log(String pluginID, Throwable ex, String msg);
+	public void log(String pluginID, int status, String msg);
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/IOTEquinoxService.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/IOTEquinoxService.java
new file mode 100644
index 0000000..ad74d62
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/IOTEquinoxService.java
@@ -0,0 +1,29 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2007 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$
+ * 
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ * 
+ * Contributors:
+ * Technical University Berlin - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otequinox.hook;
+
+/**
+ * This interface unites the aspect registry and the team loading mechanism
+ * to a OT/Equinox service. 
+ * This service is published by the otequinox plugin and consumed by the transformer hook.
+ * 
+ * @author stephan
+ * @since OTDT 1.1.4
+ */
+public interface IOTEquinoxService extends IAspectRegistry, ITeamLoader {
+	IByteCodeAnalyzer getByteCodeAnalyzer();
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/ITeamLoader.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/ITeamLoader.java
new file mode 100644
index 0000000..95bd9b5
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/hook/ITeamLoader.java
@@ -0,0 +1,62 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2007 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: MasterTeamLoader.java 15426 2007-02-25 12:52:19Z 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.otequinox.hook;
+
+
+import org.osgi.framework.Bundle;
+
+/**
+ * Interface to the team loading mechanism.
+ * 
+ * @author stephan
+ * @since OTDT 1.1.4
+ */
+public interface ITeamLoader {
+
+	/**
+ 	 * 	Load all teams registered as adapting `baseBundle'.
+	 *  Also load all adapted base classes from the base bundle.
+	 * 
+	 * @param baseBundle   the bundle to be adapted 
+	 * @param classScanner object to be used for scanning class attributes
+	 * @return whether loading teams was successful.
+	 *         If so, instantiateTeams() should be invoked later.
+	 */
+	public boolean loadTeams(Bundle baseBundle, ClassScanner classScanner);
+
+    /**
+	 * Load all internal teams of bundle, i.e., teams that adapt classes from their
+	 * enclosing plug-in only.
+	 *  
+	 * @param bundle this bundle is aspect and base at the same time
+	 * @param scanner class scanner which collects the affected base and role classes.
+	 * @return
+	 */
+	public boolean loadInternalTeams(Bundle bundle, ClassScanner scanner);
+
+	/**
+	 * Instantiate all teams for baseBundle which have been loaded via loadTeams().
+	 * @param baseBundle
+	 * @param triggerClassName class who's loading triggered this instantiation request.
+	 */
+	public void instantiateTeams(final Bundle baseBundle, String triggerClassName);
+
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/AspectBundleRole.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/AspectBundleRole.java
new file mode 100644
index 0000000..09edd38
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/AspectBundleRole.java
@@ -0,0 +1,45 @@
+/**********************************************************************
+ * 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: AspectBundleRole.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.otequinox.internal.hook;
+
+public class AspectBundleRole {
+
+	boolean isLoading = false;
+	
+	AspectBundleRole(boolean isLoading) {
+		this.isLoading = isLoading;
+	}
+
+	static boolean isLoadingTeams(BundleRegistry bundleRegistry, String bundleID) 
+	{
+		AspectBundleRole aspect = bundleRegistry.aspectBundles.get(bundleID);
+		if (aspect == null)
+			return false;
+		return aspect.isLoading;
+	}
+
+	static void markLoadingTeams(BundleRegistry bundleRegistry, String bundleID, boolean flag) {
+		AspectBundleRole aspect = bundleRegistry.aspectBundles.get(bundleID);
+		if (aspect != null)
+			aspect.isLoading = flag;	
+	}
+
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/BaseBundleRole.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/BaseBundleRole.java
new file mode 100644
index 0000000..2d02ed8
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/BaseBundleRole.java
@@ -0,0 +1,197 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2004, 2009 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: BaseBundleRole.java 23468 2010-02-04 22:34:27Z stephan $
+ * 
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ * 
+ * Contributors:
+ * Technical University Berlin - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otequinox.internal.hook;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+
+import org.eclipse.objectteams.otequinox.hook.ClassScanner;
+import org.eclipse.objectteams.otequinox.hook.HookConfigurator;
+import org.eclipse.objectteams.otequinox.hook.ILogger;
+import org.eclipse.objectteams.otequinox.hook.ITeamLoader;
+import org.osgi.framework.Bundle;
+
+/** 
+ * This class decorates each base bundle for which adapting aspect bundles exist.
+ * 
+ * @author stephan
+ * @version $Id: BaseBundleRole.java 23468 2010-02-04 22:34:27Z stephan $
+ */
+@SuppressWarnings("nls")
+public class BaseBundleRole {
+
+	enum State {
+		INITIAL,           // nothing known yet.
+		WAIT_FOR_TEAM,     // a base bundle for which an aspect was found which cannot yet be loaded. 
+		ACTIVATED,         // was waiting, activation has been done, 
+		TEAMS_LOADED,      // ready to be loaded/transformed
+		TEAMS_INSTANTIATED // in this final state the team has been instantiated
+	}
+	
+	/** Pseudo ID of a basePlugin specifying that the team(s) adapt base classes from their own plugin. */
+	static final String SELF = "SELF";
+	
+	/* Symbolic name of the base bundle. */
+	final String symbolicName;
+	/* The corresponding base bundle itself. */
+	Bundle bundle;
+	/* The bundles by which teams are loaded. */
+	ArrayList<Bundle> aspectBundles = new ArrayList<Bundle>();
+	
+	/* State according to the above state model. */
+	State state = State.INITIAL;
+
+	// used by the ClassLoaderDelegateHook to cut recursive search:
+	HashSet<String> missingClassNames = new HashSet<String>(); // currently unused
+	HashMap<String,Class<?>> knownAlienClasses= new HashMap<String, Class<?>>();
+	
+	BaseBundleRole(Bundle bundle) {
+		this.bundle = bundle;
+		this.symbolicName = bundle.getSymbolicName();
+	}
+
+	void setup(Bundle bundle, State state) {
+		this.state = state;
+		this.bundle = bundle;
+	}
+	
+	/** Found an adapted base via the aspect: create a role, 
+	 *  or retrieve and enhance an existing one (by adding the aspect bundle). */
+	static BaseBundleRole createBaseBundleRole(
+			BundleRegistry  bundleRegistry, 
+			Bundle          baseBundle,
+			Bundle          aspectBundle) 
+	{
+		String symbolicName = baseBundle.getSymbolicName();
+		BaseBundleRole baseRole = bundleRegistry.adaptedBaseBundles.get(symbolicName);
+		if (baseRole == null) {
+			baseRole = new BaseBundleRole(baseBundle);
+			bundleRegistry.adaptedBaseBundles.put(symbolicName, baseRole);
+		}
+		baseRole.aspectBundles.add(aspectBundle);
+		return baseRole;
+	}
+	
+	/**
+	 * Perform actions due when the base plugin has finished its activation.
+	 * Mainly delegate these actions to the bundle's BaseBundleRole.
+	 * 
+	 * @param bundleRegistry
+	 * @param bundle
+	 * @param loader
+	 * @return a Runnable encapsulating the action of instantiating teams  (or null).
+	 */
+	static void endActivation(
+				BundleRegistry         bundleRegistry, 
+				Bundle                 bundle, 
+				SafeAspectRegistry     aspectRegistry,
+				ITeamLoader            loader) 
+	{
+		String symbolicName = bundle.getSymbolicName();
+		BaseBundleRole baseRole = bundleRegistry.adaptedBaseBundles.get(symbolicName);
+		if (baseRole != null) {
+			String[] aspects = aspectRegistry.getAdaptingAspectPlugins(bundle);
+			if (aspects.length == 0)
+				return; // error during service access
+			
+			// before retrieving aspect roles ensure base bundle has its 
+			// classloader initialized (which triggers aspect role creation):
+			bundle.getResource("META-INF/MANIFEST.MF"); // ignore result //$NON-NLS-1$
+			
+			try {
+				for (String aspect : aspects) {
+					AspectBundleRole aspectBundleRole = bundleRegistry.aspectBundles.get(aspect);
+					aspectBundleRole.isLoading = true;
+				}
+				baseRole.endActivation(loader);
+			} finally {
+				for (String aspect : aspects) {
+					AspectBundleRole aspectBundleRole = bundleRegistry.aspectBundles.get(aspect);
+					aspectBundleRole.isLoading = false;
+				}				
+			}			
+		}
+	}
+	private void endActivation(final ITeamLoader loader) 
+	{
+		ILogger log = HookConfigurator.getLogger();
+
+		switch (this.state) {
+			case WAIT_FOR_TEAM:
+				// FIXME(SH): remove once we are sure it never happens
+				log.log(Util.ERROR, "Asynchronuous activation while initializing class loader: "+this);
+				this.state = State.ACTIVATED;
+				break;
+			case TEAMS_LOADED:
+				// fetch and answer this request
+				this.state = State.TEAMS_INSTANTIATED;
+				
+				// Delegate to the TransformerPlugin to perform PHASE 2 of aspect activation:
+				// + loading adapted base classes
+				// + instantiating loaded teams.
+				log.log(Util.OK, "PHASE 2: Handling base plugin with aspects pending for instantiation: "
+						+this.bundle.getSymbolicName());
+				loader.instantiateTeams(this.bundle, /*triggerClassname*/null);
+				break;
+			default: // noop
+		}
+	}
+
+	/**
+	 * Load all known teams adapting this base bundle.
+	 * @param loader     the team loader service from org.eclipse.objectteams.otequinox.
+	 */
+	void loadTeams(ITeamLoader loader, ClassScanner scanner) {
+		ILogger log = HookConfigurator.getLogger();
+
+		if (this.state != State.WAIT_FOR_TEAM)
+			log.log(Util.ERROR, "Unexpected state when loading teams for "+this);
+		
+		// perform PHASE 1:
+		log.log(Util.OK, "PHASE 1: Load team classes adapting plugin "+this.symbolicName);
+		
+		if (loader.loadTeams(this.bundle, scanner)) 
+		{
+			if (this.state == State.ACTIVATED) {
+				// FIXME(SH): remove once we are sure it never happens
+				
+				this.state = State.TEAMS_INSTANTIATED;
+				
+				log.log(Util.ERROR, "!!!! Unexpected control flow !!!!");
+				
+				log.log(Util.INFO, "PHASE 2: Handling base plugin with aspects pending for instantiation: "+this.symbolicName);
+				loader.instantiateTeams(this.bundle, /*triggerClassname*/null);
+			} else {
+				this.state = State.TEAMS_LOADED;
+			}
+		} else {
+			this.state = State.TEAMS_INSTANTIATED;
+		}
+	}
+	
+	@Override
+	public String toString() {
+		String result= "Base Bundle "+this.symbolicName +"("+this.state+")";
+		for (Bundle aspect : this.aspectBundles) {
+			result += "\n\t"+aspect;
+		}
+		return result;
+	}
+	
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/BundleRegistry.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/BundleRegistry.java
new file mode 100644
index 0000000..88d41f7
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/BundleRegistry.java
@@ -0,0 +1,98 @@
+/**********************************************************************
+ * 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: BundleRegistry.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.otequinox.internal.hook;
+
+import java.util.HashMap;
+
+import org.eclipse.objectteams.otequinox.hook.ClassScanner;
+import org.eclipse.objectteams.otequinox.hook.IAspectRegistry;
+import org.eclipse.objectteams.otequinox.hook.ITeamLoader;
+import org.osgi.framework.Bundle;
+
+/** Maintain mappings from plugin ids to base/aspect bundles.
+ *  Please just think of this class as a team and {Base,Aspect}BundleRole
+ *  as its roles ;-)
+ */
+public class BundleRegistry {
+	
+	HashMap<String,BaseBundleRole> adaptedBaseBundles = new HashMap<String, BaseBundleRole>();
+
+	HashMap<String,AspectBundleRole> aspectBundles = new HashMap<String, AspectBundleRole>();
+
+	public boolean isAdaptedBaseBundle(String symbolicName) {
+		return adaptedBaseBundles.containsKey(symbolicName);
+	}
+
+	void createAspectRole (String symbolicName) {
+		aspectBundles.put(symbolicName, new AspectBundleRole(true));	
+	}
+	
+	void resetLoading (String[] aspects) {
+		for (String aspect : aspects) 
+			aspectBundles.get(aspect).isLoading = false;
+	}
+
+	/** Found an adapted base directly.
+	 *  After this the bundle is waiting for transformation. 
+	 */
+	BaseBundleRole createWaitingBase(Bundle baseBundle) 
+	{
+		String symbolicName = baseBundle.getSymbolicName();
+		BaseBundleRole baseRole = this.adaptedBaseBundles.get(symbolicName);
+		if (baseRole == null) {
+			baseRole = new BaseBundleRole(baseBundle);
+			this.adaptedBaseBundles.put(symbolicName, baseRole);
+		}
+		baseRole.setup(baseBundle, BaseBundleRole.State.WAIT_FOR_TEAM);
+		return baseRole;
+	}
+
+	/** Delegate to the TransformerPlugin to perform PHASE 1 of aspect activation:
+	 *  + loading of teams adapting baseBundle
+	 * @param baseBundle TODO
+	 * @param registry
+	 * @param loader
+ 	 * @param scanner  instance to use for scanning OT byte code attributes.
+	 * @return  a collection of names of adapted base classes (non-null)
+	 */
+	void checkLoadTeams (Bundle baseBundle, IAspectRegistry registry, ITeamLoader loader, ClassScanner scanner) 
+	{
+	// DEBUG:
+	//		if (isKnowID(baseBundle.getSymbolicName()))
+	//			System.out.println(">>3>>"+baseBundle.getSymbolicName());
+		
+		String[] aspectBundleNames = registry.getAdaptingAspectPlugins(baseBundle);
+		if (aspectBundleNames.length == 0)
+			return;
+		
+		try {
+			for (String aspect : aspectBundleNames)
+				this.createAspectRole(aspect); // enable transformation for these plugins
+			
+			// requesting PHASE 2 (request might be answered during loadTeams)
+			BaseBundleRole baseRole = this.createWaitingBase(baseBundle);
+					
+			baseRole.loadTeams(loader, scanner);
+		} finally {
+			this.resetLoading(aspectBundleNames);
+		}
+	}
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/ByteCodeAnalyzer.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/ByteCodeAnalyzer.java
new file mode 100644
index 0000000..1db9f60
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/ByteCodeAnalyzer.java
@@ -0,0 +1,85 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2007 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: TransformerPlugin.java 15357 2007-02-18 17:01:36Z 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.otequinox.internal.hook;
+
+/** 
+ * This class is used to peek the byte code before passing it to the transformers or the JVM. 
+ * 
+ * @author stephan
+ */
+public class ByteCodeAnalyzer {
+
+	// cannot import jdt.core, so repeat some constants here:
+	static final int AccTeam = 0x8000;
+	static final int Utf8Tag = 1;
+	static final int LongTag = 5;
+	static final int DoubleTag = 6;
+	static final int[] CPEntryLengths = new int[] {
+		0,
+		3, // UTF-8
+		0,
+		5, // integer
+		5, // float
+		9, // long
+		9, // double
+		3, // class
+		3, // string
+		5, // field
+		5, // method
+		5, // ifc method
+		5,  // name and type
+	};
+
+	/** Peek the bytecode for class flags to test if current class is a team. */
+	static boolean isTeam(byte[] classbytes) 
+	{
+		int constantPoolCount = combineTwoBytes(classbytes, 8);
+		int readOffset = 10;
+		
+		// skip over constant pool:
+		for (int i = 1; i < constantPoolCount; i++) {
+			int tag = classbytes[readOffset];
+			if (tag == Utf8Tag) 
+				readOffset += combineTwoBytes(classbytes, readOffset + 1);
+			if (tag < CPEntryLengths.length)
+				readOffset += CPEntryLengths[tag];
+			else 
+				throw new RuntimeException("bad tag?"); //$NON-NLS-1$
+			if (tag == DoubleTag || tag == LongTag)
+				i++;
+		}
+		int classFlags = combineTwoBytes(classbytes, readOffset);
+		return (classFlags & AccTeam) != 0;
+	}
+
+	/* helper for above: read an unsigned short. */
+	static int combineTwoBytes(byte [] bytes, int start) {
+		int first  = bytes[start];
+		int second = bytes[start+1];
+	    int twoBytes = 0;
+	
+	    twoBytes = twoBytes|(first&0xff);
+	    twoBytes = twoBytes<<8;
+	    twoBytes = twoBytes|(second&0xff);
+	    return twoBytes;
+	}
+
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/Logger.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/Logger.java
new file mode 100644
index 0000000..42dba38
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/Logger.java
@@ -0,0 +1,81 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2007 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: MasterTeamLoader.java 15426 2007-02-25 12:52:19Z 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.otequinox.internal.hook;
+
+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 & Util.ERROR) != 0)
+				System.err.println(msg);
+			else
+				System.out.println(msg);
+		}
+	}
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/OTEquinoxServiceWatcher.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/OTEquinoxServiceWatcher.java
new file mode 100644
index 0000000..0dc137b
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/OTEquinoxServiceWatcher.java
@@ -0,0 +1,127 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2007 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: MasterTeamLoader.java 15426 2007-02-25 12:52:19Z 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.otequinox.internal.hook;
+
+import java.io.IOException;
+import java.net.URLConnection;
+import java.util.Properties;
+
+import org.eclipse.objectteams.otequinox.hook.IOTEquinoxService;
+import org.eclipse.osgi.baseadaptor.BaseAdaptor;
+import org.eclipse.osgi.baseadaptor.hooks.AdaptorHook;
+import org.eclipse.osgi.framework.log.FrameworkLog;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * This class waits for the IOTEquinoxService to be registered and
+ * announces it to the TransformerHook.
+ * 
+ * @author stephan
+ * @since OTDT 1.1.4
+ */
+public class OTEquinoxServiceWatcher implements AdaptorHook {
+	
+	// whom to inform:
+	private TransformerHook hook;
+
+	public OTEquinoxServiceWatcher(TransformerHook hook) {
+		this.hook= hook;
+	}
+	
+	private void initialize (final BundleContext context) 
+	{	
+		String  transformerFilter = "(objectclass="+IOTEquinoxService.class.getName()+")";  //$NON-NLS-1$ //$NON-NLS-2$
+		//Add listener to listen for the registration of the OT/Equinox service:
+		ServiceListener transformerListener = new ServiceListener() {
+			public void serviceChanged(ServiceEvent event) {
+				if(event.getType() == ServiceEvent.REGISTERED)
+					connectOTEquinoxService(context);
+			}
+		};
+		try {
+			context.addServiceListener(transformerListener,transformerFilter);
+		}
+		catch (Exception ex) {
+			ex.printStackTrace();
+		}
+		ServiceReference ref= context.getServiceReference(PackageAdmin.class.getName());
+		if (ref!=null)
+			this.hook.connectPackageAdmin((PackageAdmin)context.getService(ref));
+
+	}
+
+	private void connectOTEquinoxService (BundleContext context) {
+		ServiceReference ref= context.getServiceReference(IOTEquinoxService.class.getName());
+		if (ref!=null)
+			this.hook.connectOTEquinoxService((IOTEquinoxService)context.getService(ref));
+	}
+	
+	
+	/** 
+	 * Capture the system bundle at start-up:
+	 * (see {@link AdaptorHook#frameworkStart(BundleContext)}
+	 */
+	public void frameworkStart(BundleContext systemContext) throws BundleException {
+		initialize(systemContext);
+	}
+
+	// === other methods implementing AdaptorHook do nothing: ===
+	
+	public void addProperties(Properties properties) {
+		// do nothing
+	}
+
+	public FrameworkLog createFrameworkLog() {
+		// do nothing
+		return null;
+	}
+	public void frameworkStop(BundleContext context) throws BundleException {
+		// do nothing		
+	}
+
+	public void frameworkStopping(BundleContext context) {
+		// do nothing
+	}
+
+	public void handleRuntimeError(Throwable error) {
+		// do nothing
+	}
+
+	public void initialize(BaseAdaptor adaptor) {
+		// do nothing
+	}
+
+	public URLConnection mapLocationToURLConnection(String location)
+			throws IOException {
+		// do nothing
+		return null;
+	}
+
+	public boolean matchDNChain(String pattern, String[] dnChain) {
+		// do nothing
+		return false;
+	}	
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/OTStorageHook.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/OTStorageHook.java
new file mode 100644
index 0000000..b0cd2ec
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/OTStorageHook.java
@@ -0,0 +1,390 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2007, 2009 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$
+ * 
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ * 
+ * Contributors:
+ * Technical University Berlin - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otequinox.internal.hook;
+
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.internal.adaptor.EclipseStorageHook;
+import org.eclipse.objectteams.otequinox.hook.AspectPermission;
+import org.eclipse.osgi.baseadaptor.BaseData;
+import org.eclipse.osgi.baseadaptor.hooks.StorageHook;
+import org.eclipse.osgi.framework.log.FrameworkLog;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+import org.eclipse.osgi.framework.util.Headers;
+import org.eclipse.osgi.framework.util.KeyedElement;
+import org.osgi.framework.BundleException;
+
+/**
+ * This hook essentially intercepts calls to getManifest() as to insert directives
+ * from otequinox.forced.exports (as read from config.ini or other locations).
+ *  
+ * @author stephan
+ * @since 1.1.4
+ */
+public class OTStorageHook implements StorageHook 
+{
+	// property names:
+	private static final String FORCED_EXPORTS_DENIED  = "otequinox.forced.exports.denied"; // //$NON-NLS-1$
+	private static final String FORCED_EXPORTS_GRANTED = "otequinox.forced.exports.granted"; // //$NON-NLS-1$
+	// legacy:
+	private static final String FORCED_EXPORTS = "otequinox.forced.exports"; // //$NON-NLS-1$
+	
+	// terminal tokens:
+	private static final String EXPORT_PACKAGE = "Export-Package"; //$NON-NLS-1$
+	private static final String XFRIENDS = "x-friends:="; //$NON-NLS-1$
+
+	private static final String KEY = OTStorageHook.class.getName();
+	private static final int HASHCODE = KEY.hashCode();
+	private static final int STORAGE_VERSION = 1;
+	
+	
+	private static HashMap<String, String> grantedForcedExports = null;
+	private static HashMap<String, String> deniedForcedExports = null;
+	
+	private static void readForcedExports() {
+		// read forced exports from config.ini (system properties), or file(s)
+		deniedForcedExports= new HashMap<String, String>();
+		grantedForcedExports= new HashMap<String, String>();
+		readForcedExportsFromProperty(FORCED_EXPORTS_DENIED, AspectPermission.DENY);
+		readForcedExportsFromProperty(FORCED_EXPORTS_GRANTED, AspectPermission.GRANT);
+		// legacy:
+		readForcedExportsFromProperty(FORCED_EXPORTS, AspectPermission.GRANT);
+	}
+
+	private static void readForcedExportsFromProperty(String propKey, AspectPermission perm) {
+		String value= System.getProperty(propKey);
+		if (value != null) {
+			if (value.length() > 0 && value.charAt(0) == '@') {
+				// follows: comma-separated list for filenames to read from:
+				int pos = 1;
+				while (pos < value.length()) {
+					int comma = value.indexOf(',', pos);
+					String fileName;
+					if (comma > -1) {
+						fileName = value.substring(pos, comma);
+						pos = comma+1;
+					} else {
+						fileName = value.substring(pos);
+						pos = value.length();
+					}
+					parseForcedExportsFile(new File(fileName), perm);
+				}
+			} else {
+				parseForcedExports(value, perm);
+			}
+		}
+	}
+
+	/**
+	 * Adds the definitions from a given file to the set of granted forced exports.
+	 * @param file file to read from
+	 * @param perm either ALLOW or DENY, determines how the found forced export decls are interpreted.
+	 */
+	synchronized public static void parseForcedExportsFile(File file, AspectPermission perm) {
+		StringBuffer newVal = new StringBuffer();
+		try {
+			// read content of one file:
+			FileInputStream fis = new FileInputStream(file);
+			BufferedReader br = new BufferedReader(new InputStreamReader(fis, "UTF8")); //$NON-NLS-1$
+			String line;
+			boolean afterClosingBrace = false;
+			while ((line = br.readLine()) != null) {
+				line = line.trim();
+				if (line.length() > 0) {
+					// may need to replace linebreak after ']' with ',' - to match one-line syntax in config.ini
+					char lastReadChar = line.charAt(line.length()-1);
+					if (afterClosingBrace && lastReadChar != ',')
+						newVal.append(',');
+					afterClosingBrace = lastReadChar == ']';
+						
+					newVal.append(line);
+				}
+			}
+			parseForcedExports(newVal.toString(), perm);
+		} catch (IOException e) {
+			fwLog.log(new FrameworkLogEntry(OTStorageHook.class.getName(),
+						    "failed to read forcedExports file "+file.getName(), //$NON-NLS-1$
+						    FrameworkLogEntry.ERROR, e, null));
+		}							
+	}
+	/* Adds the given definitions to the set of granted forced exports. */
+	private static void parseForcedExports(String value, AspectPermission perm) {
+		HashMap<String, String> map = getForcedExportsMap(perm);
+		if (map == null) return; // DONT_CARE
+		int pos = 0;
+		String[] values = new String[2]; // { BaseBundleID , PackageExports }
+		while (true) {
+			pos = getForcedExportForOneBase(value, pos, values);
+			String plugin= values[0];
+			String pack= values[1];
+			if (map.containsKey(plugin)) {
+				String oldPack = map.get(plugin);
+				pack = oldPack+','+pack;  // append to existing definition
+			}
+			map.put(plugin, pack);
+			if (pos >= value.length())
+				break; // eot: done without errors
+			if (value.charAt(pos) != ',')
+				throwSyntaxError(value, pos, "missing ','"); //$NON-NLS-1$
+			pos++; // skip the ','
+		}
+	}
+	/**
+	 * fetch one chunk of forced exports specification from `spec`:
+	 * <ul>
+	 * <li>my.base.plugin[<em>specification-of-force-exported-packages</em>]<em>tail</em></li>
+	 * </ul>
+	 * is split into:
+	 * <ul>
+	 * <li>my.base.plugin</li>
+	 * <li><em>specification-of-force-exported-packages</em></li>
+	 * </ul>
+	 * and return value points to <em>tail</em>
+	 * @param spec where to read from
+	 * @param start where to start reading (within spec)
+	 * @param value store result chunks in this array: [0] = base plugin, [1] = packages specification
+	 * @return position after what has been interpreted so far
+	 */
+	private static int getForcedExportForOneBase(String spec, int start, String[] values) {
+		int open = spec.indexOf('[', start);
+		if (open == -1)
+			throwSyntaxError(spec, start, "missing '['"); //$NON-NLS-1$
+		int close = spec.indexOf(']', start);
+		if (close == -1)
+			throwSyntaxError(spec, open, "missing ']'"); //$NON-NLS-1$
+		values[0] = spec.substring(start, open);
+		values[1] = spec.substring(open+1, close);
+		return close+1;
+	}
+
+	private static void throwSyntaxError(String spec, int pos, String string) {
+		throw new RuntimeException("Illegal syntax in "+FORCED_EXPORTS+" directive at position "+pos+" (not counting whitespace): "+string+"\n value is:\n"+spec); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$		
+	}
+
+	private static HashMap<String, String> getForcedExportsMap(AspectPermission perm) {
+		switch (perm) {
+			case GRANT:     return grantedForcedExports;
+			case DENY:      return deniedForcedExports;
+			case UNDEFINED: return null; 
+//			default: // not implementing a default case, want to be warned when new enum-constants are added
+		}
+		// for binary compatibility; see also https://bugs.eclipse.org/bugs/show_bug.cgi?id=265744
+		throw new IncompatibleClassChangeError("enum "+AspectPermission.class.getName()+" has changed unexpectedly."); //$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	static FrameworkLog fwLog;
+	
+	BaseData bundleData;
+	EclipseStorageHook manifestProvider;	
+	
+	public OTStorageHook(BaseData bundleData, EclipseStorageHook manifestProvider) {
+		this.bundleData = bundleData;
+		this.manifestProvider = manifestProvider;
+		if (fwLog == null && bundleData != null)
+			fwLog = bundleData.getAdaptor().getFrameworkLog();
+	}
+	
+	public void copy(StorageHook storageHook) {
+		OTStorageHook orig= (OTStorageHook)storageHook;
+		this.bundleData= orig.bundleData;
+		this.manifestProvider= orig.manifestProvider;
+	}
+
+	public StorageHook create(BaseData bundledata) throws BundleException {
+		return new OTStorageHook(bundledata, this.manifestProvider);
+	}
+
+	public boolean forgetStartLevelChange(int startlevel) {
+		return false;
+	}
+
+	public boolean forgetStatusChange(int status) {
+		return false;
+	}
+
+	public Dictionary<?,?> getManifest(boolean firstLoad) throws BundleException 
+	{
+		final Dictionary<?,?> orig= this.manifestProvider.create(this.bundleData).getManifest(firstLoad);
+		
+		// wrap the original answer:
+		return new Headers(orig.size()) 
+		{
+			@Override
+			public Object get(Object key) {
+				Object value = orig.get(key);
+				if (!EXPORT_PACKAGE.equals(key)) 
+					return value;
+				synchronized (OTStorageHook.class){
+					if (grantedForcedExports == null)
+						readForcedExports();
+				}
+				String exports= grantedForcedExports.get(OTStorageHook.this.bundleData.getSymbolicName());
+				if (exports != null) {
+					// yes, we need to add forced exports:
+					String packages= (String)orig.get(EXPORT_PACKAGE);
+					if (!exports.contains(XFRIENDS)) {
+						// invalid directive:
+						grantedForcedExports.remove(OTStorageHook.this.bundleData.getSymbolicName()); 
+						logError("config.ini: missing x-friends directive in forced export of "+exports); //$NON-NLS-1$
+						return packages; // don't install illegal forced exports
+					}
+					if (packages != null)
+						return packages+','+exports;
+					return exports;
+				}
+				return value;
+			}
+			// other methods simply delegate:
+			@Override public synchronized int size()             { return orig.size(); }
+			@Override public synchronized Enumeration<?> keys()  { return orig.keys(); }
+		};
+	}
+
+	static void logError(String message) {
+		fwLog.log(new FrameworkLogEntry(OTStorageHook.class.getName(),
+									    message,
+									    FrameworkLogEntry.ERROR,
+									    null, null));
+	}
+
+	public int getStorageVersion() {
+		return STORAGE_VERSION;
+	}
+
+	@SuppressWarnings("unchecked")
+	public void initialize(final Dictionary manifest) throws BundleException {
+		// no-op
+	}
+
+	public StorageHook load(BaseData bundledata, DataInputStream is)
+			throws IOException 
+	{
+		return new OTStorageHook(bundledata, this.manifestProvider);
+	}
+
+	public boolean matchDNChain(String pattern) {
+		return false;
+	}
+
+	public void save(DataOutputStream os) throws IOException {
+		// no-op
+	}
+
+	public void validate() throws IllegalArgumentException {
+		// no-op
+	}
+
+	public Object getKey() {
+		return KEY;
+	}
+	
+	public boolean compare(KeyedElement other) {
+		return other.getKey() == KEY;
+	}
+
+	public int getKeyHashCode() {
+		return HASHCODE;
+	}
+
+	/** 
+	 * Query whether any forced-exports are declared in config.ini (or other location) 
+	 * for the given aspect bundle.
+	 * @param aspectBundleId
+	 * @return list of pairs (baseBundleId x packageName)
+	 */ 
+	public static ArrayList<String[]> getForcedExportsByAspect(String aspectBundleId, AspectPermission perm) 
+	{
+		// can be queried before we had a chance to initialize our data structures
+		synchronized (OTStorageHook.class) {
+			if (grantedForcedExports == null)
+				readForcedExports();
+		}
+
+		ArrayList<String[]> result= new ArrayList<String[]>(5);
+		
+		HashMap<String, String> map = getForcedExportsMap(perm);
+		if (map == null) 
+			return result; // DONT_CARE: useless query.
+		
+		for (Map.Entry<String,String> entry: map.entrySet()) {
+			String export= entry.getValue();
+			int start = 0;
+			while (start >= 0 && start < export.length()) {
+				if (start > 0) {
+					// skip separator after previous entry
+					if (export.charAt(start) == ',')
+						start++;
+					else
+						logError("Error parsing forced exports: "+export+", comma expected at position "+start); //$NON-NLS-1$ //$NON-NLS-2$
+				}
+				int pos= export.indexOf(';'+XFRIENDS, start);
+				if (pos == -1)
+					break;
+				String packageName = export.substring(start, pos);
+				List<String> aspectBundles = new ArrayList<String>(); 
+				start = scanAspectBundles(export, pos+XFRIENDS.length()+1, aspectBundles);
+				for (String aspect : aspectBundles) {
+					if (aspect.equals(aspectBundleId)) {
+						result.add(new String[]{entry.getKey(), packageName});
+					}
+				}
+			}
+		}
+		return result;
+	}
+
+	private static int scanAspectBundles(String export, int pos, List<String> result) {
+		String termChars = ",]"; //$NON-NLS-1$
+		if (export.charAt(pos) == '"') {
+			pos++;
+			termChars = "\""; //$NON-NLS-1$
+		}
+		int start = pos;
+		while (pos < export.length()) {
+			char c = export.charAt(pos);
+			switch (c) {
+			case ',':
+			case ']':
+			case '"':
+				String next = export.substring(start, pos);
+				result.add(next);
+				start = pos+1;
+				if (termChars.indexOf(c) != -1)
+					return start;
+				break;
+			}
+			pos++;
+		}
+		logError("Unterminated forced exports: "+export); //$NON-NLS-1$
+		if (pos > start)
+			result.add(export.substring(start));
+		return export.length();
+	}
+
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/SafeAspectRegistry.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/SafeAspectRegistry.java
new file mode 100644
index 0000000..86575db
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/SafeAspectRegistry.java
@@ -0,0 +1,131 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2007 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: MasterTeamLoader.java 15426 2007-02-25 12:52:19Z 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.otequinox.internal.hook;
+
+import java.util.HashSet;
+
+import org.eclipse.objectteams.otequinox.hook.IAspectRegistry;
+import org.eclipse.objectteams.otequinox.hook.ILogger;
+import org.eclipse.objectteams.otequinox.hook.IOTEquinoxService;
+import org.osgi.framework.Bundle;
+
+/**
+ * Implements IAspectRegistry via an indirection in order to catch
+ * query attempts before the OT/Equinox plugin is properly initialized.
+ *  
+ * @author stephan
+ * @since OTDT 1.1.4
+ */
+@SuppressWarnings("nls")
+public class SafeAspectRegistry implements IAspectRegistry {
+
+	// references to other important objects:
+	private TransformerHook hook;
+	private IOTEquinoxService otEquinox;
+
+	// record bundles that were loaded before the transformer was in place:
+	private HashSet<String> nonAdaptableBundles = new HashSet<String>();
+	private enum InitState { OK, NOT_YET, TOO_LATE }
+	
+	SafeAspectRegistry(TransformerHook hook) {
+		this.hook= hook;
+	}
+	void connectOTEquinoxService(IOTEquinoxService otEquinoxService, ILogger log) {
+		this.otEquinox= otEquinoxService;
+		// Check whether any of the plugins, which have been loaded before the TransformerPlugin,
+		// are declared to be adapted by an aspect => ERROR.  
+		for (String bundle: nonAdaptableBundles)
+			if (otEquinoxService.isAdaptedBasePlugin(bundle))
+				log.log(Util.ERROR, "Trying to adapt non-adaptable platform bundle "+bundle);
+	}
+
+	/** see {@link org.eclipse.objectteams.otequinox.hook.IAspectRegistry#isOTDT()} */
+	public boolean isOTDT() {
+		return (this.otEquinox != null) && this.otEquinox.isOTDT();
+	}
+	
+	/** see {@link org.eclipse.objectteams.otequinox.hook.IAspectRegistry#isAdaptedBasePlugin(String)} */
+	public boolean isAdaptedBasePlugin(String baseBundleName) {
+		InitState state= checkInitialization(baseBundleName);
+		if (state == InitState.NOT_YET)
+			return false;
+		boolean result= this.otEquinox.isAdaptedBasePlugin(baseBundleName);
+		if (!result) return false;
+		if (state == InitState.TOO_LATE) 
+			throw new RuntimeException("Boot order problem: base bundle "+baseBundleName
+									  +" was loaded before the transformer plug-in was ready!");
+		return result;
+	}
+	
+	/** see {@link org.eclipse.objectteams.otequinox.hook.IAspectRegistry#getAdaptingAspectPlugins(Bundle)} */
+	public String[] getAdaptingAspectPlugins(Bundle baseBundle) {
+		InitState state= checkInitialization(baseBundle.getSymbolicName());
+		if (state == InitState.NOT_YET)
+			return new String[0];
+		String[] result= this.otEquinox.getAdaptingAspectPlugins(baseBundle);
+		if (result.length > 0) {
+			if (state == InitState.TOO_LATE) 
+				throw new RuntimeException( "Boot order problem: base bundle "+baseBundle.getSymbolicName()
+										   +" was loaded before the transformer plug-in was ready!");
+		}
+		return result;
+	}
+
+	/** see {@link org.eclipse.objectteams.otequinox.hook.IAspectRegistry#getAdaptedBasePlugins(Bundle)} */
+	public String[] getAdaptedBasePlugins(Bundle aspectBundle) {
+		if (aspectBundle == this.hook.otEquinoxBundle) 
+			return null; // don't adapt the adaptor
+		if (this.otEquinox == null)
+			return null;
+		return this.otEquinox.getAdaptedBasePlugins(aspectBundle);
+	}
+
+	/** see {@link org.eclipse.objectteams.otequinox.hook.IAspectRegistry#hasInternalTeams(Bundle)} */
+	public boolean hasInternalTeams(Bundle baseBundle) {
+		if (this.otEquinox == null)
+			return false;
+		return this.otEquinox.hasInternalTeams(baseBundle);
+	}
+
+	private InitState checkInitialization(String baseBundle) {
+		if (nonAdaptableBundles.contains(baseBundle)) {
+			if (this.otEquinox == null)
+				return InitState.NOT_YET;
+			return InitState.TOO_LATE;
+		}
+		if (this.otEquinox == null) {
+			// can't ask the registry before this field is initialized.
+			if (!Util.isPlatformBundle(baseBundle))
+				// no transformer, no log! 
+				// But note, that the same problem should also be detected by sanityCheck!
+				System.err.println("Accessing non-adaptable element "+baseBundle); 
+			nonAdaptableBundles.add(baseBundle);
+			return InitState.NOT_YET;
+		}	
+		return InitState.OK;
+	}
+	/** see {@link org.eclipse.objectteams.otequinox.hook.IAspectRegistry#isDeniedAspectPlugin(String)} */
+	public boolean isDeniedAspectPlugin(String symbolicName) {
+		if (this.otEquinox != null)
+			return this.otEquinox.isDeniedAspectPlugin(symbolicName);
+		return false;
+	}
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/TransformerHook.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/TransformerHook.java
new file mode 100644
index 0000000..87e86e9
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/TransformerHook.java
@@ -0,0 +1,654 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2004, 2009 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: TransformerHook.java 23468 2010-02-04 22:34:27Z 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.otequinox.internal.hook;
+
+import java.io.InputStream;
+import java.lang.instrument.IllegalClassFormatException;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.StringTokenizer;
+
+import org.eclipse.osgi.baseadaptor.BaseAdaptor;
+import org.eclipse.osgi.baseadaptor.BaseData;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
+import org.eclipse.osgi.baseadaptor.hooks.ClassLoadingHook;
+import org.eclipse.osgi.baseadaptor.loader.BaseClassLoader;
+import org.eclipse.osgi.baseadaptor.loader.ClasspathEntry;
+import org.eclipse.osgi.baseadaptor.loader.ClasspathManager;
+import org.eclipse.osgi.framework.adaptor.BundleClassLoader;
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.adaptor.BundleProtectionDomain;
+import org.eclipse.osgi.framework.adaptor.BundleWatcher;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegateHook;
+import org.eclipse.osgi.framework.internal.core.BundleHost;
+import org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader;
+import org.eclipse.objectteams.otequinox.hook.ClassScanner;
+import org.eclipse.objectteams.otequinox.hook.HookConfigurator;
+import org.eclipse.objectteams.otequinox.hook.IByteCodeAnalyzer;
+import org.eclipse.objectteams.otequinox.hook.ILogger;
+import org.eclipse.objectteams.otequinox.hook.IOTEquinoxService;
+import org.eclipse.objectteams.otequinox.hook.ITeamLoader;
+import org.eclipse.objectteams.otequinox.internal.hook.Util.ProfileKind;
+import org.eclipse.objectteams.otre.jplis.ObjectTeamsTransformer;
+import org.osgi.framework.Bundle;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * This class intercepts class loading in order to perform load time weaving for OT/J.
+ * 
+ * Install using these properties (either from commandline or config.ini):
+ * <pre>
+ * osgi.framework.extensions=org.eclipse.objectteams.eclipse.transformer.hook
+ * osgi.hook.configurators.include=org.eclipse.objectteams.eclipse.transformer.hook.HookConfigurator  
+ * </pre>
+ * 
+ * The principal method is processClass() which delegates to an ObjectTeamsTransformer (OTRE).
+ * The plugin life-cycle is controlled by methods initializedClassLoader() and watchBundle().
+ * 
+ * @author stephan
+ * @version $Id: TransformerHook.java 23468 2010-02-04 22:34:27Z stephan $
+ */
+@SuppressWarnings("nls")
+public class TransformerHook implements ClassLoadingHook, BundleWatcher, ClassLoaderDelegateHook
+{
+	// As an OSGI extension bundle, we can't depend on the transformer plugin, so we have to hardcode this
+	public static final String  TRANSFORMER_PLUGIN_ID         = "org.eclipse.objectteams.otequinox" ; //$NON-NLS-1$
+	
+	private static final String WORKSPACE_INITIALIZER_PLUGIN_ID = "org.eclipse.objectteams.otdt.earlyui";
+	private static final String OTDT_QUALIFIER = "OTDT";
+
+	// another non-transformable bundle:
+	private static final String ASM_PLUGIN_ID = "org.objectweb.asm"; //$NON-NLS-1$
+
+	private static final HashSet<String> WEAVE_BUNDLES = new HashSet<String>();
+	static {
+		String value = System.getProperty("otequinox.weave");
+		if (value != null && value.length() > 0) {
+			StringTokenizer st = new StringTokenizer(value, ",");
+			while (st.hasMoreTokens()) 
+				WEAVE_BUNDLES.add(st.nextToken());
+		}
+	}
+	
+	/** kinds of classes needing transformation. */
+	enum ClassKind { BASE, ROLE, TEAM; }
+
+	
+	/** A class loader which knows about OTRE but cannot transform byte code. *
+	 *  All plugins may require class org.objectteams.ITeam and related.       */
+	private ClassLoader parentClassLoader;
+	
+	
+	// ==== STATE ====
+
+	/* storage for additional information regarding base and aspect bundles. */
+	final private BundleRegistry bundleRegistry = new BundleRegistry();
+
+	/* Local registry of all classes needing adaptation.
+     * Note that the ClassKind is recorded only for logging/profiling,
+     * so it doesn't hurt that we can't assign several kinds to the same class.
+	 */
+	final HashMap<String,ClassKind> transformableClasses= new HashMap<String, ClassKind>();
+	private void addTransformableClass(String className, ClassKind kind) {
+		this.transformableClasses.put(className, kind);
+		String ifcName= className.replace("__OT__", ""); //$NON-NLS-1$ //$NON-NLS-2$
+		if (ifcName != className)
+			this.transformableClasses.put(ifcName, kind); // for role classes also enter the ifc-part
+	}
+	
+	/**
+	 * @param classBytes array containing the class' byte code
+	 * @param className '.'-separated fully qualified class name
+	 * @param resourceLoader class loader that should be used to find further .class files.
+	 * @param bundleName name of the bundle from which the class is loaded.
+	 */
+	private ClassKind fetchTransformationKind(byte[] classBytes, final String className, ClassLoader resourceLoader, final Bundle bundle) {
+		ClassKind kind = this.transformableClasses.get(className);
+		if (kind == ClassKind.BASE && !this.bundleRegistry.isAdaptedBaseBundle(bundle.getSymbolicName()))
+			kind = null; // don't use false info
+		if (   kind != null                     // found 
+			|| this.byteCodeAnalyzer == null )  // can't do better
+		{
+			return kind;
+		}
+		String superName = this.byteCodeAnalyzer.getSuperclass(classBytes, className);
+		if (superName != null)
+			return fetchInheritedTransformationKind(superName, resourceLoader, bundle.getSymbolicName());
+		return kind; 
+	}
+	private ClassKind fetchInheritedTransformationKind(String className, ClassLoader resourceLoader, String bundleName) {
+		//TODO(SH): change to use bundle here, too (rather than bundleName)?
+		ClassKind kind = this.transformableClasses.get(className); 
+		if (kind == ClassKind.BASE && !this.bundleRegistry.isAdaptedBaseBundle(bundleName))
+			kind = null; // don't use false info
+		if (kind != null) {
+			return kind;
+		}
+		String superName = null;
+		InputStream is = resourceLoader.getResourceAsStream(className.replace('.', '/')+".class");
+		if (is != null)
+			superName = this.byteCodeAnalyzer.getSuperclass(is, className); 
+		if (superName != null)
+			return fetchInheritedTransformationKind(superName, resourceLoader, bundleName);
+		return kind; 
+	}
+	
+	/** stored between method calls, indexed by plugin ID (unspecific, base or aspect): */
+	final protected HashMap<BundleData,ProtectionDomain> domains= new HashMap<BundleData,ProtectionDomain>();
+
+	// ==== access to the PackageAdmin service (for resolving bundles etc.) ====
+	private PackageAdmin packageAdmin; 
+
+	// ==== A Bridge to the TransformerPlugin ====
+	
+	/** The bundle org.eclipse.objectteams.otequinox. */
+	Bundle otEquinoxBundle;
+
+	/** Gateway to the class org.eclipse.objectteams.otequinox.TransformerPlugin. */
+	// loading and instantiating teams:
+	private ITeamLoader teamLoadingService;    
+	// asking about aspectBindings:
+	final private SafeAspectRegistry aspectRegistry= new SafeAspectRegistry(this);
+
+	private IByteCodeAnalyzer byteCodeAnalyzer;
+	
+	// logging (console or via FrameworkLog):
+	final private ILogger logger;
+
+	final private HashSet<Bundle> uninstalling = new HashSet<Bundle>();
+	
+	// Class loaders for which initializedClassLoader is currently executing (usable, but not yet registered):
+	private HashMap<Bundle,BaseClassLoader> pendingClassLoaders = new HashMap<Bundle, BaseClassLoader>(); 
+	
+	/** Constructor invoked by the framework (via {@link HookConfigurator#addHooks(org.eclipse.osgi.baseadaptor.HookRegistry)}). */
+	public TransformerHook(BaseAdaptor adaptor) {
+		this.logger = HookConfigurator.getLogger();
+
+		// make OTRE available to all bundles via framework classpath
+		// which contains the fragment org.eclipse.objectteams.otequinox.runtime:
+		this.parentClassLoader= getClass().getClassLoader();
+		
+		this.logger.log(Util.INFO, "Created equinox adaptor hook: "+getClass().getName()+
+				".\n \t(To disable this and subsequent INFO messages from OT/Equinox set otequinox.debug to WARN or ERROR).");
+	}
+
+
+	/** Invoked when the TransformerPlugin has registered itself as a service. */
+	void connectOTEquinoxService(IOTEquinoxService otEquinoxService) {
+		this.teamLoadingService= otEquinoxService;
+		this.aspectRegistry.connectOTEquinoxService(otEquinoxService, this.logger);
+		this.byteCodeAnalyzer= otEquinoxService.getByteCodeAnalyzer();
+		this.logger.log(Util.INFO, "OT/Equinox: connected the transformer service");
+	}
+	
+	void connectPackageAdmin(PackageAdmin packageAdmin) {
+		this.packageAdmin = packageAdmin;
+	}
+	
+// SH: extra safety against recursion (see Trac #173)
+	ThreadLocal<String> currentlyProcessedClassName = new ThreadLocal<String>();
+// :HS
+	/**
+	 * Delegate some classes to the ObjectTeamsTransformer for byte code weaving.
+	 */
+	public byte[] processClass(String name, byte[] classbytes,
+			ClasspathEntry classpathEntry, BundleEntry entry,
+			ClasspathManager manager) 
+	{	
+		String previousClassName = currentlyProcessedClassName.get();
+		this.currentlyProcessedClassName.set(name);
+		try {
+		
+			Bundle bundle = manager.getBaseData().getBundle();
+			ProtectionDomain domain = domains.get(manager.getBaseData());
+			if (bundle == this.otEquinoxBundle)
+				return classbytes; // don't transform the adaptor ;-)
+			if (ASM_PLUGIN_ID.equals(bundle.getSymbolicName())) // name comparison since multiple instances of this bundle could exist
+				return classbytes; // don't transform ASM
+					
+// SH: extra safety against recursion (see Trac #173)
+			if (name.equals(previousClassName)) {
+				logger.log(new Exception("unexpected loading situation"), "Recursion occurred loading class "+name+" from bundle "+bundle.getSymbolicName());
+				return null;				
+			}
+// :HS
+			
+			// NB: we must use a fresh instance of ObjectTeamsTransformer
+			// on each invocation because the OTRE is _not_ thread safe, 
+			// specifically the field ObjectTeamsTransformation.factory 
+			// MUST NOT be accessed or even assigned concurrently.
+			ObjectTeamsTransformer objectTeamsTransformer= null;
+
+			ClassLoader resourceLoader = null;
+			if (ClassScanner.REPOSITORY_USE_RESOURCE_LOADER)
+				resourceLoader = (ClassLoader) manager.getBaseClassLoader(); 
+			
+			// ==== loading an adapted base class? 
+			long time = 0;
+			if (Util.PROFILE) time= System.nanoTime();
+			ClassKind classKind= fetchTransformationKind(classbytes, name, resourceLoader, bundle);
+			if (Util.PROFILE) Util.profile(time, ProfileKind.SuperClassFetching, "", this.logger);
+			if (classKind == ClassKind.BASE) {
+				objectTeamsTransformer= new ObjectTeamsTransformer();
+				classbytes= transformClass(objectTeamsTransformer, resourceLoader,
+										   name, classbytes, domain, 
+								           "potential base", ProfileKind.BaseTransformation);
+			// ==== loading a role class? 
+			} else if (classKind == ClassKind.ROLE) {
+				objectTeamsTransformer= new ObjectTeamsTransformer();
+				classbytes= transformClass(objectTeamsTransformer, resourceLoader,
+										   name, classbytes, domain, 
+										   "role", ProfileKind.AspectTransformation); //$NON-NLS-1$
+			} else  {
+				// ==== loading a team class?
+				boolean isLoading = AspectBundleRole.isLoadingTeams(bundleRegistry, bundle.getSymbolicName());
+				if (isLoading || ByteCodeAnalyzer.isTeam(classbytes)) {
+					try {
+						if (bundle.getState() < Bundle.STARTING && this.aspectRegistry.hasInternalTeams(bundle)) {
+							this.logger.doLog(Util.ERROR, "Illegal state: loading team class "+name+" from bundle "+bundle.getSymbolicName()+" \n"+
+														  "while this bundle has not been activated yet (state="+bundle.getState()+").\n"+
+														  "Aspect bindings will not be woven.\n"+
+														  "Note: Perhaps the bundle lacks an activation policy (lazy)?");
+							return null;
+						}
+						AspectBundleRole.markLoadingTeams(bundleRegistry, bundle.getSymbolicName(), true);
+						// transform the aspect:
+						objectTeamsTransformer= new ObjectTeamsTransformer();
+						classbytes= transformClass(objectTeamsTransformer, resourceLoader,
+												   name, classbytes, domain, 
+												   "team", ProfileKind.AspectTransformation);				 //$NON-NLS-1$
+					} finally {
+						if (!isLoading) 
+							AspectBundleRole.markLoadingTeams(bundleRegistry, bundle.getSymbolicName(), false);
+					}
+				} else if (   this.otEquinoxBundle != null 
+						   && WEAVE_BUNDLES.contains(bundle.getSymbolicName())
+						   && this.aspectRegistry.isAdaptedBasePlugin(bundle.getSymbolicName()))
+				{
+					objectTeamsTransformer= new ObjectTeamsTransformer();
+					classbytes= transformClass(objectTeamsTransformer, resourceLoader,
+											   name, classbytes, domain, 
+									           "ordinary class (could be sub base class)", ProfileKind.BaseTransformation);
+				}
+			}
+		}
+		catch (IllegalClassFormatException icfe) {
+			icfe.printStackTrace();
+		}
+		finally {
+			this.currentlyProcessedClassName.set(previousClassName);
+		}
+		return classbytes;
+	}
+
+	// transform, log and profile:
+	private byte[] transformClass(ObjectTeamsTransformer objectTeamsTransformer, ClassLoader resourceLoader,
+								  String name, byte[] classbytes, ProtectionDomain domain, 
+								  String kind, ProfileKind profileKind)
+			throws IllegalClassFormatException 
+	{
+		this.logger.log(Util.OK, "about to transform "+kind+" class "+name);
+		long time= 0;
+		if (Util.PROFILE) time= System.nanoTime();
+		classbytes = objectTeamsTransformer.transform(resourceLoader, name, null, domain, classbytes);
+		if (Util.PROFILE) Util.profile(time, profileKind, name, this.logger);
+		return classbytes;
+	}
+	
+	// hook method, no specific action
+	@SuppressWarnings("unchecked")
+	public boolean addClassPathEntry(
+			ArrayList cpEntries, 
+			String cp,
+			ClasspathManager hostmanager, 
+			BaseData sourcedata,
+			ProtectionDomain sourcedomain) 
+	{
+		return false;
+	}
+
+	// hook method, no specific action
+	public String findLibrary(BaseData data, String libName) {
+		return null;
+	}
+	
+	/** Make all bundles share the system classloader as their parent,
+	 *  in order to expose the OTRE to all.
+	 */
+	public ClassLoader getBundleClassLoaderParent() {
+		return parentClassLoader;
+	}
+
+	public BaseClassLoader createClassLoader(
+			final ClassLoader parent,
+			ClassLoaderDelegate delegate, 
+			BundleProtectionDomain domain,
+			BaseData data, 
+			String[] bundleclasspath) 
+	{
+		Bundle bundle = data.getBundle();
+
+		if (this.pendingClassLoaders.containsKey(bundle))
+			// a class loader is being announced via initializedClassloader,
+			// yet, before that method returns, the Bundle isn't yet wired to the class loader,
+			// so grab it from our intermediate storage:
+			return this.pendingClassLoaders.get(bundle);
+		
+		// some paranoid sanity checks (shouldn't trigger any more)
+		if (   bundle != this.otEquinoxBundle
+			&& !uninstalling.contains(bundle) // perhaps can't query transformer yet (uninstalling unused during launch)
+			&& this.aspectRegistry.isAdaptedBasePlugin(bundle.getSymbolicName())) 
+		{
+			BaseBundleRole baseBundle= this.bundleRegistry.adaptedBaseBundles.get(bundle.getSymbolicName());
+			if (baseBundle != null && baseBundle.state != BaseBundleRole.State.INITIAL) {
+				
+				// defensive:
+				this.logger.log(Util.ERROR, "Circular class path for "+bundle.getSymbolicName());
+				// return a dummy class loader that rather fails than dead-locking.
+				return new DefaultClassLoader(parent, delegate, domain, data, bundleclasspath) 
+				{
+					@Override
+					public Class<?> findLocalClass(String classname) throws ClassNotFoundException {
+						throw new ClassNotFoundException("Has already reported class path circularity");
+					}
+					@Override public URL findLocalResource(String resource) { return null; }
+
+					@Override public void initialize() { /* nop to avoid infinite recursion */ }
+
+					@Override
+					public Class<?> loadClass(String name) throws ClassNotFoundException {
+						throw new ClassNotFoundException("Has already reported class path circularity");
+					}
+				};
+			}
+		}
+		
+		// normally we let the Framework create classloaders:
+		return null;
+	}
+
+	public void initializedClassLoader(final BaseClassLoader bundleClassLoader, BaseData data) 
+	{
+// DEBUG:
+//		if (isKnowID(data.getSymbolicName()))
+//			System.out.println(">>1>>"+data.getSymbolicName());
+		
+	
+ 		ProtectionDomain domain = bundleClassLoader.getDomain();
+		if (domain != null)
+			domains.put(data, domain);
+	
+		Bundle bundle = data.getBundle();		
+		if (bundle == otEquinoxBundle) // don't adapt the transformer. 
+			return;
+		
+		if (this.uninstalling.contains(bundle)) // don't adapt bundle that is being uninstalled
+			return;
+
+		if (this.pendingClassLoaders.containsKey(bundle))
+			return; // already initialized (in this very call-stack?)
+		
+		// ==== perhaps it's an aspect adapting some base bundle(s): 
+
+		// register class loaders for teams per plugin ID of adapted base
+		String[] adaptedBaseBundles = this.aspectRegistry.getAdaptedBasePlugins(bundle);
+		if (adaptedBaseBundles != null) 
+			for (String baseID : adaptedBaseBundles) {
+				Bundle baseBundle = null;
+				if (baseID.toUpperCase().equals(BaseBundleRole.SELF)) {
+					baseBundle = bundle;
+				} else {
+					Bundle[] baseBundles = packageAdmin.getBundles(baseID, null);
+					if (baseBundles == null) {
+						this.logger.log(Util.ERROR, "Adapted base bundle "+baseID+" not found.");
+					} else if (baseBundles.length > 1) {
+						baseBundle = baseBundles[0];
+						for (int i=1; i<baseBundles.length; i++)
+							if (baseBundle.getVersion().compareTo(baseBundles[i].getVersion()) < 0) // smaller version than next
+								baseBundle = baseBundles[i];
+						this.logger.log(Util.WARNING, "Found more than one version of adapted base bundle "+baseID+", picked version "+baseBundle.getVersion()+".");
+					} else {
+						baseBundle = baseBundles[0];
+					}
+				}
+				if (baseBundle != null)
+					BaseBundleRole.createBaseBundleRole(this.bundleRegistry, baseBundle, bundle);
+			}
+		
+		// ==== perhaps it's a base bundle:
+		
+		// Trigger PHASE 1 of aspect activation:
+		if (this.teamLoadingService != null)
+			checkLoadTeams(bundleClassLoader, bundle);
+		else if (!Util.isPlatformBundle(bundle.getSymbolicName()))
+			this.logger.log(Util.WARNING, "Not loading teams for bundle "+bundle.getSymbolicName()+
+					           " (transformerPlugin not yet initialized)");
+	}
+	
+	private void checkLoadTeams(BaseClassLoader bundleClassLoader, Bundle bundle) 
+	{
+		this.pendingClassLoaders.put(bundle,bundleClassLoader);
+		ClassScanner scanner = new ClassScanner();
+		this.bundleRegistry.checkLoadTeams(bundle, this.aspectRegistry, this.teamLoadingService, scanner);
+		recordRolesAndBases(scanner);
+		this.pendingClassLoaders.remove(bundle);
+	}
+	
+	private void recordRolesAndBases(ClassScanner scanner) {
+		Collection<String> baseClassNames = scanner.getCollectedBaseClassNames();
+		if (baseClassNames != null)
+			for (String baseClassName : baseClassNames)
+				addTransformableClass(baseClassName, ClassKind.BASE);
+		Collection<String> roleClassNames = scanner.getCollectedRoleClassNames();						
+		if (roleClassNames != null)
+			for (String roleClassName : roleClassNames)
+				addTransformableClass(roleClassName, ClassKind.ROLE);
+	}
+	
+	/**
+	 * This method watches the life-cycle of plugins, and implements 2-phase loading for the 
+	 * transformer bundle org.eclipse.objectteams.otequinox:
+	 * <pre>
+	 * STEP 1. as soon as possible (may need to try more than once):
+	 * 			-> retrieve and store bundle org.eclipse.objectteams.otequinox in "transformerBundle" 
+	 * 			-> activate org.eclipse.objectteams.otequinox
+	 * STEP 2. after activating org.eclipse.objectteams.otequinox
+	 * 			-> load class TransformerPlugin and store in "transformerPluginClass"
+	 * </pre>
+	 * 
+	 * For all other plugins check whether PHASE-2 of loading has to be performed when
+	 * the plugin's activation finishes.
+	 */
+	public void watchBundle(final Bundle bundle, int type) {
+// DEBUG:		
+//		if (isKnownID(bundle.getSymbolicName()))
+//			System.out.println(">>2>>"+bundle.getSymbolicName());
+		
+		if (type == BundleWatcher.START_ACTIVATION) {
+			if (TRANSFORMER_PLUGIN_ID.equals(bundle.getSymbolicName()))
+				otEquinoxBundle = bundle;
+			checkJdtCoreOrig(bundle);
+			checkInternalTeams(bundle);
+		}
+
+		if (type == BundleWatcher.END_ACTIVATION) {
+			Util.reportBundleStateChange(bundle, type, this.logger);
+
+			// start the transformerBundle as early as possible:
+			// (runtime must be activated for initialization of locations).
+			if ("org.eclipse.core.runtime".equals(bundle.getSymbolicName())) 
+				startTransformerBundle();
+			
+			if (bundle != otEquinoxBundle) {
+				// ==== trigger remaining actions now that the plugin is activated:
+				BaseBundleRole.endActivation(bundleRegistry, bundle, this.aspectRegistry, this.teamLoadingService);
+			}
+		} else if (type == BundleWatcher.START_UNINSTALLING) {
+			this.uninstalling.add(bundle);
+		} else if (type == BundleWatcher.END_UNINSTALLING) {
+			this.uninstalling.remove(bundle);
+		}
+	}
+	
+	/** Load all internal teams of bundle, if any. */
+	private void checkInternalTeams(Bundle bundle) {
+		if (this.aspectRegistry.hasInternalTeams(bundle)) {
+			// aspectRole needed to record 'aspectRole.isLoading' from processClass().
+			this.bundleRegistry.createAspectRole(bundle.getSymbolicName());
+			this.logger.log(Util.OK, "Will load internal teams of "+bundle.getSymbolicName());
+			ClassScanner scanner = new ClassScanner();
+			if (this.teamLoadingService.loadInternalTeams(bundle, scanner)) {
+				recordRolesAndBases(scanner);
+				BaseBundleRole baseRole= BaseBundleRole.createBaseBundleRole(bundleRegistry, bundle, bundle); // self-adapting
+				baseRole.state= BaseBundleRole.State.TEAMS_LOADED;
+				this.logger.log(Util.INFO, "Loaded internal teams of "+bundle.getSymbolicName());
+			} else {
+				this.logger.log(Util.ERROR, "Failed to load internal teams of "+bundle.getSymbolicName());
+			}
+		}
+	}
+
+	/** Loading an original jdt.core is fatal for us: */
+	private void checkJdtCoreOrig(Bundle bundle) {
+		if (!this.aspectRegistry.isOTDT()) return; // nothing to check
+		if ("org.eclipse.jdt.core".equals(bundle.getSymbolicName())) {
+			if (bundle instanceof BundleHost) {
+				BundleHost host = (BundleHost)bundle;
+				if (!host.getVersion().getQualifier().contains(OTDT_QUALIFIER))
+					throw new Error("Fatal dependency problem: loading wrong version '"+host.getVersion()+"' of plug-in "+bundle.getSymbolicName());
+			}
+		}
+	}
+
+	private void startTransformerBundle() {
+		if (this.otEquinoxBundle != null)
+			return;		
+		this.otEquinoxBundle = startBundle(TRANSFORMER_PLUGIN_ID, true);
+		// check if we want to set a workspace location (earliest opportunity):
+		startBundle(WORKSPACE_INITIALIZER_PLUGIN_ID, false); // ignore if not present
+	}
+
+	private Bundle startBundle(String bundleID, boolean logError) {
+		Bundle[] candidates = packageAdmin.getBundles(bundleID, null);
+		if (candidates == null) {
+			if (logError)
+				this.logger.log(ILogger.ERROR, "Bundle "+bundleID+" not found");
+			return null;
+		}
+		Bundle bundle = candidates[0]; // [0] corresponds to the highest version 
+		try {
+			bundle.start();
+		} catch (Exception e) {
+			if (logError)
+				this.logger.log(e, "Error starting the OT/Equinox transformer plug-in");
+		}
+		return bundle;
+	}
+	
+	public Class<?> preFindClass(String name, BundleClassLoader classLoader, BundleData data) 
+			throws ClassNotFoundException 
+	{
+		if (aspectRegistry.isDeniedAspectPlugin(data.getSymbolicName()))
+			throw new ClassNotFoundException(name+" from denied aspect bundle "+data.getSymbolicName());
+		
+		BaseBundleRole baseBundle= this.bundleRegistry.adaptedBaseBundles.get(data.getSymbolicName());
+		if (baseBundle == null)
+			return null; // nothing to contribute
+		
+		synchronized (baseBundle) {
+			if (baseBundle.missingClassNames.contains(name))
+				throw new ClassNotFoundException(name); // already failed before
+		}
+		return baseBundle.knownAlienClasses.get(name); // check for previous success
+	}
+	
+	ThreadLocal<String> currentlySearchedClassName = new ThreadLocal<String>();
+	
+	public Class<?> postFindClass(String name, BundleClassLoader classLoader, BundleData data) {
+		if (name.equals(currentlySearchedClassName.get()))
+			return null;
+		String bundleSymbolicName = data.getSymbolicName();
+		BaseBundleRole baseBundle= this.bundleRegistry.adaptedBaseBundles.get(bundleSymbolicName);
+		if (baseBundle == null)
+			return null; // not a registered base plugin, nothing to contribute
+		if (baseBundle.state == BaseBundleRole.State.WAIT_FOR_TEAM) {
+			logger.log(Util.WARNING, "Base plugin "+bundleSymbolicName+" is not yet ready for delegated classloading");
+		} else if (baseBundle.aspectBundles.isEmpty()) {
+			logger.log(Util.WARNING, ">>> adapting aspect bundles not yet wired when loading: "+name);
+		} else {
+			// a team class may be alien to the current plugin, yet the woven plugin
+			// may depend on the team. That's why we need this ClassLoaderDelegateHook.
+			
+			// avoid infinite recursion:
+			currentlySearchedClassName.set(name);
+			try {
+				for (Bundle aspectBundle: baseBundle.aspectBundles) {
+					if (aspectBundle.getBundleId() == data.getBundleID())
+						continue; // self adaptation, nothing to contribute
+					try {
+						Class<?> result= aspectBundle.loadClass(name);
+						if (result != null) {
+							baseBundle.knownAlienClasses.put(name, result);
+							logger.log(Util.OK, "loaded alien class from aspect plugin: "+name);
+							return result;
+						}
+					} catch (ClassNotFoundException e) { 
+						// noop, keep traversing aspect bundles
+					}
+				}
+			} finally {
+				currentlySearchedClassName.set(null);
+			}
+			synchronized(baseBundle) {
+				// remember this class so we never again need to search
+				baseBundle.missingClassNames.add(name); 
+			}
+		}
+		return null;
+	}
+	public String postFindLibrary(String name, BundleClassLoader classLoader, BundleData data) {
+		return null;
+	}
+	public URL postFindResource(String name, BundleClassLoader classLoader, BundleData data) {
+		return null;
+	}
+	@SuppressWarnings("unchecked")
+	public Enumeration postFindResources(String name, BundleClassLoader classLoader, BundleData data) {
+		return null;
+	}
+	public String preFindLibrary(String name, BundleClassLoader classLoader, BundleData data) {
+		return null;
+	}
+	public URL preFindResource(String name, BundleClassLoader classLoader, BundleData data) {
+		return null;
+	}
+	@SuppressWarnings("unchecked")
+	public Enumeration preFindResources(String name, BundleClassLoader classLoader, BundleData data) {
+		return null;
+	}
+
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/Util.java b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/Util.java
new file mode 100644
index 0000000..eca9938
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox.hook/src/org/eclipse/objectteams/otequinox/internal/hook/Util.java
@@ -0,0 +1,138 @@
+/**********************************************************************
+ * 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.otequinox.internal.hook;
+
+import java.util.HashSet;
+
+import org.eclipse.objectteams.otequinox.hook.ILogger;
+import org.eclipse.osgi.framework.adaptor.BundleWatcher;
+import org.osgi.framework.Bundle;
+
+public class Util 
+{
+	// from org.eclipse.core.runtime.IStatus:
+	public static final int OK = 0;
+	public static final int INFO = 0x01;
+	public static final int WARNING = 0x02;
+	public static final int ERROR = 0x04;
+	
+	// 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>();
+			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"
+				})
+				PLATFORM_BUNDLES.add(bundle);
+		}
+	}
+
+	static boolean isPlatformBundle(String bundleName) {
+		checkInit();
+		return PLATFORM_BUNDLES.contains(bundleName);
+	}
+	
+	static void reportBundleStateChange(Bundle bundle, int type, ILogger logger) {
+		if (Util.WARN_LEVEL == Util.OK) {
+			String msg = "";                                                  //$NON-NLS-1$
+			switch (type) {
+			case BundleWatcher.START_INSTALLING: msg += "Installing "; break; //$NON-NLS-1$
+			case BundleWatcher.END_INSTALLING:   msg += "Installed  "; break; //$NON-NLS-1$
+			case BundleWatcher.START_ACTIVATION: msg += "Activating "; break; //$NON-NLS-1$
+			case BundleWatcher.END_ACTIVATION:   msg += "Activated  "; break; //$NON-NLS-1$
+			}
+			logger.log(OK, msg+bundle.toString());
+		}
+	}
+
+	@SuppressWarnings("nls")
+	public static void profile(long startTime, ProfileKind kind, String msg, ILogger 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();
+	}
+}