initial commit in accordance with CQ 3784
diff --git a/plugins/org.eclipse.objectteams.otequinox/.classpath b/plugins/org.eclipse.objectteams.otequinox/.classpath
new file mode 100644
index 0000000..304e861
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/.classpath
@@ -0,0 +1,7 @@
+<?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="output" path="bin"/>
+</classpath>
diff --git a/plugins/org.eclipse.objectteams.otequinox/.project b/plugins/org.eclipse.objectteams.otequinox/.project
new file mode 100644
index 0000000..7cbf18f
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.objectteams.otequinox</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/.settings/org.eclipse.jdt.core.prefs b/plugins/org.eclipse.objectteams.otequinox/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..64ec586
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,70 @@
+#Thu Mar 12 15:19:36 CET 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+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=ignore
+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=ignore
+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=warning
+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/META-INF/MANIFEST.MF b/plugins/org.eclipse.objectteams.otequinox/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..38e210e
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/META-INF/MANIFEST.MF
@@ -0,0 +1,18 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: ObjectTeams/Equinox Integration
+Bundle-SymbolicName: org.eclipse.objectteams.otequinox;singleton:=true
+Bundle-Version: 1.4.0.qualifier
+Bundle-Vendor: objectteams.org
+Require-Bundle: org.eclipse.equinox.common,
+ org.eclipse.equinox.registry,
+ org.eclipse.osgi;bundle-version="[3.6.0.v20090928,4.0.0)",
+ org.objectweb.asm;bundle-version="[3.1.0,4.0.0)"
+Import-Package: org.eclipse.core.internal.runtime;resolution:=optional,
+ org.eclipse.objectteams.otequinox.hook,
+ org.eclipse.objectteams.otre
+Bundle-Activator: org.eclipse.objectteams.otequinox.TransformerPlugin
+Export-Package: org.eclipse.objectteams.otequinox
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-ActivationPolicy: lazy
+
diff --git a/plugins/org.eclipse.objectteams.otequinox/META-INF/p2.inf b/plugins/org.eclipse.objectteams.otequinox/META-INF/p2.inf
new file mode 100644
index 0000000..e2b8d9e
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/META-INF/p2.inf
@@ -0,0 +1,10 @@
+instructions.install = \
+	setProgramProperty(propName:osgi.hook.configurators.include,propValue:org.eclipse.objectteams.otequinox.hook.HookConfigurator); \
+	setProgramProperty(propName:osgi.classloader.singleThreadLoads,propValue:false); \
+	setProgramProperty(propName:osgi.classloader.lock,propValue:classname); \
+	setProgramProperty(propName:ot.equinox,propValue:1);
+instructions.uninstall = \
+	setProgramProperty(propName:osgi.hook.configurators.include,propValue:); \
+	setProgramProperty(propName:osgi.classloader.singleThreadLoads,propValue:); \
+	setProgramProperty(propName:osgi.classloader.lock,propValue:); \
+	setProgramProperty(propName:ot.equinox,propValue:);
diff --git a/plugins/org.eclipse.objectteams.otequinox/about.ini b/plugins/org.eclipse.objectteams.otequinox/about.ini
new file mode 100644
index 0000000..8b89ca6
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/about.ini
@@ -0,0 +1,14 @@
+aboutText=Object Teams -- Equinox integration (OT/Equinox)\n\
+\n\
+OTDT Version: 1.4.0M2 - developed for Eclipse version 3.6.0M4\n\
+\n\
+(c) Copyright Technical University Berlin and others, 2005, 2010\n\
+Visit http://www.eclipse.org/objectteams\n\
+\n\
+Upon installation, OT/Equinox is enabled in your configuration/config.ini file.\n\
+For instructions on how to temporarily disable OT/Equinox see the instructions at:\n\
+	http://www.objectteams.org/distrib/features.html#otequinox 
+
+
+featureImage=ote_32n.png
+
diff --git a/plugins/org.eclipse.objectteams.otequinox/build.properties b/plugins/org.eclipse.objectteams.otequinox/build.properties
new file mode 100644
index 0000000..8e8d1f7
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/build.properties
@@ -0,0 +1,13 @@
+bin.includes = META-INF/,\
+               plugin.xml,\
+               .,\
+               schema/,\
+               about.ini,\
+               ote_32n.png
+jars.compile.order = .
+src.includes = build.properties,\
+               META-INF/,\
+               .classpath,\
+               .project,\
+               schema/
+source.. = src/
diff --git a/plugins/org.eclipse.objectteams.otequinox/ote_32n.png b/plugins/org.eclipse.objectteams.otequinox/ote_32n.png
new file mode 100644
index 0000000..490ac03
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/ote_32n.png
Binary files differ
diff --git a/plugins/org.eclipse.objectteams.otequinox/plugin.xml b/plugins/org.eclipse.objectteams.otequinox/plugin.xml
new file mode 100644
index 0000000..5c9aff3
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/plugin.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin>
+   <extension-point id="aspectBindings" name="OT/J Aspect Bindings" schema="schema/aspectBindings.exsd"/>
+   <extension-point id="aspectBindingNegotiators" name="OT/Equinox negotiators for aspect binding requests" schema="schema/aspectBindingNegotiators.exsd"/>
+</plugin>
diff --git a/plugins/org.eclipse.objectteams.otequinox/schema/aspectBindingNegotiators.exsd b/plugins/org.eclipse.objectteams.otequinox/schema/aspectBindingNegotiators.exsd
new file mode 100644
index 0000000..9d54188
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/schema/aspectBindingNegotiators.exsd
@@ -0,0 +1,145 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.objectteams.otequinox" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+      <appInfo>
+         <meta.schema plugin="org.eclipse.objectteams.otequinox" id="aspectBindingNegotiators" name="OT/Equinox negotiators for aspect binding requests"/>
+      </appInfo>
+      <documentation>
+         Allow client plugins to participate in negotiation whether aspect binding requests 
+(incl. forced exports) should be denied or granted.
+&lt;p&gt;
+All extensions will be asked whenever an aspect plugin requests an aspect binding 
+or a forced export for which no permission could be found in persistent storage 
+of neither the eclipse installation nor the workspace.
+&lt;ul&gt;
+&lt;li&gt;As soon as any extension DENYs a given request this aspect plugin is blocked.&lt;/li&gt;
+&lt;li&gt;If no extension GRANTs the request the aspect plugin is blocked, too.&lt;/li&gt;
+&lt;li&gt;Only if at least one extension GRANTs the request and no extension DENYs
+the aspect may proceed and will be woven into its base entities.&lt;/li&gt;
+&lt;/ul&gt;
+&lt;/p&gt;
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <annotation>
+         <appInfo>
+            <meta.element />
+         </appInfo>
+      </annotation>
+      <complexType>
+         <sequence>
+            <element ref="negotiator" minOccurs="1" maxOccurs="unbounded"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute translatable="true"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="negotiator">
+      <annotation>
+         <documentation>
+            A participant in the protocol for aspect binding negotiation,
+which is able to grant or deny a request by an aspect bundle.
+         </documentation>
+      </annotation>
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  Fully qualified name of the class that shall participate in the negotiation protocol.
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="java" basedOn=":org.eclipse.objectteams.otequinox.IAspectRequestNegotiator"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="since"/>
+      </appInfo>
+      <documentation>
+         OTDT 1.2.6
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="examples"/>
+      </appInfo>
+      <documentation>
+         Example of a declaration of a &lt;code&gt;negotiator&lt;/code&gt;:&lt;pre&gt;
+&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+&lt;?eclipse version=&quot;3.2&quot;?&gt;
+&lt;plugin&gt;
+   &lt;extension
+         point=&quot;org.eclipse.objectteams.otequinox.aspectBindingNegotiators&quot;&gt;
+      &lt;negotiator
+            class=&quot;yesser.AspectRequestGranter&quot;&gt;
+      &lt;/negotiator&gt;
+   &lt;/extension&gt;
+&lt;/plugin&gt;
+&lt;/pre&gt;
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="apiInfo"/>
+      </appInfo>
+      <documentation>
+         The class named in the &lt;code&gt;class&lt;/code&gt; property must implement the &lt;code&gt;org.eclipse.objectteams.otequinox.IAspectRequestNegotiator&lt;/code&gt; interface.
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="copyright"/>
+      </appInfo>
+      <documentation>
+         &lt;em&gt;
+&lt;p&gt;
+This file is part of &quot;Object Teams Development Tooling&quot;-Software
+&lt;/p&gt;&lt;p&gt;
+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
+&lt;a href=&quot;http://www.eclipse.org/legal/epl-v10.html&quot;&gt;http://www.eclipse.org/legal/epl-v10.html&lt;/a&gt;
+&lt;/p&gt;&lt;p&gt;
+Please visit &lt;a href=&quot;http://www.objectteams.org&quot;&gt;www.objectteams.org&lt;/a&gt; for updates and contact.
+&lt;/p&gt;&lt;p&gt;
+Contributors:&lt;br&gt;
+Technical University Berlin - Initial API and implementation
+&lt;/p&gt;
+&lt;/em&gt;
+      </documentation>
+   </annotation>
+
+</schema>
diff --git a/plugins/org.eclipse.objectteams.otequinox/schema/aspectBindings.exsd b/plugins/org.eclipse.objectteams.otequinox/schema/aspectBindings.exsd
new file mode 100644
index 0000000..e2d90ed
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/schema/aspectBindings.exsd
@@ -0,0 +1,278 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.objectteams.otequinox" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+      <appInfo>
+         <meta.schema plugin="org.eclipse.objectteams.otequinox" id="aspectBindings" name="OT/J Aspect Bindings"/>
+      </appInfo>
+      <documentation>
+         This extension point allows to define a new &lt;strong&gt;relationship&lt;/strong&gt; between plug-ins 
+called &lt;strong&gt;&quot;aspectBinding&quot;&lt;/strong&gt;.
+By an aspectBinding an aspect plug-in declares which &lt;strong&gt;base plug-ins&lt;/strong&gt; it wishes to adapt 
+and which &lt;strong&gt;team classes&lt;/strong&gt; are used for that purpose.
+Only teams in this list are allowed to adapt classes from another plug-in, and such
+adaptation is restricted to classes residing in the specified base plug-in.
+
+Each team class mentioned in an aspectBinding is automatically &lt;strong&gt;instantiated&lt;/strong&gt; 
+before the corresponding base plug-in is fully activated.
+In addition extensions may specify that a given team is also &lt;strong&gt;activated&lt;/strong&gt; 
+after instantiation.
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <annotation>
+         <appInfo>
+            <meta.element />
+         </appInfo>
+      </annotation>
+      <complexType>
+         <sequence>
+            <element ref="aspectBinding" minOccurs="1" maxOccurs="unbounded"/>
+         </sequence>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute translatable="true"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="aspectBinding">
+      <annotation>
+         <appInfo>
+            <meta.element icon="icon"/>
+         </appInfo>
+         <documentation>
+            Specifies an individual aspect binding by which a given team class adapts classes from a given base plugin.
+         </documentation>
+      </annotation>
+      <complexType>
+         <sequence>
+            <element ref="basePlugin"/>
+            <element ref="team" minOccurs="1" maxOccurs="unbounded"/>
+         </sequence>
+         <attribute name="icon" type="string" use="default" value="platform:/plugin/org.eclipse.objectteams.otdt.ui/icons/ot/calloutbinding_obj.gif">
+            <annotation>
+               <documentation>
+                  &lt;i&gt;Default value to provide an icon for this element kind. No need to edit&lt;/i&gt;
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="resource"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="basePlugin">
+      <annotation>
+         <appInfo>
+            <meta.element icon="icon"/>
+         </appInfo>
+         <documentation>
+            &lt;p&gt;
+The base plug-in whose classes may be adapted by the given team(s). 
+The base bundle must be a regular bundle, not a fragment.
+If a bundle &lt;b&gt;fragment&lt;/b&gt; should be adapted the aspect binding must refer to the fragment&apos;s host bundle
+and additionally a &lt;code&gt;requiredFragment&lt;/code&gt; should be added.
+&lt;/p&gt;
+&lt;p&gt;
+By specifying &lt;code&gt;SELF&lt;/code&gt; as the basePlugin, a team may adapt classes from its own bundle.
+&lt;/p&gt;
+         </documentation>
+      </annotation>
+      <complexType>
+         <sequence>
+            <element ref="forcedExports" minOccurs="0" maxOccurs="1"/>
+            <element ref="requiredFragment" minOccurs="0" maxOccurs="unbounded"/>
+         </sequence>
+         <attribute name="id" type="string" use="required">
+            <annotation>
+               <documentation>
+                  Qualified identifier of the base plug-in.
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="icon" type="string" use="default" value="platform:/plugin/org.eclipse.pde.ui/icons/obj16/plugin_obj.gif">
+            <annotation>
+               <documentation>
+                  &lt;i&gt;Default value to provide an icon for this element kind. No need to edit&lt;/i&gt;
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="resource"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="team">
+      <annotation>
+         <appInfo>
+            <meta.element labelAttribute="class" icon="icon"/>
+         </appInfo>
+         <documentation>
+            The team class of this plug-in which is allowed to adapt classes of the given base plug-in.
+         </documentation>
+      </annotation>
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  Fully qualified name of a team class.
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="java" basedOn=":org.objectteams.ITeam"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+         <attribute name="superclass" type="string">
+            <annotation>
+               <documentation>
+                  Fully qualified name of the team&apos;s super-class if that is not org.objectteams.Team but another team of the same plugin.
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="java" basedOn=":org.objectteams.ITeam"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+         <attribute name="activation">
+            <annotation>
+               <documentation>
+                  Request activation of this team class: 
+NONE:        don&apos;t automatically activate (default)
+THREAD:      activate for one thread (discouraged)
+ALL_THREADS: activate for all threads
+               </documentation>
+            </annotation>
+            <simpleType>
+               <restriction base="string">
+                  <enumeration value="NONE">
+                  </enumeration>
+                  <enumeration value="THREAD">
+                  </enumeration>
+                  <enumeration value="ALL_THREADS">
+                  </enumeration>
+               </restriction>
+            </simpleType>
+         </attribute>
+         <attribute name="icon" type="string" use="default" value="platform:/plugin/org.eclipse.objectteams.otdt.ui/icons/ot/team_obj.gif">
+            <annotation>
+               <documentation>
+                  &lt;i&gt;Default value to provide an icon for this element kind. No need to edit&lt;/i&gt;
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="resource"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="forcedExports" type="string">
+      <annotation>
+         <documentation>
+            Declare any requests to access packages from the base plugin which are not exported.
+Provide comma separated list of package names (as in the OSGi Export-Package: header).
+         </documentation>
+      </annotation>
+   </element>
+
+   <element name="requiredFragment">
+      <annotation>
+         <documentation>
+            Name of a fragment of the base bundle that is required by the aspect.
+         </documentation>
+      </annotation>
+      <complexType>
+         <attribute name="id" type="string" use="required">
+            <annotation>
+               <documentation>
+                  Symbolic name of the fragment.
+               </documentation>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="since"/>
+      </appInfo>
+      <documentation>
+         OTDT 0.9.1 based on Eclipse 3.2.
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="examples"/>
+      </appInfo>
+      <documentation>
+         See example plugin org.eclipse.objectteams.otequinox.branding.
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="apiInfo"/>
+      </appInfo>
+      <documentation>
+         The &lt;code&gt;class&lt;/code&gt; named in the class property must be a team class thus implementing the &lt;code&gt;org.objectteams.ITeam&lt;/code&gt; interface.
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="implementation"/>
+      </appInfo>
+      <documentation>
+         None.
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="copyright"/>
+      </appInfo>
+      <documentation>
+         &lt;em&gt;
+&lt;p&gt;
+This file is part of &quot;Object Teams Development Tooling&quot;-Software
+&lt;/p&gt;&lt;p&gt;
+Copyright 2006, 2010 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
+&lt;a href=&quot;http://www.eclipse.org/legal/epl-v10.html&quot;&gt;http://www.eclipse.org/legal/epl-v10.html&lt;/a&gt;
+&lt;/p&gt;&lt;p&gt;
+Please visit &lt;a href=&quot;http://www.objectteams.org&quot;&gt;www.objectteams.org&lt;/a&gt; for updates and contact.
+&lt;/p&gt;&lt;p&gt;
+Contributors:&lt;br&gt;
+Technical University Berlin - Initial API and implementation
+&lt;/p&gt;
+&lt;/em&gt;
+      </documentation>
+   </annotation>
+
+</schema>
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/ActivationKind.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/ActivationKind.java
new file mode 100644
index 0000000..2beae77
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/ActivationKind.java
@@ -0,0 +1,32 @@
+/**********************************************************************
+ * 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: ActivationKind.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;
+
+/**
+ * Possible values for the "activation" attribute of a "team" element within an "aspectBinding" extension.
+ *  
+ * @author stephan
+ * @since 1.2.7 (was inline previously)
+ */
+public enum ActivationKind {
+	/** Don't activate team by default. */
+	NONE, 
+	/** Activate team for current thread. */
+	THREAD, 
+	/** Globally activate team. */
+	ALL_THREADS;
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/AspectBindingRequestAnswer.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/AspectBindingRequestAnswer.java
new file mode 100644
index 0000000..98d769e
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/AspectBindingRequestAnswer.java
@@ -0,0 +1,47 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2009 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: AspectBindingRequestAnswer.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;
+
+import org.eclipse.objectteams.otequinox.hook.AspectPermission;
+
+/** 
+ * Answer for an aspect binding request. See extension point org.eclipse.objectteams.otequinox.aspectBindingNegotiators.
+ * 
+ * @author stephan
+ * @see 1.2.6
+ */
+public class AspectBindingRequestAnswer 
+{
+	/** Should this answer be remembered persistently? */
+	public boolean persistent;
+	/** Should this answer be applied to all subsequent requests? */
+	public boolean allRequests;
+	/** The actual answer. */
+	public AspectPermission permission;
+	
+	/**
+	 * @param persistent  Should this answer be remembered persistently?
+	 * @param allRequests Should this answer be applied to all subsequent requests?
+	 * @param permission  One of DENY, GRANT, UNDEFINED (let others decide).
+	 */
+	public AspectBindingRequestAnswer(boolean persistent, boolean allRequests, AspectPermission permission) {
+		this.persistent = persistent;
+		this.allRequests = allRequests;
+		this.permission = permission;
+	}
+	
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/Constants.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/Constants.java
new file mode 100644
index 0000000..c4a7f3b
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/Constants.java
@@ -0,0 +1,61 @@
+/**********************************************************************
+ * 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: Constants.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;
+
+/**
+ * Constants used for OT/Equinox.
+ * 
+ * @author stephan
+ * @since 1.2.7
+ */
+public interface Constants {
+	
+	/** ID of this plugin. */
+	public static final String TRANSFORMER_PLUGIN_ID         = "org.eclipse.objectteams.otequinox" ; //$NON-NLS-1$
+	
+	// === Extension point elements: ===
+	
+	/** Simple name of the extension point org.eclipse.objectteams.otequinox.aspectBindings. */
+	static final String ASPECT_BINDING_EXTPOINT_ID    = "aspectBindings";
+	static final String ASPECT_BINDING_FQEXTPOINT_ID  = TRANSFORMER_PLUGIN_ID+'.'+ASPECT_BINDING_EXTPOINT_ID;
+	
+	/** Attribute of "team" and "basePlugin" elements. */
+	static final String ID = "id";
+
+	/** Element of EP aspectBindings denoting a team class. */
+	static final String TEAM = "team";
+	/** Attribute of a "team" element denoting the fully qualified class name. */
+	static final String CLASS = "class";
+	/** Attribute of a "team" element denoting the team's superclass. */
+	static final String SUPERCLASS = "superclass";
+	/** Attribute of a "team" element denoting the requested activation: one of "NONE", "THREAD", "ALL_THREADS". */
+	static final String ACTIVATION = "activation";
+	
+	/** Element of EP aspectBindings denoting an adapted base plugin. */
+	static final String BASE_PLUGIN = "basePlugin";
+	/** Subelement of a "basePlugin" denoting a framgment of the base plugin that is required by the aspect. */
+	static final String REQUIRED_FRAGMENT = "requiredFragment";
+	/** Pseudo ID of a basePlugin specifying that the team(s) adapt base classes from their own plugin. */
+	static final String SELF = "SELF";
+
+	/** Element of EP aspectBinding - child of basePlugin node - requesting exports forced on the given base plug-in. */
+	public static final String FORCED_EXPORTS_ELEMENT = "forcedExports";
+	
+	/** Simple name of the extension point org.eclipse.objectteams.otequinox.aspectBindingNegotiators. */
+	public static final String ASPECT_NEGOTIATOR_EXTPOINT_ID = "aspectBindingNegotiators";
+
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/IAspectRequestNegotiator.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/IAspectRequestNegotiator.java
new file mode 100644
index 0000000..353370e
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/IAspectRequestNegotiator.java
@@ -0,0 +1,49 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2009 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: IAspectRequestNegotiator.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;
+
+import org.eclipse.objectteams.otequinox.hook.AspectPermission;
+
+/**
+ * Interface for extenders wishing to participate in negotiation about aspect binding requests
+ * including forced exports.
+ * 
+ * @author stephan
+ * @since 1.2.6
+ */
+public interface IAspectRequestNegotiator {
+	
+	/** 
+	 * Check whether a request for forced exports should be granted.
+	 * @param aspectBundleSymbolicName the aspect issuing the request
+	 * @param baseBundleSymbolicName   the affected base bundle
+	 * @param basePackage              the affected base package
+	 * @param previousNegotiation      the result of negotations up-to this point
+	 * @return a structure holding the answer, must not be null.
+	 */
+	AspectBindingRequestAnswer checkForcedExport(String aspectBundleSymbolicName, String baseBundleSymbolicName, String basePackage, AspectPermission previousNegotiation);
+
+	/** 
+	 * Check whether a request for an aspect binding should be granted.
+	 * @param aspectBundleSymbolicName the aspect issuing the request
+	 * @param baseBundleSymbolicName   the affected base bundle
+	 * @param teamClass 	           an affecting team class involved in this aspect binding
+	 * @param previousNegotiation      the result of negotations up-to this point
+	 * @return a structure holding the answer, must not be null.
+	 */
+	AspectBindingRequestAnswer checkAspectBinding(String aspectBundleSymbolicName, String baseBundleSymbolicName, String teamClass, AspectPermission previousNegotiation);
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/TransformerPlugin.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/TransformerPlugin.java
new file mode 100644
index 0000000..a6f8193
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/TransformerPlugin.java
@@ -0,0 +1,565 @@
+/**********************************************************************
+ * 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: TransformerPlugin.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;
+
+import static org.eclipse.objectteams.otequinox.Constants.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+import org.eclipse.core.internal.runtime.InternalPlatform;
+import org.eclipse.core.runtime.IConfigurationElement; //from: org.eclipse.equinox.registry
+import org.eclipse.core.runtime.RegistryFactory;
+import org.eclipse.objectteams.otequinox.internal.ASMByteCodeAnalyzer;
+import org.eclipse.objectteams.otequinox.internal.AspectBinding;
+import org.eclipse.objectteams.otequinox.internal.AspectPermissionManager;
+import org.eclipse.objectteams.otequinox.internal.MasterTeamLoader;
+import org.eclipse.objectteams.otequinox.hook.ClassScanner;
+import org.eclipse.objectteams.otequinox.hook.HookConfigurator;
+import org.eclipse.objectteams.otequinox.hook.IAspectRegistry;
+import org.eclipse.objectteams.otequinox.hook.IByteCodeAnalyzer;
+import org.eclipse.objectteams.otequinox.hook.ILogger;
+import org.eclipse.objectteams.otequinox.hook.IOTEquinoxService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * The main class (activator) of the transformer plugin.
+ * It maintains the aspect registry of this plugin.
+ * This class optionally uses  {@link InternalPlatform} for the purpose of accessing the workspace location,
+ * i.e., if this class is not found, no workspace location will be used.
+ * 
+ * @author stephan
+ * @version $Id: TransformerPlugin.java 23468 2010-02-04 22:34:27Z stephan $
+ */
+@SuppressWarnings("restriction") // accessing InternalPlatform
+public class TransformerPlugin implements BundleActivator, IOTEquinoxService
+{	
+	private static List<String> KNOWN_OTDT_ASPECTS = new ArrayList<String>();
+	static {
+		KNOWN_OTDT_ASPECTS.add("org.eclipse.objectteams.otdt.jdt.ui");
+		KNOWN_OTDT_ASPECTS.add("org.eclipse.objectteams.otdt.compiler.adaptor");
+		KNOWN_OTDT_ASPECTS.add("org.eclipse.objectteams.otdt.refactoring.adaptor");
+		KNOWN_OTDT_ASPECTS.add("org.eclipse.objectteams.otdt.pde.ui");
+		KNOWN_OTDT_ASPECTS.add("org.eclipse.objectteams.otdt.samples");
+	}
+	/** main internal registry of aspect bindings. */
+	private static HashMap<String, ArrayList<AspectBinding>> aspectBindingsByBasePlugin = 
+			   new HashMap<String, ArrayList<AspectBinding>>();
+	private static HashMap<String, ArrayList<AspectBinding>> aspectBindingsByAspectPlugin = 
+		       new HashMap<String, ArrayList<AspectBinding>>();
+	// map base bundle name to list of adapted base class names
+	private static HashMap<String, ArrayList<String>> adaptedBaseClassNames =
+		       new HashMap<String, ArrayList<String>>();
+	// set of aspect plug-ins which have internal teams:
+	private Set<String> selfAdaptingAspects= new HashSet<String>();
+	
+	/** Aspect Permissions and Negotiation (delegate to dedicated manager). */
+	private AspectPermissionManager permissionManager;
+	
+	/** The instance that is created by the framework. */
+	private static TransformerPlugin instance;
+	private ServiceRegistration serviceRegistration;
+	private ILogger log;
+	
+	/** instances which may have pending team classes waiting for instantiation. */
+	private HashMap<Bundle,List<MasterTeamLoader>> masterTeamLoaders = new HashMap<Bundle, List<MasterTeamLoader>>();
+	
+	/** Service needed for handling fragments */
+	private PackageAdmin packageAdmin;
+
+	// note: actually List<Team> but Team cannot be mentioned in this plugin.
+	private List<Object> teamInstances = new ArrayList<Object>();
+	
+	public static boolean IS_OTDT = false;
+	
+	public boolean isOTDT() {
+		return IS_OTDT;
+	}
+	
+	public TransformerPlugin() {
+		instance = this;
+	}
+	public void start(BundleContext context) throws Exception {
+		if (!HookConfigurator.OT_EQUINOX_ENABLED)
+			throw new BundleException("Not starting the transformer plugin because OT/Equinox has not been enabled (set system property \"ot.equinox\").");
+
+		this.log = HookConfigurator.getLogger();
+		log(ILogger.INFO, "activating org.eclipse.objectteams.otequinox");
+		
+		ServiceReference ref= context.getServiceReference(PackageAdmin.class.getName());
+		if (ref!=null)
+			this.packageAdmin = (PackageAdmin)context.getService(ref);
+		else
+			this.log(ILogger.ERROR, "Failed to load PackageAdmin service. Will not be able to handle fragments.");
+
+		this.permissionManager = new AspectPermissionManager(this.log, context.getBundle(), this.packageAdmin);
+		this.permissionManager.loadAspectBindingNegotiators(context);
+		loadAspectBindings();
+		this.serviceRegistration = context.registerService(IOTEquinoxService.class.getName(), this, new Properties());		
+	}
+		
+	/* be a good citizen: clean up. */
+	public void stop(BundleContext context) throws Exception {
+		serviceRegistration.unregister();
+	}
+
+	public static TransformerPlugin getDefault() {
+		return instance;
+	}
+	
+	/** public API: Do we know about any team that has not yet been initiaized as requested? */
+	public static boolean isWaitingForTeams() {
+		synchronized (instance.masterTeamLoaders) {
+			if (!instance.masterTeamLoaders.isEmpty())
+				return true;
+		}		
+		synchronized (aspectBindingsByBasePlugin) {
+			for (ArrayList<AspectBinding> aspects : aspectBindingsByBasePlugin.values())
+				for (AspectBinding aspectBinding : aspects) 
+					if (!aspectBinding.activated)
+						return true;	
+		}
+		return false;
+	}
+
+	/** 
+	 * Internal API for TransformerHook:
+	 * see {@link IAspectRegistry#isDeniedAspectPlugin(String)}
+	 */
+	public boolean isDeniedAspectPlugin(String symbolicName) {
+		return this.permissionManager.isDeniedAspectPlugin(symbolicName);
+	}
+
+	/* Load extensions for org.eclipse.objectteams.otequinox.aspectBindings and check aspect permissions. */
+	private void loadAspectBindings() {
+		IConfigurationElement[] aspectBindingConfigs = RegistryFactory.getRegistry().getConfigurationElementsFor(
+				TRANSFORMER_PLUGIN_ID, ASPECT_BINDING_EXTPOINT_ID);
+		
+		for (int i = 0; i < aspectBindingConfigs.length; i++) {
+			IConfigurationElement currentBindingConfig = aspectBindingConfigs[i];
+
+			//aspect:
+			String aspectBundleId= currentBindingConfig.getContributor().getName();
+			IS_OTDT |= KNOWN_OTDT_ASPECTS.contains(aspectBundleId);
+			
+			//base:
+			IConfigurationElement[] basePlugins = currentBindingConfig.getChildren(BASE_PLUGIN);
+			if (basePlugins.length != 1) {
+				log(ILogger.ERROR, "aspectBinding of "+aspectBundleId+" must declare exactly one basePlugin");
+				continue;
+			}
+			String baseBundleId = basePlugins[0].getAttribute(ID);
+			
+			//base fragments?
+			IConfigurationElement[] fragments = basePlugins[0].getChildren(REQUIRED_FRAGMENT);
+			if (fragments != null && !checkRequiredFragments(aspectBundleId, baseBundleId, fragments)) // reported inside
+				continue;
+			
+			AspectBinding binding = new AspectBinding(aspectBundleId, baseBundleId, basePlugins[0].getChildren(Constants.FORCED_EXPORTS_ELEMENT));
+			// TODO(SH): maybe enforce that every bundle id is given only once?
+
+			//teams:
+			IConfigurationElement[] teams = currentBindingConfig.getChildren(TEAM);
+			binding.initTeams(teams.length);
+			try {
+				for (int j = 0; j < teams.length; j++) {
+					String teamClass = teams[j].getAttribute(CLASS);
+					binding.teamClasses[j] = teamClass;
+					String activation = teams[j].getAttribute(ACTIVATION);
+					binding.setActivation(j, activation);
+				}
+				
+				String realBaseBundleId = baseBundleId.toUpperCase().equals(SELF) ? aspectBundleId : baseBundleId;
+				addBindingForBaseBundle(realBaseBundleId, binding);
+				addBindingForAspectBundle(aspectBundleId, binding);
+				
+				// now that binding.teamClasses is filled connect to super team, if requested:
+				for (int j = 0; j < teams.length; j++) {
+					String superTeamName = teams[j].getAttribute(SUPERCLASS);
+					if (superTeamName != null)
+						addSubTeam(aspectBundleId, binding.teamClasses[j], superTeamName);
+				}
+				log(ILogger.INFO, "registered:\n"+binding);
+			} catch (Throwable t) {
+				log(t, "Invalid aspectBinding extension");
+			}
+		}
+	}
+
+	private boolean checkRequiredFragments(String aspectBundleId, String baseBundleId, IConfigurationElement[] fragments) 
+	{
+		// checking only, no real action needed.
+		boolean hasError = false;
+		for (IConfigurationElement fragment : fragments) {
+			String fragId = fragment.getAttribute(ID);
+			if (fragId == null) {
+				log(ILogger.ERROR, "Mandatory attribute \"id\" missing from element \"requiredFragment\" of aspect binding in "+aspectBundleId);
+				return false;
+			} 
+			if (packageAdmin == null) {
+				log(ILogger.ERROR, "Not checking required fragment "+fragId+" in aspect binding of "+aspectBundleId+", package admin service not present");
+				return false; // report only once.
+			}
+			
+			Bundle[] fragmentBundles = packageAdmin.getBundles(fragId, null);
+			if (fragmentBundles == null || fragmentBundles.length == 0) {
+				log(ILogger.ERROR, "Required fragment "+fragId+" not found in aspect binding of "+aspectBundleId);
+				hasError = true;
+				continue;
+			}
+			Bundle fragmentBundle = fragmentBundles[0];
+			String aspectBindingHint = " (aspect binding of "+aspectBundleId+")";
+			if (packageAdmin.getBundleType(fragmentBundle) != PackageAdmin.BUNDLE_TYPE_FRAGMENT) {
+				log(ILogger.ERROR, "Required fragment " + fragId + " is not a fragment" + aspectBindingHint);
+				hasError = true;
+				continue;
+			}
+			Bundle[] hosts = packageAdmin.getHosts(fragmentBundle);
+			if (hosts == null || hosts.length == 0) {
+				if (fragmentBundle.getState() < Bundle.RESOLVED) {
+					log(ILogger.ERROR, "Required fragment " + fragId + " is not resolved" + aspectBindingHint);
+					hasError = true;
+					continue;
+				}
+				log(ILogger.ERROR, "Required fragment "+fragId+" has no host bundle"+aspectBindingHint);
+				hasError = true;					
+				continue;
+			}
+			Bundle host = hosts[0];
+			if (!host.getSymbolicName().equals(baseBundleId)) {
+				log(ILogger.ERROR, "Required fragment "+fragId+" has wrong host "+host.getSymbolicName()+aspectBindingHint);
+				hasError = true;
+			}
+		}
+		return !hasError;
+	}
+
+
+	private static void addBindingForBaseBundle(String baseBundleId, AspectBinding binding) {
+		ArrayList<AspectBinding> bindingList = aspectBindingsByBasePlugin.get(baseBundleId);
+		if (bindingList == null) {
+			bindingList = new ArrayList<AspectBinding>();
+			aspectBindingsByBasePlugin.put(baseBundleId, bindingList);
+		}
+		bindingList.add(binding);
+	}
+
+	private void addBindingForAspectBundle(String aspectBundleId, AspectBinding binding) {
+		ArrayList<AspectBinding> bindingList = aspectBindingsByAspectPlugin.get(aspectBundleId);
+		if (bindingList == null) {
+			bindingList = new ArrayList<AspectBinding>();
+			aspectBindingsByAspectPlugin.put(aspectBundleId, bindingList);
+		}
+		bindingList.add(binding);
+		if (binding.basePlugin.toUpperCase().equals(SELF))
+			selfAdaptingAspects.add(aspectBundleId);
+	}
+	
+	/**
+	 * Record a sub-class relationship of two teams within the same aspect bundle.
+	 * 
+	 * @param aspectBundleId
+	 * @param subTeamName
+	 * @param teamName
+	 */
+	private void addSubTeam(String aspectBundleId, String subTeamName, String teamName) {
+		ArrayList<AspectBinding> bindingList = aspectBindingsByAspectPlugin.get(aspectBundleId);
+		if (bindingList == null) {
+			Exception e = new Exception("No such aspect binding");
+			log(e, "Class "+teamName+" not registered (declared to be superclass of team "+subTeamName);
+		} else {
+			for (AspectBinding binding : bindingList)
+				if (binding.teamClasses != null)
+					for (int i=0; i < binding.teamClasses.length; i++) 
+						if (binding.teamClasses[i].equals(teamName)) {
+							if (binding.subTeamClasses[i] == null)
+								binding.subTeamClasses[i] = new ArrayList<String>();
+							binding.subTeamClasses[i].add(subTeamName);
+							return;
+						}
+			Exception e = new Exception("No such aspect binding");
+			log(e, "Class "+teamName+" not registered(2) (declared to be superclass of team "+subTeamName);
+		}		
+	}
+
+	/**
+	 * Internal API for TransformerHook:
+	 * see {@link IAspectRegistry#getAdaptedBasePlugins(Bundle)}
+	 */
+	public String[] getAdaptedBasePlugins(Bundle aspectBundle) {
+		ArrayList<AspectBinding> bindings = aspectBindingsByAspectPlugin.get(aspectBundle.getSymbolicName());
+		if (bindings == null) return null;
+		String[] basePlugins = new String[bindings.size()];
+		for (int i=0; i<basePlugins.length; i++) {
+			basePlugins[i] = bindings.get(i).basePlugin;
+		}
+		return basePlugins;
+	}
+
+	/** Is `symbolicName' the name of a base plugin for which an adapting team is registered? */
+	public boolean isAdaptedBasePlugin(String symbolicName) {
+		ArrayList<AspectBinding> list = aspectBindingsByBasePlugin.get(symbolicName);
+		return list != null && !list.isEmpty();
+	}
+	
+	/**
+	 * public API:
+	 * {@link IAspectRegistry#getAdaptingAspectPlugins(Bundle)} 
+	 */
+	public String[] getAdaptingAspectPlugins(Bundle basePlugin) {
+		return getAdaptingAspectPlugins(basePlugin.getSymbolicName());
+	}
+	/**
+	 * public API:
+  	 * Get the names of aspect plugins adapting a given base plugin.
+	 * @param basePluginName symbolic name of a base plugin.
+	 * @return non-null array of symbolic names of aspect plugins.
+	 */
+	public String[] getAdaptingAspectPlugins(String basePluginName) {
+		ArrayList<AspectBinding> list = aspectBindingsByBasePlugin.get(basePluginName);
+		
+		if (list == null)
+			return new String[0];
+		
+		String[] aspects = new String[list.size()];
+		for (int i=0; i< list.size(); i++) 
+			aspects[i] = list.get(i).aspectPlugin;
+		
+		return aspects;
+	}
+	
+	/**
+     * Recored the names of base classes adapted by a given team from a given aspect bundle.
+	 */
+	public void storeAdaptedBaseClassNames(String aspectBundleName, String teamName, Collection<String> baseClassNames) 
+	{
+		// search the base plugin being adapted by the given team:
+		String basePlugin = null;
+		bindings:
+		for (AspectBinding aspectBinding : aspectBindingsByAspectPlugin.get(aspectBundleName)) {
+			for (String aspectTeamClass : aspectBinding.teamClasses)
+				if (aspectTeamClass.equals(teamName)) {
+					basePlugin = aspectBinding.basePlugin;
+					break bindings;
+				}
+		}
+		if (basePlugin == null && selfAdaptingAspects.contains(aspectBundleName))
+			basePlugin = aspectBundleName;
+		if (basePlugin == null) {
+			log(ILogger.ERROR, "Base plugin for team "+teamName+" from "+aspectBundleName+" not found!");
+			return;
+		}
+		// merge base class names into existing:
+		synchronized (adaptedBaseClassNames) {
+			ArrayList<String> baseBundleClassNames = adaptedBaseClassNames.get(basePlugin);
+			if (baseBundleClassNames == null) {
+				baseBundleClassNames = new ArrayList<String>();
+				adaptedBaseClassNames.put(basePlugin, baseBundleClassNames);
+			}
+			baseBundleClassNames.addAll(baseClassNames);
+		}
+	}
+
+	/**
+	 * Internal API for TransformerHook:
+	 * see {@link org.eclipse.objectteams.otequinox.hook.ITeamLoader#loadTeams(Bundle, IClassScanner)}
+	 */
+	public boolean loadTeams(Bundle baseBundle, ClassScanner classScanner) {
+		ArrayList<AspectBinding> bindings = aspectBindingsByBasePlugin.get(baseBundle.getSymbolicName());
+		return delegateToMasterTeamLoader(baseBundle, classScanner, bindings);
+	}
+	
+	/**
+	 * Internal API for TransformerHook:
+	 * see {@link org.eclipse.objectteams.otequinox.hook.IAspectRegistry#hasInternalTeams(Bundle)}
+	 */
+	public boolean hasInternalTeams(Bundle bundle) {
+		return selfAdaptingAspects.contains(bundle.getSymbolicName());
+	}
+	
+	/**
+	 * Internal API for TransformerHook:
+	 * see {@link org.eclipse.objectteams.otequinox.hook.ITeamLoader#loadInternalTeams(Bundle,ClassScanner)}
+	 */
+	public boolean loadInternalTeams(Bundle bundle, ClassScanner scanner) {
+		ArrayList<AspectBinding> selfBindings = new ArrayList<AspectBinding>();
+		synchronized (aspectBindingsByAspectPlugin) {
+			ArrayList<AspectBinding> bindings = aspectBindingsByAspectPlugin.get(bundle.getSymbolicName());
+			if (bindings == null)
+				return false;
+			for (int i = 0; i < bindings.size(); )
+				if (bindings.get(i).basePlugin.toUpperCase().equals(SELF))
+					selfBindings.add(bindings.remove(i));
+				else
+					i++;
+		}
+		return delegateToMasterTeamLoader(bundle, scanner, selfBindings);
+	}
+
+	// this performs some work for load[Internal]Teams:
+	// create&register a MasterTeamLoader and use it for loading the teams.
+	private boolean delegateToMasterTeamLoader(Bundle baseBundle,
+											   ClassScanner scanner,
+											   ArrayList<AspectBinding> bindings) 
+	{
+		if (bindings == null || bindings.isEmpty())
+			return false;
+		MasterTeamLoader masterTeamLoader = new MasterTeamLoader(baseBundle);
+		boolean success = masterTeamLoader.loadTeams(baseBundle, scanner, bindings);
+		if (success)
+			addMasterTeamLoader(baseBundle, masterTeamLoader);
+		return success;
+	}
+	
+	/**
+	 * Internal API for TransformerHook:
+	 * see {@link org.eclipse.objectteams.otequinox.hook.ITeamLoader#instantiateTeams(Bundle)}
+	 */
+	public void instantiateTeams(Bundle baseBundle, String triggerClassname) {
+		instance.internalInstantiateTeams(baseBundle, triggerClassname);
+	}
+	
+	/** Add a master team loader which may hold a list of teams waiting for instantiation.
+	 */
+	private void addMasterTeamLoader(Bundle baseBundle, MasterTeamLoader masterTeamLoader) {
+		synchronized (this.masterTeamLoaders) {
+			List<MasterTeamLoader> loaders = this.masterTeamLoaders.get(baseBundle);
+			if (loaders == null) {
+				loaders = new ArrayList<MasterTeamLoader>();
+				this.masterTeamLoaders.put(baseBundle, loaders);
+			}
+			loaders.add(masterTeamLoader);
+		}
+	}
+	
+
+	/**
+	 * Instantiate all teams affecting the given base bundle. Don't, however, load the class who's loading
+	 * triggered this call.
+	 * 
+	 * This method checks whether the AspectPermissionManager.isReady(). If not, defer instantiation and 
+	 * return the set of all affected base classes for use as a trigger for deferred instantiation. 
+	 *  
+	 * @param baseBundle
+     * @param triggerClassname if non-null: the name of a base class who's loading triggered this instantiation.
+	 */
+	private void internalInstantiateTeams(Bundle baseBundle, String triggerClassname) 
+	{			
+		List<MasterTeamLoader> loaders = null;
+		synchronized (this.masterTeamLoaders) {
+			loaders= this.masterTeamLoaders.get(baseBundle);
+			if (this.masterTeamLoaders.isEmpty() || loaders == null)
+				return;
+			
+			// check permission for forcedExports of all adapting aspect bundles
+			// (do this only if team loaders are found, but before any side effects occur)
+			synchronized (aspectBindingsByBasePlugin) {
+				List<AspectBinding> aspects= aspectBindingsByBasePlugin.get(baseBundle.getSymbolicName());
+				if (aspects != null) {
+					if (this.permissionManager.isReady()) {
+						for (AspectBinding aspectBinding : aspects)
+							if (!this.permissionManager.checkForcedExports(aspectBinding.aspectPlugin, baseBundle.getSymbolicName(), aspectBinding.forcedExports))
+								return; // don't activate teams of rejected aspect bundle
+					} else {
+						this.permissionManager.addForcedExportsObligations(aspects, baseBundle);
+					}
+				}
+			}
+
+			loaders = new ArrayList<MasterTeamLoader>(loaders);
+			this.masterTeamLoaders.remove(baseBundle);
+		}
+		while (!loaders.isEmpty()) {
+			// be sure not to hold any lock during this statement:
+			List<Object> newInstances = loaders.remove(0).instantiateLoadedTeams(baseBundle, triggerClassname, this.permissionManager);
+			synchronized (this.teamInstances) {
+				this.teamInstances.addAll(newInstances);
+			}
+		}
+		// mark the fact that all teams adapting this base bundle have now been activated:
+		synchronized (aspectBindingsByBasePlugin) {
+			List<AspectBinding> aspects= aspectBindingsByBasePlugin.get(baseBundle.getSymbolicName());
+			if (aspects != null)
+				for (AspectBinding aspectBinding : aspects)
+					aspectBinding.activated= true;
+		}
+	}
+
+	/** Copy all registered team instances into the given list,
+	 *  which must by of type List<Team>; (can't mention Team in this plugin).
+     */
+	@SuppressWarnings("unchecked")
+	public static synchronized void getTeamInstances(List list) {
+		list.addAll(instance.teamInstances);
+	}
+	
+	// configure OT/Equinox debugging:
+	public static int WARN_LEVEL = ILogger.ERROR;
+	static {
+		String level = System.getProperty("otequinox.debug");
+		if (level != null) {
+			level = level.toUpperCase();
+			if (level.equals("OK"))
+				WARN_LEVEL = ILogger.OK;
+			else if (level.equals("INFO"))
+				WARN_LEVEL = ILogger.INFO;
+			else if (level.startsWith("WARN"))
+				WARN_LEVEL = ILogger.WARNING;
+			else if (level.startsWith("ERR"))
+				WARN_LEVEL = ILogger.ERROR;
+			else
+				WARN_LEVEL = ILogger.OK;
+		}
+	}
+
+	public void log (Throwable ex, String msg) {
+		msg = "OT/Equinox: "+msg;
+		System.err.println(msg);
+		ex.printStackTrace();
+		this.log.log(TRANSFORMER_PLUGIN_ID, ex, msg);
+	}
+	
+	public void log(int status, String msg) {
+		if (status >= WARN_LEVEL)
+			doLog(status, msg);
+	}
+
+	public void doLog(int status, String msg) {
+		msg = "OT/Equinox: "+msg;
+		this.log.log(TRANSFORMER_PLUGIN_ID, status, msg);
+	}
+
+	public IByteCodeAnalyzer getByteCodeAnalyzer() {
+		return new ASMByteCodeAnalyzer();
+	}
+}
+
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/internal/ASMByteCodeAnalyzer.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/internal/ASMByteCodeAnalyzer.java
new file mode 100644
index 0000000..cf8cbed
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/internal/ASMByteCodeAnalyzer.java
@@ -0,0 +1,152 @@
+/**********************************************************************

+ * 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: ASMByteCodeAnalyzer.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;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.util.HashMap;

+import java.util.Map;

+

+import org.eclipse.objectteams.otequinox.hook.IByteCodeAnalyzer;

+import org.objectweb.asm.ClassReader;

+import org.objectweb.asm.commons.EmptyVisitor;

+

+/**

+ * This class performs some fast readClass analyses

+ * to determine further processing.

+ * 

+ * @author Oliver Frank

+ * @since 1.2.3

+ */

+public class ASMByteCodeAnalyzer implements IByteCodeAnalyzer {

+	private static final int ACC_TEAM = 0x8000;

+

+	private static class ClassInformation {

+		private boolean isTeam;

+		private String superClassName;

+

+		public ClassInformation(boolean isTeam, String superClassName) {

+			super();

+			this.isTeam = isTeam;

+			this.superClassName = superClassName;

+		}

+

+		public boolean isTeam() {

+			return isTeam;

+		}

+

+		public String getSuperClassName() {

+			if (superClassName != null)

+				return superClassName.replace('/', '.');

+			return null;

+		}

+	}

+

+	private static class MyVisitor extends EmptyVisitor {

+		MyVisitor() { super();}

+		private boolean isTeam;

+		private String superClassName;

+

+		public boolean isTeam() {

+			return isTeam;

+		}

+

+		public String getSuperClassName() {

+			return superClassName;

+		}

+

+		@Override

+		public void visit(int version, int access, String name,

+				String signature, String superName, String[] interfaces) {

+			superClassName = superName;

+			isTeam = (access & ACC_TEAM) != 0;

+		}

+	}

+

+	private Map<String, ClassInformation> classInformationMap = new HashMap<String, ClassInformation>();

+

+	/** 

+	 * Fetch the superclass name of a given class.

+	 * @param classStream stream providing the class bytes

+	 * @param className fully qualified name of the class

+	 * @return fully qualified ('.'-separated) name of the class' superclass or null if an exception occurred.

+	 */

+	public String getSuperclass(InputStream classStream, String className) {

+		try {

+			return getClassInformation(null, classStream, className)

+					.getSuperClassName();

+		} catch (IOException e) {

+			return null;

+		}

+	}

+	

+	/** 

+	 * Fetch the superclass name of a given class.

+	 * @param classStream stream providing the class bytes

+	 * @param className fully qualified name of the class

+	 * @return fully qualified ('.'-separated) name of the class' superclass or null if an exception occurred.

+	 */

+	public String getSuperclass(byte[] classBytes, String className) {

+		try {

+			return getClassInformation(classBytes, null, className)

+					.getSuperClassName();

+		} catch (IOException e) {

+			return null;

+		}

+	}

+

+	public boolean isTeam(byte[] classBytes, String className)

+			throws IOException {

+		return getClassInformation(classBytes, null, className).isTeam();

+	}

+

+	private ClassInformation getClassInformation(byte[] classBytes,

+			InputStream classStream, String className) throws IOException 

+	{

+		synchronized (this.classInformationMap) {

+			ClassInformation classInformation = classInformationMap.get(className);

+			if (classInformation != null) {

+				return classInformation;

+			}

+			if (classBytes != null) {

+				classInformation = this.getClassInformationPrivate(classBytes);

+			} else {

+				classInformation = this.getClassInformationPrivate(classStream);

+			}

+			classInformationMap.put(className, classInformation);

+			return classInformation;

+		}

+	}

+

+

+	private ClassInformation getClassInformationPrivate(InputStream classStream)

+			throws IOException {

+		return getClassInformationPrivate(new ClassReader(classStream));

+	}

+

+	private ClassInformation getClassInformationPrivate(byte[] classBytes) {

+		return getClassInformationPrivate(new ClassReader(classBytes));

+	}

+

+	private ClassInformation getClassInformationPrivate(ClassReader reader) {

+		MyVisitor visitor = new MyVisitor();

+

+		reader.accept(visitor, 0);

+		return new ClassInformation(visitor.isTeam(), visitor

+				.getSuperClassName());

+	}

+}

diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/internal/AspectBinding.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/internal/AspectBinding.java
new file mode 100644
index 0000000..7b1547a
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/internal/AspectBinding.java
@@ -0,0 +1,64 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2009 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$
+ * 
+ * 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;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.objectteams.otequinox.ActivationKind;
+
+/** 
+ * A simple record representing the information read from an extension to org.eclipse.objectteams.otequinox.aspectBindings.
+ * @author stephan
+ * @since 1.3.0 (was a nested class before that) 
+ */
+public class AspectBinding {
+	public String aspectPlugin;
+	public String basePlugin;
+	public IConfigurationElement[] forcedExports;
+	public ActivationKind[] activations = null; 
+	public String[]         teamClasses;
+	public List<String>[]   subTeamClasses;
+	public boolean          activated= false;
+	
+	public AspectBinding(String aspectId, String baseId, IConfigurationElement[] forcedExportsConfs) {
+		this.aspectPlugin= aspectId;
+		this.basePlugin= baseId;
+		this.forcedExports= forcedExportsConfs;
+	}
+	
+	@SuppressWarnings("unchecked")
+	public void initTeams(int count) {
+		this.teamClasses    = new String[count];
+		this.subTeamClasses = new List[count]; // new List<String>[count] is illegal!
+		this.activations    = new ActivationKind[count];
+	}
+	
+	public void setActivation(int i, String specifier) {
+		if (specifier == null)
+			this.activations[i] = ActivationKind.NONE;
+		else
+			this.activations[i] = ActivationKind.valueOf(specifier);
+	}
+	public String toString() {
+		String result = "\tbase plugin "+basePlugin+"\n\tadapted by aspect pluging "+aspectPlugin;
+		for (String teamClass : teamClasses) {
+			result += "\n\t\t + team "+teamClass;
+		}
+		return result;
+	}
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/internal/AspectPermissionManager.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/internal/AspectPermissionManager.java
new file mode 100644
index 0000000..38e425b
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/internal/AspectPermissionManager.java
@@ -0,0 +1,616 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2009 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: AspectPermissionManager.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;
+
+import static org.eclipse.objectteams.otequinox.hook.AspectPermission.*;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+import org.eclipse.core.internal.runtime.InternalPlatform;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.RegistryFactory;
+import org.eclipse.objectteams.otequinox.ActivationKind;
+import org.eclipse.objectteams.otequinox.AspectBindingRequestAnswer;
+import org.eclipse.objectteams.otequinox.Constants;
+import org.eclipse.objectteams.otequinox.IAspectRequestNegotiator;
+import org.eclipse.objectteams.otequinox.TransformerPlugin;
+import org.eclipse.objectteams.otequinox.internal.MasterTeamLoader.TeamClassRecord;
+import org.eclipse.osgi.service.datalocation.Location;
+import org.eclipse.objectteams.otequinox.hook.AspectPermission;
+import org.eclipse.objectteams.otequinox.hook.HookConfigurator;
+import org.eclipse.objectteams.otequinox.hook.ILogger;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * Manage permissions of aspect bundles requesting to apply aspectBindings and forcedExports.
+ * The following pieces of information are checked:
+ * <ul>
+ * <li>properties set in installation-wide config.ini or as command line args (handled by {@link HookConfigurator} (plus internal class OTStorageHook))</li>
+ * <li>defaults set per workspace (file negotiationDefaults.txt)</li>
+ * <li>individual GRANT/DENY per workspace (files grantedForcedExports.txt, deniedForcedExports.txt)</li>
+ * <li>answers from registered negotiators (extension point org.eclipse.objectteams.otequinox.aspectBindingNegotiators, see {@link IAspectRequestNegotiator})</li>
+ * </ul>
+ *
+ * <p>
+ * The final answer for a given request is combined from all sources where the priority of any {@link #DENY} answer is highest, 
+ * of {@link #UNDEFINED} is lowest.
+ * </p>
+ * <p>
+ * If a negotiator has determined a decision and its answer has the <code>persistent</code> flag set,
+ * this particular aspect permission is stored as per-workspace configuration.
+ * </p>
+ * @author stephan
+ * @since 1.2.6
+ */
+@SuppressWarnings("restriction")
+public class AspectPermissionManager {
+
+	// property names for default configuration:
+	private static final String FORCED_EXPORT_DEFAULT  = "forced.export.default";
+	private static final String ASPECT_BINDING_DEFAULT = "aspect.binding.default";
+	
+	// workspace files where negotiation configuration is stored:
+	private static final String NEGOTIATION_DEFAULTS_FILE   = "negotiationDefaults.txt";  
+	private static final String GRANTED_FORCED_EXPORTS_FILE = "grantedForcedExports.txt";
+	private static final String DENIED_FORCED_EXPORTS_FILE  = "deniedForcedExports.txt";
+
+
+	// set of aspect plug-ins for which some permission has been denied:
+	private Set<String> deniedAspects = new HashSet<String>();
+	// default permission for aspect bindings:
+	private AspectPermission defaultAspectBindingPermission = GRANT;
+	// default permission for forced exports:
+	private AspectPermission defaultForcedExportPermission = UNDEFINED; // not yet granted, but open for receiving a GRANT
+	// for negotiation of aspect binding requests (incl. forced export):
+	private List<IAspectRequestNegotiator> negotiators = new ArrayList<IAspectRequestNegotiator>();
+	
+	
+	// collect all forced exports (denied/granted), granted should balance to an empty structure.
+	// structure is: aspect-id -> (base bundle x base package)*
+	private HashMap<String, ArrayList<String[]>> deniedForcedExportsByAspect= new HashMap<String, ArrayList<String[]>>();
+	private HashMap<String, ArrayList<String[]>> grantedForcedExportsByAspect= new HashMap<String, ArrayList<String[]>>();
+	
+	// key is aspectId+"->"+baseId, value is array of team names
+	private HashMap<String, Set<String>> deniedTeamsByAspectBinding = new HashMap<String, Set<String>>();
+	private HashMap<String, Set<String>> grantedTeamsByAspectBinding = new HashMap<String, Set<String>>();
+
+	// the workspace directory for storing the state of this plugin
+	private IPath otequinoxState;
+	// back link needed for accessing the state location:
+	private Bundle transformerBundle;
+	// helper instance needed to stop bundles by name
+	private PackageAdmin packageAdmin;
+	// shared logger:
+	private ILogger log;
+	
+	public AspectPermissionManager(ILogger log, Bundle bundle, PackageAdmin packageAdmin) {
+		this.log = log;
+		this.transformerBundle = bundle;
+		this.packageAdmin = packageAdmin;
+	}
+		
+	/* local cache for isReady(): */
+	private boolean isWaitingForLocation = true;
+	
+	/** Before using this permission manager a client must check whether we're ready (instance location set). */
+	public boolean isReady() {
+		if (!isWaitingForLocation)
+			return true;
+		try {
+			InternalPlatform platform = InternalPlatform.getDefault();
+			Location instanceLocation = platform.getInstanceLocation();
+			if (!instanceLocation.isSet())
+				return false; // not yet capable
+			this.isWaitingForLocation = false;
+			fetchAspectBindingPermssionsFromWorkspace();
+		} catch (NoClassDefFoundError ncdfe) {
+			this.log.log(ILogger.WARNING, "Optional class InternalPlatform not found, cannot access workspace location");
+			this.isWaitingForLocation = false;
+			return true;
+		}
+		if (!this.obligations.isEmpty())
+			for (Runnable job : this.obligations)
+				job.run();
+		return true;
+	}
+
+	/** 
+	 * Fetch stored permissions from this plugin's workspace state.
+	 * 
+	 * @pre instance location should be set (see {@link #isReady()}),
+	 *    otherwise will silently return without accessing workspace settings. 
+	 */
+	private void fetchAspectBindingPermssionsFromWorkspace() 
+	{		
+		try {
+			this.otequinoxState = InternalPlatform.getDefault().getStateLocation(this.transformerBundle, true);
+		} catch (NoClassDefFoundError ncdfe) {
+			this.log.log(ILogger.WARNING, "Optional class InternalPlatform not found, cannot access workspace location");
+			return;
+		}
+
+		// defaults:
+		IPath configFilePath = this.otequinoxState.append(NEGOTIATION_DEFAULTS_FILE);
+		File configFile = new File(configFilePath.toOSString());		
+		if (configFile.exists()) {
+			Properties props = new Properties();
+			try {
+				boolean migrated = false; // TODO(SH): remove this migration support in 1.3.0
+				props.load(new FileInputStream(configFile));
+				String value = (String) props.get(ASPECT_BINDING_DEFAULT);
+				if (value != null)
+					try {
+						defaultAspectBindingPermission = AspectPermission.valueOf(value);
+					} catch (IllegalArgumentException iae) {
+						if ("DONT_CARE".equals(value)) {
+							defaultAspectBindingPermission = AspectPermission.UNDEFINED;
+							migrated = true;
+						} else {
+							// this code should remain even after 1.3.0:
+							defaultAspectBindingPermission = AspectPermission.DENY;
+							log(iae, "Cannot set default aspect permission from file "+NEGOTIATION_DEFAULTS_FILE+", assuming DENY.");
+						}
+					}
+				value = (String) props.get(FORCED_EXPORT_DEFAULT);
+				if (value != null)
+					try {
+						defaultForcedExportPermission = AspectPermission.valueOf(value);
+					} catch (IllegalArgumentException iae) {
+						if ("DONT_CARE".equals(value)) {
+							defaultForcedExportPermission = AspectPermission.UNDEFINED;
+							migrated = true;
+						} else {
+							// this code should remain even after 1.3.0:
+							defaultForcedExportPermission = AspectPermission.DENY;
+							log(iae, "Cannot set default forced exports permission from file "+NEGOTIATION_DEFAULTS_FILE+", assuming DENY.");
+						}
+					}
+				if (migrated)
+					writeNegotiationDefaults(configFile);
+			} catch (IOException ioex) {
+				log(ioex, "Failed to read configuration file "+configFilePath.toOSString());
+			}
+		} else {
+			try {
+				File stateDir = new File(this.otequinoxState.toOSString());
+				if (!stateDir.exists())
+					stateDir.mkdirs();
+				configFile.createNewFile();
+				writeNegotiationDefaults(configFile);
+			} catch (IOException ioex) {
+				log(ioex, "Failed to create configuration file "+configFilePath.toOSString());
+			}
+		}
+		
+		// explicitly denied:
+		configFilePath = this.otequinoxState.append(DENIED_FORCED_EXPORTS_FILE);
+		configFile = new File(configFilePath.toOSString());
+		if (configFile.exists())
+			HookConfigurator.parseForcedExportsFile(configFile, DENY);
+		
+		// explicitly granted:
+		configFilePath = this.otequinoxState.append(GRANTED_FORCED_EXPORTS_FILE);
+		configFile = new File(configFilePath.toOSString());
+		if (configFile.exists())
+			HookConfigurator.parseForcedExportsFile(configFile, GRANT);
+	}
+
+	private void writeNegotiationDefaults(File configFile)
+			throws IOException 
+	{
+		FileWriter writer = new FileWriter(configFile);
+		writer.append(ASPECT_BINDING_DEFAULT+'='+defaultAspectBindingPermission.toString()+'\n');
+		writer.append(FORCED_EXPORT_DEFAULT+'='+defaultForcedExportPermission.toString()+'\n');
+		writer.flush();
+		writer.close();
+		log(ILogger.INFO, "Created aspect binding defaults file "+configFile.getCanonicalPath());
+	}
+
+		
+	/** Load extensions for EP org.eclipse.objectteams.otequinox.aspectBindingNegotiators. */
+	public void loadAspectBindingNegotiators(BundleContext context) {
+		IConfigurationElement[] aspectBindingNegotiatorsConfigs = RegistryFactory.getRegistry().getConfigurationElementsFor(
+				Constants.TRANSFORMER_PLUGIN_ID, Constants.ASPECT_NEGOTIATOR_EXTPOINT_ID);		
+		for (int i = 0; i < aspectBindingNegotiatorsConfigs.length; i++) {
+			IConfigurationElement currentNegotiatorConfig = aspectBindingNegotiatorsConfigs[i];
+			try {
+				Object negotiator = currentNegotiatorConfig.createExecutableExtension("class");
+				if (negotiator != null)
+					this.negotiators.add(((IAspectRequestNegotiator)negotiator));
+			} catch (CoreException e) {
+				log(e, "Failed to instantiate extension "+currentNegotiatorConfig);
+			}
+		}
+	}
+
+	/** Delegatee of internal API {@link TransformerPlugin#isDeniedAspectPlugin(String)}. */
+	public boolean isDeniedAspectPlugin(String symbolicName) {
+		return this.deniedAspects.contains(symbolicName);
+	}
+
+	
+	/**
+	 * Check whether a given aspect requests forced exports from base, 
+	 * and whether these requests are granted/denied by checking all available sources.
+	 * 
+	 * Clients should ask {@link #isReady()} (ie., instance location is set) before calling this method,
+	 * otherwise workspace settings have to be silently ignored (any error should be signaled by client).
+	 * 
+	 * @param aspectId      symbolic name of the aspect bundle
+	 * @param baseBundleId  symbolic name of the bound base bundle
+     * @param forcedExports any forced exports requested in this aspect binding.
+	 * @return whether all requests (if any) have been granted
+	 */
+	public boolean checkForcedExports(String aspectId, String baseBundleId, IConfigurationElement[] forcedExports) 
+	{
+		if (forcedExports == null || forcedExports.length == 0)
+			return true;
+		
+		ArrayList<String[]> deniedForcedExports = getConfiguredForcedExports(aspectId, DENY,  deniedForcedExportsByAspect);
+		ArrayList<String[]> grantedForcedExports= getConfiguredForcedExports(aspectId, GRANT, grantedForcedExportsByAspect);
+
+		// iterate all requested forcedExports to search for a matching permission:
+		for (IConfigurationElement forcedExport : forcedExports) { // [0..1] (as defined in the schema)
+			String forcedExportsRequest = forcedExport.getValue();
+			if (forcedExportsRequest == null)
+				continue;
+			for (String singleForcedExportRequest : forcedExportsRequest.split(",")) 
+			{
+				singleForcedExportRequest = singleForcedExportRequest.trim();
+
+				String[] listEntry;
+				boolean grantReported = false;
+				AspectPermission negotiatedPermission = this.defaultForcedExportPermission;
+				
+				// DENY by default?
+				if (negotiatedPermission == DENY) {
+					log(ILogger.ERROR, "Default denial of forced export regarding package "+singleForcedExportRequest+
+									   " from bundle "+baseBundleId+" as requested by bundle "+aspectId+"; bundle not activated");
+					this.deniedAspects.add(aspectId); // keep for answering the TransformerHook.
+					return false; // NOPE!					
+				}
+				
+				// DENY from configuration?
+				listEntry = findRequestInList(baseBundleId, singleForcedExportRequest, deniedForcedExports);
+				if (listEntry != null) {
+					log(ILogger.ERROR, "Explicit denial of forced export regarding package "+singleForcedExportRequest+
+									   " from bundle "+baseBundleId+" as requested by bundle "+aspectId+"; bundle not activated");
+					this.deniedAspects.add(aspectId); // keep for answering the TransformerHook.
+					return false; // NOPE!
+				}
+
+				// GRANT from configuration?
+				listEntry = findRequestInList(baseBundleId, singleForcedExportRequest, grantedForcedExports);
+				if (listEntry != null) {
+					log(ILogger.INFO, "Forced export granted for "+aspectId+": "+singleForcedExportRequest+" (from bundle "+baseBundleId+")");
+					grantReported = true;
+					grantedForcedExports.remove(listEntry);
+					negotiatedPermission = GRANT;
+				}
+
+				// default and persistent configuration did not DENY, proceed to the negotiators:
+				boolean shouldPersist = false;
+				for (IAspectRequestNegotiator negotiator : this.negotiators) {
+					AspectBindingRequestAnswer answer = negotiator.checkForcedExport(aspectId, baseBundleId, singleForcedExportRequest, negotiatedPermission);
+					if (answer != null) {
+						if (answer.permission.compareTo(negotiatedPermission) > 0) // increasing priority of answer?
+						{ 
+							shouldPersist = answer.persistent;
+							negotiatedPermission = answer.permission;
+							// locally store as default for subsequent requests (not persistent, see below):
+							if (answer.allRequests)
+								this.defaultForcedExportPermission = negotiatedPermission;
+							if (negotiatedPermission == DENY)
+								break; // end of discussion.
+						}
+					}
+				}
+
+				// make decision persistent?
+				if (shouldPersist && negotiatedPermission != UNDEFINED)
+					// FIXME(SH): handle "allRequests":
+					persistForcedExportsAnswer(aspectId, baseBundleId, singleForcedExportRequest, negotiatedPermission);
+				
+				// report:
+				if (negotiatedPermission == GRANT) {
+					if (!grantReported)
+						log(ILogger.INFO, "Negotiation granted forced export for "+aspectId+
+										  ": "+singleForcedExportRequest+" (from bundle "+baseBundleId+')');
+				} else {
+					String verb = "did not grant";
+					if (negotiatedPermission == DENY)
+						verb = "denied";
+					log(ILogger.ERROR, "Negotiation "+verb+" forced export for "+aspectId+
+									   ": "+singleForcedExportRequest+" (from bundle "+baseBundleId+")"+
+									   ". Aspect is not activated.");
+					this.deniedAspects.add(aspectId); // keep for answering the TransformerHook.
+					return false; // don't install illegal aspect
+				}
+			}
+		}
+		if (!grantedForcedExports.isEmpty())
+			reportUnmatchForcedExports(aspectId, grantedForcedExports);
+		return true;
+	}
+
+	/**
+	 * Get the forced exports configured for a given aspect bundle with permission <code>perm</code>.
+	 * Consult {@link HookConfigurator} and store the result in <code>map</code>.
+	 * 
+	 * @param aspectId  symbolic name of the aspect in focus
+	 * @param perm		are we asking about DENY or GRANT?
+	 * @param map		in/out param for storing results from OTStorageHook
+	 * @return		 	list of pairs (base bundle x base package)
+	 */
+	private ArrayList<String[]> getConfiguredForcedExports(String                               aspectId, 
+														   AspectPermission 				    perm, 
+														   HashMap<String, ArrayList<String[]>> map) 
+    {
+		ArrayList<String[]> forcedExports= map.get(aspectId);
+		if (forcedExports == null) {
+			// fetch declarations from config.ini or other locations.
+			forcedExports= HookConfigurator.getForcedExportsByAspect(aspectId, perm);
+			map.put(aspectId, forcedExports);
+		}
+		return forcedExports;
+	}
+
+	private String[] findRequestInList(String baseBundleId, String basePackage, ArrayList<String[]> list) {
+		if (list != null)
+			for (String[] singleExport : list)
+				if (   singleExport[0].equals(baseBundleId)
+					&& singleExport[1].equals(basePackage))
+				{
+					return singleExport;
+				}
+		return null;
+	}
+
+	/**
+	 * If the structure of grantedForcedExports is not empty we have mismatches between forced-export declarations.
+	 * Report these mismatches as warnings.
+	 */
+	void reportUnmatchForcedExports(String aspectId, ArrayList<String[]> unmatchedForcedExports) 
+	{
+		for (String[] export: unmatchedForcedExports) {
+			String baseId = export[0];
+			String pack   = export[1];
+			log(ILogger.WARNING, "Aspect "+aspectId+
+							  " does not declare forced export of package "+
+							  pack+" from bundle "+baseId+
+							  " as declared in config.ini (or system property)");
+		}
+	}
+
+	/* Simple strategy to append a forced export to a file (existing or to be created). */
+	private void persistForcedExportsAnswer(String aspectId, String baseBundleId, String basePackage, AspectPermission negotiatedPermission) 
+	{
+		if (this.otequinoxState == null) {
+			log(ILogger.ERROR, "Can't persist forcedExports permission, no workspace location accessable.");
+			return;
+		}
+		try {
+			String fileName = (negotiatedPermission == DENY) ? DENIED_FORCED_EXPORTS_FILE : GRANTED_FORCED_EXPORTS_FILE;
+			IPath forcedExportsPath = this.otequinoxState.append(fileName);
+			File forcedExportsFile = new File(forcedExportsPath.toOSString());
+			if (!forcedExportsFile.exists())
+				forcedExportsFile.createNewFile();
+			FileWriter writer = new FileWriter(forcedExportsFile, true); // FIXME(SH): consider merge (after decision about file format)
+			writer.append('\n');
+			writer.append(baseBundleId);
+			writer.append("\n[\n\t");
+			writer.append(basePackage);
+			writer.append(";x-friends:=\"");
+			writer.append(aspectId);
+			writer.append("\"\n]\n");
+			writer.flush();
+			writer.close();
+		} catch (IOException ioe) {
+			log(ioe, "Failed to persist negotiation result");
+		}
+	}
+	
+	/**
+	 * Check permission for the aspect binding of one specific team.
+	 * 
+	 * Clients should ask {@link #isReady()} (ie., instance location is set) before calling this method,
+	 * otherwise workspace settings have to be silently ignored (any error should be signaled by client).
+ 	 * 
+	 * @param aspectBundleId
+	 * @param baseBundleId
+	 * @param teamClass
+	 * @return whether this team is permitted to adapt classes from the given base bundle.
+	 */
+	boolean checkTeamBinding(String aspectBundleId, String baseBundleId, String teamClass) 
+	{
+		boolean shouldReportGrant = false; // grant by default should not be reported
+		AspectPermission negotiatedPermission = this.defaultAspectBindingPermission;
+
+		// DENY by default?
+		if (negotiatedPermission == DENY) {
+			log(ILogger.ERROR, "Default denial of aspect binding regarding base bundle "+baseBundleId+
+							   " as requested by bundle "+aspectBundleId+"; bundle not activated");
+			this.deniedAspects.add(aspectBundleId); // keep for answering the TransformerHook.
+			return false; // NOPE!					
+		}
+
+		
+		String key = aspectBundleId+"->"+baseBundleId;
+		
+		// denied from configuration?
+		Set<String> deniedTeams = deniedTeamsByAspectBinding.get(key);
+		if (deniedTeams != null && !deniedTeams.isEmpty()) {
+			if (deniedTeams.contains(teamClass)) {
+				deniedAspects.add(aspectBundleId);
+				return false;
+			}
+		}
+		
+		// granted from configuration?
+		Set<String> grantedTeams = grantedTeamsByAspectBinding.get(key);
+		if (grantedTeams != null && grantedTeams.contains(teamClass)) {
+			negotiatedPermission = GRANT;
+			shouldReportGrant = true;
+		}
+		
+		// default and persistent configuration did not DENY, proceed to the negotiators:
+		boolean shouldPersist = false;
+		for (IAspectRequestNegotiator negotiator : this.negotiators) {
+			AspectBindingRequestAnswer answer = negotiator.checkAspectBinding(aspectBundleId, baseBundleId, teamClass, negotiatedPermission);
+			if (answer != null) {
+				if (answer.permission.compareTo(negotiatedPermission) > 0) // increasing priority of answer?
+				{ 
+					shouldPersist = answer.persistent;
+					negotiatedPermission = answer.permission;
+					shouldReportGrant = negotiatedPermission == GRANT;
+					// locally store as default for subsequent requests:
+					if (answer.allRequests)
+						this.defaultAspectBindingPermission = negotiatedPermission;
+
+					if (negotiatedPermission == DENY)
+						break; // end of discussion.
+				}
+			}
+		}
+
+		// make decision persistent?
+		if (shouldPersist && negotiatedPermission != UNDEFINED)
+			persistTeamBindingAnswer(aspectBundleId, baseBundleId, teamClass, negotiatedPermission);
+		
+		// report:
+		if (negotiatedPermission == GRANT) {
+			if (shouldReportGrant)
+				log(ILogger.INFO, "Negotiation granted aspect binding for "+aspectBundleId+
+								  " to base bundle "+baseBundleId+" by means of team "+teamClass+'.');
+		} else {
+			String verb = "did not grant";
+			if (negotiatedPermission == DENY)
+				verb = "denied";
+			log(ILogger.ERROR, "Negotiation "+verb+" aspect binding for "+aspectBundleId+
+							   " to base bundle "+baseBundleId+" by means of team "+teamClass+
+							   ". Aspect is not activated.");
+			this.deniedAspects.add(aspectBundleId); // keep for answering the TransformerHook.
+			return false; // don't install illegal aspect
+		}
+		return true;
+	}
+
+	private void persistTeamBindingAnswer(String aspectBundleId, String baseBundleId, String teamClass, AspectPermission negotiatedPermission) 
+	{
+		// FIXME(SH): implement persisting these!		
+	}
+
+	void log (Throwable ex, String msg) {
+		msg = "OT/Equinox: "+msg;
+		this.log.log(Constants.TRANSFORMER_PLUGIN_ID, ex, msg);
+	}
+		
+	void log(int status, String msg) {
+		if (status >= TransformerPlugin.WARN_LEVEL)
+			this.log.log(Constants.TRANSFORMER_PLUGIN_ID, status, "OT/Equinox: "+msg);
+	}
+
+	List<Runnable> obligations = new ArrayList<Runnable>();
+	public void addBaseBundleObligations(final List<Object> teamInstances, final ArrayList<TeamClassRecord> teamClasses, final Bundle baseBundle) {
+		schedule(new Runnable() {
+			public void run() {
+				List<TeamClassRecord> teamsToRevert = new ArrayList<TeamClassRecord>();
+				// aspect bindings:
+				for (TeamClassRecord teamClass : teamClasses)
+					if (!checkTeamBinding(teamClass.aspectBundle.getSymbolicName(), baseBundle.getSymbolicName(), teamClass.teamName))
+						teamsToRevert.add(teamClass);
+				if (!teamsToRevert.isEmpty())
+					revert(teamsToRevert);
+			}
+			void revert(List<TeamClassRecord> teamsToRevert) {
+				try {
+					Set<Bundle> bundlesToStop = new HashSet<Bundle>();
+					Class<?>[] deactivationArgumentTypes = new Class[]{Thread.class};
+					Object[] deactivationArguments       = new Object[] {TeamClassRecord.get_ALL_THREADS()};
+					for (int i=0, c=0; c< teamsToRevert.size(); c++) {
+						TeamClassRecord teamClass = teamClasses.get(c);
+						if (teamClass.activation != ActivationKind.NONE) {
+							Object teamInstance = teamInstances.get(i++);
+							Method deactivationMethod = teamClass.clazz.getMethod("deactivate", deactivationArgumentTypes);
+							deactivationMethod.invoke(teamInstance, deactivationArguments);
+							// could also check if roles are present already ...
+						}
+						bundlesToStop.add(teamClass.aspectBundle);
+					}
+					for (Bundle bundle : bundlesToStop) {
+						log(ILogger.ERROR, "Stopping aspect bundle "+bundle.getSymbolicName()+" with denied aspect binding(s)");
+						bundle.stop();
+					}
+				} catch (Exception e) {
+					log(e, "Failed to revert aspect bundle with denied aspect bindings.");
+				}
+			}
+		});
+	}
+
+	public void addForcedExportsObligations(final List<AspectBinding> aspects, final Bundle baseBundle) {
+		schedule(new Runnable () {
+			public void run() {
+				for (AspectBinding aspectBinding : aspects)
+					if (!checkForcedExports(aspectBinding.aspectPlugin, baseBundle.getSymbolicName(), aspectBinding.forcedExports))
+						stopIllegalBundle(aspectBinding.aspectPlugin);
+			}
+		});
+	}
+
+	void schedule(Runnable job) {
+		if (isReady()) // became ready since last query?
+			job.run();
+		else
+			synchronized(obligations) {
+				obligations.add(job);
+			}
+	}
+		
+	void stopIllegalBundle(String symbolicName) {
+		String msgCore = "stop bundle "+symbolicName+" whose requests for forced exports have been denied";
+		if (this.packageAdmin == null) {
+			log(ILogger.ERROR, "Needing to "+msgCore+" but package admin is not available");
+		} else {
+			Bundle[] bundles = this.packageAdmin.getBundles(symbolicName, null);
+			if (bundles == null)
+				log(ILogger.ERROR, "Needing to "+msgCore+" but bundle cannot be retrieved");
+			else
+				try {
+					bundles[0].stop();
+				} catch (BundleException e) {
+					log(e, "Failed to " + msgCore);
+				}
+		}
+	}
+	
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/internal/MasterTeamLoader.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/internal/MasterTeamLoader.java
new file mode 100644
index 0000000..989a83b
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/internal/MasterTeamLoader.java
@@ -0,0 +1,292 @@
+/**********************************************************************
+ * 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: MasterTeamLoader.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;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.internal.registry.osgi.OSGIUtils;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.objectteams.otequinox.ActivationKind;
+import org.eclipse.objectteams.otequinox.TransformerPlugin;
+import org.eclipse.osgi.framework.internal.core.BundleHost;
+import org.eclipse.objectteams.otequinox.hook.ClassScanner;
+import org.eclipse.objectteams.otequinox.hook.ILogger;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+/**
+ * Each instance of this class is responsible for loading all teams adapting a specific base plugin.
+ * It stores the team classes loaded successfully.
+ * After team loading a call to instantiateLoadedTeams() shall be issued to instantiate
+ * those teams, but that call pre-assumes that adapted base classes have already been loaded.
+ * 
+ * @author stephan
+ * @version $Id: MasterTeamLoader.java 23468 2010-02-04 22:34:27Z stephan $
+ */
+@SuppressWarnings("restriction")
+public class MasterTeamLoader {
+	
+	/** A simple record describing a configured and loaded team class: */
+	static class TeamClassRecord {
+		// store these info to check consistency among different aspectBindings mentioning the same team class:
+		static Set<Class<?>> allInstantiatedTeams = new HashSet<Class<?>>();
+		static Map<Class<?>, ActivationKind> allActivations = new HashMap<Class<?>, ActivationKind>();
+		
+		/** The bundle providing this team classs. */
+		Bundle aspectBundle;
+		/** The qualified team name. */
+		String teamName;
+		/** The loaded team class. */
+		Class<?> clazz;
+		/** The activation kind which was requested from the extension. */
+		ActivationKind activation;
+		/**
+		 * @param clazz      the loaded team class
+		 * @param activation the activation kind which was requested from the extension
+		 */
+		TeamClassRecord(Class<?> clazz, ActivationKind activation) {
+			this.clazz = clazz;
+			this.activation = activation;
+		}
+
+		public TeamClassRecord(Bundle aspectBundle, String teamName, ActivationKind activation) throws Exception {
+			this.activation = activation;
+			this.aspectBundle = aspectBundle;
+			this.teamName = teamName;
+			// enable to revert to early loading:
+			//this.clazz = this.aspectBundle.loadClass(this.teamName);
+		}
+		
+		/** Constant from org.objectteams.Team acquired via reflection. */
+		static Thread ALL_THREADS = null;
+		
+		/** Retrieve org.objectteams.Team.ALL_THREADS. */
+		static synchronized Thread get_ALL_THREADS()
+			throws Exception
+		{
+			if (ALL_THREADS == null) {
+				Class<?> ooTeam = Class.forName("org.objectteams.Team");
+				Field constantField = ooTeam.getDeclaredField("ALL_THREADS");
+				ALL_THREADS = (Thread)constantField.get(null);
+			}
+			return ALL_THREADS;
+		}
+		void readOTAttributes(ClassScanner scanner)
+				throws Exception, IOException
+		{
+			readOTAttributes(scanner, this.teamName);
+		}
+		
+		void readOTAttributes(ClassScanner scanner, String teamName)
+				throws Exception, IOException 
+		{
+			TransformerPlugin.getDefault().log(IStatus.OK, "reading attributes of team "+teamName);
+			ClassLoader loader = (ClassLoader) ((BundleHost)aspectBundle).getLoaderProxy().getBundleLoader().createClassLoader();
+			scanner.readOTAttributes(aspectBundle, teamName, loader);
+			Collection<String> baseClassNames = scanner.getCollectedBaseClassNames(teamName);
+			if (baseClassNames != null && !baseClassNames.isEmpty())
+				TransformerPlugin.getDefault().storeAdaptedBaseClassNames(this.aspectBundle.getSymbolicName(), teamName, baseClassNames);
+		}
+		
+		public boolean isAlreadyHandled() throws ClassNotFoundException {
+			if (this.clazz == null)
+				this.clazz = this.aspectBundle.loadClass(this.teamName);
+			return allInstantiatedTeams.contains(this.clazz);
+		}
+
+		public Object newInstance() throws Exception {
+			if (this.clazz == null)
+				this.clazz = this.aspectBundle.loadClass(this.teamName);
+			allInstantiatedTeams.add(this.clazz); // do this before accessing the constructor to avoid circularity (see Trac #257).
+			Object newInstance = this.clazz.newInstance();
+			return newInstance;
+		}
+
+		public void markAsActivated() {
+			allActivations.put(this.clazz, this.activation);			
+		}
+		
+		public ActivationKind getActualActivation() {
+			ActivationKind kind = allActivations.get(this.clazz);
+			if (kind == null)
+				return ActivationKind.NONE;
+			return kind;
+		}		
+	}
+	
+	Bundle baseBundle;
+	
+	/** Team classes waiting for activation. */
+	private List<TeamClassRecord> teamClasses = new ArrayList<TeamClassRecord>();
+	
+	private HashMap<Bundle,List<String>> baseBundleToAspectName = new HashMap<Bundle, List<String>>(); 
+
+	public MasterTeamLoader(Bundle baseBundle) {
+		this.baseBundle = baseBundle;
+	}
+
+	/** 
+	 * Load all teams adapting baseBundle and the adapted base classes.
+	 * TODO(SH): No checks are yet performed, whether the teams found in extensions actually
+	 * match the given baseBundle.
+	 *
+	 * @param baseBundle   an adaptable base bundle which was just activated.
+	 * @param classScanner helper for reading OT bytecode attributes.
+	 * @param bindings     declared aspect bindings for this bundle
+	 * @return whether or not teams have been loaded successfully
+	 */
+	public boolean loadTeams(Bundle baseBundle, ClassScanner classScanner, ArrayList<AspectBinding> bindings) 
+	{
+		for (final AspectBinding binding : bindings) {
+			String aspectBundleName = binding.aspectPlugin;
+			
+			log(IStatus.OK, ">>> TransformerPlugin loading aspect plugin "+aspectBundleName+" <<<");
+			
+			final Bundle aspectBundle = OSGIUtils.getDefault().getBundle(aspectBundleName);
+			if (aspectBundle == null) {
+				Throwable t = new Exception("Aspect bundle "+aspectBundleName+" does not exist.");
+				t.fillInStackTrace();
+				log(t, "Failed to load teams for an aspect plugin.");
+			}
+
+			// load and store the team classes:
+			for (int i = 0; i < binding.teamClasses.length; i++) {
+				try {
+					TeamClassRecord teamClassRecord = new TeamClassRecord(
+												aspectBundle, binding.teamClasses[i],
+												binding.activations[i]
+										);
+					this.teamClasses.add(teamClassRecord);
+					teamClassRecord.readOTAttributes(classScanner); // disable to revert to early loading
+					if (binding.subTeamClasses[i] != null)
+						for (String subTeamName : binding.subTeamClasses[i])
+							teamClassRecord.readOTAttributes(classScanner, subTeamName); // FIXME(SH): really use the same TeamClassRecord??
+
+				} catch (Throwable t) {
+					log(t, "Exception occurred while loading team class"); //$NON-NLS-1$				
+				}
+			}
+			
+			// store aspectBundleName for PHASE 2:
+			List<String> aspectNames = baseBundleToAspectName.get(baseBundle);
+			if (aspectNames == null) 
+				baseBundleToAspectName.put(baseBundle, aspectNames = new ArrayList<String>()); 
+			aspectNames.add(aspectBundleName);			
+		}
+		return !this.teamClasses.isEmpty();
+	}
+
+	/**
+	 * Instantiate all team classes loaded before. 
+	 * Before doing so all adapted base classes have to be loaded, too.
+     * 
+	 * @pre the AspectPermissionManager should be ready (ie., instance location is set), otherwise
+	 * 	workspace settings have to be ignored (error should be signaled by client).
+	 * 
+	 * @param  baseBundle		 only teams adapting this base bundle should be instantiated
+	 * @param  triggerClassname  loading of this class triggered this instantiation (may be null)
+     * @param  permissionManager helper for checking permissions of aspectBinding / forcedExport
+	 * @return list of team instances (maybe empty). Null signal that instantiation had to be deferred.
+	 */
+	public List<Object> instantiateLoadedTeams(Bundle baseBundle, String triggerClassname, AspectPermissionManager permissionManager) 
+	{
+		List<Object> teamInstances = new ArrayList<Object>(this.teamClasses.size());
+
+		// permission checking can be performed now or later, depending on readiness:
+		boolean permissionManagerReady = permissionManager.isReady();
+		
+		// ==== check permissions before we start activating:
+		if (permissionManagerReady) { // otherwise we will register pending obligations below.
+			boolean hasDenial = false;
+			for (TeamClassRecord teamClass : this.teamClasses)
+				if (!permissionManager.checkTeamBinding(teamClass.aspectBundle.getSymbolicName(), baseBundle.getSymbolicName(), teamClass.teamName)) 
+				{
+					hasDenial = true;
+					try {
+						teamClass.aspectBundle.stop();
+						log(ILogger.ERROR, "Stopped bundle "+teamClass.aspectBundle.getSymbolicName()+" which requests unconfirmed aspect binding(s).");
+					} catch (BundleException e) {
+						log(e, "Failed to stop bundle "+teamClass.aspectBundle.getSymbolicName()+" which requests unconfirmed aspect binding(s).");
+					}
+				}
+			if (hasDenial)
+				return teamInstances; // still empty list
+		}
+
+		// ==== instantiate the teams:
+		for (TeamClassRecord teamClass : this.teamClasses) {
+			if (teamClass.activation ==  ActivationKind.NONE)
+				continue;
+			try {
+				if (teamClass.isAlreadyHandled()) { // previously instantiated due to a different aspectBinding/basePlugin?
+					ActivationKind actualActivation = teamClass.getActualActivation();
+					if (   actualActivation     != teamClass.activation
+						&& actualActivation     != ActivationKind.NONE
+						&& teamClass.activation != ActivationKind.NONE)
+					{
+						log(IStatus.WARNING, "Conflicting activation requests in aspect bindings for team class "+teamClass.teamName);
+					}
+				} else {
+					Object newTeam = teamClass.newInstance();
+					teamInstances.add(newTeam);
+				
+					Class<?>[] activationArgumentTypes = new Class[0];
+					Object[] activationArguments    = null;
+					switch(teamClass.activation) {
+					case ALL_THREADS:
+						 activationArgumentTypes = new Class[]{Thread.class};
+						 activationArguments = new Object[] {TeamClassRecord.get_ALL_THREADS()};
+						 // fall through
+					case THREAD:
+						Method activationMethod = teamClass.clazz.getMethod("activate", activationArgumentTypes);
+						activationMethod.invoke(newTeam, activationArguments);
+						log(IStatus.OK, ">>> instantiated team: "+teamClass.clazz+", activation: "+teamClass.activation+" <<<");
+						teamClass.markAsActivated();
+						break;
+					case NONE:
+						// nothing ;-)
+					}
+				}
+			} catch (Throwable t) {
+				t.printStackTrace();
+			}
+		}
+		if (!permissionManagerReady)
+			permissionManager.addBaseBundleObligations(teamInstances, new ArrayList<TeamClassRecord>(this.teamClasses), baseBundle);
+		return teamInstances;
+	}
+	
+	void log(int status, String msg) {
+		TransformerPlugin.getDefault().log(status, msg);
+	}
+	void log(Throwable t, String msg) {
+		TransformerPlugin.getDefault().log(t, msg);
+	}
+}