/*
* (C) Copyright 2006-2012 Nuxeo SA (http://nuxeo.com/) and others.
*
* 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.
*
* Contributors:
* Bogdan Stefanescu
* Florent Guillaume
*/
package org.nuxeo.runtime.model.impl;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.runtime.model.ComponentName;
import org.nuxeo.runtime.model.RegistrationInfo;
/**
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
*/
public class ComponentRegistry {
private final Log log = LogFactory.getLog(ComponentRegistry.class);
/**
* All registered components including unresolved ones. You can check the state of a component for getting the
* unresolved ones.
*/
protected Map<ComponentName, RegistrationInfoImpl> components;
/** Map of aliased name to canonical name. */
protected Map<ComponentName, ComponentName> aliases;
/**
* Maps a component name to a set of component names that are depending on that component. Values are always
* unaliased.
*/
protected MappedSet requirements;
/**
* Map pending components to the set of unresolved components they are waiting for. Key is always unaliased.
*/
protected MappedSet pendings;
public ComponentRegistry() {
components = new HashMap<ComponentName, RegistrationInfoImpl>();
aliases = new HashMap<ComponentName, ComponentName>();
requirements = new MappedSet();
pendings = new MappedSet();
}
public void destroy() {
components = null;
aliases = null;
requirements = null;
pendings = null;
}
protected ComponentName unaliased(ComponentName name) {
ComponentName alias = aliases.get(name);
return alias == null ? name : alias;
}
public final boolean isResolved(ComponentName name) {
RegistrationInfo ri = components.get(unaliased(name));
if (ri == null) {
return false;
}
return ri.getState() > RegistrationInfo.REGISTERED;
}
/**
* Fill the pending map with all unresolved dependencies of the given component. Returns false if no unresolved
* dependencies are found, otherwise returns true.
*
* @param ri
* @return
*/
protected final boolean computePendings(RegistrationInfo ri) {
Set<ComponentName> set = ri.getRequiredComponents();
if (set == null || set.isEmpty()) {
return false;
}
boolean hasUnresolvedDependencies = false;
// fill the requirements and pending map
for (ComponentName name : set) {
if (!isResolved(name)) {
pendings.put(ri.getName(), name);
hasUnresolvedDependencies = true;
}
requirements.put(name, ri.getName());
}
return hasUnresolvedDependencies;
}
/**
* @param ri
* @return true if the component was resolved, false if the component is pending
*/
public boolean addComponent(RegistrationInfoImpl ri) {
ComponentName name = ri.getName();
Set<ComponentName> al = ri.getAliases();
String aliasInfo = al.isEmpty() ? "" : ", aliases=" + al;
log.info("Registering component: " + name + aliasInfo);
ri.register();
components.put(name, ri);
for (ComponentName n : al) {
aliases.put(n, name);
}
boolean hasUnresolvedDependencies = computePendings(ri);
if (!hasUnresolvedDependencies) {
resolveComponent(ri);
return true;
}
return false;
}
public RegistrationInfoImpl removeComponent(ComponentName name) {
RegistrationInfoImpl ri = components.remove(name);
if (ri != null) {
try {
unresolveComponent(ri);
} finally {
ri.unregister();
}
}
return ri;
}
public Set<ComponentName> getMissingDependencies(ComponentName name) {
return pendings.get(name);
}
public RegistrationInfoImpl getComponent(ComponentName name) {
return components.get(unaliased(name));
}
public boolean contains(ComponentName name) {
return components.containsKey(unaliased(name));
}
public int size() {
return components.size();
}
public Collection<RegistrationInfoImpl> getComponents() {
return components.values();
}
public RegistrationInfoImpl[] getComponentsArray() {
return components.values().toArray(new RegistrationInfoImpl[components.size()]);
}
public Map<ComponentName, Set<ComponentName>> getPendingComponents() {
return pendings.map;
}
protected void resolveComponent(RegistrationInfoImpl ri) {
ComponentName riName = ri.getName();
Set<ComponentName> names = new HashSet<ComponentName>();
names.add(riName);
names.addAll(ri.getAliases());
ri.resolve();
// try to resolve pending components that are waiting the newly
// resolved component
Set<ComponentName> dependsOnMe = new HashSet<ComponentName>();
for (ComponentName n : names) {
Set<ComponentName> reqs = requirements.get(n);
if (reqs != null) {
dependsOnMe.addAll(reqs); // unaliased
}
}
if (dependsOnMe == null || dependsOnMe.isEmpty()) {
return;
}
for (ComponentName name : dependsOnMe) { // unaliased
for (ComponentName n : names) {
pendings.remove(name, n);
}
Set<ComponentName> set = pendings.get(name);
if (set == null || set.isEmpty()) {
RegistrationInfoImpl waitingRi = components.get(name);
resolveComponent(waitingRi);
}
}
}
protected void unresolveComponent(RegistrationInfoImpl ri) {
Set<ComponentName> reqs = ri.getRequiredComponents();
ComponentName name = ri.getName();
ri.unresolve();
pendings.remove(name);
if (reqs != null) {
for (ComponentName req : reqs) {
requirements.remove(req, name);
}
}
Set<ComponentName> set = requirements.get(name); // unaliased
if (set != null && !set.isEmpty()) {
for (ComponentName dep : set.toArray(new ComponentName[set.size()])) {
RegistrationInfoImpl depRi = components.get(dep);
if (depRi != null) {
unresolveComponent(depRi);
}
}
}
}
static class MappedSet {
protected Map<ComponentName, Set<ComponentName>> map;
public MappedSet() {
map = new HashMap<ComponentName, Set<ComponentName>>();
}
public Set<ComponentName> get(ComponentName name) {
return map.get(name);
}
public Set<ComponentName> put(ComponentName key, ComponentName value) {
Set<ComponentName> set = map.get(key);
if (set == null) {
set = new HashSet<ComponentName>();
map.put(key, set);
}
set.add(value);
return set;
}
public Set<ComponentName> remove(ComponentName key) {
return map.remove(key);
}
public Set<ComponentName> remove(ComponentName key, ComponentName value) {
Set<ComponentName> set = map.get(key);
if (set != null) {
set.remove(value);
if (set.isEmpty()) {
map.remove(key);
}
}
return set;
}
public boolean isEmpty() {
return map.isEmpty();
}
public int size() {
return map.size();
}
public void clear() {
map.clear();
}
}
}