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