Author: aadamchik
Date: Sat Nov 21 18:22:39 2009
New Revision: 882956
URL: http://svn.apache.org/viewvc?rev=882956&view=rev
Log:
playing with DI ideas
* configuration binding to object interface instead of implementation
* circular dependency detection
* provider injection support
Added:
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DIUtil.java
- copied, changed from r882933,
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InjectionStack.java
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation1_DepOn2.java
- copied, changed from r882933,
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation1_DepOn2Provider.java
- copied, changed from r882933,
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2_ConstructorProvider.java
- copied, changed from r882933,
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation5.java
- copied, changed from r882933,
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface5.java
- copied, changed from r882933,
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java
Modified:
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Binder.java
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Injector.java
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/ListBuilder.java
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/MapBuilder.java
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ConstructorProvider.java
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultBindingBuilder.java
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultInjector.java
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultListBuilder.java
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultMapBuilder.java
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/FieldInjectingProvider.java
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/spi/DefaultInjectorTest.java
Modified:
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Binder.java
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Binder.java?rev=882956&r1=882955&r2=882956&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Binder.java
(original)
+++ cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Binder.java
Sat Nov 21 18:22:39 2009
@@ -28,17 +28,17 @@
/**
* Starts a binding of a map "configuration" that will be injected into an
- * implementation class identified via the "implementationType" parameter.
- * Configurations can only be injected via a constructor. Each type can
take at most
- * one configuration object via a constructor.
+ * implementation class for the specified "interfaceType" parameter.
Configurations
+ * can only be injected via a constructor. An object can take at most one
+ * configuration object via a constructor.
*/
- <T> MapBuilder<T> bindMap(Class<T> implementationType);
+ <T> MapBuilder<T> bindMap(Class<T> interfaceType);
/**
* Starts a binding of a list "configuration" that will be injected into an
- * implementation class identified via the "implementationType" parameter.
- * Configurations can only be injected via a constructor. Each type can
take at most
- * one configuration object via a constructor.
+ * implementation class for the specified "interfaceType" parameter.
Configurations
+ * can only be injected via a constructor. An object can take at most one
+ * configuration object via a constructor.
*/
- <T> ListBuilder<T> bindList(Class<T> implementationType);
+ <T> ListBuilder<T> bindList(Class<T> interfaceType);
}
Modified:
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Injector.java
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Injector.java?rev=882956&r1=882955&r2=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Injector.java
(original)
+++
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Injector.java
Sat Nov 21 18:22:39 2009
@@ -28,6 +28,8 @@
* {...@link DIException} if the type is not bound, or an instance can not
be created.
*/
<T> T getInstance(Class<T> type) throws DIException;
+
+ <T> Provider<T> getProvider(Class<T> type) throws DIException;
<T> Map<String, ?> getMapConfiguration(Class<T> type);
Modified:
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/ListBuilder.java
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/ListBuilder.java?rev=882956&r1=882955&r2=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/ListBuilder.java
(original)
+++
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/ListBuilder.java
Sat Nov 21 18:22:39 2009
@@ -25,7 +25,7 @@
*/
public interface ListBuilder<T> {
- <E> ListBuilder<T> add(Class<? extends E> valueType) throws DIException;
+ <E> ListBuilder<T> add(Class<? extends E> interfaceType) throws
DIException;
<E> ListBuilder<T> add(E value) throws DIException;
}
Modified:
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/MapBuilder.java
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/MapBuilder.java?rev=882956&r1=882955&r2=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/MapBuilder.java
(original)
+++
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/MapBuilder.java
Sat Nov 21 18:22:39 2009
@@ -25,7 +25,7 @@
*/
public interface MapBuilder<T> {
- <E> MapBuilder<T> put(String key, Class<? extends E> valueType) throws
DIException;
+ <E> MapBuilder<T> put(String key, Class<? extends E> interfaceType) throws
DIException;
<E> MapBuilder<T> put(String key, E value) throws DIException;
}
Modified:
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ConstructorProvider.java
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ConstructorProvider.java?rev=882956&r1=882955&r2=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ConstructorProvider.java
(original)
+++
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ConstructorProvider.java
Sat Nov 21 18:22:39 2009
@@ -20,6 +20,7 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
@@ -30,10 +31,12 @@
class ConstructorProvider<T> implements Provider<T> {
+ private Class<T> interfaceType;
private Constructor<? extends T> constructor;
private Injector injector;
- ConstructorProvider(Class<? extends T> implementation, Injector injector) {
+ ConstructorProvider(Class<T> interfaceType, Class<? extends T>
implementation,
+ Injector injector) {
initConstructor(implementation);
@@ -45,6 +48,7 @@
this.constructor.setAccessible(true);
this.injector = injector;
+ this.interfaceType = interfaceType;
}
private void initConstructor(Class<? extends T> implementation) {
@@ -100,6 +104,7 @@
public T get() {
Class<?>[] constructorParameters = constructor.getParameterTypes();
+ Type[] genericTypes = constructor.getGenericParameterTypes();
Object[] args = new Object[constructorParameters.length];
for (int i = 0; i < constructorParameters.length; i++) {
@@ -110,13 +115,27 @@
// implementations in the declaration will make the injection much
more
// complicated
if (Map.class.equals(parameter)) {
- args[i] =
injector.getMapConfiguration(constructor.getDeclaringClass());
+ args[i] = injector.getMapConfiguration(interfaceType);
}
// parameter must be declared as "java.util.List". Use of specific
list
// implementations in the declaration will make the injection much
more
// complicated
else if (List.class.equals(parameter)) {
- args[i] =
injector.getListConfiguration(constructor.getDeclaringClass());
+ args[i] = injector.getListConfiguration(interfaceType);
+ }
+ else if (Provider.class.equals(parameter)) {
+
+ Class<?> objectClass =
DIUtil.getParameterClass(genericTypes[i]);
+
+ if (objectClass == null) {
+ String message = String.format(
+ "Constrcutor provider parameter %s must be "
+ + "parameterized to be usable for
injection",
+ parameter.getName());
+ throw new DIException(message);
+ }
+
+ args[i] = injector.getProvider(objectClass);
}
else {
args[i] = injector.getInstance(parameter);
Copied:
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DIUtil.java
(from r882933,
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java)
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DIUtil.java?p2=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DIUtil.java&p1=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java&r1=882933&r2=882956&rev=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java
(original)
+++
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DIUtil.java
Sat Nov 21 18:22:39 2009
@@ -16,17 +16,24 @@
* specific language governing permissions and limitations
* under the License.
****************************************************************/
-package org.apache.cayenne.di.mock;
+package org.apache.cayenne.di.spi;
-import org.apache.cayenne.di.Inject;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
-public class MockImplementation2 implements MockInterface2 {
+class DIUtil {
- @Inject
- private MockInterface1 service;
+ static Class<?> getParameterClass(Type type) {
- public String getAlteredName() {
- return "altered_" + service.getName();
- }
+ if (type instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ Type[] parameters = parameterizedType.getActualTypeArguments();
+
+ if (parameters.length == 1) {
+ return (Class<?>) parameters[0];
+ }
+ }
+ return null;
+ }
}
Modified:
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultBindingBuilder.java
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultBindingBuilder.java?rev=882956&r1=882955&r2=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultBindingBuilder.java
(original)
+++
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultBindingBuilder.java
Sat Nov 21 18:22:39 2009
@@ -38,11 +38,13 @@
// creating a provider chain for lazy resolution of singletons...
ConstructorProvider<T> provider0 = new ConstructorProvider<T>(
+ interfaceType,
implementation,
injector);
FieldInjectingProvider<T> provider1 = new FieldInjectingProvider<T>(
provider0,
- injector);
+ injector,
+ key);
SingletonProvider<T> provider2 = new SingletonProvider<T>(provider1);
// TODO: andrus 11/15/2009 - report overriding the binding??
@@ -57,7 +59,8 @@
providerType);
FieldInjectingProvider<T> provider1 = new FieldInjectingProvider<T>(
provider0,
- injector);
+ injector,
+ key);
SingletonProvider<T> provider2 = new SingletonProvider<T>(provider1);
// TODO: andrus 11/15/2009 - report overriding existing binding??
Modified:
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultInjector.java
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultInjector.java?rev=882956&r1=882955&r2=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultInjector.java
(original)
+++
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultInjector.java
Sat Nov 21 18:22:39 2009
@@ -32,12 +32,14 @@
private Map<String, Provider<?>> bindings;
private Map<String, MapProvider> mapConfigurations;
private Map<String, ListProvider> listConfigurations;
+ private InjectionStack injectionStack;
public DefaultInjector(Module... modules) throws DIException {
this.bindings = new HashMap<String, Provider<?>>();
this.mapConfigurations = new HashMap<String, MapProvider>();
this.listConfigurations = new HashMap<String, ListProvider>();
+ this.injectionStack = new InjectionStack();
if (modules != null && modules.length > 0) {
DefaultBinder binder = new DefaultBinder(this);
@@ -47,6 +49,10 @@
}
}
+ InjectionStack getInjectionStack() {
+ return injectionStack;
+ }
+
Map<String, Provider<?>> getBindings() {
return bindings;
}
@@ -60,19 +66,7 @@
}
public <T> T getInstance(Class<T> type) throws DIException {
- if (type == null) {
- throw new NullPointerException("Null type");
- }
-
- String key = KeyGenerator.toKey(type);
- Provider<T> provider = (Provider<T>) bindings.get(key);
-
- if (provider == null) {
-
- throw new DIException("Type is not bound in the registry: " +
type.getName());
- }
-
- return provider.get();
+ return getProvider(type).get();
}
public <T> List<?> getListConfiguration(Class<T> type) {
@@ -110,4 +104,19 @@
return provider.get();
}
+ public <T> Provider<T> getProvider(Class<T> type) throws DIException {
+ if (type == null) {
+ throw new NullPointerException("Null type");
+ }
+
+ String key = KeyGenerator.toKey(type);
+ Provider<T> provider = (Provider<T>) bindings.get(key);
+
+ if (provider == null) {
+ throw new DIException("Type is not bound in the registry: " +
type.getName());
+ }
+
+ return provider;
+ }
+
}
Modified:
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultListBuilder.java
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultListBuilder.java?rev=882956&r1=882955&r2=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultListBuilder.java
(original)
+++
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultListBuilder.java
Sat Nov 21 18:22:39 2009
@@ -31,14 +31,7 @@
implementationTypeKey = KeyGenerator.toKey(implementationType);
}
- public <E> ListBuilder<T> add(Class<? extends E> valueType) throws
DIException {
-
- // creating a provider chain for lazy resolution of singletons...
- ConstructorProvider<E> provider0 = new
ConstructorProvider<E>(valueType, injector);
- FieldInjectingProvider<E> provider1 = new FieldInjectingProvider<E>(
- provider0,
- injector);
- SingletonProvider<E> provider2 = new SingletonProvider<E>(provider1);
+ public <E> ListBuilder<T> add(Class<? extends E> interfaceType) throws
DIException {
ListProvider listProvider = injector.getListConfigurations().get(
implementationTypeKey);
@@ -47,7 +40,7 @@
injector.getListConfigurations().put(implementationTypeKey,
listProvider);
}
- listProvider.add(provider2);
+ listProvider.add(injector.getProvider(interfaceType));
return this;
}
Modified:
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultMapBuilder.java
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultMapBuilder.java?rev=882956&r1=882955&r2=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultMapBuilder.java
(original)
+++
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultMapBuilder.java
Sat Nov 21 18:22:39 2009
@@ -31,16 +31,9 @@
implementationTypeKey = KeyGenerator.toKey(implementationType);
}
- public <E> MapBuilder<T> put(String key, Class<? extends E> valueType)
+ public <E> MapBuilder<T> put(String key, Class<? extends E> interfaceType)
throws DIException {
- // creating a provider chain for lazy resolution of singletons...
- ConstructorProvider<E> provider0 = new
ConstructorProvider<E>(valueType, injector);
- FieldInjectingProvider<E> provider1 = new FieldInjectingProvider<E>(
- provider0,
- injector);
- SingletonProvider<E> provider2 = new SingletonProvider<E>(provider1);
-
MapProvider mapProvider = injector.getMapConfigurations().get(
implementationTypeKey);
if (mapProvider == null) {
@@ -49,7 +42,7 @@
}
// TODO: andrus 11/15/2009 - report overriding the key??
- mapProvider.put(key, provider2);
+ mapProvider.put(key, injector.getProvider(interfaceType));
return this;
}
Modified:
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/FieldInjectingProvider.java
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/FieldInjectingProvider.java?rev=882956&r1=882955&r2=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/FieldInjectingProvider.java
(original)
+++
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/FieldInjectingProvider.java
Sat Nov 21 18:22:39 2009
@@ -24,17 +24,19 @@
import org.apache.cayenne.di.DIException;
import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.di.Injector;
import org.apache.cayenne.di.Provider;
class FieldInjectingProvider<T> implements Provider<T> {
- private Injector injector;
+ private String bindingKey;
+ private DefaultInjector injector;
private Provider<T> delegate;
- FieldInjectingProvider(Provider<T> delegate, Injector injector) {
+ FieldInjectingProvider(Provider<T> delegate, DefaultInjector injector,
+ String bindingKey) {
this.delegate = delegate;
this.injector = injector;
+ this.bindingKey = bindingKey;
}
private Collection<Field> initInjectionPoints(
@@ -69,19 +71,51 @@
object.getClass(),
new ArrayList<Field>());
- for (Field field : injectableFields) {
- Object value = injector.getInstance(field.getType());
- try {
- field.set(object, value);
- }
- catch (Exception e) {
- String message = String.format(
- "Error injecting into field %s.%s of type %s",
- field.getDeclaringClass().getName(),
- field.getName(),
- field.getType().getName());
- throw new DIException(message, e);
+ injector.getInjectionStack().push(bindingKey);
+
+ try {
+
+ for (Field field : injectableFields) {
+ Object value;
+ Class<?> fieldType = field.getType();
+
+ if (Provider.class.equals(fieldType)) {
+
+ Class<?> objectClass = DIUtil.getParameterClass(field
+ .getGenericType());
+
+ if (objectClass == null) {
+ String message = String.format(
+ "Provider field %s.%s of type %s must be "
+ + "parameterized to be usable for
injection",
+ field.getDeclaringClass().getName(),
+ field.getName(),
+ fieldType.getName());
+ throw new DIException(message);
+ }
+
+ value = injector.getProvider(objectClass);
+ }
+ else {
+ value = injector.getInstance(fieldType);
+ }
+
+ try {
+ field.set(object, value);
+ }
+ catch (Exception e) {
+ String message = String.format(
+ "Error injecting into field %s.%s of type %s",
+ field.getDeclaringClass().getName(),
+ field.getName(),
+ fieldType.getName());
+ throw new DIException(message, e);
+ }
}
}
+ finally {
+ injector.getInjectionStack().pop();
+ }
}
+
}
Added:
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InjectionStack.java
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InjectionStack.java?rev=882956&view=auto
==============================================================================
---
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InjectionStack.java
(added)
+++
cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InjectionStack.java
Sat Nov 21 18:22:39 2009
@@ -0,0 +1,69 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+package org.apache.cayenne.di.spi;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.cayenne.di.DIException;
+
+/**
+ * A helper object that tracks the injection stack to prevent circular
dependencies.
+ */
+class InjectionStack {
+
+ private ThreadLocal<LinkedList<String>> stack;
+
+ InjectionStack() {
+ this.stack = new ThreadLocal<LinkedList<String>>();
+ }
+
+ void reset() {
+ List<String> localStack = stack.get();
+ if (localStack != null) {
+ localStack.clear();
+ }
+ }
+
+ void push(String bindingKey) throws DIException {
+ LinkedList<String> localStack = stack.get();
+ if (localStack == null) {
+ localStack = new LinkedList<String>();
+ stack.set(localStack);
+ }
+
+ if (localStack.contains(bindingKey)) {
+ throw new DIException("Circular dependency detected: "
+ + localStack
+ + ". You should try injecting a provider instead.");
+ }
+
+ localStack.add(bindingKey);
+ }
+
+ void pop() {
+ LinkedList<String> localStack = stack.get();
+ if (localStack != null) {
+ localStack.removeLast();
+ }
+ else {
+ throw new IndexOutOfBoundsException("0");
+ }
+ }
+}
Copied:
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation1_DepOn2.java
(from r882933,
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java)
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation1_DepOn2.java?p2=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation1_DepOn2.java&p1=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java&r1=882933&r2=882956&rev=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java
(original)
+++
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation1_DepOn2.java
Sat Nov 21 18:22:39 2009
@@ -20,13 +20,15 @@
import org.apache.cayenne.di.Inject;
-public class MockImplementation2 implements MockInterface2 {
+public class MockImplementation1_DepOn2 implements MockInterface1 {
+ // this creates a circular dependency when MockImplementation2 is bound to
+ // MockInterface2.
@Inject
- private MockInterface1 service;
+ private MockInterface2 interface2;
- public String getAlteredName() {
- return "altered_" + service.getName();
+ public String getName() {
+ return interface2.getName();
}
}
Copied:
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation1_DepOn2Provider.java
(from r882933,
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java)
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation1_DepOn2Provider.java?p2=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation1_DepOn2Provider.java&p1=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java&r1=882933&r2=882956&rev=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java
(original)
+++
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation1_DepOn2Provider.java
Sat Nov 21 18:22:39 2009
@@ -19,14 +19,17 @@
package org.apache.cayenne.di.mock;
import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.di.Provider;
-public class MockImplementation2 implements MockInterface2 {
+public class MockImplementation1_DepOn2Provider implements MockInterface1 {
+ // this creates a circular dependency when MockImplementation2 is bound to
+ // MockInterface2, however injecting a provider should prevent errors...
@Inject
- private MockInterface1 service;
+ private Provider<MockInterface2> interface2Provider;
- public String getAlteredName() {
- return "altered_" + service.getName();
+ public String getName() {
+ return interface2Provider.get().getName();
}
}
Modified:
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java?rev=882956&r1=882955&r2=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java
(original)
+++
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java
Sat Nov 21 18:22:39 2009
@@ -28,5 +28,9 @@
public String getAlteredName() {
return "altered_" + service.getName();
}
+
+ public String getName() {
+ return "MockImplementation2Name";
+ }
}
Copied:
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2_ConstructorProvider.java
(from r882933,
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java)
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2_ConstructorProvider.java?p2=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2_ConstructorProvider.java&p1=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java&r1=882933&r2=882956&rev=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java
(original)
+++
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2_ConstructorProvider.java
Sat Nov 21 18:22:39 2009
@@ -19,14 +19,23 @@
package org.apache.cayenne.di.mock;
import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.di.Provider;
-public class MockImplementation2 implements MockInterface2 {
+public class MockImplementation2_ConstructorProvider implements MockInterface2
{
- @Inject
- private MockInterface1 service;
+ private Provider<MockInterface1> provider;
+
+ public MockImplementation2_ConstructorProvider(
+ @Inject Provider<MockInterface1> provider) {
+ this.provider = provider;
+ }
public String getAlteredName() {
- return "altered_" + service.getName();
+ return "altered_" + provider.get().getName();
+ }
+
+ public String getName() {
+ return "MockImplementation2Name";
}
}
Copied:
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation5.java
(from r882933,
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java)
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation5.java?p2=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation5.java&p1=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java&r1=882933&r2=882956&rev=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java
(original)
+++
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation5.java
Sat Nov 21 18:22:39 2009
@@ -18,7 +18,10 @@
****************************************************************/
package org.apache.cayenne.di.mock;
-public interface MockInterface2 {
+public class MockImplementation5 implements MockInterface5 {
- String getAlteredName();
+ @Override
+ public String toString() {
+ return "xyz";
+ }
}
Modified:
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java?rev=882956&r1=882955&r2=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java
(original)
+++
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java
Sat Nov 21 18:22:39 2009
@@ -21,4 +21,6 @@
public interface MockInterface2 {
String getAlteredName();
+
+ String getName();
}
Copied:
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface5.java
(from r882933,
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java)
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface5.java?p2=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface5.java&p1=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java&r1=882933&r2=882956&rev=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java
(original)
+++
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface5.java
Sat Nov 21 18:22:39 2009
@@ -18,7 +18,6 @@
****************************************************************/
package org.apache.cayenne.di.mock;
-public interface MockInterface2 {
+public interface MockInterface5 {
- String getAlteredName();
}
Modified:
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/spi/DefaultInjectorTest.java
URL:
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/spi/DefaultInjectorTest.java?rev=882956&r1=882955&r2=882956&view=diff
==============================================================================
---
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/spi/DefaultInjectorTest.java
(original)
+++
cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/spi/DefaultInjectorTest.java
Sat Nov 21 18:22:39 2009
@@ -21,20 +21,26 @@
import junit.framework.TestCase;
import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.DIException;
import org.apache.cayenne.di.Module;
import org.apache.cayenne.di.mock.MockImplementation1;
import org.apache.cayenne.di.mock.MockImplementation1Alt;
+import org.apache.cayenne.di.mock.MockImplementation1_DepOn2;
+import org.apache.cayenne.di.mock.MockImplementation1_DepOn2Provider;
import org.apache.cayenne.di.mock.MockImplementation1_ListConfiguration;
import org.apache.cayenne.di.mock.MockImplementation1_MapConfiguration;
import org.apache.cayenne.di.mock.MockImplementation2;
import org.apache.cayenne.di.mock.MockImplementation2Sub1;
+import org.apache.cayenne.di.mock.MockImplementation2_ConstructorProvider;
import org.apache.cayenne.di.mock.MockImplementation3;
import org.apache.cayenne.di.mock.MockImplementation4;
+import org.apache.cayenne.di.mock.MockImplementation5;
import org.apache.cayenne.di.mock.MockInterface1;
import org.apache.cayenne.di.mock.MockInterface1Provider;
import org.apache.cayenne.di.mock.MockInterface2;
import org.apache.cayenne.di.mock.MockInterface3;
import org.apache.cayenne.di.mock.MockInterface4;
+import org.apache.cayenne.di.mock.MockInterface5;
public class DefaultInjectorTest extends TestCase {
@@ -189,9 +195,11 @@
binder.bind(MockInterface1.class).to(
MockImplementation1_MapConfiguration.class);
- binder.bindMap(MockImplementation1_MapConfiguration.class).put(
- "x",
- "xvalue").put("y", "yvalue").put("x", "xvalue1");
+ binder
+ .bindMap(MockInterface1.class)
+ .put("x", "xvalue")
+ .put("y", "yvalue")
+ .put("x", "xvalue1");
}
};
@@ -202,17 +210,14 @@
assertEquals(";x=xvalue1;y=yvalue", service.getName());
}
- public void testListInjection() {
+ public void testListInjection_addValue() {
Module module = new Module() {
public void configure(Binder binder) {
binder.bind(MockInterface1.class).to(
MockImplementation1_ListConfiguration.class);
- binder
- .bindList(MockImplementation1_ListConfiguration.class)
- .add("xvalue")
- .add("yvalue");
+
binder.bindList(MockInterface1.class).add("xvalue").add("yvalue");
}
};
@@ -222,4 +227,83 @@
assertNotNull(service);
assertEquals(";xvalue;yvalue", service.getName());
}
+
+ public void testListInjection_addType() {
+ Module module = new Module() {
+
+ public void configure(Binder binder) {
+
binder.bind(MockInterface5.class).to(MockImplementation5.class);
+
+ binder.bind(MockInterface1.class).to(
+ MockImplementation1_ListConfiguration.class);
+
+
binder.bindList(MockInterface1.class).add(MockInterface5.class).add(
+ "yvalue");
+ }
+ };
+
+ DefaultInjector injector = new DefaultInjector(module);
+
+ MockInterface1 service = injector.getInstance(MockInterface1.class);
+ assertNotNull(service);
+ assertEquals(";xyz;yvalue", service.getName());
+ }
+
+ public void testFieldInjection_CircularDependency() {
+
+ Module module = new Module() {
+
+ public void configure(Binder binder) {
+
binder.bind(MockInterface1.class).to(MockImplementation1_DepOn2.class);
+
binder.bind(MockInterface2.class).to(MockImplementation2.class);
+ }
+ };
+
+ DefaultInjector injector = new DefaultInjector(module);
+
+ try {
+ injector.getInstance(MockInterface1.class);
+ fail("Circular dependency is not detected.");
+ }
+ catch (DIException e) {
+ // expected
+ }
+ catch (StackOverflowError e) {
+ fail("Circular dependency is not detected, causing stack
overflow");
+ }
+ }
+
+ public void testProviderInjection_Constructor() {
+
+ Module module = new Module() {
+
+ public void configure(Binder binder) {
+
binder.bind(MockInterface1.class).to(MockImplementation1.class);
+ binder.bind(MockInterface2.class).to(
+ MockImplementation2_ConstructorProvider.class);
+ }
+ };
+
+ DefaultInjector injector = new DefaultInjector(module);
+
+ MockInterface2 service = injector.getInstance(MockInterface2.class);
+ assertEquals("altered_MyName", service.getAlteredName());
+ }
+
+ public void testProviderInjection_CircularDependency() {
+
+ Module module = new Module() {
+
+ public void configure(Binder binder) {
+ binder.bind(MockInterface1.class).to(
+ MockImplementation1_DepOn2Provider.class);
+
binder.bind(MockInterface2.class).to(MockImplementation2.class);
+ }
+ };
+
+ DefaultInjector injector = new DefaultInjector(module);
+
+ MockInterface1 service = injector.getInstance(MockInterface1.class);
+ assertEquals("MockImplementation2Name", service.getName());
+ }
}