001/* 002 * (C) Copyright 2006-2011 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 */ 020 021package org.nuxeo.runtime.model.impl; 022 023import java.util.ArrayList; 024import java.util.Collection; 025import java.util.Collections; 026import java.util.HashMap; 027import java.util.HashSet; 028import java.util.LinkedHashSet; 029import java.util.Map; 030import java.util.Set; 031import java.util.concurrent.ConcurrentHashMap; 032 033import org.apache.commons.logging.Log; 034import org.apache.commons.logging.LogFactory; 035import org.nuxeo.common.collections.ListenerList; 036import org.nuxeo.runtime.ComponentEvent; 037import org.nuxeo.runtime.ComponentListener; 038import org.nuxeo.runtime.RuntimeService; 039import org.nuxeo.runtime.api.Framework; 040import org.nuxeo.runtime.model.ComponentInstance; 041import org.nuxeo.runtime.model.ComponentManager; 042import org.nuxeo.runtime.model.ComponentName; 043import org.nuxeo.runtime.model.Extension; 044import org.nuxeo.runtime.model.RegistrationInfo; 045 046/** 047 * @author Bogdan Stefanescu 048 * @author Florent Guillaume 049 */ 050public class ComponentManagerImpl implements ComponentManager { 051 052 private static final Log log = LogFactory.getLog(ComponentManagerImpl.class); 053 054 // must use an ordered Set to avoid loosing the order of the pending 055 // extensions 056 protected final Map<ComponentName, Set<Extension>> pendingExtensions; 057 058 private ListenerList listeners; 059 060 private final Map<String, RegistrationInfoImpl> services; 061 062 protected Set<String> blacklist; 063 064 protected ComponentRegistry reg; 065 066 public ComponentManagerImpl(RuntimeService runtime) { 067 reg = new ComponentRegistry(); 068 pendingExtensions = new HashMap<ComponentName, Set<Extension>>(); 069 listeners = new ListenerList(); 070 services = new ConcurrentHashMap<String, RegistrationInfoImpl>(); 071 blacklist = new HashSet<String>(); 072 } 073 074 @Override 075 public synchronized Collection<RegistrationInfo> getRegistrations() { 076 return new ArrayList<RegistrationInfo>(reg.getComponents()); 077 } 078 079 @Override 080 public synchronized Map<ComponentName, Set<ComponentName>> getPendingRegistrations() { 081 Map<ComponentName, Set<ComponentName>> pending = new HashMap<>(); 082 for (Map.Entry<ComponentName, Set<ComponentName>> p : reg.getPendingComponents().entrySet()) { 083 pending.put(p.getKey(), new LinkedHashSet<>(p.getValue())); 084 } 085 return pending; 086 } 087 088 @Override 089 public synchronized Map<ComponentName, Set<Extension>> getMissingRegistrations() { 090 Map<ComponentName, Set<Extension>> missing = new HashMap<>(); 091 // also add pending extensions, not resolved because of missing target extension point 092 for (Set<Extension> p : pendingExtensions.values()) { 093 for (Extension e : p) { 094 missing.computeIfAbsent(e.getComponent().getName(), k -> new LinkedHashSet<>()).add(e); 095 } 096 } 097 return missing; 098 } 099 100 public synchronized Collection<ComponentName> getNeededRegistrations() { 101 return pendingExtensions.keySet(); 102 } 103 104 public synchronized Collection<Extension> getPendingExtensions(ComponentName name) { 105 return pendingExtensions.get(name); 106 } 107 108 @Override 109 public synchronized RegistrationInfo getRegistrationInfo(ComponentName name) { 110 return reg.getComponent(name); 111 } 112 113 @Override 114 public synchronized boolean isRegistered(ComponentName name) { 115 return reg.contains(name); 116 } 117 118 @Override 119 public synchronized int size() { 120 return reg.size(); 121 } 122 123 @Override 124 public synchronized ComponentInstance getComponent(ComponentName name) { 125 RegistrationInfo ri = reg.getComponent(name); 126 return ri != null ? ri.getComponent() : null; 127 } 128 129 @Override 130 public synchronized void shutdown() { 131 ShutdownTask.shutdown(this); 132 listeners = null; 133 reg.destroy(); 134 reg = null; 135 } 136 137 @Override 138 public Set<String> getBlacklist() { 139 return Collections.unmodifiableSet(blacklist); 140 } 141 142 @Override 143 public void setBlacklist(Set<String> blacklist) { 144 this.blacklist = blacklist; 145 } 146 147 @Override 148 public synchronized void register(RegistrationInfo regInfo) { 149 RegistrationInfoImpl ri = (RegistrationInfoImpl) regInfo; 150 ComponentName name = ri.getName(); 151 if (blacklist.contains(name.getName())) { 152 log.warn("Component " + name.getName() + " was blacklisted. Ignoring."); 153 return; 154 } 155 if (reg.contains(name)) { 156 if (name.getName().startsWith("org.nuxeo.runtime.")) { 157 // XXX we hide the fact that nuxeo-runtime bundles are 158 // registered twice 159 // TODO fix the root cause and remove this 160 return; 161 } 162 handleError("Duplicate component name: " + name, null); 163 return; 164 } 165 for (ComponentName n : ri.getAliases()) { 166 if (reg.contains(n)) { 167 handleError("Duplicate component name: " + n + " (alias for " + name + ")", null); 168 return; 169 } 170 } 171 172 ri.attach(this); 173 174 try { 175 log.info("Registering component: " + name); 176 if (!reg.addComponent(ri)) { 177 log.info("Registration delayed for component: " + name + ". Waiting for: " 178 + reg.getMissingDependencies(ri.getName())); 179 } 180 } catch (RuntimeException e) { 181 // don't raise this exception, 182 // we want to isolate component errors from other components 183 handleError("Failed to register component: " + name + " (" + e.toString() + ')', e); 184 return; 185 } 186 } 187 188 @Override 189 public synchronized void unregister(RegistrationInfo regInfo) { 190 unregister(regInfo.getName()); 191 } 192 193 @Override 194 public synchronized void unregister(ComponentName name) { 195 try { 196 log.info("Unregistering component: " + name); 197 reg.removeComponent(name); 198 } catch (RuntimeException e) { 199 log.error("Failed to unregister component: " + name, e); 200 } 201 } 202 203 @Override 204 public void addComponentListener(ComponentListener listener) { 205 listeners.add(listener); 206 } 207 208 @Override 209 public void removeComponentListener(ComponentListener listener) { 210 listeners.remove(listener); 211 } 212 213 @Override 214 public ComponentInstance getComponentProvidingService(Class<?> serviceClass) { 215 RegistrationInfoImpl ri = services.get(serviceClass.getName()); 216 if (ri == null) { 217 return null; 218 } 219 if (ri.isResolved()) { 220 // activate it first 221 ri.activate(); 222 } 223 if (ri.isActivated()) { 224 return ri.getComponent(); 225 } 226 log.debug("The component exposing the service " + serviceClass + " is not resolved or not started"); 227 return null; 228 } 229 230 @Override 231 public <T> T getService(Class<T> serviceClass) { 232 ComponentInstance comp = getComponentProvidingService(serviceClass); 233 return comp != null ? comp.getAdapter(serviceClass) : null; 234 } 235 236 @Override 237 public Collection<ComponentName> getActivatingRegistrations() { 238 return getRegistrations(RegistrationInfo.ACTIVATING); 239 } 240 241 @Override 242 public Collection<ComponentName> getStartFailureRegistrations() { 243 return getRegistrations(RegistrationInfo.START_FAILURE); 244 } 245 246 protected Collection<ComponentName> getRegistrations(int state) { 247 RegistrationInfo[] comps = null; 248 synchronized (this) { 249 comps = reg.getComponentsArray(); 250 } 251 Collection<ComponentName> ret = new ArrayList<ComponentName>(); 252 for (RegistrationInfo ri : comps) { 253 if (ri.getState() == state) { 254 ret.add(ri.getName()); 255 } 256 } 257 return ret; 258 } 259 260 void sendEvent(ComponentEvent event) { 261 log.debug("Dispatching event: " + event); 262 Object[] listeners = this.listeners.getListeners(); 263 for (Object listener : listeners) { 264 ((ComponentListener) listener).handleEvent(event); 265 } 266 } 267 268 public synchronized void registerExtension(Extension extension) { 269 ComponentName name = extension.getTargetComponent(); 270 RegistrationInfoImpl ri = reg.getComponent(name); 271 if (ri != null && ri.component != null) { 272 if (log.isDebugEnabled()) { 273 log.debug("Register contributed extension: " + extension); 274 } 275 loadContributions(ri, extension); 276 ri.component.registerExtension(extension); 277 sendEvent(new ComponentEvent(ComponentEvent.EXTENSION_REGISTERED, 278 ((ComponentInstanceImpl) extension.getComponent()).ri, extension)); 279 } else { 280 // put the extension in the pending queue 281 if (log.isDebugEnabled()) { 282 log.debug("Enqueue contributed extension to pending queue: " + extension); 283 } 284 Set<Extension> extensions = pendingExtensions.get(name); 285 if (extensions == null) { 286 extensions = new LinkedHashSet<Extension>(); // must keep order 287 // in which 288 // extensions are 289 // contributed 290 pendingExtensions.put(name, extensions); 291 } 292 extensions.add(extension); 293 sendEvent(new ComponentEvent(ComponentEvent.EXTENSION_PENDING, 294 ((ComponentInstanceImpl) extension.getComponent()).ri, extension)); 295 } 296 } 297 298 public synchronized void unregisterExtension(Extension extension) { 299 // TODO check if framework is shutting down and in that case do nothing 300 if (log.isDebugEnabled()) { 301 log.debug("Unregister contributed extension: " + extension); 302 } 303 ComponentName name = extension.getTargetComponent(); 304 RegistrationInfo ri = reg.getComponent(name); 305 if (ri != null) { 306 ComponentInstance co = ri.getComponent(); 307 if (co != null) { 308 co.unregisterExtension(extension); 309 } 310 } else { // maybe it's pending 311 Set<Extension> extensions = pendingExtensions.get(name); 312 if (extensions != null) { 313 // FIXME: extensions is a set of Extensions, not ComponentNames. 314 extensions.remove(name); 315 if (extensions.isEmpty()) { 316 pendingExtensions.remove(name); 317 } 318 } 319 } 320 sendEvent(new ComponentEvent(ComponentEvent.EXTENSION_UNREGISTERED, 321 ((ComponentInstanceImpl) extension.getComponent()).ri, extension)); 322 } 323 324 public static void loadContributions(RegistrationInfoImpl ri, Extension xt) { 325 ExtensionPointImpl xp = ri.getExtensionPoint(xt.getExtensionPoint()); 326 if (xp != null && xp.contributions != null) { 327 try { 328 Object[] contribs = xp.loadContributions(ri, xt); 329 xt.setContributions(contribs); 330 } catch (RuntimeException e) { 331 handleError("Failed to load contributions for component " + xt.getComponent().getName(), e); 332 } 333 } 334 } 335 336 public synchronized void registerServices(RegistrationInfoImpl ri) { 337 if (ri.serviceDescriptor == null) { 338 return; 339 } 340 for (String service : ri.serviceDescriptor.services) { 341 log.info("Registering service: " + service); 342 services.put(service, ri); 343 // TODO: send notifications 344 } 345 } 346 347 public synchronized void unregisterServices(RegistrationInfoImpl ri) { 348 if (ri.serviceDescriptor == null) { 349 return; 350 } 351 for (String service : ri.serviceDescriptor.services) { 352 services.remove(service); 353 // TODO: send notifications 354 } 355 } 356 357 @Override 358 public synchronized String[] getServices() { 359 return services.keySet().toArray(new String[services.size()]); 360 } 361 362 protected static void handleError(String message, Exception e) { 363 log.error(message, e); 364 Framework.getRuntime().getWarnings().add(message); 365 } 366 367}