001/* 002 * (C) Copyright 2006-2012 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Bogdan Stefanescu 016 * Florent Guillaume 017 */ 018package org.nuxeo.runtime.model.impl; 019 020import java.util.Collection; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.Map; 024import java.util.Set; 025 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028import org.nuxeo.runtime.model.ComponentName; 029import org.nuxeo.runtime.model.RegistrationInfo; 030 031/** 032 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 033 */ 034public class ComponentRegistry { 035 036 private final Log log = LogFactory.getLog(ComponentRegistry.class); 037 038 /** 039 * All registered components including unresolved ones. You can check the state of a component for getting the 040 * unresolved ones. 041 */ 042 protected Map<ComponentName, RegistrationInfoImpl> components; 043 044 /** Map of aliased name to canonical name. */ 045 protected Map<ComponentName, ComponentName> aliases; 046 047 /** 048 * Maps a component name to a set of component names that are depending on that component. Values are always 049 * unaliased. 050 */ 051 protected MappedSet requirements; 052 053 /** 054 * Map pending components to the set of unresolved components they are waiting for. Key is always unaliased. 055 */ 056 protected MappedSet pendings; 057 058 public ComponentRegistry() { 059 components = new HashMap<ComponentName, RegistrationInfoImpl>(); 060 aliases = new HashMap<ComponentName, ComponentName>(); 061 requirements = new MappedSet(); 062 pendings = new MappedSet(); 063 } 064 065 public void destroy() { 066 components = null; 067 aliases = null; 068 requirements = null; 069 pendings = null; 070 } 071 072 protected ComponentName unaliased(ComponentName name) { 073 ComponentName alias = aliases.get(name); 074 return alias == null ? name : alias; 075 } 076 077 public final boolean isResolved(ComponentName name) { 078 RegistrationInfo ri = components.get(unaliased(name)); 079 if (ri == null) { 080 return false; 081 } 082 return ri.getState() > RegistrationInfo.REGISTERED; 083 } 084 085 /** 086 * Fill the pending map with all unresolved dependencies of the given component. Returns false if no unresolved 087 * dependencies are found, otherwise returns true. 088 * 089 * @param ri 090 * @return 091 */ 092 protected final boolean computePendings(RegistrationInfo ri) { 093 Set<ComponentName> set = ri.getRequiredComponents(); 094 if (set == null || set.isEmpty()) { 095 return false; 096 } 097 boolean hasUnresolvedDependencies = false; 098 // fill the requirements and pending map 099 for (ComponentName name : set) { 100 if (!isResolved(name)) { 101 pendings.put(ri.getName(), name); 102 hasUnresolvedDependencies = true; 103 } 104 requirements.put(name, ri.getName()); 105 } 106 return hasUnresolvedDependencies; 107 } 108 109 /** 110 * @param ri 111 * @return true if the component was resolved, false if the component is pending 112 */ 113 public boolean addComponent(RegistrationInfoImpl ri) { 114 ComponentName name = ri.getName(); 115 Set<ComponentName> al = ri.getAliases(); 116 String aliasInfo = al.isEmpty() ? "" : ", aliases=" + al; 117 log.info("Registering component: " + name + aliasInfo); 118 ri.register(); 119 components.put(name, ri); 120 for (ComponentName n : al) { 121 aliases.put(n, name); 122 } 123 boolean hasUnresolvedDependencies = computePendings(ri); 124 if (!hasUnresolvedDependencies) { 125 resolveComponent(ri); 126 return true; 127 } 128 return false; 129 } 130 131 public RegistrationInfoImpl removeComponent(ComponentName name) { 132 RegistrationInfoImpl ri = components.remove(name); 133 if (ri != null) { 134 try { 135 unresolveComponent(ri); 136 } finally { 137 ri.unregister(); 138 } 139 } 140 return ri; 141 } 142 143 public Set<ComponentName> getMissingDependencies(ComponentName name) { 144 return pendings.get(name); 145 } 146 147 public RegistrationInfoImpl getComponent(ComponentName name) { 148 return components.get(unaliased(name)); 149 } 150 151 public boolean contains(ComponentName name) { 152 return components.containsKey(unaliased(name)); 153 } 154 155 public int size() { 156 return components.size(); 157 } 158 159 public Collection<RegistrationInfoImpl> getComponents() { 160 return components.values(); 161 } 162 163 public RegistrationInfoImpl[] getComponentsArray() { 164 return components.values().toArray(new RegistrationInfoImpl[components.size()]); 165 } 166 167 public Map<ComponentName, Set<ComponentName>> getPendingComponents() { 168 return pendings.map; 169 } 170 171 protected void resolveComponent(RegistrationInfoImpl ri) { 172 ComponentName riName = ri.getName(); 173 Set<ComponentName> names = new HashSet<ComponentName>(); 174 names.add(riName); 175 names.addAll(ri.getAliases()); 176 177 ri.resolve(); 178 // try to resolve pending components that are waiting the newly 179 // resolved component 180 Set<ComponentName> dependsOnMe = new HashSet<ComponentName>(); 181 for (ComponentName n : names) { 182 Set<ComponentName> reqs = requirements.get(n); 183 if (reqs != null) { 184 dependsOnMe.addAll(reqs); // unaliased 185 } 186 } 187 if (dependsOnMe == null || dependsOnMe.isEmpty()) { 188 return; 189 } 190 for (ComponentName name : dependsOnMe) { // unaliased 191 for (ComponentName n : names) { 192 pendings.remove(name, n); 193 } 194 Set<ComponentName> set = pendings.get(name); 195 if (set == null || set.isEmpty()) { 196 RegistrationInfoImpl waitingRi = components.get(name); 197 resolveComponent(waitingRi); 198 } 199 } 200 } 201 202 protected void unresolveComponent(RegistrationInfoImpl ri) { 203 Set<ComponentName> reqs = ri.getRequiredComponents(); 204 ComponentName name = ri.getName(); 205 ri.unresolve(); 206 pendings.remove(name); 207 if (reqs != null) { 208 for (ComponentName req : reqs) { 209 requirements.remove(req, name); 210 } 211 } 212 Set<ComponentName> set = requirements.get(name); // unaliased 213 if (set != null && !set.isEmpty()) { 214 for (ComponentName dep : set.toArray(new ComponentName[set.size()])) { 215 RegistrationInfoImpl depRi = components.get(dep); 216 if (depRi != null) { 217 unresolveComponent(depRi); 218 } 219 } 220 } 221 } 222 223 static class MappedSet { 224 protected Map<ComponentName, Set<ComponentName>> map; 225 226 public MappedSet() { 227 map = new HashMap<ComponentName, Set<ComponentName>>(); 228 } 229 230 public Set<ComponentName> get(ComponentName name) { 231 return map.get(name); 232 } 233 234 public Set<ComponentName> put(ComponentName key, ComponentName value) { 235 Set<ComponentName> set = map.get(key); 236 if (set == null) { 237 set = new HashSet<ComponentName>(); 238 map.put(key, set); 239 } 240 set.add(value); 241 return set; 242 } 243 244 public Set<ComponentName> remove(ComponentName key) { 245 return map.remove(key); 246 } 247 248 public Set<ComponentName> remove(ComponentName key, ComponentName value) { 249 Set<ComponentName> set = map.get(key); 250 if (set != null) { 251 set.remove(value); 252 if (set.isEmpty()) { 253 map.remove(key); 254 } 255 } 256 return set; 257 } 258 259 public boolean isEmpty() { 260 return map.isEmpty(); 261 } 262 263 public int size() { 264 return map.size(); 265 } 266 267 public void clear() { 268 map.clear(); 269 } 270 } 271}