You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2009/11/16 08:07:21 UTC

svn commit: r880636 - in /cayenne/sandbox/cayenne-di/src: main/java/org/apache/cayenne/di/ main/java/org/apache/cayenne/di/spi/ test/java/org/apache/cayenne/di/mock/ test/java/org/apache/cayenne/di/spi/

Author: aadamchik
Date: Mon Nov 16 07:07:12 2009
New Revision: 880636

URL: http://svn.apache.org/viewvc?rev=880636&view=rev
Log:
playing with DI ideas

* working field injection
* working class and provider binding types

Added:
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultConstructorProvider.java
      - copied, changed from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InstanceBinding.java
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InjectingProvider.java
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MembersInjector.java
      - copied, changed from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ProviderConstructorProvider.java
      - copied, changed from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MapBinder.java
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/SingletonProvider.java
      - copied, changed from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InstanceBinding.java
    cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation1Alt.java
      - copied, changed from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Binding.java
    cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java
      - copied, changed from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java
    cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2Sub1.java
      - copied, changed from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java
    cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation3.java
      - copied, changed from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Inject.java
    cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface1Provider.java
      - copied, changed from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java
    cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java
      - copied, changed from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Inject.java
    cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface3.java
      - copied, changed from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/BindingTargetVisitor.java
Removed:
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Binding.java
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/BindingTargetVisitor.java
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InstanceBinding.java
Modified:
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Inject.java
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Provider.java
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InterfaceBindingBuilder.java
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MapBinder.java
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MapInjector.java
    cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/spi/MapInjectorTest.java

Modified: cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Inject.java
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Inject.java?rev=880636&r1=880635&r2=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Inject.java (original)
+++ cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Inject.java Mon Nov 16 07:07:12 2009
@@ -18,6 +18,16 @@
  ****************************************************************/
 package org.apache.cayenne.di;
 
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(RUNTIME)
+@Target( {
+    FIELD
+})
 public @interface Inject {
 
 }

Modified: cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Provider.java
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Provider.java?rev=880636&r1=880635&r2=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Provider.java (original)
+++ cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Provider.java Mon Nov 16 07:07:12 2009
@@ -26,5 +26,5 @@
  */
 public interface Provider<T> {
 
-	T get();
+	T get() throws DIException;
 }

Copied: cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultConstructorProvider.java (from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InstanceBinding.java)
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultConstructorProvider.java?p2=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultConstructorProvider.java&p1=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InstanceBinding.java&r1=836396&r2=880636&rev=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InstanceBinding.java (original)
+++ cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultConstructorProvider.java Mon Nov 16 07:07:12 2009
@@ -18,24 +18,25 @@
  ****************************************************************/
 package org.apache.cayenne.di.spi;
 
-import org.apache.cayenne.di.Binding;
+import org.apache.cayenne.di.DIException;
+import org.apache.cayenne.di.Provider;
 
