Clean-up:
- nullness using external annotations
- new initialization model (see InitializedPlugin)
- stale comments
diff --git a/plugins/org.eclipse.objectteams.otequinox/.classpath b/plugins/org.eclipse.objectteams.otequinox/.classpath
index 8d9a643..b7569ad 100644
--- a/plugins/org.eclipse.objectteams.otequinox/.classpath
+++ b/plugins/org.eclipse.objectteams.otequinox/.classpath
@@ -2,12 +2,12 @@
 <classpath>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
 		<attributes>
-			<attribute name="annotationpath" value="annotations/jre"/>
+			<attribute name="annotationpath" value="/org.eclipse.objectteams.otequinox/annotations/jre"/>
 		</attributes>
 	</classpathentry>
 	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins">
 		<attributes>
-			<attribute name="annotationpath" value="annotations/plugins"/>
+			<attribute name="annotationpath" value="/org.eclipse.objectteams.otequinox/annotations/plugins"/>
 		</attributes>
 	</classpathentry>
 	<classpathentry kind="src" path="src"/>
diff --git a/plugins/org.eclipse.objectteams.otequinox/annotations/plugins/org/eclipse/core/internal/runtime/InternalPlatform.eea b/plugins/org.eclipse.objectteams.otequinox/annotations/plugins/org/eclipse/core/internal/runtime/InternalPlatform.eea
index ee081b2..52b240e 100644
--- a/plugins/org.eclipse.objectteams.otequinox/annotations/plugins/org/eclipse/core/internal/runtime/InternalPlatform.eea
+++ b/plugins/org.eclipse.objectteams.otequinox/annotations/plugins/org/eclipse/core/internal/runtime/InternalPlatform.eea
@@ -1,5 +1,8 @@
 class org/eclipse/core/internal/runtime/InternalPlatform
 
+getLog
+ (Lorg/osgi/framework/Bundle;)Lorg/eclipse/core/runtime/ILog;
+ (Lorg/osgi/framework/Bundle;)L1org/eclipse/core/runtime/ILog;
 getStateLocation
  (Lorg/osgi/framework/Bundle;Z)Lorg/eclipse/core/runtime/IPath;
  (Lorg/osgi/framework/Bundle;Z)L1org/eclipse/core/runtime/IPath;
