Bug 334498 - [DI] ContextInjectionFactory.make on nested class within @PostConstruct method of outer class leads to endless recursion
diff --git a/bundles/org.eclipse.e4.core.di/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.core.di/META-INF/MANIFEST.MF
index 1e9ad34..4e51ffa 100644
--- a/bundles/org.eclipse.e4.core.di/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.e4.core.di/META-INF/MANIFEST.MF
@@ -1,7 +1,7 @@
 Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-SymbolicName: org.eclipse.e4.core.di
-Bundle-Version: 0.10.0.qualifier
+Bundle-Version: 1.0.0.qualifier
 Bundle-Name: %pluginName
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
diff --git a/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/InjectorImpl.java b/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/InjectorImpl.java
index 11339f3..e21bff2 100644
--- a/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/InjectorImpl.java
+++ b/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/InjectorImpl.java
@@ -74,6 +74,8 @@
 	private Map<Class<?>, Method[]> methodsCache = new WeakHashMap<Class<?>, Method[]>();
 	private Map<Class<?>, Map<Method, Boolean>> isOverriddenCache = new WeakHashMap<Class<?>, Map<Method, Boolean>>();
 
+	private Set<Class<?>> classesBeingCreated = new HashSet<Class<?>>(5);
+
 	public void inject(Object object, PrimaryObjectSupplier objectSupplier) {
 		inject(object, objectSupplier, null);
 	}
