(no commit message)
authorErik Brakkee <erik@brakkee.org>
Sat, 31 Jul 2010 14:26:39 +0000 (14:26 +0000)
committerErik Brakkee <erik@brakkee.org>
Sat, 31 Jul 2010 14:26:39 +0000 (14:26 +0000)
test/enterprise/src/main/java/org/wamblee/test/inject/Binding.java [new file with mode: 0644]
test/enterprise/src/main/java/org/wamblee/test/inject/JavaEETestInjector.java
test/enterprise/src/main/java/org/wamblee/test/inject/JavaEETestInjectorFactory.java
test/enterprise/src/test/java/org/wamblee/test/inject/JavaEETestInjectorFactoryTest.java

diff --git a/test/enterprise/src/main/java/org/wamblee/test/inject/Binding.java b/test/enterprise/src/main/java/org/wamblee/test/inject/Binding.java
new file mode 100644 (file)
index 0000000..14e620a
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2005-2010 the original author or authors.
+ * 
+ * Licensed 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.wamblee.test.inject;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.wamblee.reflection.Accessor;
+import org.wamblee.reflection.AnnotationUtils;
+
+/**
+ * This class represents an injection binding. It provides injection of a defined object (typically mock or stub)
+ * into other objects. The binding is defined by the required annotation that must be on the field, the field type, 
+ * and the object to be injected. 
+ * 
+ * @author Erik Brakkee
+ *
+ * @param <T>
+ */
+public class Binding<T> {
+    private Class<T> clazz;
+    private Class<? extends Annotation> annotation;
+    private Object value;
+    private Map<Class, List<Accessor>> accessorCache;
+
+    /**
+     * Constructs the binding. 
+     * @param aClass Required type of the field injected into. 
+     * @param aAnnotation Annotation that must be present on the field.
+     * @param aValue Value of the annotation. 
+     */
+    public Binding(Class<T> aClass, Class<? extends Annotation> aAnnotation,
+        Object aValue) {
+        clazz = aClass;
+        annotation = aAnnotation;
+        value = aValue;
+        accessorCache = new ConcurrentHashMap<Class, List<Accessor>>();
+    }
+
+    public void inject(Object aObject) {
+        List<Accessor> accessors = getAccessors(aObject);
+        for (Accessor accessor : accessors) {
+            if (clazz.isAssignableFrom(accessor.getType())) {
+                accessor.set(aObject, value);
+            }
+        }
+    }
+
+    private List<Accessor> getAccessors(Object aObject) {
+        Class type = aObject.getClass();
+        List<Accessor> accessors = accessorCache.get(type);
+        if (accessors == null) {
+            accessors = AnnotationUtils.analyse(aObject.getClass(), annotation);
+            accessorCache.put(type, accessors);
+        }
+        return accessors;
+    }
+}
index 9ea0517506fea7357937117ac1bb0fe571ef253e..8fe52ac10203de9030c599709c430d8b4fd85584 100644 (file)
  */
 package org.wamblee.test.inject;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
@@ -23,37 +27,98 @@ import javax.persistence.PersistenceContext;
 import org.wamblee.inject.Injector;
 import org.wamblee.reflection.Accessor;
 import org.wamblee.reflection.AnnotationUtils;