diff --git a/plugins/org.eclipse.objectteams.otequinox/annotations/plugins/org/eclipse/core/runtime/IConfigurationElement.eea b/plugins/org.eclipse.objectteams.otequinox/annotations/plugins/org/eclipse/core/runtime/IConfigurationElement.eea
new file mode 100644
index 0000000..399e02f
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/annotations/plugins/org/eclipse/core/runtime/IConfigurationElement.eea
@@ -0,0 +1,4 @@
+class org/eclipse/core/runtime/IConfigurationElement
+getChildren
+ (Ljava/lang/String;)[Lorg/eclipse/core/runtime/IConfigurationElement;
+ (Ljava/lang/String;)[1Lorg/eclipse/core/runtime/IConfigurationElement;
diff --git a/plugins/org.eclipse.objectteams.otequinox/annotations/plugins/org/osgi/framework/BundleActivator.eea b/plugins/org.eclipse.objectteams.otequinox/annotations/plugins/org/osgi/framework/BundleActivator.eea
new file mode 100644
index 0000000..363496b
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/annotations/plugins/org/osgi/framework/BundleActivator.eea
@@ -0,0 +1,4 @@
+class org/osgi/framework/BundleActivator
+start
+ (Lorg/osgi/framework/BundleContext;)V
+ (L1org/osgi/framework/BundleContext;)V
diff --git a/plugins/org.eclipse.objectteams.otequinox/annotations/plugins/org/osgi/framework/ServiceListener.eea b/plugins/org.eclipse.objectteams.otequinox/annotations/plugins/org/osgi/framework/ServiceListener.eea
new file mode 100644
index 0000000..9e500a2
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/annotations/plugins/org/osgi/framework/ServiceListener.eea
@@ -0,0 +1,4 @@
+class org/osgi/framework/ServiceListener
+serviceChanged
+ (Lorg/osgi/framework/ServiceEvent;)V
+ (L1org/osgi/framework/ServiceEvent;)V
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBinding.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBinding.java
index 0d2795b..f29b888 100644
--- a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBinding.java
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBinding.java
@@ -251,7 +251,7 @@
 	public @Nullable Bundle aspectBundle; // null if we don't have a PackageAdmin for bundle lookup
 	public String basePluginName;
 	public BaseBundle baseBundle;
-	public @Nullable IConfigurationElement[] forcedExports; // not yet evaluated
+	public IConfigurationElement[] forcedExports;
 	public TeamBinding[]   teams;
 	public Set<String> allBaseClassNames = new HashSet<>();
 
@@ -261,7 +261,7 @@
 	
 	Set<TeamBinding> teamsInProgress = new HashSet<>(); // TODO cleanup teams that are done
 	
-	public AspectBinding(String aspectId, @Nullable Bundle aspectBundle, BaseBundle baseBundle, @Nullable IConfigurationElement[] forcedExportsConfs, int count) 
+	public AspectBinding(String aspectId, @Nullable Bundle aspectBundle, BaseBundle baseBundle, IConfigurationElement[] forcedExportsConfs, int count) 
 	{
 		this.aspectPlugin= aspectId;
 		this.aspectBundle= aspectBundle;
@@ -277,7 +277,7 @@
 		@NonNull ActivationKind kind = ActivationKind.NONE;
 		try {
 			if (activationSpecifier != null)
-				kind = ActivationKind.valueOf(activationSpecifier); // well-known API of all enums
+				kind = ActivationKind.valueOf(activationSpecifier);
 		} catch (IllegalArgumentException iae) {	
 			log(iae, "Invalid activation kind "+activationSpecifier+" for team "+teamName);
 		}
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBindingRegistry.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBindingRegistry.java
index a56fe00..a5639cc 100644
--- a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBindingRegistry.java
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBindingRegistry.java
@@ -38,7 +38,6 @@
 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.core.runtime.IExtensionRegistry;
 import org.eclipse.core.runtime.IStatus;
-import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.objectteams.internal.osgi.weaving.AspectBinding.BaseBundle;
@@ -120,8 +119,7 @@
 				
  			//base fragments?
 			IConfigurationElement[] fragments = basePlugins[0].getChildren(REQUIRED_FRAGMENT);
-			if (fragments != null 
-					&& !checkRequiredFragments(aspectBundleId, baseBundleId, fragments, packageAdmin)) // reported inside
+			if (!checkRequiredFragments(aspectBundleId, baseBundleId, fragments, packageAdmin)) // reported inside
 				continue;
 			
 			IConfigurationElement[] teams = currentBindingConfig.getChildren(TEAM);
@@ -130,7 +128,7 @@
 			AspectBinding binding = new AspectBinding(aspectBundleId,
 														aspectBundle,
 														baseBundle,
-														basePlugins[0].getChildren(Constants.FORCED_EXPORTS_ELEMENT), 
+														basePlugins[0].getChildren(Constants.FORCED_EXPORTS_ELEMENT),
 														teamCount);
 			bindings[i] = binding;
 			// TODO(SH): maybe enforce that every bundle id is given only once?
@@ -147,7 +145,7 @@
 					teamSet.add(team);
 				}
 				
-				@NonNull String realBaseBundleId = baseBundleId.toUpperCase().equals(SELF) ? aspectBundleId : baseBundleId;
+				String realBaseBundleId = baseBundleId.toUpperCase().equals(SELF) ? aspectBundleId : baseBundleId;
 				addBindingForBaseBundle(realBaseBundleId, binding);
 				addBindingForAspectBundle(aspectBundleId, binding);
 				hook.setBaseTripWire(packageAdmin, realBaseBundleId, baseBundle);
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectPermissionManager.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectPermissionManager.java
index 00993e4..68f9006 100644
--- a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectPermissionManager.java
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectPermissionManager.java
@@ -76,8 +76,6 @@
 @NonNullByDefault
 public class AspectPermissionManager {
 
-	// FIXME: forced exports are unimplemented as of OT/Equinox 2.3.0!
-
 	// 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";
@@ -215,7 +213,7 @@
 	}
 	private AspectPermission internalCheckForcedExports(AspectBinding aspectBinding) {
 		IConfigurationElement[] forcedExports = aspectBinding.forcedExports;
-		if (forcedExports == null || forcedExports.length == 0)
+		if (forcedExports.length == 0)
 			return GRANT;
 		
 		String aspectId = aspectBinding.aspectPlugin;
@@ -230,7 +228,7 @@
 				continue;
 			for (@SuppressWarnings("null")@NonNull String singleForcedExportRequest : forcedExportsRequest.split(","))
 			{
-				singleForcedExportRequest = singleForcedExportRequest.trim(); // well-known API
+				singleForcedExportRequest = singleForcedExportRequest.trim();
 
 				String[] listEntry;
 				boolean grantReported = false;
@@ -327,14 +325,13 @@
 		return forcedExports;
 	}
 
-	private @Nullable String[] findRequestInList(String baseBundleId, String basePackage, @Nullable List<String[]> list) {
-		if (list != null)
-			for (String[] singleExport : list)
-				if (   singleExport[0].equals(baseBundleId)
-					&& singleExport[1].equals(basePackage))
-				{
-					return singleExport;
-				}
+	private @Nullable String[] findRequestInList(String baseBundleId, String basePackage, List<String[]> list) {
+		for (String[] singleExport : list)
+			if (   singleExport[0].equals(baseBundleId)
+				&& singleExport[1].equals(basePackage))
+			{
+				return singleExport;
+			}
 		return null;
 	}
 
@@ -602,7 +599,7 @@
 				String value = (String) props.get(ASPECT_BINDING_DEFAULT);
 				if (value != null)
 					try {
-						defaultAspectBindingPermission = AspectPermission.valueOf(value); // known API of all enums
+						defaultAspectBindingPermission = AspectPermission.valueOf(value);
 					} catch (IllegalArgumentException iae) {
 						defaultAspectBindingPermission = AspectPermission.DENY;
 						log(iae, "Cannot set default aspect permission from file "+NEGOTIATION_DEFAULTS_FILE+", assuming DENY.");
@@ -610,7 +607,7 @@
 				value = (String) props.get(FORCED_EXPORT_DEFAULT);
 				if (value != null)
 					try {
-						defaultForcedExportPermission = AspectPermission.valueOf(value); // known API of all enums
+						defaultForcedExportPermission = AspectPermission.valueOf(value);
 					} catch (IllegalArgumentException iae) {
 						defaultForcedExportPermission = AspectPermission.DENY;
 						log(iae, "Cannot set default forced exports permission from file "+NEGOTIATION_DEFAULTS_FILE+", assuming DENY.");
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/OTWeavingHook.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/OTWeavingHook.java
index d5e67bb..3d79911 100644
--- a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/OTWeavingHook.java
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/OTWeavingHook.java
@@ -104,11 +104,9 @@
 	private @Nullable Class<?> ooTeam;
 
 	/** Call-back once the extension registry is up and running. */
-	public void activate(BundleContext bundleContext, ServiceReference<IExtensionRegistry> serviceReference) {
+	public void activate(@NonNull BundleContext bundleContext, ServiceReference<IExtensionRegistry> serviceReference) {
 		loadAspectBindingRegistry(bundleContext, serviceReference);
-		TransformerPlugin activator = TransformerPlugin.getDefault();
-		activator.registerAspectBindingRegistry(this.aspectBindingRegistry);
-		activator.registerAspectPermissionManager(this.permissionManager);
+		TransformerPlugin.initialize(bundleContext, this.aspectBindingRegistry, this.permissionManager);
 	}
 
 	// ====== Aspect Bindings & Permissions: ======
@@ -128,7 +126,7 @@
 		if (extensionRegistry == null) {
 			log(IStatus.ERROR, "Failed to acquire ExtensionRegistry service, cannot load aspect bindings.");
 		} else {
-			permissionManager = new AspectPermissionManager(context.getBundle(), packageAdmin); // known API
+			permissionManager = new AspectPermissionManager(context.getBundle(), packageAdmin);
 			permissionManager.loadAspectBindingNegotiators(extensionRegistry);
 
 			aspectBindingRegistry.loadAspectBindings(extensionRegistry, packageAdmin, this);
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/IAspectRegistry.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/IAspectRegistry.java
index 5f00886..d891a94 100644
--- a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/IAspectRegistry.java
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/IAspectRegistry.java
@@ -34,7 +34,7 @@
 	public boolean isOTDT();
 	
 	/** Is `symbolicName' the name of a base plugin for which an adapting team is registered? */
-	public boolean isAdaptedBasePlugin(String baseBundleName);
+	public boolean isAdaptedBasePlugin(@Nullable String baseBundleName);
 
 	/**
   	 * Get the names of aspect plugins adapting a given base plugin.
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
index b90b5bf..7f405b5 100644
--- 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
@@ -27,7 +27,8 @@
 import org.eclipse.equinox.log.ExtendedLogReaderService;
 import org.eclipse.equinox.log.ExtendedLogService;
 import org.eclipse.equinox.log.Logger;
-import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.objectteams.internal.osgi.weaving.AspectBinding;
 import org.eclipse.objectteams.internal.osgi.weaving.AspectBindingRegistry;
 import org.eclipse.objectteams.internal.osgi.weaving.AspectPermissionManager;
@@ -45,51 +46,109 @@
 import org.osgi.framework.hooks.weaving.WovenClassListener;
 import org.osgi.util.tracker.ServiceTracker;
 
+@NonNullByDefault
 public class TransformerPlugin implements BundleActivator, IAspectRegistry {
 
-	private static BundleContext context;
+	/**
+	 * State class representing the initialized state, i.e., after {@link start()}
+	 * and {@link #initialize()} have been called.
+	 */
+	static class InitializedPlugin extends TransformerPlugin {
 
+		AspectBindingRegistry aspectBindingRegistry;
+		@Nullable AspectPermissionManager aspectPermissionManager;
+		ILog log;
+		List<Team> teamInstances = new ArrayList<>();
+		
+		public InitializedPlugin(AspectBindingRegistry aspectBindingRegistry, @Nullable AspectPermissionManager permissionManager, ILog log) {
+			this.aspectBindingRegistry = aspectBindingRegistry;
+			this.aspectPermissionManager = permissionManager;
+			this.log = log;
+		}
+
+		@Override
+		public boolean isDeniedAspectPlugin(String symbolicName) {
+			final AspectPermissionManager manager = this.aspectPermissionManager;
+			if (manager != null)
+				return manager.isDeniedAspectPlugin(symbolicName);
+			return false;
+		}
+
+		@Override
+		public boolean isOTDT() {
+			return this.aspectBindingRegistry.isOTDT();
+		}
+
+		@Override
+		public boolean isAdaptedBasePlugin(@Nullable String baseBundleName) {
+			return this.aspectBindingRegistry.isAdaptedBasePlugin(baseBundleName);
+		}
+
+		@Override
+		public @Nullable String[] getAdaptedBasePlugins(Bundle aspectBundle) {
+			return this.aspectBindingRegistry.getAdaptedBasePlugins(aspectBundle);
+		}
+
+		@Override
+		public String[] getAdaptingAspectPlugins(@Nullable String id) {
+			List<AspectBinding> aspectBindings = this.aspectBindingRegistry.getAdaptingAspectBindings(id);
+			if (aspectBindings == null)
+				return new String[0];
+			String[] result = new String[aspectBindings.size()];
+			for (int i = 0; i < result.length; i++)
+				result[i] = aspectBindings.get(i).aspectPlugin;
+			return result;
+		}
+	}
+	private static @Nullable InitializedPlugin plugin;
+	/**
+	 * Single point of access: either we get a fully initialized instance, or ISE is thrown.
+	 * @throws IllegalStateException if the plugin has not been initialized yet.
+	 */
+	private static InitializedPlugin plugin() {
+		InitializedPlugin plugin = TransformerPlugin.plugin;
+		if (plugin == null)
+			throw notInitialized();
+		return plugin;
+	}
+	
+	static @Nullable BundleContext context;
 	public static Bundle getBundle() {
-		return context.getBundle();
+		BundleContext context = TransformerPlugin.context;
+		if (context != null)
+			return context.getBundle();
+		throw new IllegalStateException("TransformerPlugin has not been started");
 	}
 
-	private static ILog log;
 	private static List<IStatus> pendingLogEntries = new ArrayList<>();
-	private static TransformerPlugin plugin;
 	
-	private AspectBindingRegistry aspectBindingRegistry;
-	private List<Team> teamInstances = new ArrayList<>();
-	private AspectPermissionManager aspectPermissionManager;
 
 	/*
 	 * (non-Javadoc)
 	 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
 	 */
 	public void start(final BundleContext bundleContext) throws Exception {
-		plugin = this;
 		TransformerPlugin.context = bundleContext;
-		
-		acquireLog(bundleContext);
 	
 		if (!"false".equals(System.getProperty("ot.equinox"))) {
 			OTREInit();
 			
 			// register our weaving service:
 			final OTWeavingHook otWeavingHook = new OTWeavingHook();
-			context.registerService(new String[] { WeavingHook.class.getName(), WovenClassListener.class.getName() },
+			bundleContext.registerService(new String[] { WeavingHook.class.getName(), WovenClassListener.class.getName() },
 					otWeavingHook, null);
 			
 			// but wait until the extension registry is available for reading aspectBindings:
 			try {
-				ServiceReference<IExtensionRegistry> reference = context.getServiceReference(IExtensionRegistry.class);
+				ServiceReference<IExtensionRegistry> reference = bundleContext.getServiceReference(IExtensionRegistry.class);
 				if (reference != null) {
 					otWeavingHook.activate(bundleContext, reference);
 				} else {
-					context.addServiceListener(
+					bundleContext.addServiceListener(
 						new ServiceListener() { 
 							public void serviceChanged(ServiceEvent event) {
 								if(event.getType() == ServiceEvent.REGISTERED)
-									otWeavingHook.activate(bundleContext, context.getServiceReference(IExtensionRegistry.class));
+									otWeavingHook.activate(bundleContext, bundleContext.getServiceReference(IExtensionRegistry.class));
 							}
 						},
 						"(objectclass="+IExtensionRegistry.class.getName()+")"); //$NON-NLS-1$ //$NON-NLS-2$
@@ -102,14 +161,14 @@
 	}
 
 	@SuppressWarnings("restriction")
-	private static void acquireLog(BundleContext bundleContext) {
+	private static ILog acquireLog(BundleContext bundleContext) {
 		try {
-			log = org.eclipse.core.internal.runtime.InternalPlatform.getDefault().getLog(bundleContext.getBundle());
+			return org.eclipse.core.internal.runtime.InternalPlatform.getDefault().getLog(bundleContext.getBundle());
 		} catch (NullPointerException npe) {
 			// in case InternalPlatform isn't initialized yet, perform the same tasks manually:
 
 			ServiceTracker<ExtendedLogService,ExtendedLogService> tracker
-					= new ServiceTracker<ExtendedLogService,ExtendedLogService>(context, ExtendedLogService.class, null);
+					= new ServiceTracker<ExtendedLogService,ExtendedLogService>(bundleContext, ExtendedLogService.class, null);
 			tracker.open();
 			ExtendedLogService logService = tracker.getService();
 			Bundle bundle = bundleContext.getBundle();
@@ -118,11 +177,11 @@
 			org.eclipse.core.internal.runtime.Log result = new org.eclipse.core.internal.runtime.Log(bundle, logger);
 
 			ServiceTracker<ExtendedLogReaderService, ExtendedLogReaderService> logReaderTracker 
-					= new ServiceTracker<ExtendedLogReaderService,ExtendedLogReaderService>(context, ExtendedLogReaderService.class.getName(), null);
+					= new ServiceTracker<ExtendedLogReaderService,ExtendedLogReaderService>(bundleContext, ExtendedLogReaderService.class.getName(), null);
 			logReaderTracker.open();
 			ExtendedLogReaderService logReader = logReaderTracker.getService();
 			logReader.addLogListener(result, result);
-			log = result;
+			return result;
 		}
 	}
 
@@ -141,8 +200,8 @@
 	 * (non-Javadoc)
 	 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
 	 */
-	public void stop(BundleContext bundleContext) throws Exception {
-		TransformerPlugin.context = null;
+	public void stop(@Nullable BundleContext bundleContext) throws Exception {
+		plugin = null;
 	}
 
 	// configure OT/Equinox debugging:
@@ -167,8 +226,9 @@
 	public static synchronized void log (Throwable ex, String msg) {
 		msg = "OT/Equinox: "+msg;
 		Status status = new Status(IStatus.ERROR, TRANSFORMER_PLUGIN_ID, msg, ex);
-		if (log != null) {
-			log.log(status);
+		final InitializedPlugin plugin = TransformerPlugin.plugin;
+		if (plugin != null) {
+			plugin.log.log(status);
 		} else {
 			System.err.println(msg);
 			ex.printStackTrace();
@@ -183,8 +243,9 @@
 
 	public static synchronized void doLog(int level, String msg) {
 		Status status = new Status(level, TRANSFORMER_PLUGIN_ID, "OT/Equinox: "+msg);
-		if (log != null)
-			log.log(status);
+		final InitializedPlugin plugin = TransformerPlugin.plugin;
+		if (plugin != null)
+			plugin.log.log(status);
 		else
 			pendingLogEntries.add(status);
 	}
@@ -196,8 +257,9 @@
 			pendingLogEntries = new ArrayList<>();
 		}
 		for (IStatus status : copy) {
-			if (log != null) {
-				log.log(status);
+			final InitializedPlugin plugin = TransformerPlugin.plugin;
+			if (plugin != null) {
+				plugin.log.log(status);
 			} else {
 				if (status.getCode() == IStatus.ERROR)
 					System.err.println(status.getMessage());
@@ -207,70 +269,74 @@
 		}
 	}
 	
+	public static void initialize(BundleContext bundleContext, AspectBindingRegistry aspectBindingRegistry, @Nullable AspectPermissionManager permissionManager) {
+		plugin = new InitializedPlugin(aspectBindingRegistry, permissionManager, acquireLog(bundleContext));
+	}
 
+	/**
+	 * Get the singleton instance of this class.
+	 * <p>
+	 * This method must not be called before the plugin is fully initialized, which depends
+	 * on two triggers:
+	 * </p>
+	 * <ul>
+	 * <li>This current plugin must be started by Equinox (should be guaranteed on access by Equinox).</li>
+	 * <li>The extension registry has been started, which in turn triggers reading extensions against our extension points.</li>
+	 * </ul>
+	 */
 	public static TransformerPlugin getDefault() {
-		return plugin;
+		return plugin();
 	}
 
-	public void registerAspectBindingRegistry(AspectBindingRegistry aspectBindingRegistry) {
-		this.aspectBindingRegistry = aspectBindingRegistry;
+	public static synchronized void registerTeamInstance(Team instance) {
+		plugin().teamInstances.add(instance);
 	}
-
-	public void registerAspectPermissionManager(AspectPermissionManager permissionManager) {
-		this.aspectPermissionManager = permissionManager;
+	/**
+	 * Copy all registered team instances into the given list,
+     */
+	public static synchronized void getTeamInstances(List<Team> list) {
+		list.addAll(plugin().teamInstances);
 	}
 
 	/**
 	 * public API:
 	 * {@link IAspectRegistry#getAdaptingAspectPlugins(Bundle)} 
 	 */
-	public @NonNull String[] getAdaptingAspectPlugins(@NonNull Bundle basePlugin) {
+	public String[] getAdaptingAspectPlugins(Bundle basePlugin) {
 		return getAdaptingAspectPlugins(basePlugin.getSymbolicName());
 	}
 
-	public @NonNull String[] getAdaptingAspectPlugins(String id) {
-		List<AspectBinding> aspectBindings = this.aspectBindingRegistry.getAdaptingAspectBindings(id);
-		if (aspectBindings == null)
-			return new String[0];
-		String[] result = new String[aspectBindings.size()];
-		for (int i = 0; i < result.length; i++)
-			result[i] = aspectBindings.get(i).aspectPlugin;
-		return result;
-	}
-
-	public static synchronized void registerTeamInstance(@NonNull Team instance) {
-		plugin.teamInstances.add(instance);
-	}
-	/**
-	 * Copy all registered team instances into the given list,
-     */
-	public static synchronized void getTeamInstances(List<Team> list) {
-		list.addAll(plugin.teamInstances);
+	public String[] getAdaptingAspectPlugins(@Nullable String id) {
+		throw notInitialized();
 	}
 
 	@Override
 	public boolean isOTDT() {
-		return this.aspectBindingRegistry.isOTDT();
+		throw notInitialized();
 	}
 
 	@Override
-	public boolean isAdaptedBasePlugin(@NonNull String baseBundleName) {
-		return this.aspectBindingRegistry.isAdaptedBasePlugin(baseBundleName);
+	public boolean isAdaptedBasePlugin(@Nullable String baseBundleName) {
+		throw notInitialized();
 	}
 
 	@Override
-	public String[] getAdaptedBasePlugins(@NonNull Bundle aspectBundle) {
-		return this.aspectBindingRegistry.getAdaptedBasePlugins(aspectBundle);
+	public @Nullable String[] getAdaptedBasePlugins(Bundle aspectBundle) {
+		throw notInitialized();
 	}
 
 	@Override
-	public boolean hasInternalTeams(@NonNull Bundle bundle) {
+	public boolean hasInternalTeams(Bundle bundle) {
 		// TODO Auto-generated method stub
 		return false;
 	}
 
 	@Override
-	public boolean isDeniedAspectPlugin(@NonNull String symbolicName) {
-		return aspectPermissionManager.isDeniedAspectPlugin(symbolicName);
+	public boolean isDeniedAspectPlugin(String symbolicName) {
+		throw notInitialized();
+	}
+
+	static IllegalStateException notInitialized() {
+		return new IllegalStateException("TransformerPlugin has not been initialized");
 	}
 }