/* * Beanfabrics Framework Copyright (C) by Michael Karneim, beanfabrics.org * Use is subject to license terms. See license.txt. */ // TODO javadoc - remove this comment only when the class and all non-public // methods and fields are documented package org.beanfabrics.support; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; import java.util.HashMap; import java.util.Map; import org.beanfabrics.context.ContextListener; import org.beanfabrics.context.ContextOwner; import org.beanfabrics.context.ParentAddedEvent; import org.beanfabrics.context.ParentRemovedEvent; import org.beanfabrics.context.ServiceAddedEvent; import org.beanfabrics.context.ServiceEntry; import org.beanfabrics.context.ServiceRemovedEvent; import org.beanfabrics.log.Logger; import org.beanfabrics.log.LoggerFactory; import org.beanfabrics.util.ReflectionUtil; /** * @author Michael Karneim */ public class ServiceSupport implements Support { private final static Logger LOG = LoggerFactory.getLogger(ServiceSupport.class); public static ServiceSupport get(ContextOwner owner) { if (!(owner instanceof Supportable)) { throw new IllegalArgumentException("owner must implement " + Supportable.class.getName()); } Supportable s = (Supportable)owner; ServiceSupport support = s.getSupportMap().get(ServiceSupport.class); if (support == null) { support = new ServiceSupport(owner); s.getSupportMap().put(ServiceSupport.class, support); } return support; } private final ContextOwner owner; private Map<Member, ServiceMemberSupport> map = new HashMap<Member, ServiceMemberSupport>(); public ServiceSupport(ContextOwner owner) { if (owner == null) { throw new IllegalArgumentException("owner==null"); } this.owner = owner; } public void setup(Method method) { if (map.containsKey(method) == false) { ServiceMethodSupport support = new ServiceMethodSupport(owner, method); map.put(method, support); } } public void setup(Field field) { if (map.containsKey(field) == false) { ServiceFieldSupport support = new ServiceFieldSupport(owner, field); map.put(field, support); } } private static class ServiceMemberSupport implements ContextListener { protected final ContextOwner owner; protected Class type; private ServiceEntry serviceEntry; public ServiceMemberSupport(ContextOwner owner) { super(); this.owner = owner; (this.owner).getContext().addContextListener(this); } protected void findServiceEntry() { ServiceEntry found = this.owner.getContext().findService(type); if (found != null) { this.setServiceEntry(found); } } public void serviceRemoved(ServiceRemovedEvent evt) { if (serviceEntry != null && serviceEntry == evt.getServiceEntry()) { ServiceEntry nextService = this.owner.getContext().findService(type); this.setServiceEntry(nextService); } } public void serviceAdded(ServiceAddedEvent evt) { if (evt.getServiceEntry().getType().equals(type)) { if (serviceEntry == null || evt.getServiceEntry().getDistance() < serviceEntry.getDistance()) { setServiceEntry(evt.getServiceEntry()); } } } public void parentRemoved(ParentRemovedEvent evt) { } public void parentAdded(ParentAddedEvent evt) { } protected void setServiceEntry(ServiceEntry entry) { if (LOG.isDebugEnabled()) { if (entry == null) { LOG.debug("unsetting service for type " + type.getName()); } else { LOG.debug("setting service for type " + type.getName() + " with entry " + entry.getType().getName()); } } this.serviceEntry = entry; } } private static class ServiceFieldSupport extends ServiceMemberSupport { private final Field annotatedField; public ServiceFieldSupport(ContextOwner owner, Field annotatedField) { super(owner); this.annotatedField = annotatedField; Service anno = annotatedField.getAnnotation(Service.class); if (Object.class.equals(anno.value())) { this.type = annotatedField.getType(); } else { // TODO (mk) ensure that anno.value() is (sub)type of "type" this.type = anno.value(); } findServiceEntry(); } @Override protected void setServiceEntry(ServiceEntry entry) { super.setServiceEntry(entry); updateAnnotatedField(entry == null ? null : entry.getService()); } private void updateAnnotatedField(Object service) { try { ReflectionUtil.setFieldValue(owner, annotatedField, service); } catch (IllegalArgumentException e) { throw e; } catch (IllegalAccessException e) { throw new UndeclaredThrowableException(e); } } } private static class ServiceMethodSupport extends ServiceMemberSupport { private final Method annotatedMethod; private ServiceMethodSupport(ContextOwner owner, Method annotatedMethod) { super(owner); // TODO (mk) ensure that method is a setter-Method and has exactly one parameter this.annotatedMethod = annotatedMethod; Service anno = annotatedMethod.getAnnotation(Service.class); if (Object.class.equals(anno.value())) { this.type = annotatedMethod.getParameterTypes()[0]; } else { // TODO (mk) ensure that anno.value() is (sub)type of "type" this.type = anno.value(); } findServiceEntry(); } @Override protected void setServiceEntry(ServiceEntry entry) { super.setServiceEntry(entry); callAnnotatedMethod(entry == null ? null : entry.getService()); } private void callAnnotatedMethod(Object service) { try { ReflectionUtil.invokeMethod(owner, annotatedMethod, new Object[] { service }); } catch (IllegalArgumentException e) { throw e; } catch (IllegalAccessException e) { throw new UndeclaredThrowableException(e, annotatedMethod.getName()); } catch (InvocationTargetException e) { throw new UndeclaredThrowableException(e, annotatedMethod.getName()); } } } }