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