| /** |
| * |
| * Copyright 2005 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.gbean.kernel.simple; |
| |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import javax.management.ObjectName; |
| |
| import org.gbean.kernel.Kernel; |
| import org.gbean.kernel.DependencyManager; |
| import org.gbean.kernel.LifecycleListener; |
| import org.gbean.kernel.LifecycleAdapter; |
| import org.gbean.kernel.ServiceName; |
| |
| /** |
| * DependencyManager is the record keeper of the dependencies in the kernel. The DependencyManager |
| * does not enforce any dependencies, it is simply a place where components can register their intent |
| * to be dependent on another component. Since a service can pretty much do whatever it wants |
| * a service must watch the services it depends on to assure that they are following the |
| * lifecycle contract. |
| * <p/> |
| * The DependencyManager uses the nomenclature of parent-child where a child is dependent on a parent. |
| * The names parent and child have no other meaning are just a convience to make the code readable. |
| * |
| * @version $Rev: 124822 $ $Date: 2005-01-10 11:01:13 -0800 (Mon, 10 Jan 2005) $ |
| */ |
| public class SimpleDependencyManager implements DependencyManager { |
| /** |
| * The lifecycleMonitor informs us when services go off line, |
| * so we can clean up the lingering dependencies. |
| */ |
| private final Kernel kernel; |
| |
| /** |
| * Listenes for services to unregister and removes all dependencies associated with the dependency |
| */ |
| private final LifecycleListener lifecycleListener = new DependencyManagerLifecycleListener(); |
| |
| /** |
| * A map from child names to a list of parents. |
| */ |
| private final Map childToParentMap = new HashMap(); |
| |
| /** |
| * A map from parent back to a list of its children. |
| */ |
| private final Map parentToChildMap = new HashMap(); |
| |
| /** |
| * A map from a component's ObjectName to the list of ObjectPatterns that the component is blocking |
| * from starting. |
| */ |
| private final Map startHoldsMap = new HashMap(); |
| |
| private static final ObjectName ALL = ServiceName.createName("*:*"); |
| |
| public SimpleDependencyManager(Kernel kernel) { |
| assert kernel != null; |
| this.kernel = kernel; |
| } |
| |
| // todo throw illegal state from all methods when not started |
| public synchronized void start() { |
| this.kernel.addLifecycleListener(lifecycleListener, ALL); |
| } |
| public synchronized void stop() { |
| kernel.removeLifecycleListener(lifecycleListener); |
| childToParentMap.clear(); |
| parentToChildMap.clear(); |
| startHoldsMap.clear(); |
| } |
| |
| /** |
| * Declares a dependency from a child to a parent. |
| * |
| * @param child the dependent component |
| * @param parent the component the child is depending on |
| */ |
| public synchronized void addDependency(ObjectName child, ObjectName parent) { |
| Set parents = (Set) childToParentMap.get(child); |
| if (parents == null) { |
| parents = new HashSet(); |
| childToParentMap.put(child, parents); |
| } |
| parents.add(parent); |
| |
| Set children = (Set) parentToChildMap.get(parent); |
| if (children == null) { |
| children = new HashSet(); |
| parentToChildMap.put(parent, children); |
| } |
| children.add(child); |
| } |
| |
| /** |
| * Removes a dependency from a child to a parent |
| * |
| * @param child the dependnet component |
| * @param parent the component that the child wil no longer depend on |
| */ |
| public synchronized void removeDependency(ObjectName child, ObjectName parent) { |
| Set parents = (Set) childToParentMap.get(child); |
| if (parents != null) { |
| parents.remove(parent); |
| } |
| |
| Set children = (Set) parentToChildMap.get(parent); |
| if (children != null) { |
| children.remove(child); |
| } |
| } |
| |
| /** |
| * Removes all dependencies for a child |
| * |
| * @param child the component that will no longer depend on anything |
| */ |
| public synchronized void removeAllDependencies(ObjectName child) { |
| Set parents = (Set) childToParentMap.remove(child); |
| if (parents == null) { |
| return; |
| } |
| for (Iterator iterator = parents.iterator(); iterator.hasNext();) { |
| ObjectName parent = (ObjectName) iterator.next(); |
| Set children = (Set) parentToChildMap.get(parent); |
| if (children != null) { |
| children.remove(child); |
| } |
| |
| } |
| } |
| |
| /** |
| * Adds dependencies from the child to every parent in the parents set |
| * |
| * @param child the dependent component |
| * @param parents the set of components the child is depending on |
| */ |
| public synchronized void addDependencies(ObjectName child, Set parents) { |
| Set existingParents = (Set) childToParentMap.get(child); |
| if (existingParents == null) { |
| existingParents = new HashSet(parents); |
| childToParentMap.put(child, existingParents); |
| } else { |
| existingParents.addAll(parents); |
| } |
| |
| for (Iterator i = parents.iterator(); i.hasNext();) { |
| Object startParent = i.next(); |
| Set children = (Set) parentToChildMap.get(startParent); |
| if (children == null) { |
| children = new HashSet(); |
| parentToChildMap.put(startParent, children); |
| } |
| children.add(child); |
| } |
| } |
| |
| /** |
| * Gets the set of parents that the child is depending on |
| * |
| * @param child the dependent component |
| * @return a collection containing all of the components the child depends on; will never be null |
| */ |
| public synchronized Set getParents(ObjectName child) { |
| Set parents = (Set) childToParentMap.get(child); |
| if (parents == null) { |
| return Collections.EMPTY_SET; |
| } |
| return new HashSet(parents); |
| } |
| |
| /** |
| * Gets all of the services that have a dependency on the specified startParent. |
| * |
| * @param parent the component the returned childen set depend on |
| * @return a collection containing all of the components that depend on the parent; will never be null |
| */ |
| public synchronized Set getChildren(ObjectName parent) { |
| Set children = (Set) parentToChildMap.get(parent); |
| if (children == null) { |
| return Collections.EMPTY_SET; |
| } |
| return new HashSet(children); |
| } |
| |
| /** |
| * Adds a hold on a collection of object name patterns. If the name of a component matches an object name |
| * pattern in the collection, the component should not start. |
| * |
| * @param objectName the name of the component placing the holds |
| * @param holds a collection of object name patterns which should not start |
| */ |
| public synchronized void addStartHolds(ObjectName objectName, Collection holds) { |
| Collection currentHolds = (Collection) startHoldsMap.get(objectName); |
| if (currentHolds == null) { |
| currentHolds = new LinkedList(holds); |
| startHoldsMap.put(objectName, currentHolds); |
| } else { |
| currentHolds.addAll(holds); |
| } |
| } |
| |
| /** |
| * Removes a collection of holds. |
| * |
| * @param objectName the object name of the components owning the holds |
| * @param holds a collection of the holds to remove |
| */ |
| public synchronized void removeStartHolds(ObjectName objectName, Collection holds) { |
| Collection currentHolds = (Collection) startHoldsMap.get(objectName); |
| if (currentHolds != null) { |
| currentHolds.removeAll(holds); |
| } |
| } |
| |
| /** |
| * Removes all of the holds owned by a component. |
| * |
| * @param objectName the object name of the component that will no longer have any holds |
| */ |
| public synchronized void removeAllStartHolds(ObjectName objectName) { |
| startHoldsMap.remove(objectName); |
| } |
| |
| /** |
| * Gets the object name of the service blocking the start specified service. |
| * |
| * @param objectName the service to check for blockers |
| * @return the service blocking the specified service, or null if there are no blockers |
| */ |
| public synchronized ObjectName checkBlocker(ObjectName objectName) { |
| // check if objectName name is on one of the hold lists |
| for (Iterator iterator = startHoldsMap.keySet().iterator(); iterator.hasNext();) { |
| ObjectName blocker = (ObjectName) iterator.next(); |
| List holds = (List) startHoldsMap.get(blocker); |
| for (Iterator holdsIterator = holds.iterator(); holdsIterator.hasNext();) { |
| ObjectName pattern = (ObjectName) holdsIterator.next(); |
| if (pattern.apply(objectName)) { |
| return blocker; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private class DependencyManagerLifecycleListener extends LifecycleAdapter { |
| public void unloaded(ObjectName objectName) { |
| synchronized (SimpleDependencyManager.this) { |
| removeAllDependencies(objectName); |
| removeAllStartHolds(objectName); |
| } |
| |
| } |
| } |
| } |