From: Erik Brakkee Date: Sat, 31 Jul 2010 14:26:39 +0000 (+0000) Subject: (no commit message) X-Git-Tag: wamblee-utils-0.7~126 X-Git-Url: http://wamblee.org/gitweb/?a=commitdiff_plain;h=ca6cd90ac2aeca2f61320ac15c2403dde120f3de;p=utils --- 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 index 00000000..14e620a3 --- /dev/null +++ b/test/enterprise/src/main/java/org/wamblee/test/inject/Binding.java @@ -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 + */ +public class Binding { + private Class clazz; + private Class annotation; + private Object value; + private Map> 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 aClass, Class aAnnotation, + Object aValue) { + clazz = aClass; + annotation = aAnnotation; + value = aValue; + accessorCache = new ConcurrentHashMap>(); + } + + public void inject(Object aObject) { + List accessors = getAccessors(aObject); + for (Accessor accessor : accessors) { + if (clazz.isAssignableFrom(accessor.getType())) { + accessor.set(aObject, value); + } + } + } + + private List getAccessors(Object aObject) { + Class type = aObject.getClass(); + List accessors = accessorCache.get(type); + if (accessors == null) { + accessors = AnnotationUtils.analyse(aObject.getClass(), annotation); + accessorCache.put(type, accessors); + } + return accessors; + } +} diff --git a/test/enterprise/src/main/java/org/wamblee/test/inject/JavaEETestInjector.java b/test/enterprise/src/main/java/org/wamblee/test/inject/JavaEETestInjector.java index 9ea05175..8fe52ac1 100644 --- a/test/enterprise/src/main/java/org/wamblee/test/inject/JavaEETestInjector.java +++ b/test/enterprise/src/main/java/org/wamblee/test/inject/JavaEETestInjector.java @@ -15,7 +15,11 @@ */ 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). + *

+ * Injector that performs additional injection on top of the injections that are + * standard available (e.g. entity manager in Java SE environment). + *

+ * + *

+ * 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. + *

* * @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 bindings; private Injector delegate; - private List 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 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); } } diff --git a/test/enterprise/src/main/java/org/wamblee/test/inject/JavaEETestInjectorFactory.java b/test/enterprise/src/main/java/org/wamblee/test/inject/JavaEETestInjectorFactory.java index c2228b98..fdfb84a7 100644 --- a/test/enterprise/src/main/java/org/wamblee/test/inject/JavaEETestInjectorFactory.java +++ b/test/enterprise/src/main/java/org/wamblee/test/inject/JavaEETestInjectorFactory.java @@ -15,11 +15,15 @@ */ 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 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(); 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)); } } diff --git a/test/enterprise/src/test/java/org/wamblee/test/inject/JavaEETestInjectorFactoryTest.java b/test/enterprise/src/test/java/org/wamblee/test/inject/JavaEETestInjectorFactoryTest.java index 682ea882..4ad8cad8 100644 --- a/test/enterprise/src/test/java/org/wamblee/test/inject/JavaEETestInjectorFactoryTest.java +++ b/test/enterprise/src/test/java/org/wamblee/test/inject/JavaEETestInjectorFactoryTest.java @@ -1,54 +1,126 @@ +/* + * 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); + } + + }