--- /dev/null
+/*
+ * 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;
+ }
+}
*/
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;
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);
}
}
*/
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;
/**
*/
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() {
@Override
public Injector create(Class aClass) {
- return new JavaEETestInjector(aClass, entityManager, delegate
+ return new JavaEETestInjector(aClass, bindings, delegate
.create(aClass));
}
}
+/*
+ * 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);
+ }
+
+
}