/* * Copyright (C) 2013 eXo Platform SAS. * * This 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 software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.container; import org.exoplatform.container.ConcurrentContainer.CreationalContextComponentAdapter; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; import javax.enterprise.context.spi.CreationalContext; /** * @author <a href="mailto:nfilotto@exoplatform.com">Nicolas Filotto</a> * @version $Id$ * */ public class ComponentTaskContext { /** * A {@link LinkedHashSet} representing the dependency stack */ private final LinkedHashSet<ComponentTaskContextEntry> dependencies; /** * The last dependency that has been added to the context */ private final ComponentTaskContextEntry lastDependency; /** * The last task type known */ private final ComponentTaskType lastTaskType; /** * Context used to keep in memory the components that are currently being created. * This context is used to prevent cyclic resolution due to component plugins. */ private Map<Object, CreationalContextComponentAdapter<?>> depResolutionCtx; private ComponentTaskContext(LinkedHashSet<ComponentTaskContextEntry> dependencies, Map<Object, CreationalContextComponentAdapter<?>> depResolutionCtx, ComponentTaskContextEntry lastDependency, ComponentTaskType lastTaskType) { this.dependencies = dependencies; this.depResolutionCtx = depResolutionCtx; this.lastDependency = lastDependency; this.lastTaskType = lastTaskType; } /** * Default constructor */ public ComponentTaskContext(Object componentKey, ComponentTaskType type) { LinkedHashSet<ComponentTaskContextEntry> dependencies = new LinkedHashSet<ComponentTaskContextEntry>(); ComponentTaskContextEntry entry = new ComponentTaskContextEntry(componentKey, type); dependencies.add(entry); this.dependencies = dependencies; this.lastDependency = entry; this.lastTaskType = type; } /** * Defines explicitly the last task type known */ public ComponentTaskContext setLastTaskType(ComponentTaskType lastTaskType) { return new ComponentTaskContext(dependencies, depResolutionCtx == null ? null : new HashMap<Object, ConcurrentContainer.CreationalContextComponentAdapter<?>>(depResolutionCtx), lastDependency, lastTaskType); } /** * This method will call {@link #addToContext(Object, ComponentTaskType)} with the <code>lastTaskType</code> as type */ public ComponentTaskContext addToContext(Object componentKey) throws CyclicDependencyException { return addToContext(componentKey, lastTaskType); } /** * Creates a new {@link ComponentTaskContext} based on the given dependency and the * already registered ones. If the dependency has already been registered * a {@link CyclicDependencyException} will be thrown. */ public ComponentTaskContext addToContext(Object componentKey, ComponentTaskType type) throws CyclicDependencyException { ComponentTaskContextEntry entry = new ComponentTaskContextEntry(componentKey, type); checkDependency(entry); LinkedHashSet<ComponentTaskContextEntry> dependencies = new LinkedHashSet<ComponentTaskContextEntry>(this.dependencies); dependencies.add(entry); return new ComponentTaskContext(dependencies, depResolutionCtx == null ? null : new HashMap<Object, ConcurrentContainer.CreationalContextComponentAdapter<?>>(depResolutionCtx), entry, type); } /** * Checks if the given dependency has already been defined, if so a {@link CyclicDependencyException} * will be thrown. */ public void checkDependency(Object componentKey, ComponentTaskType type) throws CyclicDependencyException { ComponentTaskContextEntry entry = new ComponentTaskContextEntry(componentKey, type); checkDependency(entry); } /** * Indicates whether the provided componentKey is the last dependency that has been added to the context. * @return <code>true</code> if the dependency is the last, <code>false</code> otherwise. */ public boolean isLast(Object componentKey) { ComponentTaskContextEntry entry = new ComponentTaskContextEntry(componentKey, lastTaskType); return lastDependency.equals(entry); } /** * Checks if the given dependency has already been defined, if so a {@link CyclicDependencyException} * will be thrown. */ private void checkDependency(ComponentTaskContextEntry entry) { if (entry.getTaskType() == ComponentTaskType.CREATE && dependencies.contains(entry) && (depResolutionCtx == null || !depResolutionCtx.containsKey(entry.getComponentKey()) || depResolutionCtx .get(entry.getComponentKey()).get() == null)) { boolean startToCheck = false; Boolean sameType = null; for (ComponentTaskContextEntry e : dependencies) { if (startToCheck) { if (e.getTaskType() != entry.getTaskType()) { sameType = Boolean.FALSE; break; } sameType = Boolean.TRUE; } else if (entry.equals(e)) { startToCheck = true; } } if (sameType != null && sameType.booleanValue()) { throw new CyclicDependencyException(entry, sameType); } } } /** * @return indicates whether the current context is the root context or not. */ public boolean isRoot() { return dependencies.size() == 1; } /** * Adds the {@link CreationalContext} of the component corresponding to the given key, to the dependency resolution * context * @param key The key of the component to add to the context * @param ctx The {@link CreationalContext} of the component to add to the context * @return {@link CreationalContextComponentAdapter} instance that has been put into the map */ @SuppressWarnings("unchecked") public <T> CreationalContextComponentAdapter<T> addComponentToContext(Object key, CreationalContextComponentAdapter<T> ctx) { if (depResolutionCtx == null) { depResolutionCtx = new HashMap<Object, ConcurrentContainer.CreationalContextComponentAdapter<?>>(); depResolutionCtx.put(key, ctx); return ctx; } CreationalContextComponentAdapter<?> prevValue = depResolutionCtx.get(key); if (prevValue == null) { depResolutionCtx.put(key, ctx); return ctx; } return (CreationalContextComponentAdapter<T>)prevValue; } /** * Removes the {@link CreationalContext} of the component corresponding to the given key, from the dependency resolution * context * @param key The key of the component to remove from the context */ public CreationalContextComponentAdapter<?> removeComponentFromContext(Object key) { if (depResolutionCtx == null) return null; return depResolutionCtx.remove(key); } /** * Tries to get the component related to the given from the context, if it can be found the current state of the component * instance is returned, otherwise <code>null</code> is returned */ public <T> T getComponentInstanceFromContext(Object key, Class<T> bindType) { if (depResolutionCtx == null) return null; CreationalContextComponentAdapter<?> ctx = depResolutionCtx.get(key); return ctx == null ? null : bindType.cast(ctx.get()); } /** * Resets the dependencies but keeps the current dependency resolution context. * @param key the key of the new first dependency * @param type the type of the corresponding task * @return a {@link ComponentTaskContext} instance with the dependencies reseted */ public ComponentTaskContext resetDependencies(Object key, ComponentTaskType type) { LinkedHashSet<ComponentTaskContextEntry> dependencies = new LinkedHashSet<ComponentTaskContextEntry>(); ComponentTaskContextEntry entry = new ComponentTaskContextEntry(key, type); dependencies.add(entry); return new ComponentTaskContext(dependencies, depResolutionCtx == null ? null : new HashMap<Object, ConcurrentContainer.CreationalContextComponentAdapter<?>>(depResolutionCtx), entry, type); } /** * {@inheritDoc} */ @Override public String toString() { return "ComponentTaskContext [dependencies=" + dependencies + ", depResolutionCtx=" + depResolutionCtx + "]"; } }