@@ -268,55 +270,63 @@
 	}
 
 	private Object internalMake(Class<?> clazz, PrimaryObjectSupplier objectSupplier, PrimaryObjectSupplier tempSupplier) {
-		boolean isSingleton = clazz.isAnnotationPresent(Singleton.class);
-		if (isSingleton) {
-			synchronized (singletonCache) {
-				if (singletonCache.containsKey(clazz))
-					return singletonCache.get(clazz);
-			}
-		}
+		if (classesBeingCreated.contains(clazz))
+			throw new InjectionException("Recursive reference trying to create class " + clazz.getName()); //$NON-NLS-1$
+		try {
+			classesBeingCreated.add(clazz);
 
-		Constructor<?>[] constructors = clazz.getDeclaredConstructors();
-		// Sort the constructors by descending number of constructor arguments
-		ArrayList<Constructor<?>> sortedConstructors = new ArrayList<Constructor<?>>(constructors.length);
-		for (Constructor<?> constructor : constructors)
-			sortedConstructors.add(constructor);
-		Collections.sort(sortedConstructors, new Comparator<Constructor<?>>() {
-			public int compare(Constructor<?> c1, Constructor<?> c2) {
-				int l1 = c1.getParameterTypes().length;
-				int l2 = c2.getParameterTypes().length;
-				return l2 - l1;
-			}
-		});
-
-		for (Constructor<?> constructor : sortedConstructors) {
-			// skip private and protected constructors; allow public and package visibility
-			int modifiers = constructor.getModifiers();
-			if (((modifiers & Modifier.PRIVATE) != 0) || ((modifiers & Modifier.PROTECTED) != 0))
-				continue;
-
-			// unless this is the default constructor, it has to be tagged
-			if (!constructor.isAnnotationPresent(Inject.class) && constructor.getParameterTypes().length != 0)
-				continue;
-
-			ConstructorRequestor requestor = new ConstructorRequestor(constructor, this, objectSupplier, tempSupplier);
-			Object[] actualArgs = resolveArgs(requestor, objectSupplier, tempSupplier, false, true, false);
-			if (unresolved(actualArgs) != -1)
-				continue;
-			requestor.setResolvedArgs(actualArgs);
-
-			Object newInstance = requestor.execute();
-			if (newInstance != null) {
-				inject(newInstance, objectSupplier, tempSupplier);
-				if (isSingleton) {
-					synchronized (singletonCache) { // TBD this is not quite right, synch the method
-						singletonCache.put(clazz, newInstance);
-					}
+			boolean isSingleton = clazz.isAnnotationPresent(Singleton.class);
+			if (isSingleton) {
+				synchronized (singletonCache) {
+					if (singletonCache.containsKey(clazz))
+						return singletonCache.get(clazz);
 				}
-				return newInstance;
 			}
+
+			Constructor<?>[] constructors = clazz.getDeclaredConstructors();
+			// Sort the constructors by descending number of constructor arguments
+			ArrayList<Constructor<?>> sortedConstructors = new ArrayList<Constructor<?>>(constructors.length);
+			for (Constructor<?> constructor : constructors)
+				sortedConstructors.add(constructor);
+			Collections.sort(sortedConstructors, new Comparator<Constructor<?>>() {
+				public int compare(Constructor<?> c1, Constructor<?> c2) {
+					int l1 = c1.getParameterTypes().length;
+					int l2 = c2.getParameterTypes().length;
+					return l2 - l1;
+				}
+			});
+
+			for (Constructor<?> constructor : sortedConstructors) {
+				// skip private and protected constructors; allow public and package visibility
+				int modifiers = constructor.getModifiers();
+				if (((modifiers & Modifier.PRIVATE) != 0) || ((modifiers & Modifier.PROTECTED) != 0))
+					continue;
+
+				// unless this is the default constructor, it has to be tagged
+				if (!constructor.isAnnotationPresent(Inject.class) && constructor.getParameterTypes().length != 0)
+					continue;
+
+				ConstructorRequestor requestor = new ConstructorRequestor(constructor, this, objectSupplier, tempSupplier);
+				Object[] actualArgs = resolveArgs(requestor, objectSupplier, tempSupplier, false, true, false);
+				if (unresolved(actualArgs) != -1)
+					continue;
+				requestor.setResolvedArgs(actualArgs);
+
+				Object newInstance = requestor.execute();
+				if (newInstance != null) {
+					inject(newInstance, objectSupplier, tempSupplier);
+					if (isSingleton) {
+						synchronized (singletonCache) { // TBD this is not quite right, synch the method
+							singletonCache.put(clazz, newInstance);
+						}
+					}
+					return newInstance;
+				}
+			}
+			throw new InjectionException("Could not find satisfiable constructor in " + clazz.getName()); //$NON-NLS-1$
+		} finally {
+			classesBeingCreated.remove(clazz);
 		}
-		throw new InjectionException("Could not find satisfiable constructor in " + clazz.getName()); //$NON-NLS-1$
 	}
 
 	public void resolveArguments(IRequestor requestor, boolean initial) {
diff --git a/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/di/RecursiveObjectCreationTest.java b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/di/RecursiveObjectCreationTest.java
new file mode 100644
index 0000000..b7950b3
--- /dev/null
+++ b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/di/RecursiveObjectCreationTest.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.internal.tests.di;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+
+import junit.framework.TestCase;
+
+import org.eclipse.e4.core.contexts.ContextInjectionFactory;
+import org.eclipse.e4.core.contexts.EclipseContextFactory;
+import org.eclipse.e4.core.contexts.IEclipseContext;
+import org.eclipse.e4.core.di.InjectionException;
+
+public class RecursiveObjectCreationTest extends TestCase {
+	
+	static public class CheckSelfInject {
+		
+		public CheckSelfInject other;
+		
+		@Inject
+		public CheckSelfInject(CheckSelfInject other) {
+			this.other = other;
+		}
+	}
+
+	/**
+	 * Checks a simple case of constructor needing the same class
+	 */
+	public void testSelfInject() {
+		IEclipseContext context = EclipseContextFactory.create();
+		boolean exceptionReceived = false;
+		try {
+			CheckSelfInject testInstance = ContextInjectionFactory.make(CheckSelfInject.class, context);
+			assertNotNull(testInstance); // unreachable
+		} catch (InjectionException e) {
+			exceptionReceived = true; 
+		}
+		assertTrue(exceptionReceived);
+	}
+	
+	///////////////////////////////////////////////////////////////////////
+
+	static public class TestOuterClass {
+		
+		public class TestInnerClassInject {
+			@Inject
+			public TestInnerClassInject() {
+				// placeholder
+			}
+		}
+		
+		public TestInnerClassInject innerInject;
+		
+		@Inject
+		public TestOuterClass() {
+			// placeholder
+		}
+		
+		@PostConstruct
+		public void init(IEclipseContext context) {
+			innerInject = ContextInjectionFactory.make(TestInnerClassInject.class, context);
+		}
+	}
+
+	/**
+	 * Checks inner class using outer class which is still being created
+	 */
+	public void testNested() {
+		IEclipseContext context = EclipseContextFactory.create();
+		boolean exceptionReceived = false;
+		try {
+			TestOuterClass outer = ContextInjectionFactory.make(TestOuterClass.class, context);
+			assertNotNull(outer); // unreachable
+		} catch (InjectionException e) {
+			exceptionReceived = true; 
+		}
+		assertTrue(exceptionReceived);
+	}
+}
diff --git a/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/tests/CoreTestSuite.java b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/tests/CoreTestSuite.java
index 407a2b6..a99e9ea 100644
--- a/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/tests/CoreTestSuite.java
+++ b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/tests/CoreTestSuite.java
@@ -47,6 +47,7 @@
 import org.eclipse.e4.core.internal.tests.di.InjectionOrderTest;
 import org.eclipse.e4.core.internal.tests.di.InjectionResultLeakTest;
 import org.eclipse.e4.core.internal.tests.di.InvokeTest;
+import org.eclipse.e4.core.internal.tests.di.RecursiveObjectCreationTest;
 import org.eclipse.e4.core.internal.tests.di.extensions.InjectionEventTest;
 import org.eclipse.e4.core.internal.tests.di.extensions.InjectionMixedSuppliersTest;
 import org.eclipse.e4.core.internal.tests.di.extensions.InjectionPreferencesTest;
@@ -100,5 +101,6 @@
 		addTestSuite(DependenciesLeakTest.class);
 		addTestSuite(ActivationInjectionTest.class);
 		addTestSuite(GenericsInjectionTest.class);
+		addTestSuite(RecursiveObjectCreationTest.class);
 	}
 }