001/* 002 * (C) Copyright 2006-2017 Nuxeo (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.Collections; 024import java.util.HashMap; 025import java.util.HashSet; 026import java.util.LinkedHashMap; 027import java.util.Map; 028import java.util.Set; 029 030import org.apache.commons.logging.Log; 031import org.apache.commons.logging.LogFactory; 032import org.nuxeo.runtime.model.ComponentName; 033import org.nuxeo.runtime.model.RegistrationInfo; 034 035/** 036 * This class is synchronized to safely update and access the different maps managed by the registry 037 * 038 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 039 */ 040public class ComponentRegistry { 041 042 private final Log log = LogFactory.getLog(ComponentRegistry.class); 043 044 /** 045 * All registered components including unresolved ones. You can check the state of a component for getting the 046 * unresolved ones. 047 */ 048 protected Map<ComponentName, RegistrationInfo> components; 049 050 /** 051 * The list of resolved components. We need to use a linked hash map preserve the resolve order. We don't use a 052 * simple list to optimize removal by name (used by unregister operations). 053 * 054 * @since 9.2 055 */ 056 protected LinkedHashMap<ComponentName, RegistrationInfo> resolved; 057 058 /** Map of aliased name to canonical name. */ 059 protected Map<ComponentName, ComponentName> aliases; 060 061 /** 062 * Maps a component name to a set of component names that are depending on that component. Values are always 063 * unaliased. 064 */ 065 protected MappedSet requirements; 066 067 /** 068 * Map pending components to the set of unresolved components they are waiting for. Key is always unaliased. 069 */ 070 protected MappedSet pendings; 071 072 /** 073 * Map deployment source ids to component names This was previously managed by DefaultRuntimeContext - but is no 074 * more usable in the original form. This map is only useful for unregister by location - which is used by some 075 * tests. Remove this if the unregister API will be removed. 076 * 077 * @since 9.2 078 */ 079 protected Map<String, ComponentName> deployedFiles; 080 081 public ComponentRegistry() { 082 components = new HashMap<>(); 083 aliases = new HashMap<>(); 084 requirements = new MappedSet(); 085 pendings = new MappedSet(); 086 resolved = new LinkedHashMap<>(); 087 deployedFiles = new HashMap<>(); 088 } 089 090 public ComponentRegistry(ComponentRegistry reg) { 091 components = new HashMap<>(reg.components); 092 aliases = new HashMap<>(reg.aliases); 093 requirements = new MappedSet(reg.requirements); 094 pendings = new MappedSet(reg.pendings); 095 resolved = new LinkedHashMap<>(reg.resolved); 096 deployedFiles = new HashMap<>(reg.deployedFiles); 097 } 098 099 public synchronized void destroy() { 100 components = null; 101 aliases = null; 102 requirements = null; 103 pendings = null; 104 deployedFiles = null; 105 } 106 107 public synchronized final boolean isResolved(ComponentName name) { 108 RegistrationInfo ri = components.get(unaliased(name)); 109 if (ri == null) { 110 return false; 111 } 112 return ri.getState() > RegistrationInfo.REGISTERED; 113 } 114 115 /** 116 * @return true if the component was resolved, false if the component is pending 117 */ 118 public synchronized boolean addComponent(RegistrationInfo ri) { 119 ComponentName name = ri.getName(); 120 Set<ComponentName> al = ri.getAliases(); 121 String aliasInfo = al.isEmpty() ? "" : ", aliases=" + al; 122 log.info("Registering component: " + name + aliasInfo); 123 if (ri.useFormerLifecycleManagement()) { 124 ((RegistrationInfoImpl) ri).register(); 125 } else { 126 ri.setState(RegistrationInfo.REGISTERED); 127 } 128 // map the source id with the component name - see ComponentManager.unregisterByLocation 129 String sourceId = ri.getSourceId(); 130 if (sourceId != null) { 131 deployedFiles.put(sourceId, ri.getName()); 132 } 133 components.put(name, ri); 134 for (ComponentName n : al) { 135 aliases.put(n, name); 136 } 137 boolean hasUnresolvedDependencies = computePendings(ri); 138 if (!hasUnresolvedDependencies) { 139 resolveComponent(ri); 140 return true; 141 } 142 return false; 143 } 144 145 public synchronized RegistrationInfo removeComponent(ComponentName name) { 146 RegistrationInfo ri = components.remove(name); 147 if (ri != null) { 148 try { 149 unresolveComponent(ri); 150 } finally { 151 if (ri.useFormerLifecycleManagement()) { 152 ((RegistrationInfoImpl) ri).unregister(); 153 } else { 154 ri.setState(RegistrationInfo.UNREGISTERED); 155 } 156 } 157 } 158 return ri; 159 } 160 161 /** 162 * @return an unmodifiable collection of resolved registration infos, sorted by {@link LinkedHashMap} 163 * @since 9.2 164 */ 165 public synchronized Collection<RegistrationInfo> getResolvedRegistrationInfo() { 166 return Collections.unmodifiableCollection(resolved.values()); 167 } 168 169 /** 170 * @return an unmodifiable collection of resolved component names, sorted by {@link LinkedHashMap} 171 * @since 9.2 172 */ 173 public synchronized Collection<ComponentName> getResolvedNames() { 174 return Collections.unmodifiableCollection(resolved.keySet()); 175 } 176 177 /** 178 * @return an unmodifiable collection of missing dependencies 179 * @since 9.2 180 */ 181 public synchronized Set<ComponentName> getMissingDependencies(ComponentName name) { 182 return Collections.unmodifiableSet(pendings.get(name)); 183 } 184 185 /** 186 * Get the registration info for the given component name or null if none was registered. 187 * 188 * @since 9.2 189 */ 190 public synchronized RegistrationInfo getComponent(ComponentName name) { 191 return components.get(unaliased(name)); 192 } 193 194 /** 195 * Check if the component is already registered against this registry 196 */ 197 public synchronized boolean contains(ComponentName name) { 198 return components.containsKey(unaliased(name)); 199 } 200 201 /** 202 * Get the registered components count 203 */ 204 public synchronized int size() { 205 return components.size(); 206 } 207 208 /** 209 * @return an unmodifiable collection of registered components 210 */ 211 public synchronized Collection<RegistrationInfo> getComponents() { 212 return Collections.unmodifiableCollection(components.values()); 213 } 214 215 /** 216 * Get a copy of the registered components as an array. 217 */ 218 public synchronized RegistrationInfo[] getComponentsArray() { 219 return components.values().toArray(new RegistrationInfo[components.size()]); 220 } 221 222 /** 223 * @return an unmodifiable map of pending components 224 */ 225 public synchronized Map<ComponentName, Set<ComponentName>> getPendingComponents() { 226 return Collections.unmodifiableMap(pendings.map); 227 } 228 229 protected ComponentName unaliased(ComponentName name) { 230 ComponentName alias = aliases.get(name); 231 return alias == null ? name : alias; 232 } 233 234 /** 235 * Fill the pending map with all unresolved dependencies of the given component. Returns false if no unresolved 236 * dependencies are found, otherwise returns true. 237 */ 238 protected final boolean computePendings(RegistrationInfo ri) { 239 Set<ComponentName> set = ri.getRequiredComponents(); 240 if (set == null || set.isEmpty()) { 241 return false; 242 } 243 boolean hasUnresolvedDependencies = false; 244 // fill the requirements and pending map 245 for (ComponentName name : set) { 246 if (!isResolved(name)) { 247 pendings.put(ri.getName(), name); 248 hasUnresolvedDependencies = true; 249 } 250 requirements.put(name, ri.getName()); 251 } 252 return hasUnresolvedDependencies; 253 } 254 255 protected void resolveComponent(RegistrationInfo ri) { 256 ComponentName riName = ri.getName(); 257 Set<ComponentName> names = new HashSet<>(); 258 names.add(riName); 259 names.addAll(ri.getAliases()); 260 261 if (ri.useFormerLifecycleManagement()) { 262 ((RegistrationInfoImpl) ri).resolve(); 263 } else { 264 ri.setState(RegistrationInfo.RESOLVED); 265 } 266 resolved.put(ri.getName(), ri); // track resolved components 267 268 // try to resolve pending components that are waiting the newly 269 // resolved component 270 Set<ComponentName> dependsOnMe = new HashSet<>(); 271 for (ComponentName n : names) { 272 Set<ComponentName> reqs = requirements.get(n); 273 if (reqs != null) { 274 dependsOnMe.addAll(reqs); // unaliased 275 } 276 } 277 if (dependsOnMe == null || dependsOnMe.isEmpty()) { 278 return; 279 } 280 for (ComponentName name : dependsOnMe) { // unaliased 281 for (ComponentName n : names) { 282 pendings.remove(name, n); 283 } 284 Set<ComponentName> set = pendings.get(name); 285 if (set == null || set.isEmpty()) { 286 RegistrationInfo waitingRi = components.get(name); 287 resolveComponent(waitingRi); 288 } 289 } 290 } 291 292 protected void unresolveComponent(RegistrationInfo ri) { 293 Set<ComponentName> reqs = ri.getRequiredComponents(); 294 ComponentName name = ri.getName(); 295 if (ri.useFormerLifecycleManagement()) { 296 ((RegistrationInfoImpl) ri).unresolve(); 297 } else { 298 ri.setState(RegistrationInfo.REGISTERED); 299 } 300 resolved.remove(name); 301 pendings.remove(name); 302 if (reqs != null) { 303 for (ComponentName req : reqs) { 304 requirements.remove(req, name); 305 } 306 } 307 Set<ComponentName> set = requirements.get(name); // unaliased 308 if (set != null && !set.isEmpty()) { 309 for (ComponentName dep : set.toArray(new ComponentName[set.size()])) { 310 RegistrationInfo depRi = components.get(dep); 311 if (depRi != null) { 312 unresolveComponent(depRi); 313 } 314 } 315 } 316 } 317 318 protected static class MappedSet { 319 320 protected Map<ComponentName, Set<ComponentName>> map; 321 322 public MappedSet() { 323 map = new HashMap<>(); 324 } 325 326 /** 327 * Create a clone of a mapped set (set values are cloned too) 328 */ 329 public MappedSet(MappedSet mset) { 330 this(); 331 for (Map.Entry<ComponentName, Set<ComponentName>> entry : mset.map.entrySet()) { 332 ComponentName name = entry.getKey(); 333 Set<ComponentName> set = entry.getValue(); 334 Set<ComponentName> newSet = new HashSet<>(set); 335 map.put(name, newSet); 336 } 337 } 338 339 public Set<ComponentName> get(ComponentName name) { 340 return map.get(name); 341 } 342 343 public Set<ComponentName> put(ComponentName key, ComponentName value) { 344 Set<ComponentName> set = map.get(key); 345 if (set == null) { 346 set = new HashSet<>(); 347 map.put(key, set); 348 } 349 set.add(value); 350 return set; 351 } 352 353 public Set<ComponentName> remove(ComponentName key) { 354 return map.remove(key); 355 } 356 357 public Set<ComponentName> remove(ComponentName key, ComponentName value) { 358 Set<ComponentName> set = map.get(key); 359 if (set != null) { 360 set.remove(value); 361 if (set.isEmpty()) { 362 map.remove(key); 363 } 364 } 365 return set; 366 } 367 368 public boolean isEmpty() { 369 return map.isEmpty(); 370 } 371 372 public int size() { 373 return map.size(); 374 } 375 376 public void clear() { 377 map.clear(); 378 } 379 } 380}