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/19 22:27:39 UTC

svn commit: r882303 - 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: Thu Nov 19 21:27:38 2009
New Revision: 882303

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

* adding constructor injection

Added:
    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/FieldInjectingProvider.java
      - copied, changed from r880636, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InjectingProvider.java
    cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation4.java
      - copied, changed from r880636, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultConstructorProvider.java
    cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface4.java
      - copied, changed from r880636, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Binder.java
Removed:
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultConstructorProvider.java
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InjectingProvider.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/Inject.java
    cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InterfaceBindingBuilder.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/Binder.java
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Binder.java?rev=882303&r1=882302&r2=882303&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 Thu Nov 19 21:27:38 2009
@@ -20,9 +20,10 @@
 
 public interface Binder {
 
-	/**
-	 * Starts a binding of a specific interface. Binding should continue using
-	 * returned BindingBuilder.
-	 */
-	<T> BindingBuilder<T> bind(Class<T> type);
+    /**
+     * Starts a binding of a specific interface. Binding should continue using returned
+     * BindingBuilder.
+     */
+    <T> BindingBuilder<T> bind(Class<T> interfaceType);
+
 }

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=882303&r1=882302&r2=882303&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 Thu Nov 19 21:27:38 2009
@@ -19,6 +19,7 @@
 package org.apache.cayenne.di;
 
 import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import java.lang.annotation.Retention;
