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().getErrors().add(msg); 304 throw e; 305 } 306 } 307 308 public synchronized void restart() { 309 deactivate(); 310 activate(); 311 } 312 313 @Override 314 public int getApplicationStartedOrder() { 315 if (component == null) { 316 return 0; 317 } 318 Object ci = component.getInstance(); 319 if (!(ci instanceof Component)) { 320 return 0; 321 } 322 return ((Component) ci).getApplicationStartedOrder(); 323 } 324 325 @Override 326 public void notifyApplicationStarted() { 327 if (component != null) { 328 Object ci = component.getInstance(); 329 if (ci instanceof Component) { 330 try { 331 ((Component) ci).applicationStarted(component); 332 } catch (RuntimeException e) { 333 log.error(String.format("Component %s notification of application started failed: %s", 334 component.getName(), e.getMessage()), e); 335 state = START_FAILURE; 336 } 337 } 338 } 339 } 340 341 public synchronized void activate() { 342 if (state != RESOLVED) { 343 return; 344 } 345 346 component = createComponentInstance(); 347 348 state = ACTIVATING; 349 manager.sendEvent(new ComponentEvent(ComponentEvent.ACTIVATING_COMPONENT, this)); 350 351 // activate component 352 component.activate(); 353 log.info("Component activated: " + name); 354 355 state = ACTIVATED; 356 manager.sendEvent(new ComponentEvent(ComponentEvent.COMPONENT_ACTIVATED, this)); 357 358 // register contributed extensions if any 359 if (extensions != null) { 360 checkExtensions(); 361 for (Extension xt : extensions) { 362 xt.setComponent(component); 363 try { 364 manager.registerExtension(xt); 365 } catch (RuntimeException e) { 366 String msg = "Failed to register extension to: " + xt.getTargetComponent() + ", xpoint: " 367 + xt.getExtensionPoint() + " in component: " + xt.getComponent().getName(); 368 log.error(msg, e); 369 msg += " (" + e.toString() + ')'; 370 Framework.getRuntime().getErrors().add(msg); 371 } 372 } 373 } 374 375 // register pending extensions if any 376 List<ComponentName> names = new ArrayList<>(1 + aliases.size()); 377 names.add(name); 378 names.addAll(aliases); 379 for (ComponentName n : names) { 380 Set<Extension> pendingExt = manager.pendingExtensions.remove(n); 381 if (pendingExt == null) { 382 continue; 383 } 384 for (Extension xt : pendingExt) { 385 ComponentManagerImpl.loadContributions(this, xt); 386 try { 387 component.registerExtension(xt); 388 } catch (RuntimeException e) { 389 String msg = "Failed to register extension to: " + xt.getTargetComponent() + ", xpoint: " 390 + xt.getExtensionPoint() + " in component: " + xt.getComponent().getName(); 391 log.error(msg, e); 392 msg += " (" + e.toString() + ')'; 393 Framework.getRuntime().getErrors().add(msg); 394 } 395 } 396 } 397 } 398 399 public synchronized void deactivate() { 400 if (state != ACTIVATED && state != START_FAILURE) { 401 return; 402 } 403 404 state = DEACTIVATING; 405 manager.sendEvent(new ComponentEvent(ComponentEvent.DEACTIVATING_COMPONENT, this)); 406 407 // unregister contributed extensions if any 408 if (extensions != null) { 409 for (Extension xt : extensions) { 410 try { 411 manager.unregisterExtension(xt); 412 } catch (RuntimeException e) { 413 String message = "Failed to unregister extension. Contributor: " + xt.getComponent() + " to " 414 + xt.getTargetComponent() + "; xpoint: " + xt.getExtensionPoint(); 415 log.error(message, e); 416 Framework.getRuntime().getErrors().add(message); 417 } 418 } 419 } 420 421 component.deactivate(); 422 423 component = null; 424 425 state = RESOLVED; 426 manager.sendEvent(new ComponentEvent(ComponentEvent.COMPONENT_DEACTIVATED, this)); 427 } 428 429 public synchronized void resolve() { 430 if (state != REGISTERED) { 431 return; 432 } 433 434 // register services 435 manager.registerServices(this); 436 437 state = RESOLVED; 438 manager.sendEvent(new ComponentEvent(ComponentEvent.COMPONENT_RESOLVED, this)); 439 // TODO lazy activation 440 activate(); 441 } 442 443 public synchronized void unresolve() { 444 if (state == REGISTERED || state == UNREGISTERED) { 445 return; 446 } 447 448 // un-register services 449 manager.unregisterServices(this); 450 451 if (state == ACTIVATED || state == START_FAILURE) { 452 deactivate(); 453 } 454 state = REGISTERED; 455 manager.sendEvent(new ComponentEvent(ComponentEvent.COMPONENT_UNRESOLVED, this)); 456 } 457 458 @Override 459 // not synchronized, intermediate states from other synchronized methods 460 // are not a problem 461 public boolean isActivated() { 462 return state == ACTIVATED; 463 } 464 465 @Override 466 // not synchronized, intermediate states from other synchronized methods 467 // are not a problem 468 public boolean isResolved() { 469 return state == RESOLVED; 470 } 471 472 @Override 473 public String[] getProvidedServiceNames() { 474 if (serviceDescriptor != null) { 475 return serviceDescriptor.services; 476 } 477 return null; 478 } 479 480 public ServiceDescriptor getServiceDescriptor() { 481 return serviceDescriptor; 482 } 483 484 @Override 485 public String getImplementation() { 486 return implementation; 487 } 488 489 public void checkExtensions() { 490 // HashSet<String> targets = new HashSet<String>(); 491 for (ExtensionImpl xt : extensions) { 492 if (xt.target == null) { 493 Framework.getRuntime().getWarnings().add( 494 "Bad extension declaration (no target attribute specified). Component: " + getName()); 495 continue; 496 } 497 // TODO do nothing for now -> fix the faulty components and then 498 // activate these warnings 499 // String key = xt.target.getName()+"#"+xt.getExtensionPoint(); 500 // if (targets.contains(key)) { // multiple extensions to same 501 // target point declared in same component 502 // String message = 503 // "Component "+getName()+" contains multiple extensions to "+key; 504 // Framework.getRuntime().getWarnings().add(message); 505 // //TODO: un-comment the following line if you want to treat this 506 // as a dev. error 507 // //Framework.handleDevError(new Error(message)); 508 // } else { 509 // targets.add(key); 510 // } 511 } 512 } 513 514 @Override 515 public URL getXmlFileUrl() { 516 return xmlFileUrl; 517 } 518 519 @Override 520 public boolean equals(Object obj) { 521 if (obj == this) { 522 return true; 523 } 524 if (obj instanceof RegistrationInfo) { 525 return name.equals(((RegistrationInfo) obj).getName()); 526 } 527 return false; 528 } 529 530 @Override 531 public int hashCode() { 532 return name.hashCode(); 533 } 534 535}