+import org.wamblee.reflection.ObjectTraversal;
+import org.wamblee.reflection.ObjectTraversal.ObjectVisitor;
 
 /**
- * Injector that performs additional injection on top of the injections that are standard
- * available (e.g. entity manager in Java SE environment).
+ * <p>
+ * Injector that performs additional injection on top of the injections that are
+ * standard available (e.g. entity manager in Java SE environment).
+ * </p>
+ * 
+ * <p>
+ * It works by first delegating to the default injector (typically CDI). Afterwards it traverses the
+ * object graph of the injected object and performs custom injection of test objects as specified by the
+ * {@link Binding} class. This approach makes sure that test dependencies also find their way into 
+ * objects that were created by the injection framework. 
+ * </p>
  * 
  * @author Erik Brakkee
  */
 public class JavaEETestInjector implements Injector {
 
-    private EntityManager entityManager;
+    private class InjectionVisitor implements ObjectVisitor {
+        @Override
+        public boolean mustVisit(Class aClass) {
+            if (EntityManager.class.isAssignableFrom(aClass)) { 
+                return false;
+            }
+            return true;
+        }
+        @Override
+        public boolean mustVisit(Field aField) {
+            // just process any field with annotations
+            return aField.getAnnotations().length > 0;  
+        }
+        @Override
+        public boolean mustVisit(Method aMethod) {
+            return false;
+        }
+        @Override
+        public boolean visitArray(Object aArray) {
+            return true;
+        }
+        @Override
+        public boolean visitList(List aObject) {
+            return true;
+        }
+        @Override
+        public boolean visitMap(Map aObject) {
+            return true;
+        }
+        @Override
+        public boolean visitPlainObject(Object aObject) {
+            performTestInjections(aObject);
+            return true;
+        }
+        @Override
+        public boolean visitSet(Set aSet) {
+            return true;
+        }
+    }
+
+    private List<Binding> bindings;
     private Injector delegate;
-    private List<Accessor> accessors;
 
     /**
-     * Constructs the injector. 
-     * @param aClass Class to inject for. 
-     * @param aEntityManager Entity manager. 
-     * @param aDelegate Injecto to delegate to to perform the standard injections. 
+     * Constructs the injector.
+     * 
+     * @param aClass
+     *            Class to inject for.
+     * @param aBindings
+     *            Binding of an object value.
+     * @param aDelegate
+     *            Injecto to delegate to to perform the standard injections.
      */
-    public JavaEETestInjector(Class aClass, EntityManager aEntityManager,
+    public JavaEETestInjector(Class aClass, List<Binding> aBindings,
         Injector aDelegate) {
-        entityManager = aEntityManager;
+        bindings = aBindings;
         delegate = aDelegate;
-        accessors = AnnotationUtils.analyse(aClass, PersistenceContext.class);
     }
 
     @Override
     public void inject(Object aComponent) {
+        // basic injection
         delegate.inject(aComponent);
-        for (Accessor accessor: accessors) {
-            accessor.set(aComponent, entityManager);
+        
+        // Now perform test injections.
+        ObjectTraversal traversal = new ObjectTraversal(new InjectionVisitor());
+        traversal.accept(aComponent);
+    }
+
+    private void performTestInjections(Object aComponent) {
+        for (Binding binding : bindings) {
+            binding.inject(aComponent);
         }
     }
 
index c2228b987a9de41c6c2f9ac342df1e6eb1bed5ac..fdfb84a782ba71993d92788075515677590274ce 100644 (file)
  */
 package org.wamblee.test.inject;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
 
 import org.wamblee.inject.Injector;
-import org.wamblee.inject.InjectorFactory;
 import org.wamblee.inject.InjectorBuilder;
+import org.wamblee.inject.InjectorFactory;
 import org.wamblee.test.persistence.JpaBuilder;
 
 /**
@@ -46,26 +50,46 @@ import org.wamblee.test.persistence.JpaBuilder;
  */
 public class JavaEETestInjectorFactory implements InjectorFactory {
 
-    private EntityManager entityManager;
+    private List<Binding> bindings;
     private InjectorFactory delegate;
     
     /**
      * Constructs the factory. 
-     * @param aEntityManager Contextual entity manager to inject. 
      * @param aInjectorFactory Injector factory to delegate to. 
      */
-    public JavaEETestInjectorFactory(EntityManager aEntityManager, InjectorFactory aInjectorFactory) { 
-        entityManager = aEntityManager; 
+    public JavaEETestInjectorFactory(InjectorFactory aInjectorFactory) {
+        bindings = new ArrayList<Binding>();        
         delegate = aInjectorFactory;
     }
 
+    /**
+     * Adds default entity manager binding. Any field annotated with @PersistenceContext and of type 
+     * entity manager will get injected.
+     * @param aEntityManager Entitymanager object to inject.
+     * @return Factory to allow chaining.
+     */
+    public JavaEETestInjectorFactory addEntityManagerBinding(EntityManager aEntityManager) {
+        Binding em = new Binding(EntityManager.class, PersistenceContext.class, aEntityManager);
+        addBinding(em);
+        return this;
+    }
+    
+    /**
+     * Adds another custom injection binding. 
+     * @param aBinding Injection binding to use. 
+     * @return the factoryto allow chaining.
+     */
+    public JavaEETestInjectorFactory addBinding(Binding aBinding) { 
+        bindings.add(aBinding);
+        return this; 
+    }
+
     /**
      * Constructs the factory with the default injector factory obtained from 
      * {@link InjectorBuilder#getInjector()}. 
-     * @param aEntityManager Contextual entity manager to inject. 
      */
-    public JavaEETestInjectorFactory(EntityManager aEntityManager) {
-        this(aEntityManager,  getDefaultInjectorFactory());
+    public JavaEETestInjectorFactory() {
+        this(getDefaultInjectorFactory());
     }
 
     private static InjectorFactory getDefaultInjectorFactory() {
@@ -75,7 +99,7 @@ public class JavaEETestInjectorFactory implements InjectorFactory {
 
     @Override
     public Injector create(Class aClass) {
-        return new JavaEETestInjector(aClass, entityManager, delegate
+        return new JavaEETestInjector(aClass, bindings, delegate
             .create(aClass));
     }
 }
index 682ea88256adb646ad35e7fe9f25809c47d47283..4ad8cad8a0984ffe8181b64bca68adbab94bee91 100644 (file)
+/*
+ * Copyright 2005-2010 the original author or authors.
+ * 
+ * Licensed 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.wamblee.test.inject;
 
+import static junit.framework.Assert.*;
+import static org.mockito.Mockito.*;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Level;
+
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
 
+import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
 import org.wamblee.inject.InjectorBuilder;
-
-import static org.mockito.Mockito.*;
-import static junit.framework.TestCase.*;
+import org.wamblee.reflection.ObjectTraversal;
 
 public class JavaEETestInjectorFactoryTest {
-    
-    @PersistenceContext
-    private EntityManager entityManager; 
-    
-    private EntityManager entityManager2; 
-   
-    @PersistenceContext
-    public EntityManager getEntityManager() { 
-        return entityManager2;
+
+    @Before
+    public void setUp() {
+        ObjectTraversal.LOGGER.setLevel(Level.FINEST);
+        ConsoleHandler handler = new ConsoleHandler();
+        handler.setLevel(Level.FINEST);
+        ObjectTraversal.LOGGER.addHandler(handler);
     }
-    
-    public void setEntityManager(EntityManager aEm) { 
-        entityManager2 = aEm;
+
+    public static final class X {
+
+        @PersistenceContext
+        private EntityManager entityManager;
+
+        private EntityManager entityManager2;
+
+        @PersistenceContext
+        public EntityManager getEntityManager() {
+            return entityManager2;
+        }
+
+        public void setEntityManager(EntityManager aEm) {
+            entityManager2 = aEm;
+        }
     }
 
     @Test
-    public void testXyz() { 
+    public void testXyz() {
         EntityManager em = mock(EntityManager.class);
-        InjectorBuilder.setInjectorFactory(new JavaEETestInjectorFactory(em));
-       
-        assertNull(entityManager);
-        assertNull(entityManager2);
-        InjectorBuilder.getInjector().inject(this);
-        
+        InjectorBuilder.setInjectorFactory(new JavaEETestInjectorFactory().addEntityManagerBinding(em));
+
+        X x = new X();
+
+        assertNull(x.entityManager);
+        assertNull(x.entityManager2);
+        InjectorBuilder.getInjector().inject(x);
+
         assertNotNull(em);
-        assertSame(em, entityManager); 
-        assertSame(em, entityManager2);
+        assertSame(em, x.entityManager);
+        assertSame(em, x.entityManager2);
+
     }
-    
-    public static class X { 
+
+    public static class Y {
         @PersistenceContext
-        Integer wrongType; 
+        Integer wrongType;
     }
-    
-    @Test(expected = IllegalArgumentException.class)
-    public void testWrongType() { 
+
+    @Test
+    public void testWrongType() {
         EntityManager em = mock(EntityManager.class);
-        InjectorBuilder.setInjectorFactory(new JavaEETestInjectorFactory(em));
-        X x= new X(); 
-        InjectorBuilder.getInjector().inject(x);
+        InjectorBuilder.setInjectorFactory(new JavaEETestInjectorFactory().addEntityManagerBinding(em));
+        Y y = new Y();
+        InjectorBuilder.getInjector().inject(y);
+        assertNull(y.wrongType);
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target( { ElementType.FIELD, ElementType.METHOD })
+    public static @interface MyAnnotation {
+
     }
+    
+    public static class Z {
+        @MyAnnotation
+        private int x;
+        
+        // any annotation will force traversal.
+        @Mock
+        private Z z; 
+
+    }
+
+    @Test
+    public void testCustomInjection() {
+        JavaEETestInjectorFactory factory = new JavaEETestInjectorFactory();
+        factory.addBinding(new Binding(int.class, MyAnnotation.class, 100));
+        InjectorBuilder.setInjectorFactory(factory);
+
+        Z z = new Z();
+        z.z = new Z();
+        assertEquals(0, z.x);
+        InjectorBuilder.getInjector().inject(z);
+        assertEquals(100, z.x);
+        assertEquals(100, z.z.x);
+    }
+    
+    
 }