Bug 304586 - Injected methods only react to changes to method arguments
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/ContextChangeEvent.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/ContextChangeEvent.java
index db2fe9b..c1489e1 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/ContextChangeEvent.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/ContextChangeEvent.java
@@ -10,6 +10,8 @@
  *******************************************************************************/
 package org.eclipse.e4.core.services.context;
 
+import org.eclipse.e4.core.services.injector.IObjectProvider;
+
 /**
  * An event describing a change to an {@link IEclipseContext}. The following types of events are
  * currently defined:
@@ -59,7 +61,7 @@
 	public static final int UNINJECTED = 4;
 
 	private Object[] args;
-	private IEclipseContext context;
+	private IObjectProvider context;
 	private int eventType;
 	private String key;
 
@@ -73,7 +75,7 @@
 	 * @param args
 	 * @param name
 	 */
-	ContextChangeEvent(IEclipseContext context, int eventType, Object[] args, String name,
+	ContextChangeEvent(IObjectProvider context, int eventType, Object[] args, String name,
 			Object oldValue) {
 		this.context = context;
 		this.key = name;
@@ -97,7 +99,7 @@
 	 * 
 	 * @return the context where the change occurred
 	 */
-	public IEclipseContext getContext() {
+	public IObjectProvider getContext() {
 		return context;
 	}
 
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/EclipseContextFactory.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/EclipseContextFactory.java
index 29b219b..7f1c0f8 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/EclipseContextFactory.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/EclipseContextFactory.java
@@ -15,7 +15,9 @@
 import org.eclipse.e4.core.services.context.spi.IContextConstants;
 import org.eclipse.e4.core.services.context.spi.IEclipseContextStrategy;
 import org.eclipse.e4.core.services.context.spi.ILookupStrategy;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
 import org.eclipse.e4.core.services.internal.context.EclipseContext;
+import org.eclipse.e4.core.services.internal.context.ObjectProviderContext;
 import org.eclipse.e4.internal.core.services.osgi.OSGiContextStrategy;
 import org.osgi.framework.BundleContext;
 
@@ -94,6 +96,12 @@
 	 */
 	public static ContextChangeEvent createContextEvent(IEclipseContext context, int eventType,
 			Object[] args, String name, Object oldValue) {
-		return new ContextChangeEvent(context, eventType, args, name, oldValue);
+		return new ContextChangeEvent(new ObjectProviderContext(context), eventType, args, name,
+				oldValue);
+	}
+
+	public static ContextChangeEvent createContextEvent(IObjectProvider provider, int eventType,
+			Object[] args, String name, Object oldValue) {
+		return new ContextChangeEvent(provider, eventType, args, name, oldValue);
 	}
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/spi/ContextInjectionFactory.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/spi/ContextInjectionFactory.java
index 983f036..9b1a551 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/spi/ContextInjectionFactory.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/spi/ContextInjectionFactory.java
@@ -16,6 +16,7 @@
 import org.eclipse.e4.core.services.IDisposable;
 import org.eclipse.e4.core.services.context.IEclipseContext;
 import org.eclipse.e4.core.services.context.IEclipseContextAware;
+import org.eclipse.e4.core.services.internal.context.EclipseContext;
 import org.eclipse.e4.core.services.internal.context.ObjectProviderContext;
 
 /**
@@ -89,8 +90,8 @@
 	static public Object inject(Object object, IEclipseContext context) {
 		// TBD IEclipseContext might have a delayed runAndTrack. In this case
 		// the object might be returned before the actual injection took place.
-		context.runAndTrack(ObjectProviderContext.getObjectProvider(context),
-				new Object[] { object });
+		ObjectProviderContext provider = ObjectProviderContext.getObjectProvider(context);
+		provider.init(object);
 		return object;
 	}
 
@@ -145,8 +146,7 @@
 	 *            the context previously injected into the object
 	 */
 	static public void uninject(Object object, IEclipseContext context) {
-		ObjectProviderContext objectProvider = ObjectProviderContext.getObjectProvider(context);
-		objectProvider.getInjector().uninject(object);
+		((EclipseContext) context).removeListenersTo(object);
 	}
 
 	/**
@@ -169,7 +169,7 @@
 		ObjectProviderContext objectProvider = ObjectProviderContext.getObjectProvider(context);
 		Object result = objectProvider.getInjector().make(clazz);
 		if (result != null)
-			context.runAndTrack(objectProvider, new Object[] { result });
+			inject(result, context);
 		return result;
 	}
 }
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/injector/IObjectProvider.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/injector/IObjectProvider.java
index 5a4d39c..1e0909c 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/injector/IObjectProvider.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/injector/IObjectProvider.java
@@ -10,6 +10,8 @@
  *******************************************************************************/
 package org.eclipse.e4.core.services.injector;
 
+import org.eclipse.e4.core.services.context.IRunAndTrack;
+
 /**
  * This interface describes an "object provider" - something that knows how to instantiate objects
  * corresponding to the key. NOTE: This is a preliminary form; this API will change.
@@ -26,4 +28,5 @@
 
 	public String getKey(IObjectDescriptor key);
 
+	public void runAndTrack(final IRunAndTrack runnable, Object[] args);
 }
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/injector/Injector.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/injector/Injector.java
index d328d2e..11d2681 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/injector/Injector.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/injector/Injector.java
@@ -12,7 +12,6 @@
 
 import java.lang.reflect.InvocationTargetException;
 import org.eclipse.core.runtime.CoreException;
-import org.eclipse.e4.core.services.IDisposable;
 import org.eclipse.e4.core.services.internal.context.ContextInjector;
 
 /**
@@ -20,7 +19,7 @@
  * 
  * @noextend This class is not intended to be subclassed by clients.
  */
-final public class Injector implements IDisposable {
+final public class Injector {
 
 	final private ContextInjector eInjector;
 
@@ -32,22 +31,6 @@
 		return eInjector.inject(object);
 	}
 
-	public void reinject() {
-		eInjector.reinject();
-	}
-
-	public boolean uninject(Object object) {
-		return eInjector.uninject(object);
-	}
-
-	public void added(IObjectDescriptor descriptor) {
-		eInjector.added(descriptor);
-	}
-
-	public void removed(IObjectDescriptor descriptor) {
-		eInjector.removed(descriptor);
-	}
-
 	public Object make(Class clazz) throws InvocationTargetException, InstantiationException {
 		return eInjector.make(clazz);
 	}
@@ -66,12 +49,4 @@
 		return eInjector.injectStatic(clazz);
 	}
 
-	public void reparent(IObjectProvider oldParent) {
-		eInjector.reparent(oldParent);
-	}
-
-	public void dispose() {
-		eInjector.dispose();
-	}
-
 }
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/Computation.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/Computation.java
index c200b38..2116716 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/Computation.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/Computation.java
@@ -18,6 +18,7 @@
 import java.util.Set;
 import org.eclipse.e4.core.services.context.ContextChangeEvent;
 import org.eclipse.e4.core.services.context.IEclipseContext;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
 
 abstract class Computation {
 	Map dependencies = new HashMap();
@@ -48,7 +49,9 @@
 	public abstract boolean equals(Object arg0);
 
 	final void handleInvalid(ContextChangeEvent event, List scheduled) {
-		IEclipseContext context = event.getContext();
+		IObjectProvider provider = event.getContext();
+		IEclipseContext context = ((ObjectProviderContext) provider).getContext();
+
 		String name = event.getName();
 		Set names = (Set) dependencies.get(context);
 		if (name == null && event.getEventType() == ContextChangeEvent.DISPOSE) {
@@ -151,4 +154,8 @@
 		}
 	}
 
+	public Set dependsOnNames(IEclipseContext context) {
+		return (Set) dependencies.get(context);
+	}
+
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ContextInjector.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ContextInjector.java
index 4c4c618..de4f43b 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ContextInjector.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ContextInjector.java
@@ -19,16 +19,11 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Iterator;
-import java.util.List;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.e4.core.internal.services.ServicesActivator;
-import org.eclipse.e4.core.services.IDisposable;
-import org.eclipse.e4.core.services.context.IEclipseContext;
-import org.eclipse.e4.core.services.context.spi.ContextInjectionFactory;
 import org.eclipse.e4.core.services.context.spi.IContextConstants;
-import org.eclipse.e4.core.services.injector.IObjectDescriptor;
 import org.eclipse.e4.core.services.injector.IObjectProvider;
 import org.eclipse.e4.core.services.internal.annotations.AnnotationsSupport;
 
@@ -38,190 +33,6 @@
  */
 public class ContextInjector {
 
-	private class Processor {
-
-		final private IObjectDescriptor descriptor;
-		protected boolean addition;
-
-		protected boolean shouldProcessPostConstruct = false;
-		protected boolean isInDispose = false;
-		protected Object userObject;
-
-		protected boolean injectWithNulls = false;
-
-		protected boolean processStatic = false;
-
-		private List postConstructMethods;
-
-		public ArrayList classHierarchy = new ArrayList(5);
-
-		public Processor(IObjectDescriptor descriptor, boolean addition, boolean isInDispose) {
-			this.descriptor = descriptor;
-			this.addition = addition;
-			this.isInDispose = isInDispose;
-		}
-
-		public void setObject(Object userObject) {
-			this.userObject = userObject;
-			// this operation also resets state variables
-			classHierarchy.clear();
-		}
-
-		public void setInjectNulls(boolean injectWithNulls) {
-			this.injectWithNulls = injectWithNulls;
-		}
-
-		public void setProcessStatic(boolean processStatic) {
-			this.processStatic = processStatic;
-		}
-
-		/**
-		 * The method assumes injection is needed for this field.
-		 */
-		public boolean processField(final Field field, InjectionProperties properties) {
-			if (Modifier.isStatic(field.getModifiers()) != processStatic)
-				return true;
-			if (descriptor != null) { // filter if descriptor is specified
-				String descriptorsKey = context.getKey(descriptor);
-				if (!descriptorsKey.equals(context.getKey(properties)))
-					return true;
-			}
-			Object value = null;
-			if (addition) {
-				Object provider = properties.getProvider();
-				if (provider != null)
-					value = provider;
-				else if (context.containsKey(properties))
-					value = context.get(properties);
-				else {
-					if (!properties.isOptional()) {
-						if (shouldTrace)
-							System.out.println("Could not set " + field.getName()
-									+ " because of the missing: " + context.getKey(properties));
-						return false;
-					}
-					return true;
-				}
-			}
-			return setField(userObject, field, value);
-		}
-
-		public boolean processMethod(final Method method, boolean optional)
-				throws InvocationTargetException {
-			if (Modifier.isStatic(method.getModifiers()) != processStatic)
-				return true;
-			// we only get here if we are injecting
-			InjectionProperties[] properties = annotationSupport.getInjectParamProperties(method);
-			if (descriptor != null) {
-				// is it one of the arguments of this method?
-				boolean found = false;
-				String descriptorsKey = context.getKey(descriptor);
-				for (int i = 0; i < properties.length; i++) {
-					if (descriptorsKey.equals(context.getKey(properties[i]))) {
-						found = true;
-						break;
-					}
-				}
-				if (!found)
-					return true;
-			}
-
-			Object[] actualParams = processParams(properties, method.getParameterTypes(),
-					!addition, injectWithNulls);
-			if (actualParams != null)
-				callMethod(userObject, method, actualParams);
-			else if (!optional) {
-				if (shouldTrace)
-					System.out.println("Could not invoke " + method.getName()
-							+ ": no matching context elements");
-				return false;
-			}
-			return true;
-		}
-
-		public void addPostConstructMethod(Method method) {
-			if (postConstructMethods == null)
-				postConstructMethods = new ArrayList(1);
-			postConstructMethods.add(method);
-		}
-
-		public void processPostConstructMethod() throws InvocationTargetException {
-			if (!shouldProcessPostConstruct)
-				return;
-			if (postConstructMethods == null)
-				return;
-			for (Iterator it = postConstructMethods.iterator(); it.hasNext();) {
-				Method method = (Method) it.next();
-				InjectionProperties[] properties = annotationSupport
-						.getInjectParamProperties(method);
-				Object[] actualParams = processParams(properties, method.getParameterTypes(),
-						!addition, injectWithNulls);
-				if (actualParams == null)
-					logError(userObject, new IllegalArgumentException());
-				else
-					callMethod(userObject, method, actualParams);
-			}
-			postConstructMethods.clear();
-		}
-
-	}
-
-	// TBD investigate if this approach to reparenting works with calculated values and providers
-	private class ReparentProcessor extends Processor {
-
-		private IObjectProvider oldParent;
-
-		public ReparentProcessor(IObjectProvider oldParent) {
-			super(null, true /* set */, false);
-			this.oldParent = oldParent;
-		}
-
-		/**
-		 * Returns whether the value associated with the given key is affected by the parent change.
-		 */
-		private boolean hasChanged(InjectionProperties key) {
-			// if value is local then parent change has no effect
-			// XXX this is incorrect
-			// if (context.getLocal(key) != null)
-			// return false;
-			// XXX this is incorrect: different parents, same grandparent
-			// Object oldValue = oldParent == null ? null : oldParent.internalGet(
-			// (EclipseContext) context, key, null, false);
-			// Object newValue = context == null ? null : ((EclipseContext) context).internalGet(
-			// (EclipseContext) context, key, null, false);
-			// return oldValue != newValue;
-
-			// XXX for now, check if values are different
-			Object oldValue = oldParent.get(key);
-			Object newValue = context.get(key);
-			return (oldValue != newValue); // use pointer comparison, not #equals()
-		}
-
-		public boolean processField(final Field field, InjectionProperties properties) {
-			if (hasChanged(properties))
-				return super.processField(field, properties);
-			return true;
-		}
-
-		public boolean processMethod(final Method method, boolean optional)
-				throws InvocationTargetException {
-			// any argument changed?
-			InjectionProperties[] properties = annotationSupport.getInjectParamProperties(method);
-
-			boolean changed = false;
-			for (int i = 0; i < properties.length; i++) {
-				if (hasChanged(properties[i])) {
-					changed = true;
-					break;
-				}
-			}
-			if (changed)
-				return super.processMethod(method, optional);
-			return true;
-		}
-
-	}
-
 	final static private String DEBUG_INJECTOR = "org.eclipse.e4.core.services/debug/injector"; //$NON-NLS-1$
 	final static private boolean shouldTrace = ServicesActivator.getDefault()
 			.getBooleanDebugOption(DEBUG_INJECTOR, false);
@@ -231,60 +42,28 @@
 	final protected IObjectProvider context;
 	final private AnnotationsSupport annotationSupport;
 
-	protected WeakRefList userObjects = new WeakRefList(3); // start small
-
 	public ContextInjector(IObjectProvider context) {
 		this.context = context;
 		// plug-in class that gets replaced in Java 1.5+
-		annotationSupport = new AnnotationsSupport(context);
-	}
-
-	public void added(IObjectDescriptor descriptor) {
-		Object[] objectsCopy = userObjects.getSafeCopy();
-		Processor processor = new Processor(descriptor, true, false);
-		for (int i = 0; i < objectsCopy.length; i++) {
-			try {
-				processClassHierarchy(objectsCopy[i], processor);
-			} catch (InvocationTargetException e) {
-				logExternalError("Exception occured while processing addition on", objectsCopy[i],
-						e);
-			}
-		}
+		annotationSupport = new AnnotationsSupport(context); // XXX remove
 	}
 
 	public boolean inject(Object userObject) {
-		Processor processor = new Processor(null, true, false);
-		processor.shouldProcessPostConstruct = true;
 		boolean result = false;
 		try {
-			result = processClassHierarchy(userObject, processor);
+			result = processClassHierarchy(userObject, false /* process static */);
 		} catch (InvocationTargetException e) {
 			logExternalError("Exception occured while processing injecting", userObject, e);
 		}
-		userObjects.add(userObject);
+		context.runAndTrack(new InjectionClass(userObject, context), null);
 		return result;
 	}
 
-	public void reinject() {
-		Processor processor = new Processor(null, true, false);
-		Object[] objectsCopy = userObjects.getSafeCopy();
-		for (int i = 0; i < objectsCopy.length; i++) {
-			try {
-				processClassHierarchy(objectsCopy[i], processor);
-			} catch (InvocationTargetException e) {
-				logExternalError("Exception occured while processing removal on", objectsCopy[i], e);
-			}
-		}
-	}
-
 	// TBD use null object to inject statics
 	public boolean injectStatic(Class clazz) {
-		Processor processor = new Processor(null, true, false);
-		processor.shouldProcessPostConstruct = true;
-		processor.setProcessStatic(true);
 		try {
 			Object object = make(clazz);
-			return processClassHierarchy(object, processor);
+			return processClassHierarchy(object, true /* process static */);
 		} catch (InvocationTargetException e) {
 			// try-catch won't be necessary once we stop creating an object
 			e.printStackTrace();
@@ -295,126 +74,54 @@
 		return false;
 	}
 
-	public void removed(IObjectDescriptor descriptor) {
-		Processor processor = new Processor(descriptor, false, false);
-		Object[] objectsCopy = userObjects.getSafeCopy();
-		for (int i = 0; i < objectsCopy.length; i++) {
-			try {
-				processClassHierarchy(objectsCopy[i], processor);
-			} catch (InvocationTargetException e) {
-				logExternalError("Exception occured while processing removal on", objectsCopy[i], e);
-			}
-		}
-	}
-
-	public boolean uninject(Object releasedObject) {
-		if (!userObjects.remove(releasedObject))
-			return false;
-		Processor processor = new Processor(null, false, false);
-		processor.setInjectNulls(true);
-		try {
-			return processClassHierarchy(releasedObject, processor);
-		} catch (InvocationTargetException e) {
-			logExternalError("Exception occured while uninjecting", releasedObject, e);
-		}
-		return false;
-	}
-
-	public void dispose() {
-		Object[] objectsCopy = userObjects.getSafeCopy();
-		Processor processor = new Processor(null, false, true);
-		processor.setInjectNulls(true);
-		for (int i = 0; i < objectsCopy.length; i++) {
-			if (objectsCopy[i] instanceof IDisposable)
-				((IDisposable) objectsCopy[i]).dispose();
-			try {
-				processClassHierarchy(objectsCopy[i], processor);
-			} catch (InvocationTargetException e) {
-				logExternalError("Exception occured while disposing", objectsCopy[i], e);
-			}
-		}
-	}
-
-	public void reparent(IObjectProvider oldParent) {
-		if (oldParent == context)
-			return;
-		Object[] objectsCopy = userObjects.getSafeCopy();
-		Processor processor = new ReparentProcessor(oldParent);
-		for (int i = 0; i < objectsCopy.length; i++) {
-			try {
-				processClassHierarchy(objectsCopy[i], processor);
-			} catch (InvocationTargetException e) {
-				logExternalError("Exception occured while reparenting", objectsCopy[i], e);
-			}
-		}
-	}
-
 	/**
 	 * Make the processor visit all declared members on the given class and all superclasses
 	 * 
 	 * @throws InvocationTargetException
 	 */
-	private boolean processClass(Class objectsClass, Processor processor)
-			throws InvocationTargetException {
-		if (processor.addition) {
-			// order: superclass, fields, methods
-			if (objectsClass != null) {
-				Class superClass = objectsClass.getSuperclass();
-				if (!superClass.getName().equals(JAVA_OBJECT)) {
-					processor.classHierarchy.add(objectsClass);
-					if (!processClass(superClass, processor))
-						return false;
-					processor.classHierarchy.remove(objectsClass);
-				}
-			}
-			if (!processFields(objectsClass, processor))
-				return false;
-			if (!processMethods(objectsClass, processor))
-				return false;
-		} else {
-			// order: methods, fields, superclass
-			if (!processMethods(objectsClass, processor))
-				return false;
-			if (!processFields(objectsClass, processor))
-				return false;
-			if (objectsClass != null) {
-				Class superClass = objectsClass.getSuperclass();
-				if (!superClass.getName().equals(JAVA_OBJECT)) {
-					processor.classHierarchy.add(objectsClass);
-					if (!processClass(superClass, processor))
-						return false;
-					processor.classHierarchy.remove(objectsClass);
-				}
+	private boolean processClass(Object userObject, Class objectsClass, ArrayList classHierarchy,
+			boolean processStatic) throws InvocationTargetException {
+		// order: superclass, fields, methods
+		if (objectsClass != null) {
+			Class superClass = objectsClass.getSuperclass();
+			if (!superClass.getName().equals(JAVA_OBJECT)) {
+				classHierarchy.add(objectsClass);
+				if (!processClass(userObject, superClass, classHierarchy, processStatic))
+					return false;
+				classHierarchy.remove(objectsClass);
 			}
 		}
+		if (!processFields(userObject, objectsClass, processStatic))
+			return false;
+		if (!processMethods(userObject, objectsClass, classHierarchy, processStatic))
+			return false;
 		return true;
 	}
 
-	private boolean processClassHierarchy(Object userObject, Processor processor)
+	private boolean processClassHierarchy(Object userObject, boolean processStatic)
 			throws InvocationTargetException {
-		processor.setObject(userObject);
-		if (!processClass((userObject == null) ? null : userObject.getClass(), processor))
+		if (!processClass(userObject, (userObject == null) ? null : userObject.getClass(),
+				new ArrayList(5), processStatic))
 			return false;
-		processor.processPostConstructMethod();
 		return true;
 	}
 
 	/**
 	 * Make the processor visit all declared fields on the given class.
 	 */
-	private boolean processFields(Class objectsClass, Processor processor) {
+	private boolean processFields(Object userObject, Class objectsClass, boolean processStatic) {
 		Field[] fields = objectsClass.getDeclaredFields();
 		for (int i = 0; i < fields.length; i++) {
 			Field field = fields[i];
-
+			if (Modifier.isStatic(field.getModifiers()) != processStatic)
+				continue;
 			InjectionProperties properties = annotationSupport.getInjectProperties(field);
 			if (field.getName().startsWith(IContextConstants.INJECTION_PREFIX))
 				properties.setInject(true);
 
 			if (!properties.shouldInject())
 				continue;
-			if (!processor.processField(field, properties))
-				return false;
+			context.runAndTrack(new InjectionField(userObject, context, field), null);
 		}
 		return true;
 	}
@@ -424,66 +131,32 @@
 	 * 
 	 * @throws InvocationTargetException
 	 */
-	private boolean processMethods(Class objectsClass, Processor processor)
-			throws InvocationTargetException {
+	private boolean processMethods(Object userObject, Class objectsClass, ArrayList classHierarchy,
+			boolean processStatic) throws InvocationTargetException {
 		Method[] methods = objectsClass.getDeclaredMethods();
-		if (processor.isInDispose) {
-			for (int i = 0; i < methods.length; i++) {
-				Method method = methods[i];
-				if (method.getParameterTypes().length > 0) // TBD why?
-					continue;
-				if (!annotationSupport.isPreDestory(method))
-					continue;
-				if (!isOverridden(method, processor))
-					callMethod(processor.userObject, method, null);
-			}
-		}
 		for (int i = 0; i < methods.length; i++) {
 			Method method = methods[i];
-			if (isOverridden(method, processor))
+			if (isOverridden(method, classHierarchy))
 				continue; // process in the subclass
-			if (processor.shouldProcessPostConstruct) {
-				if (isPostConstruct(method)) {
-					processor.addPostConstructMethod(method);
-					continue;
-				}
-			}
-
+			if (Modifier.isStatic(method.getModifiers()) != processStatic)
+				continue;
 			InjectionProperties properties = annotationSupport.getInjectProperties(method);
 			if (method.getName().startsWith(IContextConstants.INJECTION_PREFIX))
 				properties.setInject(true);
 
 			if (!properties.shouldInject())
 				continue;
-			if (!processor.processMethod(method, properties.isOptional()))
-				return false;
+
+			context.runAndTrack(new InjectionMethod(userObject, context, method), null);
 		}
 		return true;
 	}
 
-	// TBD simplify this: only one non-annotation and one "implements IInitializable"?
-	/**
-	 * Returns whether the given method is a post-construction process method, as defined by the
-	 * class comment of {@link ContextInjectionFactory}.
-	 */
-	private boolean isPostConstruct(Method method) {
-		boolean isPostConstruct = annotationSupport.isPostConstruct(method);
-		if (isPostConstruct)
-			return true;
-		if (!method.getName().equals(IContextConstants.INJECTION_SET_CONTEXT_METHOD))
-			return false;
-		Class[] parms = method.getParameterTypes();
-		if (parms.length == 0)
-			return true;
-		if (parms.length == 1 && parms[0].equals(IEclipseContext.class))
-			return true;
-		return false;
-	}
-
+	// TBD this is the same method as in the class injector
 	/**
 	 * Checks if a given method is overridden with an injectable method.
 	 */
-	private boolean isOverridden(Method method, Processor processor) {
+	private boolean isOverridden(Method method, ArrayList classHierarchy) {
 		int modifiers = method.getModifiers();
 		if (Modifier.isPrivate(modifiers))
 			return false;
@@ -492,7 +165,7 @@
 		// method is not private if we reached this line, check not(public OR protected)
 		boolean isDefault = !(Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers));
 
-		for (Iterator i = processor.classHierarchy.iterator(); i.hasNext();) {
+		for (Iterator i = classHierarchy.iterator(); i.hasNext();) {
 			Class subClass = (Class) i.next();
 			Method override = null;
 			try {
@@ -528,11 +201,14 @@
 			if (!method.getName().equals(methodName))
 				continue;
 
-			InjectionProperties[] properties = annotationSupport.getInjectParamProperties(method);
-			Object[] actualParams = processParams(properties, method.getParameterTypes(), false,
-					false);
-			if (actualParams != null)
-				return callMethod(userObject, method, actualParams);
+			InjectionMethod injectMethod = new InjectionMethod(userObject, context, method);
+			try {
+				return injectMethod.invoke(false, false);
+			} catch (InjectionException e) {
+				IStatus status = new Status(IStatus.ERROR, "org.eclipse.e4.core.services",
+						"Unable to invoke method");
+				throw new CoreException(status);
+			}
 		}
 		IStatus status = new Status(IStatus.ERROR, "org.eclipse.e4.core.services",
 				"Unable to find matching method to invoke");
@@ -552,11 +228,12 @@
 			if (!method.getName().equals(methodName))
 				continue;
 
-			InjectionProperties[] properties = annotationSupport.getInjectParamProperties(method);
-			Object[] actualParams = processParams(properties, method.getParameterTypes(), false,
-					false);
-			if (actualParams != null)
-				return callMethod(userObject, method, actualParams);
+			InjectionMethod injectMethod = new InjectionMethod(userObject, context, method);
+			try {
+				return injectMethod.invoke(false, false);
+			} catch (InjectionException e) {
+				// TBD?
+			}
 		}
 		Class superClass = currentClass.getSuperclass();
 		if (superClass == null) {
@@ -593,13 +270,9 @@
 			if (!cProps.shouldInject() && constructor.getParameterTypes().length != 0)
 				continue;
 
-			InjectionProperties[] properties = annotationSupport
-					.getInjectParamsProperties(constructor);
-			Object[] actualParams = processParams(properties, constructor.getParameterTypes(),
-					false, false);
-			if (actualParams == null)
-				continue;
-			Object newInstance = callConstructor(constructor, actualParams);
+			InjectionConstructor injectedConstructor = new InjectionConstructor(null, context,
+					constructor);
+			Object newInstance = injectedConstructor.make();
 			if (newInstance != null)
 				return newInstance;
 		}
@@ -610,129 +283,6 @@
 		return null;
 	}
 
-	private Object[] processParams(InjectionProperties[] properties, Class[] parameterTypes,
-			boolean ignoreMissing, boolean injectWithNulls) {
-		Object[] actualParams = new Object[properties.length];
-		for (int i = 0; i < actualParams.length; i++) {
-			// 1) if we have a provider, use it
-			Object provider = properties[i].getProvider();
-			if (provider != null) {
-				actualParams[i] = provider;
-				continue;
-			}
-			// 2) if we have the key in the context
-			if (context.containsKey(properties[i])) {
-				if (injectWithNulls) {
-					actualParams[i] = null;
-					continue;
-				} else {
-					Object candidate = context.get(properties[i]);
-					if (candidate != null
-							&& parameterTypes[i].isAssignableFrom(candidate.getClass())) {
-						actualParams[i] = candidate;
-						continue;
-					}
-				}
-			}
-			// 3) can we ignore this argument?
-			if (ignoreMissing || properties[i].isOptional()) {
-				actualParams[i] = null;
-				continue;
-			}
-			return null;
-		}
-		return actualParams;
-	}
-
-	private boolean setField(Object userObject, Field field, Object value) {
-		if ((value != null) && !field.getType().isAssignableFrom(value.getClass())) {
-			// TBD add debug option
-			return false;
-		}
-
-		boolean wasAccessible = true;
-		if (!field.isAccessible()) {
-			field.setAccessible(true);
-			wasAccessible = false;
-		}
-		try {
-			field.set(userObject, value);
-		} catch (IllegalArgumentException e) {
-			logError(field, e);
-			return false;
-		} catch (IllegalAccessException e) {
-			logError(field, e);
-			return false;
-		} finally {
-			if (!wasAccessible)
-				field.setAccessible(false);
-		}
-		return true;
-	}
-
-	private Object callMethod(Object userObject, Method method, Object[] args)
-			throws InvocationTargetException {
-		Object result = null;
-		boolean wasAccessible = true;
-		if (!method.isAccessible()) {
-			method.setAccessible(true);
-			wasAccessible = false;
-		}
-		try {
-			result = method.invoke(userObject, args);
-		} catch (IllegalArgumentException e) {
-			// should not happen, is checked during formation of the array of actual arguments
-			logError(method, e);
-			return null;
-		} catch (IllegalAccessException e) {
-			// should not happen, is checked at the start of this method
-			logError(method, e);
-			return null;
-		} finally {
-			if (!wasAccessible)
-				method.setAccessible(false);
-		}
-		return result;
-	}
-
-	private Object callConstructor(Constructor constructor, Object[] args)
-			throws InvocationTargetException, InstantiationException {
-		if (args != null) { // make sure args are assignable
-			Class[] parameterTypes = constructor.getParameterTypes();
-			if (parameterTypes.length != args.length) {
-				// internal error, log it
-				logError(constructor, new IllegalArgumentException());
-				return null;
-			}
-			for (int i = 0; i < args.length; i++) {
-				if ((args[i] != null) && !parameterTypes[i].isAssignableFrom(args[i].getClass()))
-					return null;
-			}
-		}
-
-		Object result = null;
-		boolean wasAccessible = true;
-		if (!constructor.isAccessible()) {
-			constructor.setAccessible(true);
-			wasAccessible = false;
-		}
-		try {
-			result = constructor.newInstance(args);
-		} catch (IllegalArgumentException e) {
-			// should not happen, is checked at the start of this method
-			logError(constructor, e);
-			return null;
-		} catch (IllegalAccessException e) {
-			// should not happen as we set constructor to be accessible
-			logError(constructor, e);
-			return null;
-		} finally {
-			if (!wasAccessible)
-				constructor.setAccessible(false);
-		}
-		return result;
-	}
-
 	private void logExternalError(String msg, Object destination, Exception e) {
 		System.out.println(msg + " " + destination.toString()); //$NON-NLS-1$
 		if (e != null)
@@ -743,14 +293,4 @@
 		// IRuntimeConstants.PI_COMMON, 0, msg, e));
 	}
 
-	private void logError(Object destination, Exception e) {
-		System.out.println("Injection failed " + destination.toString()); //$NON-NLS-1$
-		if (e != null)
-			e.printStackTrace();
-		// TBD convert this into real logging
-		// String msg = NLS.bind("Injection failed", destination.toString());
-		// RuntimeLog.log(new Status(IStatus.WARNING,
-		// IRuntimeConstants.PI_COMMON, 0, msg, e));
-	}
-
 }
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/EclipseContext.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/EclipseContext.java
index 4ffe78a..50df977 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/EclipseContext.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/EclipseContext.java
@@ -122,6 +122,12 @@
 			this.runnable = runnable;
 		}
 
+		public Object getObject() {
+			if (runnable instanceof IRunAndTrackObject)
+				return ((IRunAndTrackObject) runnable).getObject();
+			return null;
+		}
+
 		final protected void doHandleInvalid(ContextChangeEvent event, List scheduledList) {
 			int eventType = event.getEventType();
 			if (eventType == ContextChangeEvent.INITIAL || eventType == ContextChangeEvent.DISPOSE) {
@@ -189,7 +195,7 @@
 	};
 
 	static class DebugSnap {
-		Set listeners = new HashSet();
+		List listeners = new ArrayList();
 		Map localValueComputations = Collections.synchronizedMap(new HashMap());
 		Map localValues = Collections.synchronizedMap(new HashMap());
 	}
@@ -205,7 +211,7 @@
 
 	private static final Object[] NO_ARGUMENTS = new Object[0];
 
-	final Set listeners = new HashSet();
+	final List listeners = new ArrayList();
 
 	final Map localValueComputations = Collections.synchronizedMap(new HashMap());
 	final Map localValues = Collections.synchronizedMap(new HashMap());
@@ -243,7 +249,7 @@
 	 */
 	public void debugSnap() {
 		snapshot = new DebugSnap();
-		snapshot.listeners = new HashSet(listeners);
+		snapshot.listeners = new ArrayList(listeners);
 		snapshot.localValueComputations = new HashMap(localValueComputations);
 		snapshot.localValues = new HashMap(localValues);
 	}
@@ -254,9 +260,9 @@
 	public void debugDiff() {
 		if (snapshot == null)
 			return;
-		Set listenerDiff = new HashSet(listeners);
+		List listenerDiff = new ArrayList(listeners);
 		listenerDiff.removeAll(snapshot.listeners);
-		listenerDiff = new HashSet(listenerDiff);// shrink the set
+		listenerDiff = new ArrayList(listenerDiff);// shrink the set
 		System.out.println("Listener diff: ");
 		for (Iterator it = listenerDiff.iterator(); it.hasNext();) {
 			System.out.println("\t" + it.next());
@@ -286,11 +292,14 @@
 		Computation[] ls = (Computation[]) listeners.toArray(new Computation[listeners.size()]);
 		ContextChangeEvent event = EclipseContextFactory.createContextEvent(this,
 				ContextChangeEvent.DISPOSE, null, null, null);
-		for (int i = 0; i < ls.length; i++) {
+		// reverse order of listeners
+		for (int i = ls.length - 1; i >= 0; i--) {
 			List scheduled = new ArrayList();
 			ls[i].handleInvalid(event, scheduled);
 			processScheduled(scheduled);
 		}
+
+		// TBD used by OSGI Context startegy - is this needed? Looks like @PreDestroy
 		if (strategy instanceof IDisposable)
 			((IDisposable) strategy).dispose();
 	}
@@ -426,6 +435,14 @@
 	}
 
 	public void set(String name, Object value) {
+		if (IContextConstants.PARENT.equals(name)) {
+			// TBD make setting parent a separate operation
+			List scheduled = new ArrayList();
+			handleReparent((EclipseContext) value, scheduled);
+			localValues.put(IContextConstants.PARENT, value);
+			processScheduled(scheduled);
+			return;
+		}
 		boolean containsKey = localValues.containsKey(name);
 		Object oldValue = localValues.put(name, value);
 		if (!containsKey || value != oldValue) {
@@ -511,4 +528,44 @@
 		return false;
 	}
 
+	public void removeListenersTo(Object object) {
+		if (object == null)
+			return;
+		synchronized (listeners) {
+			ContextChangeEvent event = EclipseContextFactory.createContextEvent(this,
+					ContextChangeEvent.UNINJECTED, null, null, null);
+			for (Iterator i = listeners.iterator(); i.hasNext();) {
+				Computation computation = (Computation) i.next();
+				if (computation instanceof TrackableComputationExt) {
+					if (object == ((TrackableComputationExt) computation).getObject()) {
+						((IRunAndTrack) computation).notify(event);
+						i.remove();
+					}
+				}
+			}
+		}
+	}
+
+	private void handleReparent(EclipseContext newParent, List scheduled) {
+		// 1) everybody who depends on me: I need to collect combined list of names injected
+		Computation[] ls = (Computation[]) listeners.toArray(new Computation[listeners.size()]);
+		Set usedNames = new HashSet();
+		for (int i = 0; i < ls.length; i++) {
+			Set listenerNames = ls[i].dependsOnNames(this);
+			if (listenerNames == null)
+				continue; // should not happen?
+			usedNames.addAll(listenerNames); // also removes duplicates 
+		}
+		// 2) for each used name:
+		for (Iterator i = usedNames.iterator(); i.hasNext();) {
+			String name = (String) i.next();
+			if (localValues.containsKey(name))
+				continue; // it is a local value
+			Object oldValue = get(name);
+			Object newValue = (newParent != null) ? newParent.get(name) : null;
+			if (oldValue != newValue)
+				invalidate(name, ContextChangeEvent.ADDED, oldValue, scheduled);
+		}
+		localValueComputations.clear();
+	}
 }
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/IRunAndTrackObject.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/IRunAndTrackObject.java
new file mode 100644
index 0000000..6206f7b
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/IRunAndTrackObject.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.e4.core.services.internal.context;
+
+import org.eclipse.e4.core.services.context.IRunAndTrack;
+
+public interface IRunAndTrackObject extends IRunAndTrack {
+
+	public Object getObject();
+
+}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionAbstract.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionAbstract.java
new file mode 100644
index 0000000..9c5d741
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionAbstract.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.e4.core.services.internal.context;
+
+import java.lang.ref.WeakReference;
+import org.eclipse.e4.core.services.context.ContextChangeEvent;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
+import org.eclipse.e4.core.services.internal.annotations.AnnotationsSupport;
+
+abstract public class InjectionAbstract implements IRunAndTrackObject {
+
+	final static protected Object NOT_A_VALUE = new Object();
+
+	final protected WeakReference userObjectRef;
+	final protected AnnotationsSupport annotationSupport;
+	final protected IObjectProvider primarySupplier;
+
+	protected boolean optional = false;
+
+	abstract public boolean notify(ContextChangeEvent event);
+
+	public InjectionAbstract(Object userObject, IObjectProvider primarySupplier) {
+		userObjectRef = new WeakReference(userObject);
+		// plug-in class that gets replaced in Java 1.5+
+		annotationSupport = new AnnotationsSupport(primarySupplier);
+		this.primarySupplier = primarySupplier;
+	}
+
+	public Object getObject() {
+		if (userObjectRef == null)
+			return null;
+		return userObjectRef.get();
+	}
+
+	protected boolean injectNulls(int eventType, IObjectProvider changed) {
+		if ((eventType != ContextChangeEvent.UNINJECTED)
+				&& (eventType != ContextChangeEvent.DISPOSE))
+			return false;
+		return (changed.equals(primarySupplier));
+	}
+
+	protected boolean ignoreMissing(int eventType, IObjectProvider changed) {
+		if (eventType == ContextChangeEvent.REMOVED)
+			return true;
+		if ((eventType != ContextChangeEvent.UNINJECTED)
+				&& (eventType != ContextChangeEvent.DISPOSE))
+			return false;
+		return (changed.equals(primarySupplier));
+	}
+
+	protected Object getValue(InjectionProperties properties, Class parameterType,
+			boolean ignoreMissing, boolean injectWithNulls) {
+		// 1) if we have a provider, use it
+		Object provider = properties.getProvider();
+		if (provider != null)
+			return provider;
+
+		// 2) if we have the key in the context
+		if (primarySupplier.containsKey(properties)) {
+			if (injectWithNulls)
+				return null;
+			Object value = primarySupplier.get(properties);
+			if (value == null)
+				return value;
+			if (parameterType.isAssignableFrom(value.getClass()))
+				return value;
+		}
+
+		// 3) can we ignore this argument?
+		if (ignoreMissing || properties.isOptional())
+			return null;
+
+		if (!optional) {
+			String msg = "Unable to find value for \"" + primarySupplier.getKey(properties) + "\"";
+			throw new IllegalArgumentException(msg);
+		}
+		return NOT_A_VALUE;
+	}
+
+	protected void logError(Object destination, Exception e) {
+		String msg = "Injection failed " + destination.toString();
+		logError(msg, e);
+	}
+
+	protected void logError(String msg, Exception e) {
+		System.out.println(msg); //$NON-NLS-1$
+		if (e != null)
+			e.printStackTrace();
+		// TBD convert this into real logging
+		// String msg = NLS.bind("Injection failed", destination.toString());
+		// RuntimeLog.log(new Status(IStatus.WARNING,
+		// IRuntimeConstants.PI_COMMON, 0, msg, e));
+	}
+
+}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionClass.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionClass.java
new file mode 100644
index 0000000..7a5da93
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionClass.java
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.e4.core.services.internal.context;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Iterator;
+import org.eclipse.e4.core.services.IDisposable;
+import org.eclipse.e4.core.services.context.ContextChangeEvent;
+import org.eclipse.e4.core.services.context.IEclipseContext;
+import org.eclipse.e4.core.services.context.spi.ContextInjectionFactory;
+import org.eclipse.e4.core.services.context.spi.IContextConstants;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
+
+public class InjectionClass extends InjectionAbstract {
+
+	final static private String JAVA_OBJECT = "java.lang.Object"; //$NON-NLS-1$
+
+	public InjectionClass(Object userObject, IObjectProvider primarySupplier) {
+		super(userObject, primarySupplier);
+	}
+
+	public boolean notify(ContextChangeEvent event) {
+		Object object = getObject();
+		if (object == null)
+			return false;
+		int eventType = event.getEventType();
+		if (eventType == ContextChangeEvent.DISPOSE) {
+			if (object instanceof IDisposable)
+				((IDisposable) object).dispose();
+			processPreDestory(object, object.getClass(), new ArrayList(5));
+			return false;
+		}
+		if (eventType == ContextChangeEvent.INITIAL) {
+			processPostConstruct(object, object.getClass(), new ArrayList(5));
+		}
+		IObjectProvider context = event.getContext();
+		context.get(new InjectionProperties(true, "e4_valid_context", true, null)); // pseudo-dependency to create a link
+		return true;
+	}
+
+	private void processPostConstruct(Object userObject, Class objectClass, ArrayList classHierarchy) {
+		Class superClass = objectClass.getSuperclass();
+		if (superClass != null && !superClass.getName().equals(JAVA_OBJECT)) {
+			classHierarchy.add(objectClass);
+			processPostConstruct(userObject, superClass, classHierarchy);
+			classHierarchy.remove(objectClass);
+		}
+		Method[] methods = objectClass.getDeclaredMethods();
+		for (int i = 0; i < methods.length; i++) {
+			Method method = methods[i];
+			if (!isPostConstruct(method))
+				continue;
+			if (!isOverridden(method, classHierarchy)) {
+				InjectionMethod methodInvoke = new InjectionMethod(getObject(), primarySupplier,
+						method);
+				try {
+					methodInvoke.invoke(false, false);
+				} catch (InjectionException e) {
+					// TBD log
+					e.printStackTrace();
+				}
+			}
+		}
+	}
+
+	// TBD simplify this: only one non-annotation and one "implements IInitializable"?
+	/**
+	 * Returns whether the given method is a post-construction process method, as defined by the
+	 * class comment of {@link ContextInjectionFactory}.
+	 */
+	private boolean isPostConstruct(Method method) {
+		boolean isPostConstruct = annotationSupport.isPostConstruct(method);
+		if (isPostConstruct)
+			return true;
+		if (!method.getName().equals(IContextConstants.INJECTION_SET_CONTEXT_METHOD))
+			return false;
+		Class[] parms = method.getParameterTypes();
+		if (parms.length == 0)
+			return true;
+		if (parms.length == 1 && parms[0].equals(IEclipseContext.class))
+			return true;
+		return false;
+	}
+
+	private void processPreDestory(Object userObject, Class objectClass, ArrayList classHierarchy) {
+		Class superClass = objectClass.getSuperclass();
+		if (superClass != null && !superClass.getName().equals(JAVA_OBJECT)) {
+			classHierarchy.add(objectClass);
+			processPreDestory(userObject, superClass, classHierarchy);
+			classHierarchy.remove(objectClass);
+		}
+		Method[] methods = objectClass.getDeclaredMethods();
+		for (int i = 0; i < methods.length; i++) {
+			Method method = methods[i];
+			if (method.getParameterTypes().length > 0) // TBD why?
+				continue;
+			if (!annotationSupport.isPreDestory(method))
+				continue;
+			if (!isOverridden(method, classHierarchy)) {
+				InjectionMethod methodInvoke = new InjectionMethod(getObject(), primarySupplier,
+						method);
+				try {
+					methodInvoke.invoke(false, false);
+				} catch (InjectionException e) {
+					// TBD log
+					e.printStackTrace();
+				}
+			}
+		}
+	}
+
+	// TBD move into the base class? For @PostConstruct?
+	/**
+	 * Checks if a given method is overridden with an injectable method.
+	 */
+	private boolean isOverridden(Method method, ArrayList classHierarchy) {
+		int modifiers = method.getModifiers();
+		if (Modifier.isPrivate(modifiers))
+			return false;
+		if (Modifier.isStatic(modifiers))
+			return false;
+		// method is not private if we reached this line, check not(public OR protected)
+		boolean isDefault = !(Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers));
+
+		for (Iterator i = classHierarchy.iterator(); i.hasNext();) {
+			Class subClass = (Class) i.next();
+			Method override = null;
+			try {
+				override = subClass.getDeclaredMethod(method.getName(), method.getParameterTypes());
+			} catch (SecurityException e) {
+				continue;
+			} catch (NoSuchMethodException e) {
+				continue; // this is the desired outcome
+			}
+			if (override != null) {
+				if (isDefault) { // must be in the same package to override
+					Package originalPackage = method.getDeclaringClass().getPackage();
+					Package overridePackage = subClass.getPackage();
+
+					if (originalPackage == null && overridePackage == null)
+						return true;
+					if (originalPackage == null || overridePackage == null)
+						return false;
+					if (originalPackage.equals(overridePackage))
+						return true;
+				} else
+					return true;
+			}
+		}
+		return false;
+	}
+
+}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionConstructor.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionConstructor.java
new file mode 100644
index 0000000..45780e0
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionConstructor.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.e4.core.services.internal.context;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import org.eclipse.e4.core.services.context.ContextChangeEvent;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
+
+/**
+ * Collection of static methods that deal with reflection-based injection at a low level.
+ */
+public class InjectionConstructor extends InjectionAbstract {
+
+	private final Constructor constructor;
+
+	public InjectionConstructor(Object userObject, IObjectProvider primarySupplier,
+			Constructor constructor) {
+		super(userObject, primarySupplier);
+		this.constructor = constructor;
+	}
+
+	public boolean notify(ContextChangeEvent event) {
+		if (event.getEventType() == ContextChangeEvent.INITIAL)
+			make();
+		return false; // constructor injection is static by nature
+	}
+
+	public Object make() {
+		Object[] actualParams = processParams();
+		if (actualParams == null)
+			return null;
+		try {
+			return callConstructor(constructor, actualParams);
+		} catch (InvocationTargetException e) {
+			String msg = "Could not invoke " + constructor.getName();
+			logError(msg, e);
+			return null;
+		} catch (InstantiationException e) {
+			String msg = "Could not instantiate " + constructor.getName();
+			logError(msg, e);
+			return null;
+		}
+	}
+
+	private Object[] processParams() {
+		Class[] parameterTypes = constructor.getParameterTypes();
+		InjectionProperties[] properties = annotationSupport.getInjectParamsProperties(constructor);
+		Object[] actualParams = new Object[properties.length];
+		for (int i = 0; i < actualParams.length; i++) {
+			try {
+				actualParams[i] = getValue(properties[i], parameterTypes[i], false, false);
+			} catch (IllegalArgumentException e) {
+				String msg = "Unable to find matching arguments for " + constructor.getName();
+				logError(msg, e);
+				return null;
+			}
+		}
+		return actualParams;
+	}
+
+	private Object callConstructor(Constructor constructor, Object[] args)
+			throws InvocationTargetException, InstantiationException {
+		if (args != null) { // make sure args are assignable
+			Class[] parameterTypes = constructor.getParameterTypes();
+			if (parameterTypes.length != args.length) {
+				// internal error, log it
+				logError(constructor, new IllegalArgumentException());
+				return null;
+			}
+			for (int i = 0; i < args.length; i++) {
+				if ((args[i] != null) && !parameterTypes[i].isAssignableFrom(args[i].getClass()))
+					return null;
+			}
+		}
+
+		Object result = null;
+		boolean wasAccessible = true;
+		if (!constructor.isAccessible()) {
+			constructor.setAccessible(true);
+			wasAccessible = false;
+		}
+		try {
+			result = constructor.newInstance(args);
+		} catch (IllegalArgumentException e) {
+			// should not happen, is checked at the start of this method
+			logError(constructor, e);
+			return null;
+		} catch (IllegalAccessException e) {
+			// should not happen as we set constructor to be accessible
+			logError(constructor, e);
+			return null;
+		} finally {
+			if (!wasAccessible)
+				constructor.setAccessible(false);
+		}
+		return result;
+	}
+}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionException.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionException.java
new file mode 100644
index 0000000..cb30fef
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionException.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.e4.core.services.internal.context;
+
+public class InjectionException extends Exception {
+
+	private static final long serialVersionUID = 3098545573510654907L;
+
+	public InjectionException() {
+		super();
+	}
+
+	public InjectionException(Throwable e) {
+		super(e);
+	}
+
+	public InjectionException(String msg) {
+		super(msg);
+	}
+
+	public InjectionException(String msg, Throwable e) {
+		super(msg, e);
+	}
+
+}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionField.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionField.java
new file mode 100644
index 0000000..a2339fa
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionField.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.e4.core.services.internal.context;
+
+import java.lang.reflect.Field;
+import org.eclipse.e4.core.services.context.ContextChangeEvent;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
+
+/**
+ * Collection of static methods that deal with reflection-based injection at a low level.
+ */
+public class InjectionField extends InjectionAbstract {
+
+	private final Field field;
+
+	public InjectionField(Object userObject, IObjectProvider primarySupplier, Field field) {
+		super(userObject, primarySupplier);
+		this.field = field;
+		InjectionProperties fieldProps = annotationSupport.getInjectProperties(field);
+		optional = fieldProps.isOptional();
+	}
+
+	public boolean notify(ContextChangeEvent event) {
+		Object userObject = getObject();
+		if (userObject == null)
+			return false;
+		// set variables based on the event type
+		int eventType = event.getEventType();
+		IObjectProvider changed = event.getContext();
+
+		boolean ignoreMissing = ignoreMissing(eventType, changed);
+		boolean injectWithNulls = injectNulls(eventType, changed);
+
+		InjectionProperties properties = annotationSupport.getInjectProperties(field);
+		Object value;
+		try {
+			value = getValue(properties, field.getType(), ignoreMissing, injectWithNulls);
+		} catch (IllegalArgumentException e) {
+			String msg = "Could not set " + field.getName();
+			logError(msg, e);
+			return true; // keep trying?
+		}
+		setField(value);
+		return true;
+	}
+
+	private boolean setField(Object value) {
+		Object userObject = getObject();
+		boolean wasAccessible = true;
+		if (!field.isAccessible()) {
+			field.setAccessible(true);
+			wasAccessible = false;
+		}
+		try {
+			field.set(userObject, value);
+		} catch (IllegalArgumentException e) {
+			logError(field, e);
+			return false;
+		} catch (IllegalAccessException e) {
+			logError(field, e);
+			return false;
+		} finally {
+			if (!wasAccessible)
+				field.setAccessible(false);
+		}
+		return true;
+	}
+
+}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionMethod.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionMethod.java
new file mode 100644
index 0000000..138b5ee
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionMethod.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.e4.core.services.internal.context;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import org.eclipse.e4.core.services.context.ContextChangeEvent;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
+
+/**
+ * Collection of static methods that deal with reflection-based injection at a low level.
+ */
+public class InjectionMethod extends InjectionAbstract {
+
+	private final Method method;
+
+	public InjectionMethod(Object userObject, IObjectProvider primarySupplier, Method method) {
+		super(userObject, primarySupplier);
+		this.method = method;
+		InjectionProperties methodProps = annotationSupport.getInjectProperties(method);
+		optional = methodProps.isOptional();
+	}
+
+	public boolean notify(ContextChangeEvent event) {
+		Object userObject = getObject();
+		if (userObject == null)
+			return false;
+		int eventType = event.getEventType();
+		IObjectProvider changed = event.getContext();
+		boolean ignoreMissing = ignoreMissing(eventType, changed);
+		boolean injectWithNulls = injectNulls(eventType, changed);
+		try {
+			invoke(ignoreMissing, injectWithNulls);
+		} catch (InjectionException e) {
+			logError(method, e);
+			return false;
+		}
+		return true;
+	}
+
+	public Object invoke(boolean ignoreMissing, boolean injectWithNulls) throws InjectionException {
+		Object[] actualParams = processParams(ignoreMissing, injectWithNulls);
+		if (actualParams == null) {
+			if (!optional) {
+				String msg = "Unable to find matching argument to call method \""
+						+ method.getName() + "\"";
+				throw new InjectionException(msg);
+			}
+			return null;
+		}
+		try {
+			return callMethod(actualParams);
+		} catch (InvocationTargetException e) {
+			String msg = "Unexpected error invoking method \"" + method.getName() + "\"";
+			throw new InjectionException(msg, e);
+		}
+	}
+
+	private Object[] processParams(boolean ignoreMissing, boolean injectWithNulls) {
+		Class[] parameterTypes = method.getParameterTypes();
+		InjectionProperties[] properties = annotationSupport.getInjectParamProperties(method);
+		Object[] actualParams = new Object[properties.length];
+		for (int i = 0; i < actualParams.length; i++) {
+			try {
+				Object actualValue = getValue(properties[i], parameterTypes[i], ignoreMissing,
+						injectWithNulls);
+				if (actualValue == NOT_A_VALUE)
+					return null;
+				actualParams[i] = actualValue;
+			} catch (IllegalArgumentException e) {
+				String msg = "Could not invoke " + method.getName();
+				logError(msg, e);
+				return null;
+			}
+		}
+		return actualParams;
+	}
+
+	private Object callMethod(Object[] args) throws InvocationTargetException {
+		Object userObject = getObject();
+		Object result = null;
+		boolean wasAccessible = true;
+		if (!method.isAccessible()) {
+			method.setAccessible(true);
+			wasAccessible = false;
+		}
+		try {
+			result = method.invoke(userObject, args);
+		} catch (IllegalArgumentException e) {
+			// should not happen, is checked during formation of the array of actual arguments
+			logError(method, e);
+			return null;
+		} catch (IllegalAccessException e) {
+			// should not happen, is checked at the start of this method
+			logError(method, e);
+			return null;
+		} finally {
+			if (!wasAccessible)
+				method.setAccessible(false);
+		}
+		return result;
+	}
+}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ObjectProviderContext.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ObjectProviderContext.java
index 0bc6164..6f94a91 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ObjectProviderContext.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ObjectProviderContext.java
@@ -10,15 +10,13 @@
  *******************************************************************************/
 package org.eclipse.e4.core.services.internal.context;
 
-import org.eclipse.e4.core.services.context.ContextChangeEvent;
 import org.eclipse.e4.core.services.context.IEclipseContext;
 import org.eclipse.e4.core.services.context.IRunAndTrack;
-import org.eclipse.e4.core.services.context.spi.IContextConstants;
 import org.eclipse.e4.core.services.injector.IObjectDescriptor;
 import org.eclipse.e4.core.services.injector.IObjectProvider;
 import org.eclipse.e4.core.services.injector.Injector;
 
-public class ObjectProviderContext implements IObjectProvider, IRunAndTrack {
+public class ObjectProviderContext implements IObjectProvider {
 
 	final static private String ECLIPSE_CONTEXT_NAME = IEclipseContext.class.getName();
 
@@ -69,52 +67,11 @@
 		return "ContextToInjectorLink(" + context + ')'; //$NON-NLS-1$
 	}
 
-	// /////////////////////////////////////////////////////////////////////////////////
-	// Context events
-
-	public boolean notify(ContextChangeEvent event) {
-		switch (event.getEventType()) {
-		case ContextChangeEvent.INITIAL:
-			injector.inject(event.getArguments()[0]);
-			break;
-		case ContextChangeEvent.UNINJECTED:
-			injector.uninject(event.getArguments()[0]);
-			break;
-		case ContextChangeEvent.DISPOSE:
-			if (event.getContext() == context)
-				injector.dispose();
-			else
-				injector.reinject();
-			break;
-		case ContextChangeEvent.ADDED: {
-			String name = event.getName();
-			if (IContextConstants.PARENT.equals(name))
-				handleParentChange(event);
-			else
-				injector.added(new InjectionProperties(true, name, false, null));
-			break;
-		}
-		case ContextChangeEvent.REMOVED: {
-			String name = event.getName();
-			if (IContextConstants.PARENT.equals(name))
-				handleParentChange(event);
-			else
-				injector.removed(new InjectionProperties(true, name, false, null));
-			break;
-		}
-		}
-		return true; // only dispose of the injector when the context has been disposed
+	public IEclipseContext getContext() {
+		return context;
 	}
 
-	private void handleParentChange(final ContextChangeEvent event) {
-		IEclipseContext eventContext = (IEclipseContext) event.getContext();
-		IEclipseContext oldParent = (IEclipseContext) event.getOldValue();
-		IEclipseContext newParent = (IEclipseContext) eventContext.get(IContextConstants.PARENT);
-		if (oldParent == newParent)
-			return;
-		injector.reparent(getObjectProvider(oldParent));
-	}
-
+	// TBD remove?
 	static public ObjectProviderContext getObjectProvider(IEclipseContext context) {
 		String key = ObjectProviderContext.class.getName();
 		if (context.containsKey(key, true))
@@ -126,4 +83,25 @@
 		return objectProvider;
 	}
 
+	public void init(Object userObject) {
+		injector.inject(userObject);
+	}
+
+	public void runAndTrack(final IRunAndTrack runnable, Object[] args) {
+		context.runAndTrack(runnable, args);
+	}
+
+	// TBD remove?
+	public boolean equals(Object obj) {
+		if (obj == this)
+			return true;
+		if (!(obj instanceof ObjectProviderContext))
+			return false;
+		return context.equals(((ObjectProviderContext) obj).context);
+	}
+
+	// TBD remove?
+	public int hashCode() {
+		return context.hashCode();
+	}
 }
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/TestHelper.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/TestHelper.java
index 9896bb5..cd0aa3c 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/TestHelper.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/TestHelper.java
@@ -10,7 +10,7 @@
  *******************************************************************************/
 package org.eclipse.e4.core.services.internal.context;
 
-import java.util.Set;
+import java.util.List;
 import org.eclipse.e4.core.services.context.IEclipseContext;
 
 /**
@@ -24,7 +24,7 @@
 		// don't allow instantiation
 	}
 
-	public static Set getListeners(IEclipseContext context) {
+	public static List getListeners(IEclipseContext context) {
 		return ((EclipseContext) context).listeners;
 	}
 }
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ValueComputation.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ValueComputation.java
index cbb4f8f..634fbe5 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ValueComputation.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ValueComputation.java
@@ -14,6 +14,7 @@
 import org.eclipse.e4.core.services.context.ContextChangeEvent;
 import org.eclipse.e4.core.services.context.IContextFunction;
 import org.eclipse.e4.core.services.context.IEclipseContext;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
 
 public class ValueComputation extends Computation {
 	Object cachedValue;
@@ -110,10 +111,13 @@
 	final protected void doHandleInvalid(ContextChangeEvent event, List scheduled) {
 		int eventType = event.getEventType();
 		// if the originating context is being disposed, remove this value computation completely
-		if (eventType == ContextChangeEvent.DISPOSE
-				&& originatingContext.equals(event.getContext())) {
-			removeAll();
-			return;
+		if (eventType == ContextChangeEvent.DISPOSE) {
+			IObjectProvider provider = event.getContext();
+			IEclipseContext eventsContext = ((ObjectProviderContext) provider).getContext();
+			if (originatingContext.equals(eventsContext)) {
+				removeAll();
+				return;
+			}
 		}
 		this.originatingContext.invalidate(this.name,
 				eventType == ContextChangeEvent.DISPOSE ? ContextChangeEvent.REMOVED : eventType,
diff --git a/tests/org.eclipse.e4.core.tests.services/src/org/eclipse/e4/core/services/internal/context/RunAndTrackTest.java b/tests/org.eclipse.e4.core.tests.services/src/org/eclipse/e4/core/services/internal/context/RunAndTrackTest.java
index 721e5d5..d923a2f 100644
--- a/tests/org.eclipse.e4.core.tests.services/src/org/eclipse/e4/core/services/internal/context/RunAndTrackTest.java
+++ b/tests/org.eclipse.e4.core.tests.services/src/org/eclipse/e4/core/services/internal/context/RunAndTrackTest.java
@@ -22,6 +22,7 @@
 import org.eclipse.e4.core.services.context.IRunAndTrack;
 import org.eclipse.e4.core.services.context.spi.ContextFunction;
 import org.eclipse.e4.core.services.context.spi.IContextConstants;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
 import org.eclipse.e4.core.tests.services.TestActivator;
 
 /**
@@ -131,7 +132,9 @@
 
 			public boolean notify(ContextChangeEvent event) {
 				oldValue = event.getOldValue();
-				newValue = event.getContext().get(NAME);
+				IObjectProvider provider = event.getContext();
+				IEclipseContext eventsContext = ((ObjectProviderContext) provider).getContext();
+				newValue = eventsContext.get(NAME);
 				return true;
 			}
 		}
@@ -176,7 +179,9 @@
 			public boolean notify(ContextChangeEvent event) {
 				// must get a value so we aren't removed
 				eventType = event.getEventType();
-				event.getContext().get(NAME);
+				IObjectProvider provider = event.getContext();
+				IEclipseContext eventsContext = ((ObjectProviderContext) provider).getContext();
+				eventsContext.get(NAME);
 				return true;
 			}
 		}