/* * Beanfabrics Framework Copyright (C) by Michael Karneim, beanfabrics.org * Use is subject to license terms. See license.txt. */ package org.beanfabrics.context; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.beanfabrics.log.Logger; import org.beanfabrics.log.LoggerFactory; /** * The {@link DefaultContext} is the default implementation of the * {@link Context} interface. * * @author Michael Karneim */ public class DefaultContext implements Context { private final static Logger LOG = LoggerFactory.getLogger(DefaultContext.class); private ContextListener parentListener; private final CopyOnWriteArrayList<ContextListener> contextListeners = new CopyOnWriteArrayList<ContextListener>();; private final List<ServiceEntry> serviceEntries = new LinkedList<ServiceEntry>(); private final List<ServiceEntry> unmodifiableServiceEntries; private final List<Context> parents; private final List<Context> unmodifiableParents; /** * Constructs a {@link DefaultContext}. */ public DefaultContext() { parents = new LinkedList<Context>(); parentListener = new ContextListener() { public void serviceRemoved(ServiceRemovedEvent evt) { removeServiceEntry(evt.getServiceEntry().getOrigin(), evt.getServiceEntry().getType()); } public void serviceAdded(ServiceAddedEvent evt) { ServiceEntry entry = evt.getServiceEntry(); addServiceEntry(new ServiceEntry(entry.getDistance() + 1, entry.getOrigin(), entry.getService(), entry.getType())); } public void parentRemoved(ParentRemovedEvent evt) { // nothing to do } public void parentAdded(ParentAddedEvent evt) { // nothing to do } }; unmodifiableParents = Collections.unmodifiableList(parents); unmodifiableServiceEntries = Collections.unmodifiableList(serviceEntries); } /** {@inheritDoc} */ public List<Context> getParents() { return unmodifiableParents; } /** {@inheritDoc} */ public synchronized void addParent(Context parent) { if (LOG.isDebugEnabled()) { LOG.debug("adding parent " + parent + " to " + this); } parent.addContextListener(this.parentListener); parents.add(parent); for (ServiceEntry entry : parent.getServiceEntries()) { addServiceEntry(new ServiceEntry(entry.getDistance() + 1, entry.getOrigin(), entry.getService(), entry.getType())); } this.fireParentAdded(parent); } /** {@inheritDoc} */ public synchronized void removeParent(Context parent) { if (LOG.isDebugEnabled()) { LOG.debug("removing parent " + parent + " from " + this); } parent.removeContextListener(this.parentListener); parents.remove(parent); for (ServiceEntry entry : parent.getServiceEntries()) { removeServiceEntry(entry.getOrigin(), entry.getType()); } this.fireParentRemoved(parent); } /** {@inheritDoc} */ public void addContextListener(ContextListener l) { if (l == null) { throw new IllegalArgumentException("l must not be null."); } contextListeners.add(l); } /** {@inheritDoc} */ public void removeContextListener(ContextListener l) { if (l == null) { throw new IllegalArgumentException("l must not be null."); } contextListeners.remove(l); } /** * Fires a parentAdded-event to each registered listener. * * @param parent the parent this context has been added to */ protected void fireParentAdded(Context parent) { ParentAddedEvent evt = new ParentAddedEvent(this, parent); for (ContextListener l : contextListeners) { l.parentAdded(evt); } } /** * Fires a parentRemoved-event to each registered listener. * * @param parent the parent this context has been removed from. */ protected void fireParentRemoved(Context parent) { ParentRemovedEvent evt = new ParentRemovedEvent(this, parent); for (ContextListener l : contextListeners) { l.parentRemoved(evt); } } /** * Fires a service-added-event to each registered listener. * * @param entry the service entry that has been added to this context */ protected void fireServiceAdded(ServiceEntry entry) { ServiceAddedEvent evt = new ServiceAddedEvent(this, entry); for (ContextListener l : contextListeners) { l.serviceAdded(evt); } } /** * Fires a service-removed-event to each registered listener. * * @param entry the service entry that has been removed from this context */ protected void fireServiceRemoved(ServiceEntry entry) { ServiceRemovedEvent evt = new ServiceRemovedEvent(this, entry); for (ContextListener l : contextListeners) { l.serviceRemoved(evt); } } /** {@inheritDoc} */ public <T> boolean addService(Class<? super T> type, T service) { ServiceEntry entry = new ServiceEntry(0, this, service, type); return addServiceEntry(entry); } /** * Adds the given entry to the list of services available in this context. * * @param entry * @return <code>true</code>, if the entry was added sucessfully */ private boolean addServiceEntry(ServiceEntry entry) { if (LOG.isDebugEnabled()) { LOG.debug("adding service '" + entry.getType().getName() + "' to " + this); } boolean result = serviceEntries.add(entry); fireServiceAdded(entry); return result; } /** * Removes the first service entry from this context that matches the given * type and origially was placed into the given "origin" context. * * @param origin * @param type * @return the removed entry */ protected ServiceEntry removeServiceEntry(Context origin, Class<?> type) { Iterator<ServiceEntry> it = serviceEntries.iterator(); while (it.hasNext()) { ServiceEntry entry = it.next(); if (entry.getType().equals(type) && entry.getOrigin() == origin) { if (LOG.isDebugEnabled()) { LOG.debug("removing service '" + entry.getType().getName() + "' from " + this); } it.remove(); fireServiceRemoved(entry); return entry; } } // no service with that type and origin found. // -> nothing to do return null; // throw new IllegalArgumentException("Service with type='" +type.getName() + "' not found"); } /** {@inheritDoc} */ @SuppressWarnings("unchecked") public <T> T removeService(Class<? extends T> type) { if (LOG.isDebugEnabled()) { LOG.debug("removeService(" + type.getName() + ")"); } ServiceEntry result = removeServiceEntry(this, type); if (result == null) { return null; } else { return (T)result.getService(); } } /** {@inheritDoc} */ public List<ServiceEntry> getServiceEntries() { return this.unmodifiableServiceEntries; } /** {@inheritDoc} */ public ServiceEntry findService(Class<?> type) { ServiceEntry result = null; for (ServiceEntry entry : serviceEntries) { if (type.equals(entry.getType())) { if (result == null || result.getDistance() > entry.getDistance()) { result = entry; } } } return result; } /** {@inheritDoc} */ @SuppressWarnings("unchecked") public <T> T getService(Class<? extends T> type) { ServiceEntry entry = findService(type); if (entry == null) { return null; } else { return (T)entry.getService(); } } }