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