001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.geronimo.gbean.runtime; 019 020 import java.io.PrintWriter; 021 import java.io.StringWriter; 022 import java.lang.reflect.Constructor; 023 import java.lang.reflect.InvocationTargetException; 024 import java.util.Collection; 025 import java.util.Collections; 026 import java.util.HashMap; 027 import java.util.HashSet; 028 import java.util.Iterator; 029 import java.util.LinkedHashSet; 030 import java.util.List; 031 import java.util.Map; 032 import java.util.Set; 033 import java.util.Arrays; 034 035 import javax.management.ObjectName; 036 037 import org.apache.commons.logging.Log; 038 import org.apache.commons.logging.LogFactory; 039 import org.apache.geronimo.gbean.AbstractName; 040 import org.apache.geronimo.gbean.AbstractNameQuery; 041 import org.apache.geronimo.gbean.GAttributeInfo; 042 import org.apache.geronimo.gbean.GBeanData; 043 import org.apache.geronimo.gbean.GBeanInfo; 044 import org.apache.geronimo.gbean.GBeanLifecycle; 045 import org.apache.geronimo.gbean.GConstructorInfo; 046 import org.apache.geronimo.gbean.GOperationInfo; 047 import org.apache.geronimo.gbean.GOperationSignature; 048 import org.apache.geronimo.gbean.GReferenceInfo; 049 import org.apache.geronimo.gbean.InvalidConfigurationException; 050 import org.apache.geronimo.gbean.ReferencePatterns; 051 import org.apache.geronimo.kernel.DependencyManager; 052 import org.apache.geronimo.kernel.GBeanNotFoundException; 053 import org.apache.geronimo.kernel.Kernel; 054 import org.apache.geronimo.kernel.NoSuchAttributeException; 055 import org.apache.geronimo.kernel.NoSuchOperationException; 056 import org.apache.geronimo.kernel.repository.Artifact; 057 import org.apache.geronimo.kernel.config.ManageableAttributeStore; 058 import org.apache.geronimo.kernel.management.State; 059 import org.apache.geronimo.kernel.management.StateManageable; 060 061 /** 062 * A GBeanInstance is a J2EE Management Managed Object, and is standard base for Geronimo services. 063 * 064 * @version $Rev:385718 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 065 */ 066 public final class GBeanInstance implements StateManageable { 067 private static final Log log = LogFactory.getLog(GBeanInstance.class); 068 069 private static final int DESTROYED = 0; 070 private static final int CREATING = 1; 071 private static final int RUNNING = 2; 072 private static final int DESTROYING = 3; 073 074 /** 075 * Attribute name used to retrieve the RawInvoker for the GBean 076 */ 077 public static final String RAW_INVOKER = "$$RAW_INVOKER$$"; 078 079 /** 080 * The kernel in which this server is registered. 081 */ 082 private final Kernel kernel; 083 084 /** 085 * The ManageableAttributeStore notified of any changes to manageable 086 * attributes. This is lazy-loaded as manageable attributes are set. 087 */ 088 private ManageableAttributeStore manageableStore; 089 090 /** 091 * the abstract name of this service 092 */ 093 private final AbstractName abstractName; 094 095 /** 096 * This handles all state transiitions for this instance. 097 */ 098 private final GBeanInstanceState gbeanInstanceState; 099 100 /** 101 * The constructor used to create the instance 102 */ 103 private final Constructor constructor; 104 105 /** 106 * A fast index based raw invoker for this GBean. 107 */ 108 private final RawInvoker rawInvoker; 109 110 /** 111 * The single listener to which we broadcast lifecycle change events. 112 */ 113 private final LifecycleBroadcaster lifecycleBroadcaster; 114 115 /** 116 * Interfaces for this GBean 117 */ 118 private final String[] interfaces; 119 120 /** 121 * Attributes lookup table 122 */ 123 private final GBeanAttribute[] attributes; 124 125 /** 126 * Attributes supported by this GBeanMBean by (String) name. 127 */ 128 private final Map attributeIndex = new HashMap(); 129 130 /** 131 * References lookup table 132 */ 133 private final GBeanReference[] references; 134 135 /** 136 * References supported by this GBeanMBean by (String) name. 137 */ 138 private final Map referenceIndex = new HashMap(); 139 140 /** 141 * Dependencies supported by this GBean. 142 */ 143 private final GBeanDependency[] dependencies; 144 145 /** 146 * Operations lookup table 147 */ 148 private final GBeanOperation[] operations; 149 150 /** 151 * Operations supported by this GBeanMBean by (GOperationSignature) name. 152 */ 153 private final Map operationIndex = new HashMap(); 154 155 /** 156 * The classloader used for all invocations and creating targets. 157 */ 158 private final ClassLoader classLoader; 159 160 /** 161 * Metadata describing the attributes, operations and references of this GBean 162 */ 163 private final GBeanInfo gbeanInfo; 164 165 /** 166 * Our name 167 */ 168 private final String name; 169 170 /** 171 * Java type of the wrapped GBean class 172 */ 173 private final Class type; 174 175 /** 176 * Has this instance been destroyed? 177 */ 178 private boolean dead = false; 179 180 /** 181 * The state of the internal gbean instance that we are wrapping. 182 */ 183 private int instanceState = DESTROYED; 184 185 /** 186 * Target instance of this GBean wrapper 187 */ 188 private Object target; 189 190 /** 191 * The time this application started. 192 */ 193 private long startTime; 194 195 /** 196 * This is used to signal the creating thread that it should 197 * fail when it returns from usercode. This is set when a 198 * reference has gone offline during construction. 199 */ 200 private boolean shouldFail = false; 201 202 /** 203 * Used to track instance 204 */ 205 private InstanceRegistry instanceRegistry; 206 207 private String stateReason; 208 209 /** 210 * Construct a GBeanMBean using the supplied GBeanData and class loader 211 * 212 * @param gbeanData the data for the new GBean including GBeanInfo, intial attribute values, and reference patterns 213 * @param classLoader the class loader used to load the gbean instance and attribute/reference types 214 * @throws org.apache.geronimo.gbean.InvalidConfigurationException 215 * if the gbeanInfo is inconsistent with the actual java classes, such as 216 * mismatched attribute types or the intial data cannot be set 217 */ 218 public GBeanInstance(GBeanData gbeanData, Kernel kernel, DependencyManager dependencyManager, LifecycleBroadcaster lifecycleBroadcaster, ClassLoader classLoader) throws InvalidConfigurationException { 219 this.abstractName = gbeanData.getAbstractName(); 220 this.kernel = kernel; 221 this.lifecycleBroadcaster = lifecycleBroadcaster; 222 this.gbeanInstanceState = new GBeanInstanceState(abstractName, kernel, dependencyManager, this, lifecycleBroadcaster); 223 this.classLoader = classLoader; 224 225 GBeanInfo gbeanInfo = gbeanData.getGBeanInfo(); 226 try { 227 type = classLoader.loadClass(gbeanInfo.getClassName()); 228 } catch (ClassNotFoundException e) { 229 throw new InvalidConfigurationException("Could not load GBeanInfo class from classloader: " + classLoader + 230 " className=" + gbeanInfo.getClassName(), e); 231 } 232 233 name = gbeanInfo.getName(); 234 235 // 236 Set constructorArgs = new HashSet(gbeanInfo.getConstructor().getAttributeNames()); 237 238 // interfaces 239 interfaces = (String[]) gbeanInfo.getInterfaces().toArray(new String[0]); 240 241 // attributes 242 Map attributesMap = new HashMap(); 243 for (Iterator iterator = gbeanInfo.getAttributes().iterator(); iterator.hasNext();) { 244 GAttributeInfo attributeInfo = (GAttributeInfo) iterator.next(); 245 attributesMap.put(attributeInfo.getName(), new GBeanAttribute(this, attributeInfo, constructorArgs.contains(attributeInfo.getName()))); 246 } 247 addManagedObjectAttributes(attributesMap); 248 attributes = (GBeanAttribute[]) attributesMap.values().toArray(new GBeanAttribute[attributesMap.size()]); 249 for (int i = 0; i < attributes.length; i++) { 250 attributeIndex.put(attributes[i].getName(), new Integer(i)); 251 } 252 253 // references 254 Set referencesSet = new HashSet(); 255 Set dependencySet = new HashSet(); 256 // add the references 257 Map dataReferences = gbeanData.getReferences(); 258 for (Iterator iterator = gbeanInfo.getReferences().iterator(); iterator.hasNext();) { 259 GReferenceInfo referenceInfo = (GReferenceInfo) iterator.next(); 260 String referenceName = referenceInfo.getName(); 261 ReferencePatterns referencePatterns = (ReferencePatterns) dataReferences.remove(referenceName); 262 if (referenceInfo.getProxyType().equals(Collection.class.getName())) { 263 referencesSet.add(new GBeanCollectionReference(this, referenceInfo, kernel, referencePatterns)); 264 265 } else { 266 referencesSet.add(new GBeanSingleReference(this, referenceInfo, kernel, referencePatterns)); 267 if (referencePatterns != null) { 268 dependencySet.add(new GBeanDependency(this, referencePatterns.getAbstractName(), kernel)); 269 } 270 } 271 } 272 if (!dataReferences.isEmpty()) { 273 throw new IllegalStateException("Attempting to set unknown references: " + dataReferences.keySet()); 274 } 275 276 references = (GBeanReference[]) referencesSet.toArray(new GBeanReference[referencesSet.size()]); 277 for (int i = 0; i < references.length; i++) { 278 referenceIndex.put(references[i].getName(), new Integer(i)); 279 } 280 281 //dependencies 282 for (Iterator iterator = gbeanData.getDependencies().iterator(); iterator.hasNext();) { 283 AbstractName dependencyName = ((ReferencePatterns) iterator.next()).getAbstractName(); 284 dependencySet.add(new GBeanDependency(this, dependencyName, kernel)); 285 } 286 287 dependencies = (GBeanDependency[]) dependencySet.toArray(new GBeanDependency[dependencySet.size()]); 288 289 // framework operations -- all framework operations have currently been removed 290 291 // operations 292 Map operationsMap = new HashMap(); 293 for (Iterator iterator = gbeanInfo.getOperations().iterator(); iterator.hasNext();) { 294 GOperationInfo operationInfo = (GOperationInfo) iterator.next(); 295 GOperationSignature signature = new GOperationSignature(operationInfo.getName(), operationInfo.getParameterList()); 296 // do not allow overriding of framework operations 297 if (!operationsMap.containsKey(signature)) { 298 GBeanOperation operation = new GBeanOperation(this, operationInfo); 299 operationsMap.put(signature, operation); 300 } 301 } 302 operations = new GBeanOperation[operationsMap.size()]; 303 int opCounter = 0; 304 for (Iterator iterator = operationsMap.entrySet().iterator(); iterator.hasNext();) { 305 Map.Entry entry = (Map.Entry) iterator.next(); 306 operations[opCounter] = (GBeanOperation) entry.getValue(); 307 operationIndex.put(entry.getKey(), new Integer(opCounter)); 308 opCounter++; 309 } 310 311 // get the constructor 312 List arguments = gbeanInfo.getConstructor().getAttributeNames(); 313 Class[] parameterTypes = new Class[arguments.size()]; 314 for (int i = 0; i < parameterTypes.length; i++) { 315 String argumentName = (String) arguments.get(i); 316 if (referenceIndex.containsKey(argumentName)) { 317 Integer index = (Integer) referenceIndex.get(argumentName); 318 GBeanReference reference = references[index.intValue()]; 319 parameterTypes[i] = reference.getProxyType(); 320 } else if (attributeIndex.containsKey(argumentName)) { 321 Integer index = (Integer) attributeIndex.get(argumentName); 322 GBeanAttribute attribute = attributes[index.intValue()]; 323 parameterTypes[i] = attribute.getType(); 324 } 325 } 326 try { 327 constructor = type.getConstructor(parameterTypes); 328 } catch (NoSuchMethodException e) { 329 StringBuffer buf = new StringBuffer("Could not find a valid constructor for GBean: ").append(gbeanInfo.getName()).append("\n"); 330 buf.append("ParameterTypes: ").append(Arrays.asList(parameterTypes)).append("\n"); 331 Constructor[] constructors = type.getConstructors(); 332 for (int i = 0; i < constructors.length; i++) { 333 Constructor testConstructor = constructors[i]; 334 buf.append("constructor types: ").append(Arrays.asList(testConstructor.getParameterTypes())).append("\n"); 335 if (testConstructor.getParameterTypes().length == parameterTypes.length) { 336 Class[] testParameterTypes = testConstructor.getParameterTypes(); 337 for (int k = 0; k < testParameterTypes.length; k++) { 338 Class testParameterType = testParameterTypes[k]; 339 if (parameterTypes[k].getName().equals(testParameterType.getName())) { 340 if (parameterTypes[k].getClassLoader() != testParameterType.getClassLoader()) { 341 buf.append("different classloaders in position: ").append(k).append(" class name: ").append(testParameterType.getName()).append("\n"); 342 buf.append("parameter type classloader: ").append(parameterTypes[k].getClassLoader()).append("\n"); 343 buf.append("constructor type classloader: ").append(testParameterType.getClassLoader()).append("\n"); 344 } 345 } else { 346 buf.append("different type in position: ").append(k).append("\n"); 347 } 348 } 349 } 350 } 351 throw new InvalidConfigurationException(buf.toString()); 352 } catch (NoClassDefFoundError e) { 353 throw new InvalidConfigurationException(e); 354 } 355 356 // rebuild the gbean info based on the current attributes, operations, and references because 357 // the above code add new attributes and operations 358 this.gbeanInfo = rebuildGBeanInfo(gbeanInfo.getConstructor(), gbeanInfo.getJ2eeType()); 359 360 // create the raw invokers 361 rawInvoker = new RawInvoker(this); 362 363 // set the initial attribute values 364 try { 365 // set the attributes 366 Map dataAttributes = gbeanData.getAttributes(); 367 for (Iterator iterator = dataAttributes.entrySet().iterator(); iterator.hasNext();) { 368 Map.Entry entry = (Map.Entry) iterator.next(); 369 String attributeName = (String) entry.getKey(); 370 Object attributeValue = entry.getValue(); 371 if (entry.getValue() != null) { 372 setAttribute(attributeName, attributeValue, false); 373 } 374 } 375 376 } catch (Exception e) { 377 throw new InvalidConfigurationException("Could not inject configuration data into the GBean " + abstractName, e); 378 } 379 380 //Add the reference to all applicable reference collections before possibly starting the gbean having an 381 //explicit reference to the reference. 382 for (int i = 0; i < references.length; i++) { 383 references[i].online(); 384 } 385 for (int i = 0; i < dependencies.length; i++) { 386 dependencies[i].online(); 387 } 388 } 389 390 public void die() throws GBeanNotFoundException { 391 synchronized (this) { 392 if (dead) { 393 // someone beat us to the punch... this instance should have never been found in the first place 394 throw new GBeanNotFoundException(abstractName); 395 } 396 dead = true; 397 } 398 399 // if the bean is already stopped or failed, this will do nothing; otherwise it will shutdown the bean 400 gbeanInstanceState.fail(); 401 402 for (int i = 0; i < references.length; i++) { 403 references[i].offline(); 404 } 405 for (int i = 0; i < dependencies.length; i++) { 406 dependencies[i].offline(); 407 } 408 409 // tell everyone we are done 410 lifecycleBroadcaster.fireUnloadedEvent(); 411 412 manageableStore = null; 413 } 414 415 public synchronized void setInstanceRegistry(InstanceRegistry instanceRegistry) { 416 this.instanceRegistry = instanceRegistry; 417 } 418 419 /** 420 * Gets the name of the GBean as defined in the gbean info. 421 * 422 * @return the gbean name 423 */ 424 public String getName() { 425 return name; 426 } 427 428 /** 429 * The class loader used to build this gbean. This class loader is set into the thread context 430 * class loader before callint the target instace. 431 * 432 * @return the class loader used to build this gbean 433 */ 434 public ClassLoader getClassLoader() { 435 return classLoader; 436 } 437 438 /** 439 * Has this gbean instance been destroyed. An destroyed gbean can no longer be used. 440 * 441 * @return true if the gbean has been destroyed 442 */ 443 public synchronized boolean isDead() { 444 return dead; 445 } 446 447 /** 448 * Gets the reason we are in the current state. 449 * @return the reason we are in the current state 450 */ 451 public String getStateReason() { 452 return stateReason; 453 } 454 455 /** 456 * Sets the reason we are in the current state. 457 * @param reason The reason we are in the current state 458 */ 459 public void setStateReason(String reason) { 460 stateReason = reason; 461 } 462 463 /** 464 * The java type of the wrapped gbean instance 465 * 466 * @return the java type of the gbean 467 */ 468 public Class getType() { 469 return type; 470 } 471 472 public synchronized Object getTarget() { 473 return target; 474 } 475 476 public final String getObjectName() { 477 return abstractName.getObjectName().getCanonicalName(); 478 } 479 480 public final ObjectName getObjectNameObject() { 481 return abstractName.getObjectName(); 482 } 483 484 public final AbstractName getAbstractName() { 485 return abstractName; 486 } 487 488 public synchronized final long getStartTime() { 489 return startTime; 490 } 491 492 public int getState() { 493 return gbeanInstanceState.getState(); 494 } 495 496 public final State getStateInstance() { 497 return gbeanInstanceState.getStateInstance(); 498 } 499 500 /** 501 * Gets an unmodifiable map from attribute names to index number (Integer). This index number 502 * can be used to efficiently set or retrieve an attribute value. 503 * 504 * @return an unmodifiable map of attribute indexes by name 505 */ 506 public Map getAttributeIndex() { 507 return Collections.unmodifiableMap(new HashMap(attributeIndex)); 508 } 509 510 /** 511 * Gets an unmodifiable map from operation signature (GOperationSignature) to index number (Integer). 512 * This index number can be used to efficciently invoke the operation. 513 * 514 * @return an unmodifiable map of operation indexec by signature 515 */ 516 public Map getOperationIndex() { 517 return Collections.unmodifiableMap(new HashMap(operationIndex)); 518 } 519 520 /** 521 * Gets the GBeanInfo used to build this gbean. 522 * 523 * @return the GBeanInfo used to build this gbean 524 */ 525 public GBeanInfo getGBeanInfo() { 526 return gbeanInfo; 527 } 528 529 /** 530 * Moves this GBeanInstance to the starting state and then attempts to move this MBean immediately 531 * to the running state. 532 * 533 * @throws IllegalStateException If the gbean is disabled 534 */ 535 public final void start() { 536 synchronized (this) { 537 if (dead) { 538 throw new IllegalStateException("A dead GBean can not be started: abstractName=" + abstractName); 539 } 540 } 541 gbeanInstanceState.start(); 542 } 543 544 /** 545 * Starts this GBeanInstance and then attempts to start all of its start dependent children. 546 * 547 * @throws IllegalStateException If the gbean is disabled 548 */ 549 public final void startRecursive() { 550 synchronized (this) { 551 if (dead) { 552 throw new IllegalStateException("A dead GBean can not be started: abstractName=" + abstractName); 553 } 554 } 555 gbeanInstanceState.startRecursive(); 556 } 557 558 /** 559 * Moves this GBeanInstance to the STOPPING state, calls stop on all start dependent children, and then attempt 560 * to move this MBean to the STOPPED state. 561 */ 562 public final void stop() { 563 gbeanInstanceState.stop(); 564 } 565 566 /** 567 * Moves this GBeanInstance to the FAILED state. There are no calls to dependent children, but they will be 568 * notified using standard J2EE management notification. 569 */ 570 final void referenceFailed() { 571 gbeanInstanceState.fail(); 572 } 573 574 /** 575 * Gets the gbean data for the gbean held by this gbean mbean. 576 * 577 * @return the gbean data 578 */ 579 public GBeanData getGBeanData() { 580 GBeanData gbeanData = new GBeanData(abstractName, gbeanInfo); 581 582 // copy target into local variables from within a synchronized block to gaurentee a consistent read 583 int state; 584 Object instance; 585 synchronized (this) { 586 state = instanceState; 587 instance = target; 588 } 589 590 // add the attributes 591 for (int i = 0; i < attributes.length; i++) { 592 GBeanAttribute attribute = attributes[i]; 593 if (attribute.isPersistent()) { 594 String name = attribute.getName(); 595 Object value; 596 if ((state != DESTROYED || attribute.isFramework()) && attribute.isReadable()) { 597 try { 598 value = attribute.getValue(instance); 599 } catch (Throwable throwable) { 600 value = attribute.getPersistentValue(); 601 log.debug("Could not get the current value of persistent attribute. The persistent " + 602 "attribute will not reflect the current state attribute. " + attribute.getDescription(), throwable); 603 } 604 } else { 605 value = attribute.getPersistentValue(); 606 } 607 gbeanData.setAttribute(name, value); 608 } 609 } 610 611 // add the references 612 for (int i = 0; i < references.length; i++) { 613 GBeanReference reference = references[i]; 614 String name = reference.getName(); 615 if(reference instanceof GBeanSingleReference) { 616 AbstractName abstractName = ((GBeanSingleReference) reference).getTargetName(); 617 if(abstractName != null) { 618 gbeanData.setReferencePattern(name, abstractName); 619 } 620 } else if(reference instanceof GBeanCollectionReference) { 621 Set patterns = ((GBeanCollectionReference) reference).getPatterns(); 622 if(patterns != null) { 623 gbeanData.setReferencePatterns(name, patterns); 624 } 625 } else { 626 throw new IllegalStateException("Unrecognized GBeanReference '"+reference.getClass().getName()+"'"); 627 } 628 } 629 //TODO copy the dependencies?? 630 return gbeanData; 631 } 632 633 /** 634 * Gets the attribute value using the attribute index. This is the most efficient way to get 635 * an attribute as it avoids a HashMap lookup. 636 * 637 * @param index the index of the attribute 638 * @return the attribute value 639 * @throws Exception if a target instance throws and exception 640 * @throws IndexOutOfBoundsException if the index is invalid 641 */ 642 public Object getAttribute(int index) throws Exception { 643 GBeanAttribute attribute = attributes[index]; 644 645 // copy target into local variables from within a synchronized block to gaurentee a consistent read 646 int state; 647 Object instance; 648 synchronized (this) { 649 state = instanceState; 650 instance = target; 651 } 652 653 if (state != DESTROYED || attribute.isFramework()) { 654 return attribute.getValue(instance); 655 } else { 656 if (attribute.isPersistent()) { 657 return attribute.getPersistentValue(); 658 } else { 659 throw new IllegalStateException("Cannot retrieve the value for non-persistent attribute \"" + attribute.getName() + "\" when GBeanInstance is DESTROYED"); 660 } 661 } 662 } 663 664 /** 665 * Gets an attribute's value by name. This get style is less efficient becuse the attribute must 666 * first be looked up in a HashMap. 667 * 668 * @param attributeName the name of the attribute to retrieve 669 * @return the attribute value 670 * @throws Exception if a problem occurs while getting the value 671 * @throws NoSuchAttributeException if the attribute name is not found in the map 672 */ 673 public Object getAttribute(String attributeName) throws NoSuchAttributeException, Exception { 674 GBeanAttribute attribute; 675 try { 676 attribute = getAttributeByName(attributeName); 677 } catch (NoSuchAttributeException e) { 678 if (attributeName.equals(RAW_INVOKER)) { 679 return rawInvoker; 680 } 681 throw e; 682 } 683 684 // copy target into local variables from within a synchronized block to gaurentee a consistent read 685 int state; 686 Object instance; 687 synchronized (this) { 688 state = instanceState; 689 instance = target; 690 } 691 692 if (state != DESTROYED || attribute.isFramework()) { 693 return attribute.getValue(instance); 694 } else { 695 if (attribute.isPersistent()) { 696 return attribute.getPersistentValue(); 697 } else { 698 throw new IllegalStateException("Cannot retrieve the value for non-persistent attribute " + attributeName + " when gbean has been destroyed: " + abstractName); 699 } 700 } 701 } 702 703 /** 704 * Sets the attribute value using the attribute index. This is the most efficient way to set 705 * an attribute as it avoids a HashMap lookup. 706 * 707 * @param index the index of the attribute 708 * @param value the new value of attribute value 709 * @throws Exception if a target instance throws and exception 710 * @throws IndexOutOfBoundsException if the index is invalid 711 */ 712 public void setAttribute(int index, Object value) throws Exception, IndexOutOfBoundsException { 713 setAttribute(index, value, true); 714 } 715 716 private void setAttribute(int index, Object value, boolean manage) throws Exception, IndexOutOfBoundsException { 717 GBeanAttribute attribute = attributes[index]; 718 719 // copy target into local variables from within a synchronized block to gaurentee a consistent read 720 int state; 721 Object instance; 722 synchronized (this) { 723 state = instanceState; 724 instance = target; 725 } 726 727 if (state != DESTROYED || attribute.isFramework()) { 728 attribute.setValue(instance, value); 729 } else { 730 attribute.setPersistentValue(value); 731 } 732 if (manage && attribute.isManageable()) { 733 updateManageableAttribute(attribute, value); 734 } 735 } 736 737 /** 738 * Sets an attribute's value by name. This set style is less efficient becuse the attribute must 739 * first be looked up in a HashMap. 740 * 741 * @param attributeName the name of the attribute to retrieve 742 * @param value the new attribute value 743 * @throws Exception if a target instance throws and exception 744 * @throws NoSuchAttributeException if the attribute name is not found in the map 745 */ 746 public void setAttribute(String attributeName, Object value) throws Exception, NoSuchAttributeException { 747 setAttribute(attributeName, value, true); 748 } 749 750 public void setAttribute(String attributeName, Object value, boolean manage) throws Exception, NoSuchAttributeException { 751 GBeanAttribute attribute = getAttributeByName(attributeName); 752 753 // copy target into local variables from within a synchronized block to gaurentee a consistent read 754 int state; 755 Object instance; 756 synchronized (this) { 757 state = instanceState; 758 instance = target; 759 } 760 761 if (state != DESTROYED || attribute.isFramework()) { 762 attribute.setValue(instance, value); 763 } else { 764 attribute.setPersistentValue(value); 765 } 766 if (manage && attribute.isManageable()) { 767 updateManageableAttribute(attribute, value); 768 } 769 } 770 771 private void updateManageableAttribute(GBeanAttribute attribute, Object value) { 772 if (manageableStore == null) { 773 manageableStore = getManageableAttributeStore(); 774 if (manageableStore == null) { 775 return; 776 } 777 } 778 Artifact configName = abstractName.getArtifact(); 779 if (configName != null) { 780 manageableStore.setValue(configName, abstractName, attribute.getAttributeInfo(), value, classLoader); 781 } else { 782 log.error("Unable to identify Configuration for GBean " + abstractName + ". Manageable attribute " + attribute.getName() + " was not updated in persistent store."); 783 } 784 } 785 786 private ManageableAttributeStore getManageableAttributeStore() { 787 Set set = kernel.listGBeans(new AbstractNameQuery(ManageableAttributeStore.class.getName())); 788 for (Iterator iterator = set.iterator(); iterator.hasNext();) { 789 AbstractName abstractName1 = (AbstractName) iterator.next(); 790 try { 791 return (ManageableAttributeStore) kernel.getGBean(abstractName1); 792 } catch (GBeanNotFoundException e) { 793 // ignored... gbean was unregistered 794 } 795 } 796 return null; 797 } 798 799 private GBeanAttribute getAttributeByName(String name) throws NoSuchAttributeException { 800 Integer index = (Integer) attributeIndex.get(name); 801 if (index == null) { 802 throw new NoSuchAttributeException("Unknown attribute \"" + name + "\" in gbean " + abstractName); 803 } 804 return attributes[index.intValue()]; 805 } 806 807 /** 808 * Invokes an opreation using the operation index. This is the most efficient way to invoke 809 * an operation as it avoids a HashMap lookup. 810 * 811 * @param index the index of the attribute 812 * @param arguments the arguments to the operation 813 * @return the result of the operation 814 * @throws Exception if a target instance throws and exception 815 * @throws IndexOutOfBoundsException if the index is invalid 816 * @throws IllegalStateException if the gbean instance has been destroyed 817 */ 818 public Object invoke(int index, Object[] arguments) throws Exception { 819 GBeanOperation operation = operations[index]; 820 821 // copy target into local variables from within a synchronized block to gaurentee a consistent read 822 int state; 823 Object instance; 824 synchronized (this) { 825 state = instanceState; 826 instance = target; 827 } 828 829 if (state == DESTROYED && !operation.isFramework()) { 830 throw new IllegalStateException("Operations can only be invoke while the GBean instance is running: " + abstractName); 831 } 832 return operation.invoke(instance, arguments); 833 } 834 835 /** 836 * Invokes an operation on the target gbean by method signature. This style if invocation is 837 * inefficient, because the target method must be looked up in a hashmap using a freshly constructed 838 * GOperationSignature object. 839 * 840 * @param operationName the name of the operation to invoke 841 * @param arguments arguments to the operation 842 * @param types types of the operation arguemtns 843 * @return the result of the operation 844 * @throws Exception if a target instance throws and exception 845 * @throws NoSuchOperationException if the operation signature is not found in the map 846 * @throws IllegalStateException if the gbean instance has been destroyed 847 */ 848 public Object invoke(String operationName, Object[] arguments, String[] types) throws Exception, NoSuchOperationException { 849 GOperationSignature signature = new GOperationSignature(operationName, types); 850 Integer index = (Integer) operationIndex.get(signature); 851 if (index == null) { 852 throw new NoSuchOperationException("Unknown operation " + signature); 853 } 854 GBeanOperation operation = operations[index.intValue()]; 855 856 // copy target into local variables from within a synchronized block to gaurentee a consistent read 857 int state; 858 Object instance; 859 synchronized (this) { 860 state = instanceState; 861 instance = target; 862 } 863 864 if (state == DESTROYED && !operation.isFramework()) { 865 throw new IllegalStateException("Operations can only be invoke while the GBean is running: " + abstractName); 866 } 867 return operation.invoke(instance, arguments); 868 } 869 870 private GBeanReference getReferenceByName(String name) { 871 Integer index = (Integer) referenceIndex.get(name); 872 if (index == null) { 873 throw new IllegalArgumentException("Unknown reference " + name); 874 } 875 return references[index.intValue()]; 876 } 877 878 boolean createInstance() throws Exception { 879 synchronized (this) { 880 // first check we are still in the correct state to start 881 if (instanceState == CREATING || instanceState == RUNNING) { 882 // another thread already completed starting 883 return false; 884 } else if (instanceState == DESTROYING) { 885 // this should never ever happen... this method is protected by the GBeanState class which should 886 // prevent stuff like this happening, but check anyway 887 stateReason = "an internal error has occurred. An attempt was made to start an instance that was still stopping which is an illegal state transition."; 888 throw new IllegalStateException("A stopping instance can not be started until fully stopped"); 889 } 890 assert instanceState == DESTROYED; 891 892 stateReason = null; 893 894 // Call all start on every reference. This way the dependecies are held until we can start 895 LinkedHashSet unstarted = new LinkedHashSet(); 896 for (int i = 0; i < dependencies.length; i++) { 897 if (!dependencies[i].start()) { 898 unstarted.add(dependencies[i].getTargetName()); 899 } 900 } 901 for (int i = 0; i < references.length; i++) { 902 if (!references[i].start()) { 903 if (references[i] instanceof GBeanSingleReference) { 904 GBeanSingleReference reference = (GBeanSingleReference) references[i]; 905 unstarted.add(reference.getTargetName()); 906 } 907 } 908 } 909 if (!unstarted.isEmpty()) { 910 if (unstarted.size() == 1) { 911 stateReason = unstarted.iterator().next() + " did not start."; 912 } else { 913 stateReason = "the following dependent services did not start: " + unstarted; 914 } 915 return false; 916 } 917 918 // we are definately going to (try to) start... if this fails the must clean up these variables 919 instanceState = CREATING; 920 startTime = System.currentTimeMillis(); 921 } 922 923 ClassLoader oldCL = Thread.currentThread().getContextClassLoader(); 924 Thread.currentThread().setContextClassLoader(classLoader); 925 Object instance = null; 926 try { 927 GConstructorInfo constructorInfo = gbeanInfo.getConstructor(); 928 Class[] parameterTypes = constructor.getParameterTypes(); 929 930 // create constructor parameter array 931 Object[] parameters = new Object[parameterTypes.length]; 932 Iterator names = constructorInfo.getAttributeNames().iterator(); 933 for (int i = 0; i < parameters.length; i++) { 934 String name = (String) names.next(); 935 if (referenceIndex.containsKey(name)) { 936 parameters[i] = getReferenceByName(name).getProxy(); 937 } else if (attributeIndex.containsKey(name)) { 938 GBeanAttribute attribute = getAttributeByName(name); 939 parameters[i] = attribute.getPersistentValue(); 940 } else { 941 stateReason = "the service constructor definition contained the name '" + name + "' which is not a known attribute or reference of the service."; 942 throw new InvalidConfigurationException("Unknown attribute or reference name in constructor: referenceName=" + name + ", gbean=" + abstractName); 943 } 944 } 945 946 // create instance 947 try { 948 instance = constructor.newInstance(parameters); 949 } catch (InvocationTargetException e) { 950 Throwable targetException = e.getTargetException(); 951 if (targetException instanceof Exception) { 952 stateReason = "the service constructor threw an exception. \n" + printException(targetException); 953 throw (Exception) targetException; 954 } else if (targetException instanceof Error) { 955 stateReason = "the service constructor threw an exception. \n" + printException(targetException); 956 throw (Error) targetException; 957 } 958 stateReason = "the service constructor threw an exception. \n" + printException(e); 959 throw e; 960 } catch (IllegalArgumentException e) { 961 stateReason = "the service constructor threw an exception due to a parameter type mismatch. \n" + printException(e); 962 log.warn("Constructor mismatch for " + abstractName, e); 963 throw e; 964 } 965 966 // write the target variable in a synchronized block so it is available to all threads 967 // we do this before calling the setters or start method so the bean can be called back 968 // from a setter start method 969 synchronized (this) { 970 target = instance; 971 } 972 973 // inject the persistent attribute value into the new instance 974 for (int i = 0; i < attributes.length; i++) { 975 checkIfShouldFail(); 976 try { 977 attributes[i].inject(instance); 978 } catch (Exception e) { 979 stateReason = "the setter for attribute '" + attributes[i].getName() + "' threw an exception. \n" + printException(e); 980 throw e; 981 } 982 } 983 984 // inject the proxies into the new instance 985 for (int i = 0; i < references.length; i++) { 986 checkIfShouldFail(); 987 try { 988 references[i].inject(instance); 989 } catch (Exception e) { 990 stateReason = "the setter for reference '" + references[i].getName() + "' threw an exception. \n" + printException(e); 991 throw e; 992 } 993 } 994 995 if (instance instanceof GBeanLifecycle) { 996 checkIfShouldFail(); 997 try { 998 ((GBeanLifecycle) instance).doStart(); 999 } catch (Exception e) { 1000 stateReason = "the doStart method threw an exception. \n" + printException(e); 1001 throw e; 1002 } 1003 } 1004 1005 1006 // all done... we are now fully running 1007 synchronized (this) { 1008 checkIfShouldFail(); 1009 if (instanceRegistry != null) { 1010 instanceRegistry.instanceCreated(instance, this); 1011 } 1012 instanceState = RUNNING; 1013 this.notifyAll(); 1014 } 1015 1016 1017 stateReason = null; 1018 return true; 1019 } catch (Throwable t) { 1020 stateReason = "Throwable during start of gbean: \n" + printException(t); 1021 // something went wrong... we need to destroy this instance 1022 synchronized (this) { 1023 instanceState = DESTROYING; 1024 } 1025 1026 if (instance instanceof GBeanLifecycle) { 1027 try { 1028 ((GBeanLifecycle) instance).doFail(); 1029 } catch (Throwable ignored) { 1030 log.error("Problem in doFail of " + abstractName, ignored); 1031 } 1032 } 1033 1034 // bean has been notified... drop our reference 1035 synchronized (this) { 1036 // stop all of the references 1037 for (int i = 0; i < references.length; i++) { 1038 references[i].stop(); 1039 } 1040 for (int i = 0; i < dependencies.length; i++) { 1041 dependencies[i].stop(); 1042 } 1043 1044 target = null; 1045 instanceState = DESTROYED; 1046 startTime = 0; 1047 this.notifyAll(); 1048 } 1049 1050 if (t instanceof Exception) { 1051 throw (Exception) t; 1052 } else if (t instanceof Error) { 1053 throw (Error) t; 1054 } else { 1055 throw new Error(t); 1056 } 1057 } finally { 1058 Thread.currentThread().setContextClassLoader(oldCL); 1059 } 1060 } 1061 1062 private String printException(Throwable t) { 1063 StringWriter stringWriter = new StringWriter(); 1064 PrintWriter printWriter = new PrintWriter(stringWriter); 1065 t.printStackTrace(printWriter); 1066 printWriter.flush(); 1067 return stringWriter.toString(); 1068 } 1069 1070 private synchronized void checkIfShouldFail() throws Exception { 1071 if (shouldFail) { 1072 shouldFail = false; 1073 throw new Exception("A reference has failed so construction can not complete"); 1074 } 1075 } 1076 1077 boolean destroyInstance(boolean stop) throws Exception { 1078 Object instance; 1079 synchronized (this) { 1080 if (!stop && instanceState == CREATING) { 1081 // signal to the creating thead that it should fail 1082 shouldFail = true; 1083 return false; 1084 } 1085 1086 // if the instance is being created we need to wait 1087 // for it to finish before we can try to stop it 1088 while (instanceState == CREATING) { 1089 // todo should we limit this wait? If so, how do we configure the wait time? 1090 try { 1091 this.wait(); 1092 } catch (InterruptedException e) { 1093 // clear the interrupted flag 1094 Thread.interrupted(); 1095 // rethrow the interrupted exception.... someone was sick of us waiting 1096 throw e; 1097 } 1098 } 1099 1100 if (instanceState == DESTROYING || instanceState == DESTROYED) { 1101 // another thread is already stopping or has already stopped 1102 return false; 1103 } 1104 assert instanceState == RUNNING; 1105 stateReason = null; 1106 1107 // we are definately going to stop... if this fails the must clean up these variables 1108 instanceState = DESTROYING; 1109 instance = target; 1110 } 1111 1112 // update the persistent attributes 1113 // do not update the persistent attibute values in the case of a failure 1114 // failed gbeans may have corrupted attributes that would be persisted 1115 Exception problem = null; 1116 if (stop && instance != null) { 1117 try { 1118 // get all the data but don't update in case there is an exception 1119 Map data = new HashMap(); 1120 for (int i = 0; i < attributes.length; i++) { 1121 GBeanAttribute attribute = attributes[i]; 1122 if (attribute.isPersistent() && attribute.isReadable()) { 1123 // copy the current attribute value to the persistent value 1124 Object value; 1125 try { 1126 value = attribute.getValue(instance); 1127 } catch (Throwable e) { 1128 // There is no reason to create a new Exception sub class as this exception will 1129 // simply be caught and logged on GBeanInstanceState 1130 throw new Exception("Problem while updaing the persistent value of attibute: " + 1131 "Attribute Name: " + attribute.getName() + ", " + 1132 "Type: " + attribute.getType() + ", " + 1133 "GBeanInstance: " + getName(), e); 1134 } 1135 data.put(attribute, value); 1136 } 1137 } 1138 // now we have all the data we can update the persistent values 1139 for (int i = 0; i < attributes.length; i++) { 1140 GBeanAttribute attribute = attributes[i]; 1141 if (attribute.isPersistent() && attribute.isReadable()) { 1142 // copy the current attribute value to the persistent value 1143 Object value = data.get(attribute); 1144 attribute.setPersistentValue(value); 1145 } 1146 } 1147 } catch (Exception e) { 1148 // the getter threw an exception; now we must to fail 1149 stop = false; 1150 problem = e; 1151 } 1152 } 1153 1154 // we notify the bean before removing our reference so the bean can be called back while stopping 1155 ClassLoader oldCL = Thread.currentThread().getContextClassLoader(); 1156 Thread.currentThread().setContextClassLoader(classLoader); 1157 try { 1158 if (instance instanceof GBeanLifecycle) { 1159 if (stop) { 1160 try { 1161 ((GBeanLifecycle) instance).doStop(); 1162 } catch (Throwable ignored) { 1163 log.error("Problem in doStop of " + abstractName, ignored); 1164 } 1165 } else { 1166 try { 1167 ((GBeanLifecycle) instance).doFail(); 1168 } catch (Throwable ignored) { 1169 log.error("Problem in doFail of " + abstractName, ignored); 1170 } 1171 } 1172 } 1173 } finally { 1174 Thread.currentThread().setContextClassLoader(oldCL); 1175 } 1176 1177 // bean has been notified... drop our reference 1178 synchronized (this) { 1179 // stop all of the references 1180 for (int i = 0; i < references.length; i++) { 1181 references[i].stop(); 1182 } 1183 for (int i = 0; i < dependencies.length; i++) { 1184 dependencies[i].stop(); 1185 } 1186 1187 target = null; 1188 instanceState = DESTROYED; 1189 if (instanceRegistry != null) { 1190 instanceRegistry.instanceDestroyed(instance); 1191 } 1192 startTime = 0; 1193 } 1194 1195 if (problem != null) { 1196 throw problem; 1197 } 1198 return true; 1199 } 1200 1201 private void addManagedObjectAttributes(Map attributesMap) { 1202 // 1203 // Special attributes 1204 // 1205 attributesMap.put("abstractName", 1206 GBeanAttribute.createSpecialAttribute((GBeanAttribute) attributesMap.get("abstractName"), 1207 this, 1208 "abstractName", 1209 AbstractName.class, 1210 getAbstractName())); 1211 1212 attributesMap.put("objectName", 1213 GBeanAttribute.createSpecialAttribute((GBeanAttribute) attributesMap.get("objectName"), 1214 this, 1215 "objectName", 1216 String.class, 1217 getObjectName())); 1218 1219 attributesMap.put("classLoader", 1220 GBeanAttribute.createSpecialAttribute((GBeanAttribute) attributesMap.get("classLoader"), 1221 this, 1222 "classLoader", 1223 ClassLoader.class, 1224 classLoader)); 1225 1226 attributesMap.put("kernel", 1227 GBeanAttribute.createSpecialAttribute((GBeanAttribute) attributesMap.get("kernel"), 1228 this, 1229 "kernel", 1230 Kernel.class, 1231 kernel)); 1232 1233 } 1234 1235 private GBeanInfo rebuildGBeanInfo(GConstructorInfo constructor, String j2eeType) { 1236 Set attributeInfos = new HashSet(); 1237 for (int i = 0; i < attributes.length; i++) { 1238 GBeanAttribute attribute = attributes[i]; 1239 attributeInfos.add(attribute.getAttributeInfo()); 1240 } 1241 Set operationInfos = new HashSet(); 1242 for (int i = 0; i < operations.length; i++) { 1243 operationInfos.add(operations[i].getOperationInfo()); 1244 } 1245 1246 Set referenceInfos = new HashSet(); 1247 for (int i = 0; i < references.length; i++) { 1248 referenceInfos.add(references[i].getReferenceInfo()); 1249 } 1250 1251 Set interfaceInfos = new HashSet(); 1252 for (int i = 0; i < interfaces.length; i++) { 1253 interfaceInfos.add(interfaces[i]); 1254 } 1255 1256 return new GBeanInfo(name, 1257 type.getName(), 1258 j2eeType, 1259 attributeInfos, 1260 constructor, 1261 operationInfos, 1262 referenceInfos, 1263 interfaceInfos); 1264 } 1265 1266 public boolean equals(Object obj) { 1267 if (obj == this) return true; 1268 if (!(obj instanceof GBeanInstance)) return false; 1269 return abstractName.equals(((GBeanInstance) obj).abstractName); 1270 } 1271 1272 public int hashCode() { 1273 return abstractName.hashCode(); 1274 } 1275 1276 public String toString() { 1277 return abstractName.toString(); 1278 } 1279 }