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