@@ -26,7 +27,7 @@
 
 @Retention(RUNTIME)
 @Target( {
-    FIELD
+    FIELD, PARAMETER
 })
 public @interface Inject {
 

Added: 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=882303&view=auto
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ConstructorProvider.java (added)
+++ cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ConstructorProvider.java Thu Nov 19 21:27:38 2009
@@ -0,0 +1,115 @@
+/*****************************************************************
+ *   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.annotation.Annotation;
+import java.lang.reflect.Constructor;
+
+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 ConstructorProvider<T> implements Provider<T> {
+
+    private Constructor<? extends T> constructor;
+    private Injector injector;
+
+    ConstructorProvider(Class<? extends T> implementation, Injector injector) {
+
+        initConstructor(implementation);
+
+        if (constructor == null) {
+            throw new DIException(
+                    "Can't find approprate constructor to create object of type "
+                            + implementation.getName());
+        }
+
+        this.constructor.setAccessible(true);
+        this.injector = injector;
+    }
+
+    private void initConstructor(Class<? extends T> implementation) {
+
+        Constructor<? extends T>[] constructors = implementation
+                .getDeclaredConstructors();
+
+        Constructor<? extends T> lastMatch = null;
+        int lastSize = -1;
+
+        // pick the first constructor with all injection-annotated parameters, or the
+        // default constructor; constructor with the longest parameter list is preferred
+        // if multiple matches are found
+        for (Constructor<? extends T> constructor : constructors) {
+
+            int size = constructor.getParameterTypes().length;
+            if (size <= lastSize) {
+                continue;
+            }
+
+            if (size == 0) {
+                lastSize = 0;
+                lastMatch = constructor;
+                continue;
+            }
+
+            boolean injectable = true;
+            for (Annotation[] annotations : constructor.getParameterAnnotations()) {
+
+                boolean parameterInjectable = false;
+                for (Annotation annotation : annotations) {
+                    if (annotation.annotationType().equals(Inject.class)) {
+                        parameterInjectable = true;
+                        break;
+                    }
+                }
+
+                if (!parameterInjectable) {
+                    injectable = false;
+                    break;
+                }
+            }
+
+            if (injectable) {
+                lastSize = size;
+                lastMatch = constructor;
+            }
+        }
+
+        this.constructor = lastMatch;
+    }
+
+    public T get() {
+
+        Class<?>[] constructorParameters = constructor.getParameterTypes();
+        Object[] args = new Object[constructorParameters.length];
+
+        for (int i = 0; i < constructorParameters.length; i++) {
+            args[i] = injector.getInstance(constructorParameters[i]);
+        }
+
+        try {
+            return constructor.newInstance(args);
+        }
+        catch (Exception e) {
+            throw new DIException("Error instantiating class "
+                    + constructor.getDeclaringClass().getName(), e);
+        }
+    }
+}

Copied: cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/FieldInjectingProvider.java (from r880636, 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/FieldInjectingProvider.java?p2=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/FieldInjectingProvider.java&p1=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InjectingProvider.java&r1=880636&r2=882303&rev=882303&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/InjectingProvider.java (original)
+++ cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/FieldInjectingProvider.java Thu Nov 19 21:27:38 2009
@@ -27,12 +27,12 @@
 import org.apache.cayenne.di.Injector;
 import org.apache.cayenne.di.Provider;
 
-class InjectingProvider<T> implements Provider<T> {
+class FieldInjectingProvider<T> implements Provider<T> {
 
     private Injector injector;
     private Provider<T> delegate;
 
-    InjectingProvider(Provider<T> delegate, Injector injector) {
+    FieldInjectingProvider(Provider<T> delegate, Injector injector) {
         this.delegate = delegate;
         this.injector = injector;
     }

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=882303&r1=882302&r2=882303&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 Thu Nov 19 21:27:38 2009
@@ -37,9 +37,12 @@
         String key = KeyGenerator.toKey(interfaceType);
 
         // creating a provider chain for lazy resolution of singletons...
-        DefaultConstructorProvider<T> provider0 = new DefaultConstructorProvider<T>(
-                implementation);
-        InjectingProvider<T> provider1 = new InjectingProvider<T>(provider0, injector);
+        ConstructorProvider<T> provider0 = new ConstructorProvider<T>(
+                implementation,
+                injector);
+        FieldInjectingProvider<T> provider1 = new FieldInjectingProvider<T>(
+                provider0,
+                injector);
         SingletonProvider<T> provider2 = new SingletonProvider<T>(provider1);
 
         // TODO: andrus 11/15/2009 - report overriding the binding??
@@ -52,7 +55,9 @@
         // creating a provider chain for lazy resolution of singletons...
         ProviderConstructorProvider<T> provider0 = new ProviderConstructorProvider<T>(
                 providerType);
-        InjectingProvider<T> provider1 = new InjectingProvider<T>(provider0, injector);
+        FieldInjectingProvider<T> provider1 = new FieldInjectingProvider<T>(
+                provider0,
+                injector);
         SingletonProvider<T> provider2 = new SingletonProvider<T>(provider1);
 
         // TODO: andrus 11/15/2009 - report overriding existing binding??

Copied: cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation4.java (from r880636, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultConstructorProvider.java)
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation4.java?p2=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation4.java&p1=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultConstructorProvider.java&r1=880636&r2=882303&rev=882303&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DefaultConstructorProvider.java (original)
+++ cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockImplementation4.java Thu Nov 19 21:27:38 2009
@@ -16,27 +16,30 @@
  *  specific language governing permissions and limitations
  *  under the License.
  ****************************************************************/
-package org.apache.cayenne.di.spi;
+package org.apache.cayenne.di.mock;
 
-import org.apache.cayenne.di.DIException;
-import org.apache.cayenne.di.Provider;
+import org.apache.cayenne.di.Inject;
 
-class DefaultConstructorProvider<T> implements Provider<T> {
+public class MockImplementation4 implements MockInterface4 {
 
-    private Class<? extends T> implementation;
+    private MockInterface1 service;
 
-    DefaultConstructorProvider(Class<? extends T> implementation) {
-        this.implementation = implementation;
+    public MockImplementation4() {
+        throw new UnsupportedOperationException(
+                "This constructor should not be picked for DI purposes");
     }
 
-    public T get() {
-        try {
-            return implementation.newInstance();
-        }
-        catch (Exception e) {
-            throw new DIException(
-                    "Error instantiating class " + implementation.getName(),
-                    e);
-        }
+    public MockImplementation4(@Inject MockInterface1 service) {
+        this.service = service;
     }
+
+    public MockImplementation4(int i, int k) {
+        throw new UnsupportedOperationException(
+                "This constructor should not be picked for DI purposes");
+    }
+
+    public String getName() {
+        return "constructor_" + service.getName();
+    }
+
 }

Copied: cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface4.java (from r880636, cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Binder.java)
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface4.java?p2=cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface4.java&p1=cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Binder.java&r1=880636&r2=882303&rev=882303&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-di/src/main/java/org/apache/cayenne/di/Binder.java (original)
+++ cayenne/sandbox/cayenne-di/src/test/java/org/apache/cayenne/di/mock/MockInterface4.java Thu Nov 19 21:27:38 2009
@@ -16,13 +16,9 @@
  *  specific language governing permissions and limitations
  *  under the License.
  ****************************************************************/
-package org.apache.cayenne.di;
+package org.apache.cayenne.di.mock;
 
-public interface Binder {
+public interface MockInterface4 {
 
-	/**
-	 * Starts a binding of a specific interface. Binding should continue using
-	 * returned BindingBuilder.
-	 */
-	<T> BindingBuilder<T> bind(Class<T> type);
+    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=882303&r1=882302&r2=882303&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 Thu Nov 19 21:27:38 2009
@@ -9,10 +9,12 @@
 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.MockImplementation4;
 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;
 
 public class MapInjectorTest extends TestCase {
 
@@ -109,6 +111,23 @@
         assertEquals("altered_MyName:XName", service.getAlteredName());
     }
 
+    public void testConstructorInjection() {
+
+        Module module = new Module() {
+
+            public void configure(Binder binder) {
+                binder.bind(MockInterface1.class).to(MockImplementation1.class);
+                binder.bind(MockInterface4.class).to(MockImplementation4.class);
+            }
+        };
+
+        MapInjector injector = new MapInjector(module);
+
+        MockInterface4 service = injector.getInstance(MockInterface4.class);
+        assertNotNull(service);
+        assertEquals("constructor_MyName", service.getName());
+    }
+
     public void testProviderBinding() {
         Module module = new Module() {
 
@@ -125,7 +144,7 @@
         assertNotNull(service);
         assertEquals("MyName", service.getName());
     }
-    
+
     public void testClassReBinding() {
 
         Module module = new Module() {