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 * Nuxeo - initial API and implementation 011 */ 012 013package org.nuxeo.runtime.model.impl; 014 015import java.net.URL; 016import java.util.ArrayList; 017import java.util.Collections; 018import java.util.HashMap; 019import java.util.HashSet; 020import java.util.List; 021import java.util.Map; 022import java.util.Set; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026import org.nuxeo.common.xmap.annotation.XContent; 027import org.nuxeo.common.xmap.annotation.XNode; 028import org.nuxeo.common.xmap.annotation.XNodeList; 029import org.nuxeo.common.xmap.annotation.XNodeMap; 030import org.nuxeo.common.xmap.annotation.XObject; 031import org.nuxeo.runtime.ComponentEvent; 032import org.nuxeo.runtime.Version; 033import org.nuxeo.runtime.api.Framework; 034import org.nuxeo.runtime.model.Component; 035import org.nuxeo.runtime.model.ComponentInstance; 036import org.nuxeo.runtime.model.ComponentManager; 037import org.nuxeo.runtime.model.ComponentName; 038import org.nuxeo.runtime.model.ConfigurationDescriptor; 039import org.nuxeo.runtime.model.Extension; 040import org.nuxeo.runtime.model.ExtensionPoint; 041import org.nuxeo.runtime.model.Property; 042import org.nuxeo.runtime.model.RegistrationInfo; 043import org.nuxeo.runtime.model.RuntimeContext; 044 045/** 046 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 047 */ 048@XObject("component") 049public class RegistrationInfoImpl implements RegistrationInfo { 050 051 private static final long serialVersionUID = -4135715215018199522L; 052 053 private static final Log log = LogFactory.getLog(RegistrationInfoImpl.class); 054 055 // Note: some of these instance variables are accessed directly from other 056 // classes in this package. 057 058 transient ComponentManagerImpl manager; 059 060 @XNode("@service") 061 ServiceDescriptor serviceDescriptor; 062 063 // the managed object name 064 @XNode("@name") 065 ComponentName name; 066 067 @XNode("@disabled") 068 boolean disabled; 069 070 @XNode("configuration") 071 ConfigurationDescriptor config; 072 073 // the registration state 074 int state = UNREGISTERED; 075 076 // my aliases 077 @XNodeList(value = "alias", type = HashSet.class, componentType = ComponentName.class) 078 Set<ComponentName> aliases = new HashSet<>(); 079 080 // the object names I depend of 081 @XNodeList(value = "require", type = HashSet.class, componentType = ComponentName.class) 082 Set<ComponentName> requires = new HashSet<>(); 083 084 @XNode("implementation@class") 085 String implementation; 086 087 @XNodeList(value = "extension-point", type = ExtensionPointImpl[].class, componentType = ExtensionPointImpl.class) 088 ExtensionPointImpl[] extensionPoints = new ExtensionPointImpl[0]; 089 090 @XNodeList(value = "extension", type = ExtensionImpl[].class, componentType = ExtensionImpl.class) 091 ExtensionImpl[] extensions = new ExtensionImpl[0]; 092 093 @XNodeMap(value = "property", key = "@name", type = HashMap.class, componentType = Property.class) 094 Map<String, Property> properties = new HashMap<>(); 095 096 @XNode("@version") 097 Version version = Version.ZERO; 098 099 /** 100 * To be set when deploying configuration components that are not in a bundle (e.g. from config. dir). Represent the 101 * bundle that will be assumed to be the owner of the component. 102 */ 103 @XNode("@bundle") 104 String bundle; 105 106 @XContent("documentation") 107 String documentation; 108 109 URL xmlFileUrl; 110 111 /** 112 * This is used by the component persistence service to identify registration that was dynamically created and 113 * persisted by users. 114 */ 115 boolean isPersistent; 116 117 transient RuntimeContext context; 118 119 // the managed component 120 transient ComponentInstance component; 121 122 public RegistrationInfoImpl() { 123 } 124 125 /** 126 * Useful when dynamically registering components 127 * 128 * @param name the component name 129 */ 130 public RegistrationInfoImpl(ComponentName name) { 131 this.name = name; 132 } 133 134 /** 135 * Attach to a manager - this method must be called after all registration fields are initialized. 136 * 137 * @param manager 138 */ 139 public void attach(ComponentManagerImpl manager) { 140 if (this.manager != null) { 141 throw new IllegalStateException("Registration '" + name + "' was already attached to a manager"); 142 } 143 this.manager = manager; 144 } 145 146 public void setContext(RuntimeContext rc) { 147 this.context = rc; 148 } 149 150 @Override 151 public boolean isDisabled() { 152 return disabled; 153 } 154 155 @Override 156 public final boolean isPersistent() { 157 return isPersistent; 158 } 159 160 @Override 161 public void setPersistent(boolean isPersistent) { 162 this.isPersistent = isPersistent; 163 } 164 165 public void destroy() { 166 requires.clear(); 167 aliases.clear(); 168 properties.clear(); 169 extensionPoints = new ExtensionPointImpl[0]; 170 extensions = new ExtensionImpl[0]; 171 version = null; 172 component = null; 173 name = null; 174 manager = null; 175 } 176 177 public final boolean isDisposed() { 178 return name == null; 179 } 180 181 @Override 182 public ExtensionPoint[] getExtensionPoints() { 183 return extensionPoints; 184 } 185 186 @Override 187 public ComponentInstance getComponent() { 188 return component; 189 } 190 191 /** 192 * Reload the underlying component if reload is supported 193 */ 194 public synchronized void reload() { 195 if (component != null) { 196 component.reload(); 197 } 198 } 199 200 @Override 201 public ComponentName getName() { 202 return name; 203 } 204 205 @Override 206 public Map<String, Property> getProperties() { 207 return properties; 208 } 209 210 public ExtensionPointImpl getExtensionPoint(String name) { 211 for (ExtensionPointImpl xp : extensionPoints) { 212 if (xp.name.equals(name)) { 213 return xp; 214 } 215 } 216 return null; 217 } 218 219 @Override 220 public int getState() { 221 return state; 222 } 223 224 @Override 225 public Extension[] getExtensions() { 226 return extensions; 227 } 228 229 @Override 230 public Set<ComponentName> getAliases() { 231 return aliases == null ? Collections.<ComponentName> emptySet() : aliases; 232 } 233 234 @Override 235 public Set<ComponentName> getRequiredComponents() { 236 return requires; 237 } 238 239 @Override 240 public RuntimeContext getContext() { 241 return context; 242 } 243 244 @Override 245 public String getBundle() { 246 return bundle; 247 } 248 249 @Override 250 public Version getVersion() { 251 return version; 252 } 253 254 @Override 255 public String getDocumentation() { 256 return documentation; 257 } 258 259 @Override 260 public String toString() { 261 return "RegistrationInfo: " + name; 262 } 263 264 @Override 265 public ComponentManager getManager() { 266 return manager; 267 } 268 269 synchronized void register() { 270 if (state != UNREGISTERED) { 271 return; 272 } 273 state = REGISTERED; 274 manager.sendEvent(new ComponentEvent(ComponentEvent.COMPONENT_REGISTERED, this)); 275 } 276 277 synchronized void unregister() { 278 if (state == UNREGISTERED) { 279 return; 280 } 281 if (state == ACTIVATED || state == RESOLVED || state == START_FAILURE ) { 282 unresolve(); 283 } 284 state = UNREGISTERED; 285 manager.sendEvent(new ComponentEvent(ComponentEvent.COMPONENT_UNREGISTERED, this)); 286 destroy(); 287 } 288 289 protected ComponentInstance createComponentInstance() { 290 try { 291 return new ComponentInstanceImpl(this); 292 } catch (RuntimeException e) { 293 String msg = "Failed to instantiate component: " + implementation; 294 log.error(msg, e); 295 msg += " (" + e.toString() + ')'; 296 Framework.getRuntime().getWarnings().add(msg); 297 Framework.handleDevError(e); 298 throw e; 299 } 300 } 301 302 public synchronized void restart() { 303 deactivate(); 304 activate(); 305 } 306 307 @Override 308 public int getApplicationStartedOrder() { 309 if (component == null) { 310 return 0; 311 } 312 Object ci = component.getInstance(); 313 if (!(ci instanceof Component)) { 314 return 0; 315 } 316 return ((Component) ci).getApplicationStartedOrder(); 317 } 318 319 @Override 320 public void notifyApplicationStarted() { 321 if (component != null) { 322 Object ci = component.getInstance(); 323 if (ci instanceof Component) { 324 try { 325 ((Component) ci).applicationStarted(component); 326 } catch (RuntimeException e) { 327 log.error(String.format("Component %s notification of application started failed.", 328 component.getName()), e); 329 state = START_FAILURE; 330 } 331 } 332 } 333 } 334 335 public synchronized void activate() { 336 if (state != RESOLVED) { 337 return; 338 } 339 340 component = createComponentInstance(); 341 342 state = ACTIVATING; 343 manager.sendEvent(new ComponentEvent(ComponentEvent.ACTIVATING_COMPONENT, this)); 344 345 // activate component 346 component.activate(); 347 log.info("Component activated: " + name); 348 349 state = ACTIVATED; 350 manager.sendEvent(new ComponentEvent(ComponentEvent.COMPONENT_ACTIVATED, this)); 351 352 // register contributed extensions if any 353 if (extensions != null) { 354 checkExtensions(); 355 for (Extension xt : extensions) { 356 xt.setComponent(component); 357 try { 358 manager.registerExtension(xt); 359 } catch (RuntimeException e) { 360 String msg = "Failed to register extension to: " + xt.getTargetComponent() + ", xpoint: " 361 + xt.getExtensionPoint() + " in component: " + xt.getComponent().getName(); 362 log.error(msg, e); 363 msg += " (" + e.toString() + ')'; 364 Framework.getRuntime().getWarnings().add(msg); 365 Framework.handleDevError(e); 366 } 367 } 368 } 369 370 // register pending extensions if any 371 List<ComponentName> names = new ArrayList<>(1 + aliases.size()); 372 names.add(name); 373 names.addAll(aliases); 374 for (ComponentName n : names) { 375 Set<Extension> pendingExt = manager.pendingExtensions.remove(n); 376 if (pendingExt == null) { 377 continue; 378 } 379 for (Extension xt : pendingExt) { 380 ComponentManagerImpl.loadContributions(this, xt); 381 try { 382 component.registerExtension(xt); 383 } catch (RuntimeException e) { 384 String msg = "Failed to register extension to: " + xt.getTargetComponent() + ", xpoint: " 385 + xt.getExtensionPoint() + " in component: " + xt.getComponent().getName(); 386 log.error(msg, e); 387 msg += " (" + e.toString() + ')'; 388 Framework.getRuntime().getWarnings().add(msg); 389 Framework.handleDevError(e); 390 } 391 } 392 } 393 } 394 395 public synchronized void deactivate() { 396 if (state != ACTIVATED && state != START_FAILURE) { 397 return; 398 } 399 400 state = DEACTIVATING; 401 manager.sendEvent(new ComponentEvent(ComponentEvent.DEACTIVATING_COMPONENT, this)); 402 403 // unregister contributed extensions if any 404 if (extensions != null) { 405 for (Extension xt : extensions) { 406 try { 407 manager.unregisterExtension(xt); 408 } catch (RuntimeException e) { 409 log.error( 410 "Failed to unregister extension. Contributor: " + xt.getComponent() + " to " 411 + xt.getTargetComponent() + "; xpoint: " + xt.getExtensionPoint(), e); 412 Framework.handleDevError(e); 413 } 414 } 415 } 416 417 component.deactivate(); 418 419 component = null; 420 421 state = RESOLVED; 422 manager.sendEvent(new ComponentEvent(ComponentEvent.COMPONENT_DEACTIVATED, this)); 423 } 424 425 public synchronized void resolve() { 426 if (state != REGISTERED) { 427 return; 428 } 429 430 // register services 431 manager.registerServices(this); 432 433 state = RESOLVED; 434 manager.sendEvent(new ComponentEvent(ComponentEvent.COMPONENT_RESOLVED, this)); 435 // TODO lazy activation 436 activate(); 437 } 438 439 public synchronized void unresolve() { 440 if (state == REGISTERED || state == UNREGISTERED) { 441 return; 442 } 443 444 // un-register services 445 manager.unregisterServices(this); 446 447 if (state == ACTIVATED || state == START_FAILURE) { 448 deactivate(); 449 } 450 state = REGISTERED; 451 manager.sendEvent(new ComponentEvent(ComponentEvent.COMPONENT_UNRESOLVED, this)); 452 } 453 454 @Override 455 // not synchronized, intermediate states from other synchronized methods 456 // are not a problem 457 public boolean isActivated() { 458 return state == ACTIVATED; 459 } 460 461 @Override 462 // not synchronized, intermediate states from other synchronized methods 463 // are not a problem 464 public boolean isResolved() { 465 return state == RESOLVED; 466 } 467 468 @Override 469 public String[] getProvidedServiceNames() { 470 if (serviceDescriptor != null) { 471 return serviceDescriptor.services; 472 } 473 return null; 474 } 475 476 public ServiceDescriptor getServiceDescriptor() { 477 return serviceDescriptor; 478 } 479 480 @Override 481 public String getImplementation() { 482 return implementation; 483 } 484 485 public void checkExtensions() { 486 // HashSet<String> targets = new HashSet<String>(); 487 for (ExtensionImpl xt : extensions) { 488 if (xt.target == null) { 489 Framework.getRuntime().getWarnings().add( 490 "Bad extension declaration (no target attribute specified). Component: " + getName()); 491 continue; 492 } 493 // TODO do nothing for now -> fix the faulty components and then 494 // activate these warnings 495 // String key = xt.target.getName()+"#"+xt.getExtensionPoint(); 496 // if (targets.contains(key)) { // multiple extensions to same 497 // target point declared in same component 498 // String message = 499 // "Component "+getName()+" contains multiple extensions to "+key; 500 // Framework.getRuntime().getWarnings().add(message); 501 // //TODO: un-comment the following line if you want to treat this 502 // as a dev. error 503 // //Framework.handleDevError(new Error(message)); 504 // } else { 505 // targets.add(key); 506 // } 507 } 508 } 509 510 @Override 511 public URL getXmlFileUrl() { 512 return xmlFileUrl; 513 } 514 515 @Override 516 public boolean equals(Object obj) { 517 if (obj == this) { 518 return true; 519 } 520 if (obj instanceof RegistrationInfo) { 521 return name.equals(((RegistrationInfo) obj).getName()); 522 } 523 return false; 524 } 525 526 @Override 527 public int hashCode() { 528 return name.hashCode(); 529 } 530 531}