/* * ============================================================================ * GNU Lesser General Public License * ============================================================================ * * Beanlet - JSE Application Container. * Copyright (C) 2006 Leon van Zantvoort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * Leon van Zantvoort * 243 Acalanes Drive #11 * Sunnyvale, CA 94086 * USA * * zantvoort@users.sourceforge.net * http://beanlet.org */ package org.beanlet.impl; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.beanlet.BeanletApplicationContext; import org.beanlet.BeanletFactory; import org.beanlet.BeanletReference; import org.beanlet.BeanletValidationException; import org.beanlet.Event; import org.beanlet.FactoryBeanlet; import org.beanlet.annotation.ConstructorElement; import org.beanlet.annotation.ConstructorParameterElement; import org.beanlet.annotation.Element; import org.beanlet.annotation.FieldElement; import org.beanlet.annotation.MethodElement; import org.beanlet.annotation.MethodParameterElement; import org.beanlet.plugin.BeanletConfiguration; import org.beanlet.common.ConstructorInjectionImpl; import org.beanlet.plugin.DependencyInjection; import org.beanlet.plugin.DependencyInjectionFactory; import org.beanlet.common.FactoryFieldInjectionImpl; import org.beanlet.common.FactoryMethodInjectionImpl; import org.beanlet.common.FieldInjectionImpl; import org.beanlet.plugin.Injectant; import org.beanlet.common.MethodInjectionImpl; import org.beanlet.common.event.OperationEventImpl; import org.jargo.ComponentContext; import org.jargo.ConstructorInjection; import org.jargo.InjectionFactory; import org.jargo.ProxyGenerator; import org.jargo.SetterInjection; /** * * @author Leon van Zantvoort */ public final class InjectionFactoryImpl<T> implements InjectionFactory<T> { private final BeanletConfiguration<T> configuration; private final String beanletName; private final DependencyInjectionFactory factory; public InjectionFactoryImpl(BeanletConfiguration<T> configuration, DependencyInjectionFactory factory) { this.configuration = configuration; this.beanletName = configuration.getComponentName(); this.factory = factory; } public ConstructorInjection<T> getConstructorInjection(Class<?> cls, ComponentContext<T> ctx) { final ConstructorInjection<T> injection; if (configuration.getFactory() != null) { List<DependencyInjection> constructorInjections = factory. getConstructorDependencyInjections(cls); if (!constructorInjections.isEmpty()) { throw new BeanletValidationException(beanletName, "Beanlets configured to be created by another beanlet " + "MUST NOT specify any constructor injection elements: " + toElementString(constructorInjections) + "."); } BeanletApplicationContext bctx = BeanletApplicationContext.instance(); Class<?> factoryType = bctx.getBeanletFactory( configuration.getFactory()).getBeanletMetaData().getType(); final List<DependencyInjection> injections; if (configuration.getFactoryMethod() == null) { injections = Collections.emptyList(); } else { injections = factory.getFactoryDependencyInjections( factoryType, configuration.getFactoryMethod()); } injection = getFactoryInjection(injections, ctx); } else { List<DependencyInjection> injections = factory. getConstructorDependencyInjections(cls); if (injections.isEmpty()) { injection = null; } else { injection = getConstructorInjection(injections, ctx); } } return injection; } public List<SetterInjection> getSetterInjections( Class<?> cls, ComponentContext<T> ctx) { List<DependencyInjection> injections = factory. getSetterDependencyInjections(cls); return getSetterInjections(injections, ctx); } private ConstructorInjection<T> getConstructorInjection( List<DependencyInjection> injections, ComponentContext<T> ctx) { final ConstructorInjection<T> injection; DependencyInjection first = injections.get(0); switch (first.getTarget().getElementType()) { case CONSTRUCTOR: if (injections.size() > 1) { throw new BeanletValidationException(beanletName, "Beanlets configured to be created by construction " + "injection MAY specify at most one element: " + toElementString(injections) + "."); } Constructor c = ((ConstructorElement) first.getTarget()). getConstructor(); if (c.getParameterTypes().length == 0) { Injectant<?> injectant = first.getInjectant(ctx); // Always request the injectant. if (injectant != null) { assert injectant.isStatic(); assert injectant.getObject() == null; injection = new ConstructorInjectionImpl<T>(c); } else { injection = null; } } else if (c.getParameterTypes().length == 1) { Injectant<?> injectant = first.getInjectant(ctx); if (injectant != null) { injection = new ConstructorInjectionImpl<T>(c, injectant.getObject()); } else { injection = null; } } else { throw new BeanletValidationException(beanletName, "Constructor specifies more than one parameter: '" + c + "'."); } break; case METHOD: if (injections.size() > 1) { throw new BeanletValidationException(beanletName, "Found multiple constructor injections or factory elements: " + toElementString(injections) + "."); } Method m = ((MethodElement) first.getTarget()).getMethod(); if (m.getParameterTypes().length == 0) { Injectant<?> injectant = first.getInjectant(ctx); // Always request the injectant. if (injectant != null) { assert injectant.isStatic(); assert injectant.getObject() == null; injection = new FactoryMethodInjectionImpl<T>(m); } else { injection = null; } } else if (m.getParameterTypes().length == 1) { Injectant<?> injectant = first.getInjectant(ctx); if (injectant != null) { injection = new FactoryMethodInjectionImpl<T>(m, injectant.getObject()); } else { injection = null; } } else { throw new BeanletValidationException(beanletName, "Factory method specifies more than one parameter: '" + m + "'."); } break; case PARAMETER: if (first.getTarget() instanceof ConstructorParameterElement) { Constructor pc = ((ConstructorParameterElement) first. getTarget()).getConstructor(); if (injections.size() != pc.getParameterTypes().length) { throw new BeanletValidationException(beanletName, "Incorrect count of constructor injections or factory elements: '" + pc + "'."); } Object[] args = new Object[injections.size()]; for (DependencyInjection i : injections) { Element e = i.getTarget(); if (!(e instanceof ConstructorParameterElement)) { throw new BeanletValidationException(beanletName, "Mixture of injectable elements found: '" + first.getTarget() + "', '" + e + "'."); } if (!((ConstructorParameterElement) e).getConstructor().equals(pc)) { throw new BeanletValidationException(beanletName, "Mixture of injectable elements found: '" + first.getTarget() + "', '" + e + "'."); } int param = ((ConstructorParameterElement) e).getParameter(); Injectant<?> injectant = i.getInjectant(ctx); args[param] = injectant == null ? null : injectant.getObject(); } injection = new ConstructorInjectionImpl<T>(pc, args); } else { Method mc = ((MethodParameterElement) first.getTarget()). getMethod(); if (injections.size() != mc.getParameterTypes().length) { throw new BeanletValidationException(beanletName, "Incorrect count of method injection or factory elements: '" + mc + "'."); } Object[] args = new Object[injections.size()]; for (DependencyInjection i : injections) { Element e = i.getTarget(); if (!(e instanceof MethodParameterElement)) { throw new BeanletValidationException(beanletName, "Mixture of injectable elements found: '" + first.getTarget() + "', '" + e + "'."); } if (!((MethodParameterElement) e).getMethod().equals(mc)) { throw new BeanletValidationException(beanletName, "Mixture of injectable elements found: '" + first.getTarget() + "', '" + e + "'."); } int param = ((MethodParameterElement) e).getParameter(); Injectant<?> injectant = i.getInjectant(ctx); args[param] = injectant == null ? null : injectant.getObject(); } injection = new FactoryMethodInjectionImpl<T>(mc, args); } break; case FIELD: if (injections.size() > 1) { throw new BeanletValidationException(beanletName, "Found multiple constructor injections or factory elements: " + toElementString(injections) + "."); } Field f = ((FieldElement) first.getTarget()).getField(); Injectant<?> injectant = first.getInjectant(ctx); // Always request the injectant. if (injectant != null) { assert injectant.isStatic(); assert injectant.getObject() == null; injection = new FactoryFieldInjectionImpl<T>(f); } else { injection = null; } break; default: assert false : first.getTarget(); injection = null; } return injection; } private ConstructorInjection<T> getFactoryInjection( final List<DependencyInjection> injections, final ComponentContext<T> ctx) { assert configuration.getFactory() != null; return new ConstructorInjection<T>() { public Object inject() { final Object object; if (configuration.getFactoryMethod() == null) { assert injections.isEmpty(); object = BeanletApplicationContext.instance(). getBeanlet(configuration.getFactory()); } else { final Event event; if (injections.isEmpty()) { event = new OperationEventImpl(configuration. getFactoryMethod(), new Class[0]); } else { DependencyInjection first = injections.get(0); Element element = first.getTarget(); if (element instanceof MethodElement) { if (injections.size() > 1) { throw new BeanletValidationException(beanletName, "Found multiple injection elements: " + toElementString(injections) + "."); } Method m = ((MethodElement) element).getMethod(); if (m.getParameterTypes().length == 0) { event = new OperationEventImpl(configuration. getFactoryMethod(), m.getParameterTypes()); } else if (m.getParameterTypes().length == 1) { Injectant<?> i = first.getInjectant(ctx); event = new OperationEventImpl(configuration. getFactoryMethod(), m.getParameterTypes(), i == null ? null : i.getObject()); } else { throw new BeanletValidationException(beanletName, "Factory method specifies more than one parameter: '" + m + "'."); } } else if (element instanceof MethodParameterElement) { Method m = ((MethodParameterElement) element).getMethod(); if (injections.size() != m.getParameterTypes().length) { throw new BeanletValidationException(beanletName, "Not all factory method parameters support injection: '" + m + "'."); } Object[] args = new Object[injections.size()]; for (DependencyInjection i : injections) { Element e = i.getTarget(); if (!(e instanceof MethodParameterElement)) { throw new BeanletValidationException(beanletName, "Mixture of injectable elements found: '" + first.getTarget() + "', '" + e + "'."); } if (!((MethodParameterElement) e).getMethod().equals(m)) { throw new BeanletValidationException(beanletName, "Mixture of injectable elements found: '" + first.getTarget() + "', '" + e + "'."); } int param = ((MethodParameterElement) e).getParameter(); Injectant<?> injectant = i.getInjectant(ctx); args[param] = injectant == null ? null : injectant.getObject(); } event = new OperationEventImpl(configuration. getFactoryMethod(), m.getParameterTypes(), args); } else { assert false; event = null; } } BeanletFactory factory = BeanletApplicationContext. instance().getBeanletFactory(configuration.getFactory()); BeanletReference reference = factory.create(); if (!reference.isExecutable(event)) { throw new BeanletValidationException( beanletName, "Factory method not found: '" + event + "'."); } else { Object beanlet = reference.execute(event); while (beanlet instanceof FactoryBeanlet) { beanlet = ((FactoryBeanlet) beanlet).getObject(); } object = beanlet; } } return object; } public T inject(ProxyGenerator<T> proxyGenerator) throws UnsupportedOperationException { throw new UnsupportedOperationException("Proxy generation not " + "supported for objects retrieved from factory beanlet. " + "Factory beanlet: '" + configuration.getFactory() + "'."); } }; } private List<SetterInjection> getSetterInjections( List<DependencyInjection> injections, ComponentContext<T> ctx) { List<SetterInjection> list = new ArrayList<SetterInjection>(); Set<Element> dupes = new HashSet<Element>(); Map<Member, Map<Element, DependencyInjection>> memberMap = new HashMap<Member, Map<Element, DependencyInjection>>(); for (DependencyInjection injection : injections) { Element element = injection.getTarget(); if (!dupes.add(element)) { throw new BeanletValidationException(beanletName, "Element is target for multiple injections: '" + element + "'."); } final Member member; if (element instanceof FieldElement) { member = ((FieldElement) element).getField(); assert !memberMap.containsKey(member); } else if (element instanceof MethodElement) { member = ((MethodElement) element).getMethod(); assert !memberMap.containsKey(member); } else if (element instanceof MethodParameterElement) { member = ((MethodParameterElement) element).getMethod(); } else { assert false : element; continue; } Map<Element, DependencyInjection> m = memberMap.get(member); if (m == null) { m = new HashMap<Element, DependencyInjection>(); memberMap.put(member, m); } m.put(element, injection); } for (Map.Entry<Member, Map<Element, DependencyInjection>> entry : memberMap.entrySet()) { Member member = entry.getKey(); Map<Element, DependencyInjection> map = entry.getValue(); assert map != null; if (member instanceof Field) { Field field = (Field) member; assert map.size() == 1; DependencyInjection injection = map.values().iterator().next(); Injectant<?> injectant = injection.getInjectant(ctx); if (injectant != null) { list.add(new FieldInjectionImpl(field, injectant.getObject())); } } else if (member instanceof Method) { Method method = (Method) member; assert !map.isEmpty(); if (map.size() == 1) { DependencyInjection injection = map.values().iterator().next(); if (method.getParameterTypes().length == 1) { Injectant<?> injectant = injection.getInjectant(ctx); if (injectant != null) { list.add(new MethodInjectionImpl(method, injectant.getObject())); } } else { throw new BeanletValidationException(beanletName, "Method does not specify exactly one parameter: '" + method + "'."); } } else { if (map.size() != method.getParameterTypes().length) { throw new BeanletValidationException(beanletName, "Not all method parameters support injection: '" + method + "'."); } Object[] args = new Object[map.size()]; for (Map.Entry<Element, DependencyInjection> e : map.entrySet()) { int param = ((MethodParameterElement) e.getKey()).getParameter(); Injectant<?> injectant = e.getValue().getInjectant(ctx); args[param] = injectant == null ? null : injectant.getObject(); } list.add(new MethodInjectionImpl(method, args)); } } else { assert false : member; } } return Collections.unmodifiableList(list); } private String toElementString(List<DependencyInjection> injections) { Set<Element> elements = new HashSet<Element>(); for (DependencyInjection injection : injections) { elements.add(injection.getTarget()); } return String.valueOf(elements); } }