-/**
- * A binding to a singleton instance.
- */
-class InstanceBinding<T> implements Binding<T> {
+class DefaultConstructorProvider<T> implements Provider<T> {
 
-    private T instance;
+    private Class<? extends T> implementation;
 
-    InstanceBinding(T instance) {
-        this.instance = instance;
+    DefaultConstructorProvider(Class<? extends T> implementation) {
+        this.implementation = implementation;
     }
 
-    public <V> V acceptVisitor(BindingTargetVisitor<? super T, V> visitor) {
-        return visitor.visitInstanceBinding(this);
-    }
-
-    public T getInstance() {
-        return instance;
+    public T get() {
+        try {
+            return implementation.newInstance();
+        }
+        catch (Exception e) {
+            throw new DIException(
+                    "Error instantiating class " + implementation.getName(),
+                    e);
+        }
     }
 }

Added: cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InjectingProvider.java
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InjectingProvider.java?rev=880636&view=auto
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InjectingProvider.java (added)
+++ cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InjectingProvider.java Mon Nov 16 07:07:12 2009
@@ -0,0 +1,87 @@
+/*****************************************************************
+ *   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.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+
+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 InjectingProvider<T> implements Provider<T> {
+
+    private Injector injector;
+    private Provider<T> delegate;
+
+    InjectingProvider(Provider<T> delegate, Injector injector) {
+        this.delegate = delegate;
+        this.injector = injector;
+    }
+
+    private Collection<Field> initInjectionPoints(
+            Class<?> type,
+            Collection<Field> injectableFields) {
+
+        if (type == null) {
+            return injectableFields;
+        }
+
+        for (Field field : type.getDeclaredFields()) {
+
+            Inject inject = field.getAnnotation(Inject.class);
+            if (inject != null) {
+                field.setAccessible(true);
+                injectableFields.add(field);
+            }
+        }
+
+        return initInjectionPoints(type.getSuperclass(), injectableFields);
+    }
+
+    public T get() throws DIException {
+        T object = delegate.get();
+        injectMembers(object);
+        return object;
+    }
+
+    private void injectMembers(T object) {
+
+        Collection<Field> injectableFields = initInjectionPoints(
+                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);
+            }
+        }
+    }
+}

Modified: cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InterfaceBindingBuilder.java
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InterfaceBindingBuilder.java?rev=880636&r1=880635&r2=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InterfaceBindingBuilder.java (original)
+++ cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InterfaceBindingBuilder.java Mon Nov 16 07:07:12 2009
@@ -18,9 +18,6 @@
  ****************************************************************/
 package org.apache.cayenne.di.spi;
 
-import java.util.Map;
-
-import org.apache.cayenne.di.Binding;
 import org.apache.cayenne.di.BindingBuilder;
 import org.apache.cayenne.di.DIException;
 import org.apache.cayenne.di.Provider;
@@ -28,35 +25,37 @@
 class InterfaceBindingBuilder<T> implements BindingBuilder<T> {
 
     private Class<T> interfaceType;
-    private Map<String, Binding<?>> bindings;
+    private MapInjector injector;
 
-    InterfaceBindingBuilder(Class<T> interfaceType, Map<String, Binding<?>> bindings) {
+    InterfaceBindingBuilder(Class<T> interfaceType, MapInjector injector) {
         this.interfaceType = interfaceType;
-        this.bindings = bindings;
+        this.injector = injector;
     }
 
     public void to(Class<? extends T> implementation) throws DIException {
-        T object;
-        try {
-            object = implementation.newInstance();
-        }
-        catch (Exception e) {
-            throw new DIException(
-                    "Error instantiating class " + implementation.getName(),
-                    e);
-        }
 
         String key = KeyGenerator.toKey(interfaceType);
-        Binding<?> oldBinding = bindings.put(key, new InstanceBinding<T>(object));
 
-        // do we care to throw on override attempt?
-        if (oldBinding != null) {
-            bindings.put(key, oldBinding);
-            throw new DIException("Attempt to redefine binding for key: " + key);
-        }
+        // creating a provider chain for lazy resolution of singletons...
+        DefaultConstructorProvider<T> provider0 = new DefaultConstructorProvider<T>(
+                implementation);
+        InjectingProvider<T> provider1 = new InjectingProvider<T>(provider0, injector);
+        SingletonProvider<T> provider2 = new SingletonProvider<T>(provider1);
+
+        // TODO: andrus 11/15/2009 - report overriding the binding??
+        injector.getBindings().put(key, provider2);
     }
 
     public void toProvider(Class<? extends Provider<? extends T>> providerType) {
-        throw new UnsupportedOperationException("TODO");
+        String key = KeyGenerator.toKey(interfaceType);
+
+        // creating a provider chain for lazy resolution of singletons...
+        ProviderConstructorProvider<T> provider0 = new ProviderConstructorProvider<T>(
+                providerType);
+        InjectingProvider<T> provider1 = new InjectingProvider<T>(provider0, injector);
+        SingletonProvider<T> provider2 = new SingletonProvider<T>(provider1);
+
+        // TODO: andrus 11/15/2009 - report overriding existing binding??
+        injector.getBindings().put(key, provider2);
     }
 }

Modified: cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MapBinder.java
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MapBinder.java?rev=880636&r1=880635&r2=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MapBinder.java (original)
+++ cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MapBinder.java Mon Nov 16 07:07:12 2009
@@ -18,22 +18,19 @@
  ****************************************************************/
 package org.apache.cayenne.di.spi;
 
-import java.util.Map;
-
 import org.apache.cayenne.di.Binder;
-import org.apache.cayenne.di.Binding;
 import org.apache.cayenne.di.BindingBuilder;
 
 class MapBinder implements Binder {
 
-    private Map<String, Binding<?>> bindings;
+    private MapInjector injector;
 
-    MapBinder(Map<String, Binding<?>> bindings) {
-        this.bindings = bindings;
+    MapBinder(MapInjector injector) {
+        this.injector = injector;
     }
 
     public <T> BindingBuilder<T> bind(Class<T> type) {
-        return new InterfaceBindingBuilder<T>(type, bindings);
+        return new InterfaceBindingBuilder<T>(type, injector);
     }
 
 }

Modified: cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MapInjector.java
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MapInjector.java?rev=880636&r1=880635&r2=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MapInjector.java (original)
+++ cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MapInjector.java Mon Nov 16 07:07:12 2009
@@ -21,31 +21,29 @@
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
-import org.apache.cayenne.di.Binding;
 import org.apache.cayenne.di.DIException;
 import org.apache.cayenne.di.Injector;
 import org.apache.cayenne.di.Module;
+import org.apache.cayenne.di.Provider;
 
 public class MapInjector implements Injector {
 
-    private Map<String, Binding<?>> bindings;
+    private Map<String, Provider<?>> bindings;
 
-    @SuppressWarnings("unchecked")
-    private GetValueBindingTargetVisitor valueExtractor;
-
-    @SuppressWarnings("unchecked")
     public MapInjector(Module... modules) throws DIException {
 
-        this.bindings = new ConcurrentHashMap<String, Binding<?>>();
+        this.bindings = new ConcurrentHashMap<String, Provider<?>>();
 
         if (modules != null && modules.length > 0) {
-            MapBinder binder = new MapBinder(bindings);
+            MapBinder binder = new MapBinder(this);
             for (Module module : modules) {
                 module.configure(binder);
             }
         }
+    }
 
-        this.valueExtractor = new GetValueBindingTargetVisitor();
+    Map<String, Provider<?>> getBindings() {
+        return bindings;
     }
 
     public <T> T getInstance(Class<T> type) throws DIException {
@@ -53,14 +51,14 @@
             throw new NullPointerException("Null type");
         }
 
-        Binding<T> binding = (Binding<T>) bindings.get(KeyGenerator.toKey(type));
+        Provider<T> provider = (Provider<T>) bindings.get(KeyGenerator.toKey(type));
 
-        if (binding == null) {
+        if (provider == null) {
             throw new DIException("Type is not bound in the DI registry: "
                     + type.getName());
         }
 
-        return (T) binding.acceptVisitor(valueExtractor);
+        return provider.get();
     }
 
 }

Copied: cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MembersInjector.java (from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java)
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MembersInjector.java?p2=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MembersInjector.java&p1=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java&r1=836396&r2=880636&rev=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java (original)
+++ cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MembersInjector.java Mon Nov 16 07:07:12 2009
@@ -18,9 +18,16 @@
  ****************************************************************/
 package org.apache.cayenne.di.spi;
 
-class GetValueBindingTargetVisitor<T, V extends T> implements BindingTargetVisitor<T, V> {
+import org.apache.cayenne.di.Injector;
 
-    public V visitInstanceBinding(InstanceBinding<? extends T> binding) {
-        return (V) binding.getInstance();
+class MembersInjector<T> {
+    
+    MembersInjector(Injector injector) {
+        
     }
+
+    void injectMembers(T object) {
+
+    }
+
 }

Copied: cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ProviderConstructorProvider.java (from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MapBinder.java)
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ProviderConstructorProvider.java?p2=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ProviderConstructorProvider.java&p1=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MapBinder.java&r1=836396&r2=880636&rev=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/MapBinder.java (original)
+++ cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ProviderConstructorProvider.java Mon Nov 16 07:07:12 2009
@@ -18,22 +18,34 @@
  ****************************************************************/
 package org.apache.cayenne.di.spi;
 
-import java.util.Map;
+import org.apache.cayenne.di.DIException;
+import org.apache.cayenne.di.Provider;
 
-import org.apache.cayenne.di.Binder;
-import org.apache.cayenne.di.Binding;
-import org.apache.cayenne.di.BindingBuilder;
+public class ProviderConstructorProvider<T> implements Provider<T> {
 
-class MapBinder implements Binder {
+    private Class<? extends Provider<? extends T>> providerType;
 
-    private Map<String, Binding<?>> bindings;
-
-    MapBinder(Map<String, Binding<?>> bindings) {
-        this.bindings = bindings;
+    ProviderConstructorProvider(Class<? extends Provider<? extends T>> providerType) {
+        this.providerType = providerType;
     }
 
-    public <T> BindingBuilder<T> bind(Class<T> type) {
-        return new InterfaceBindingBuilder<T>(type, bindings);
-    }
+    public T get() throws DIException {
 
+        Provider<? extends T> provider;
+        try {
+            provider = providerType.newInstance();
+        }
+        catch (Exception e) {
+            throw new DIException("Error instantiating provider class "
+                    + providerType.getName(), e);
+        }
+
+        T object = provider.get();
+        if (object == null) {
+            throw new DIException("Provider returned null object, provider type: "
+                    + providerType.getName());
+        }
+        
+        return object;
+    }
 }

Copied: cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/SingletonProvider.java (from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InstanceBinding.java)
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/SingletonProvider.java?p2=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/SingletonProvider.java&p1=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InstanceBinding.java&r1=836396&r2=880636&rev=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InstanceBinding.java (original)
+++ cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/SingletonProvider.java Mon Nov 16 07:07:12 2009
@@ -18,24 +18,31 @@
  ****************************************************************/
 package org.apache.cayenne.di.spi;
 
-import org.apache.cayenne.di.Binding;
+import org.apache.cayenne.di.Provider;
 
-/**
- * A binding to a singleton instance.
- */
-class InstanceBinding<T> implements Binding<T> {
+public class SingletonProvider<T> implements Provider<T> {
 
+    private Provider<T> delegate;
     private T instance;
 
-    InstanceBinding(T instance) {
-        this.instance = instance;
+    public SingletonProvider(Provider<T> delegate) {
+        this.delegate = delegate;
     }
 
-    public <V> V acceptVisitor(BindingTargetVisitor<? super T, V> visitor) {
-        return visitor.visitInstanceBinding(this);
-    }
+    public T get() {
+
+        // using double-checked locking for lazy initialization, which is not guaranteed
+        // to work on all architectures and JVMs. Better alternatives? Synchronizing the
+        // entire method?
+        if (instance == null) {
+            synchronized (this) {
+                if (instance == null) {
+                    instance = delegate.get();
+                }
+            }
+        }
 
-    public T getInstance() {
         return instance;
     }
+
 }

Copied: cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation1Alt.java (from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Binding.java)
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation1Alt.java?p2=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation1Alt.java&p1=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Binding.java&r1=836396&r2=880636&rev=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Binding.java (original)
+++ cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation1Alt.java Mon Nov 16 07:07:12 2009
@@ -16,11 +16,13 @@
  *  specific language governing permissions and limitations
  *  under the License.
  ****************************************************************/
-package org.apache.cayenne.di;
+package org.apache.cayenne.di.mock;
 
-import org.apache.cayenne.di.spi.BindingTargetVisitor;
 
-public interface Binding<T> {
+public class MockImplementation1Alt implements MockInterface1 {
+
+    public String getName() {
+        return "alt";
+    }
 
-    <V> V acceptVisitor(BindingTargetVisitor<? super T, V> visitor);
 }

Copied: cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java (from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java)
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java?p2=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java&p1=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java&r1=836396&r2=880636&rev=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java (original)
+++ cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2.java Mon Nov 16 07:07:12 2009
@@ -16,11 +16,17 @@
  *  specific language governing permissions and limitations
  *  under the License.
  ****************************************************************/
-package org.apache.cayenne.di.spi;
+package org.apache.cayenne.di.mock;
 
-class GetValueBindingTargetVisitor<T, V extends T> implements BindingTargetVisitor<T, V> {
+import org.apache.cayenne.di.Inject;
 
-    public V visitInstanceBinding(InstanceBinding<? extends T> binding) {
-        return (V) binding.getInstance();
+public class MockImplementation2 implements MockInterface2 {
+
+    @Inject
+    private MockInterface1 service;
+
+    public String getAlteredName() {
+        return "altered_" + service.getName();
     }
+
 }

Copied: cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2Sub1.java (from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java)
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2Sub1.java?p2=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2Sub1.java&p1=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java&r1=836396&r2=880636&rev=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java (original)
+++ cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation2Sub1.java Mon Nov 16 07:07:12 2009
@@ -16,11 +16,17 @@
  *  specific language governing permissions and limitations
  *  under the License.
  ****************************************************************/
-package org.apache.cayenne.di.spi;
+package org.apache.cayenne.di.mock;
 
-class GetValueBindingTargetVisitor<T, V extends T> implements BindingTargetVisitor<T, V> {
+import org.apache.cayenne.di.Inject;
 
-    public V visitInstanceBinding(InstanceBinding<? extends T> binding) {
-        return (V) binding.getInstance();
+public class MockImplementation2Sub1 extends MockImplementation2 {
+
+    @Inject
+    private MockInterface3 mockInterface3;
+
+    @Override
+    public String getAlteredName() {
+        return super.getAlteredName() + ":" + mockInterface3.getName();
     }
 }

Copied: cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation3.java (from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Inject.java)
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation3.java?p2=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation3.java&p1=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Inject.java&r1=836396&r2=880636&rev=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Inject.java (original)
+++ cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation3.java Mon Nov 16 07:07:12 2009
@@ -16,8 +16,11 @@
  *  specific language governing permissions and limitations
  *  under the License.
  ****************************************************************/
-package org.apache.cayenne.di;
+package org.apache.cayenne.di.mock;
 
-public @interface Inject {
+public class MockImplementation3 implements MockInterface3 {
 
+    public String getName() {
+        return "XName";
+    }
 }

Copied: cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface1Provider.java (from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java)
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface1Provider.java?p2=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface1Provider.java&p1=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java&r1=836396&r2=880636&rev=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/GetValueBindingTargetVisitor.java (original)
+++ cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface1Provider.java Mon Nov 16 07:07:12 2009
@@ -16,11 +16,14 @@
  *  specific language governing permissions and limitations
  *  under the License.
  ****************************************************************/
-package org.apache.cayenne.di.spi;
+package org.apache.cayenne.di.mock;
 
-class GetValueBindingTargetVisitor<T, V extends T> implements BindingTargetVisitor<T, V> {
+import org.apache.cayenne.di.DIException;
+import org.apache.cayenne.di.Provider;
 
-    public V visitInstanceBinding(InstanceBinding<? extends T> binding) {
-        return (V) binding.getInstance();
+public class MockInterface1Provider implements Provider<MockInterface1> {
+
+    public MockInterface1 get() throws DIException {
+        return new MockImplementation1();
     }
 }

Copied: cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java (from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Inject.java)
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java?p2=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java&p1=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Inject.java&r1=836396&r2=880636&rev=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Inject.java (original)
+++ cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface2.java Mon Nov 16 07:07:12 2009
@@ -16,8 +16,9 @@
  *  specific language governing permissions and limitations
  *  under the License.
  ****************************************************************/
-package org.apache.cayenne.di;
+package org.apache.cayenne.di.mock;
 
-public @interface Inject {
+public interface MockInterface2 {
 
+    String getAlteredName();
 }

Copied: cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface3.java (from r836396, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/BindingTargetVisitor.java)
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface3.java?p2=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface3.java&p1=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/BindingTargetVisitor.java&r1=836396&r2=880636&rev=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/BindingTargetVisitor.java (original)
+++ cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface3.java Mon Nov 16 07:07:12 2009
@@ -16,9 +16,9 @@
  *  specific language governing permissions and limitations
  *  under the License.
  ****************************************************************/
-package org.apache.cayenne.di.spi;
+package org.apache.cayenne.di.mock;
 
-public interface BindingTargetVisitor<T, V> {
+public interface MockInterface3 {
 
-    V visitInstanceBinding(InstanceBinding<? extends T> binding);
+    String getName();
 }

Modified: cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/spi/MapInjectorTest.java
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/spi/MapInjectorTest.java?rev=880636&r1=880635&r2=880636&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/spi/MapInjectorTest.java (original)
+++ cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/spi/MapInjectorTest.java Mon Nov 16 07:07:12 2009
@@ -5,62 +5,141 @@
 import org.apache.cayenne.di.Binder;
 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.MockImplementation2;
+import org.apache.cayenne.di.mock.MockImplementation2Sub1;
+import org.apache.cayenne.di.mock.MockImplementation3;
 import org.apache.cayenne.di.mock.MockInterface1;
-import org.apache.cayenne.di.spi.MapInjector;
+import org.apache.cayenne.di.mock.MockInterface1Provider;
+import org.apache.cayenne.di.mock.MockInterface2;
+import org.apache.cayenne.di.mock.MockInterface3;
 
 public class MapInjectorTest extends TestCase {
 
-	public void testConstructor_Empty() {
-		new MapInjector();
-		// no exceptions...
-	}
-
-	public void testConstructor_SingleModule() {
-		final boolean[] configureCalled = new boolean[1];
-
-		Module module = new Module() {
-			public void configure(Binder binder) {
-				configureCalled[0] = true;
-			}
-		};
-
-		new MapInjector(module);
-		assertTrue(configureCalled[0]);
-	}
-
-	public void testConstructor_MultiModule() {
-
-		final boolean[] configureCalled = new boolean[2];
-
-		Module module1 = new Module() {
-			public void configure(Binder binder) {
-				configureCalled[0] = true;
-			}
-		};
-
-		Module module2 = new Module() {
-			public void configure(Binder binder) {
-				configureCalled[1] = true;
-			}
-		};
-
-		new MapInjector(module1, module2);
-		assertTrue(configureCalled[0]);
-		assertTrue(configureCalled[1]);
-	}
-
-	public void testResolveService() {
-
-		Module module = new Module() {
-			public void configure(Binder binder) {
-				binder.bind(MockInterface1.class).to(MockImplementation1.class);
-			}
-		};
-
-		MapInjector injector = new MapInjector(module);
-
-		MockInterface1 service = injector.getInstance(MockInterface1.class);
-		assertNotNull(service);
-		assertEquals("MyName", service.getName());
-	}
+    public void testConstructor_Empty() {
+        new MapInjector();
+        // no exceptions...
+    }
+
+    public void testConstructor_SingleModule() {
+        final boolean[] configureCalled = new boolean[1];
+
+        Module module = new Module() {
+
+            public void configure(Binder binder) {
+                configureCalled[0] = true;
+            }
+        };
+
+        new MapInjector(module);
+        assertTrue(configureCalled[0]);
+    }
+
+    public void testConstructor_MultiModule() {
+
+        final boolean[] configureCalled = new boolean[2];
+
+        Module module1 = new Module() {
+
+            public void configure(Binder binder) {
+                configureCalled[0] = true;
+            }
+        };
+
+        Module module2 = new Module() {
+
+            public void configure(Binder binder) {
+                configureCalled[1] = true;
+            }
+        };
+
+        new MapInjector(module1, module2);
+        assertTrue(configureCalled[0]);
+        assertTrue(configureCalled[1]);
+    }
+
+    public void testClassBinding() {
+
+        Module module = new Module() {
+
+            public void configure(Binder binder) {
+                binder.bind(MockInterface1.class).to(MockImplementation1.class);
+            }
+        };
+
+        MapInjector injector = new MapInjector(module);
+
+        MockInterface1 service = injector.getInstance(MockInterface1.class);
+        assertNotNull(service);
+        assertEquals("MyName", service.getName());
+    }
+
+    public void testFieldInjection() {
+
+        Module module = new Module() {
+
+            public void configure(Binder binder) {
+                binder.bind(MockInterface1.class).to(MockImplementation1.class);
+                binder.bind(MockInterface2.class).to(MockImplementation2.class);
+            }
+        };
+
+        MapInjector injector = new MapInjector(module);
+
+        MockInterface2 service = injector.getInstance(MockInterface2.class);
+        assertNotNull(service);
+        assertEquals("altered_MyName", service.getAlteredName());
+    }
+
+    public void testFieldInjectionSuperclass() {
+
+        Module module = new Module() {
+
+            public void configure(Binder binder) {
+                binder.bind(MockInterface1.class).to(MockImplementation1.class);
+                binder.bind(MockInterface2.class).to(MockImplementation2Sub1.class);
+                binder.bind(MockInterface3.class).to(MockImplementation3.class);
+            }
+        };
+
+        MapInjector injector = new MapInjector(module);
+
+        MockInterface2 service = injector.getInstance(MockInterface2.class);
+        assertNotNull(service);
+        assertEquals("altered_MyName:XName", service.getAlteredName());
+    }
+
+    public void testProviderBinding() {
+        Module module = new Module() {
+
+            public void configure(Binder binder) {
+                binder
+                        .bind(MockInterface1.class)
+                        .toProvider(MockInterface1Provider.class);
+            }
+        };
+
+        MapInjector injector = new MapInjector(module);
+
+        MockInterface1 service = injector.getInstance(MockInterface1.class);
+        assertNotNull(service);
+        assertEquals("MyName", service.getName());
+    }
+    
+    public void testClassReBinding() {
+
+        Module module = new Module() {
+
+            public void configure(Binder binder) {
+                binder.bind(MockInterface1.class).to(MockImplementation1.class);
+                binder.bind(MockInterface1.class).to(MockImplementation1Alt.class);
+            }
+        };
+
+        MapInjector injector = new MapInjector(module);
+
+        MockInterface1 service = injector.getInstance(MockInterface1.class);
+        assertNotNull(service);
+        assertEquals("alt", service.getName());
+    }
 }