001 /** 002 * 003 * Copyright 2003-2004 The Apache Software Foundation 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * 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: 2006-08-17 15:28:09 -0700 (Thu, 17 Aug 2006) $ 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()); 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 } 353 354 // rebuild the gbean info based on the current attributes, operations, and references because 355 // the above code add new attributes and operations 356 this.gbeanInfo = rebuildGBeanInfo(gbeanInfo.getConstructor(), gbeanInfo.getJ2eeType()); 357 358 // create the raw invokers 359 rawInvoker = new RawInvoker(this); 360 361 // set the initial attribute values 362 try { 363 // set the attributes 364 Map dataAttributes = gbeanData.getAttributes(); 365 for (Iterator iterator = dataAttributes.entrySet().iterator(); iterator.hasNext();) { 366 Map.Entry entry = (Map.Entry) iterator.next(); 367 String attributeName = (String) entry.getKey(); 368 Object attributeValue = entry.getValue(); 369 if (entry.getValue() != null) { 370 setAttribute(attributeName, attributeValue, false); 371 } 372 } 373 374 } catch (Exception e) { 375 throw new InvalidConfigurationException("Could not inject configuration data into the GBean " + abstractName, e); 376 } 377 378 for (int i = 0; i < dependencies.length; i++) { 379 dependencies[i].online(); 380 } 381 for (int i = 0; i < references.length; i++) { 382 references[i].online(); 383 } 384 } 385 386 public void die() throws GBeanNotFoundException { 387 synchronized (this) { 388 if (dead) { 389 // someone beat us to the punch... this instance should have never been found in the first place 390 throw new GBeanNotFoundException(abstractName); 391 } 392 dead = true; 393 } 394 395 // if the bean is already stopped or failed, this will do nothing; otherwise it will shutdown the bean 396 gbeanInstanceState.fail(); 397 398 for (int i = 0; i < references.length; i++) { 399 references[i].offline(); 400 } 401 for (int i = 0; i < dependencies.length; i++) { 402 dependencies[i].offline(); 403 } 404 405 // tell everyone we are done 406 lifecycleBroadcaster.fireUnloadedEvent(); 407 408 manageableStore = null; 409 } 410 411 public synchronized void setInstanceRegistry(InstanceRegistry instanceRegistry) { 412 this.instanceRegistry = instanceRegistry; 413 } 414 415 /** 416 * Gets the name of the GBean as defined in the gbean info. 417 * 418 * @return the gbean name 419 */ 420 public String getName() { 421 return name; 422 } 423 424 /** 425 * The class loader used to build this gbean. This class loader is set into the thread context 426 * class loader before callint the target instace. 427 * 428 * @return the class loader used to build this gbean 429 */ 430 public ClassLoader getClassLoader() { 431 return classLoader; 432 } 433 434 /** 435 * Has this gbean instance been destroyed. An destroyed gbean can no longer be used. 436 * 437 * @return true if the gbean has been destroyed 438 */ 439 public synchronized boolean isDead() { 440 return dead; 441 } 442 443 /** 444 * Gets the reason we are in the current state. 445 * @return the reason we are in the current state 446 */ 447 public String getStateReason() { 448 return stateReason; 449 } 450 451 /** 452 * The java type of the wrapped gbean instance 453 * 454 * @return the java type of the gbean 455 */ 456 public Class getType() { 457 return type; 458 } 459 460 public synchronized Object getTarget() { 461 return target; 462 } 463 464 public final String getObjectName() { 465 return abstractName.getObjectName().getCanonicalName(); 466 } 467 468 public final ObjectName getObjectNameObject() { 469 return abstractName.getObjectName(); 470 } 471 472 public final AbstractName getAbstractName() { 473 return abstractName; 474 } 475 476 public synchronized final long getStartTime() { 477 return startTime; 478 } 479 480 public int getState() { 481 return gbeanInstanceState.getState(); 482 } 483 484 public final State getStateInstance() { 485 return gbeanInstanceState.getStateInstance(); 486 } 487 488 /** 489 * Gets an unmodifiable map from attribute names to index number (Integer). This index number 490 * can be used to efficiently set or retrieve an attribute value. 491 * 492 * @return an unmodifiable map of attribute indexes by name 493 */ 494 public Map getAttributeIndex() { 495 return Collections.unmodifiableMap(new HashMap(attributeIndex)); 496 } 497 498 /** 499 * Gets an unmodifiable map from operation signature (GOperationSignature) to index number (Integer). 500 * This index number can be used to efficciently invoke the operation. 501 * 502 * @return an unmodifiable map of operation indexec by signature 503 */ 504 public Map getOperationIndex() { 505 return Collections.unmodifiableMap(new HashMap(operationIndex)); 506 } 507 508 /** 509 * Gets the GBeanInfo used to build this gbean. 510 * 511 * @return the GBeanInfo used to build this gbean 512 */ 513 public GBeanInfo getGBeanInfo() { 514 return gbeanInfo; 515 } 516 517 /** 518 * Moves this GBeanInstance to the starting state and then attempts to move this MBean immediately 519 * to the running state. 520 * 521 * @throws IllegalStateException If the gbean is disabled 522 */ 523 public final void start() { 524 synchronized (this) { 525 if (dead) { 526 throw new IllegalStateException("A dead GBean can not be started: abstractName=" + abstractName); 527 } 528 } 529 gbeanInstanceState.start(); 530 } 531 532 /** 533 * Starts this GBeanInstance and then attempts to start all of its start dependent children. 534 * 535 * @throws IllegalStateException If the gbean is disabled 536 */ 537 public final void startRecursive() { 538 synchronized (this) { 539 if (dead) { 540 throw new IllegalStateException("A dead GBean can not be started: abstractName=" + abstractName); 541 } 542 } 543 gbeanInstanceState.startRecursive(); 544 } 545 546 /** 547 * Moves this GBeanInstance to the STOPPING state, calls stop on all start dependent children, and then attempt 548 * to move this MBean to the STOPPED state. 549 */ 550 public final void stop() { 551 gbeanInstanceState.stop(); 552 } 553 554 /** 555 * Moves this GBeanInstance to the FAILED state. There are no calls to dependent children, but they will be 556 * notified using standard J2EE management notification. 557 */ 558 final void referenceFailed() { 559 gbeanInstanceState.fail(); 560 } 561 562 /** 563 * Gets the gbean data for the gbean held by this gbean mbean. 564 * 565 * @return the gbean data 566 */ 567 public GBeanData getGBeanData() { 568 GBeanData gbeanData = new GBeanData(abstractName, gbeanInfo); 569 570 // copy target into local variables from within a synchronized block to gaurentee a consistent read 571 int state; 572 Object instance; 573 synchronized (this) { 574 state = instanceState; 575 instance = target; 576 } 577 578 // add the attributes 579 for (int i = 0; i < attributes.length; i++) { 580 GBeanAttribute attribute = attributes[i]; 581 if (attribute.isPersistent()) { 582 String name = attribute.getName(); 583 Object value; 584 if ((state != DESTROYED || attribute.isFramework()) && attribute.isReadable()) { 585 try { 586 value = attribute.getValue(instance); 587 } catch (Throwable throwable) { 588 value = attribute.getPersistentValue(); 589 log.debug("Could not get the current value of persistent attribute. The persistent " + 590 "attribute will not reflect the current state attribute. " + attribute.getDescription(), throwable); 591 } 592 } else { 593 value = attribute.getPersistentValue(); 594 } 595 gbeanData.setAttribute(name, value); 596 } 597 } 598 599 // add the references 600 for (int i = 0; i < references.length; i++) { 601 GBeanReference reference = references[i]; 602 String name = reference.getName(); 603 if(reference instanceof GBeanSingleReference) { 604 AbstractName abstractName = ((GBeanSingleReference) reference).getTargetName(); 605 if(abstractName != null) { 606 gbeanData.setReferencePattern(name, abstractName); 607 } 608 } else if(reference instanceof GBeanCollectionReference) { 609 Set patterns = ((GBeanCollectionReference) reference).getPatterns(); 610 if(patterns != null) { 611 gbeanData.setReferencePatterns(name, patterns); 612 } 613 } else { 614 throw new IllegalStateException("Unrecognized GBeanReference '"+reference.getClass().getName()+"'"); 615 } 616 } 617 //TODO copy the dependencies?? 618 return gbeanData; 619 } 620 621 /** 622 * Gets the attribute value using the attribute index. This is the most efficient way to get 623 * an attribute as it avoids a HashMap lookup. 624 * 625 * @param index the index of the attribute 626 * @return the attribute value 627 * @throws Exception if a target instance throws and exception 628 * @throws IndexOutOfBoundsException if the index is invalid 629 */ 630 public Object getAttribute(int index) throws Exception { 631 GBeanAttribute attribute = attributes[index]; 632 633 // copy target into local variables from within a synchronized block to gaurentee a consistent read 634 int state; 635 Object instance; 636 synchronized (this) { 637 state = instanceState; 638 instance = target; 639 } 640 641 if (state != DESTROYED || attribute.isFramework()) { 642 return attribute.getValue(instance); 643 } else { 644 if (attribute.isPersistent()) { 645 return attribute.getPersistentValue(); 646 } else { 647 throw new IllegalStateException("Cannot retrieve the value for non-persistent attribute \"" + attribute.getName() + "\" when GBeanInstance is DESTROYED"); 648 } 649 } 650 } 651 652 /** 653 * Gets an attribute's value by name. This get style is less efficient becuse the attribute must 654 * first be looked up in a HashMap. 655 * 656 * @param attributeName the name of the attribute to retrieve 657 * @return the attribute value 658 * @throws Exception if a problem occurs while getting the value 659 * @throws NoSuchAttributeException if the attribute name is not found in the map 660 */ 661 public Object getAttribute(String attributeName) throws NoSuchAttributeException, Exception { 662 GBeanAttribute attribute; 663 try { 664 attribute = getAttributeByName(attributeName); 665 } catch (NoSuchAttributeException e) { 666 if (attributeName.equals(RAW_INVOKER)) { 667 return rawInvoker; 668 } 669 throw e; 670 } 671 672 // copy target into local variables from within a synchronized block to gaurentee a consistent read 673 int state; 674 Object instance; 675 synchronized (this) { 676 state = instanceState; 677 instance = target; 678 } 679 680 if (state != DESTROYED || attribute.isFramework()) { 681 return attribute.getValue(instance); 682 } else { 683 if (attribute.isPersistent()) { 684 return attribute.getPersistentValue(); 685 } else { 686 throw new IllegalStateException("Cannot retrieve the value for non-persistent attribute " + attributeName + " when gbean has been destroyed: " + abstractName); 687 } 688 } 689 } 690 691 /** 692 * Sets the attribute value using the attribute index. This is the most efficient way to set 693 * an attribute as it avoids a HashMap lookup. 694 * 695 * @param index the index of the attribute 696 * @param value the new value of attribute value 697 * @throws Exception if a target instance throws and exception 698 * @throws IndexOutOfBoundsException if the index is invalid 699 */ 700 public void setAttribute(int index, Object value) throws Exception, IndexOutOfBoundsException { 701 setAttribute(index, value, true); 702 } 703 704 private void setAttribute(int index, Object value, boolean manage) throws Exception, IndexOutOfBoundsException { 705 GBeanAttribute attribute = attributes[index]; 706 707 // copy target into local variables from within a synchronized block to gaurentee a consistent read 708 int state; 709 Object instance; 710 synchronized (this) { 711 state = instanceState; 712 instance = target; 713 } 714 715 if (state != DESTROYED || attribute.isFramework()) { 716 attribute.setValue(instance, value); 717 } else { 718 attribute.setPersistentValue(value); 719 } 720 if (manage && attribute.isManageable()) { 721 updateManageableAttribute(attribute, value); 722 } 723 } 724 725 /** 726 * Sets an attribute's value by name. This set style is less efficient becuse the attribute must 727 * first be looked up in a HashMap. 728 * 729 * @param attributeName the name of the attribute to retrieve 730 * @param value the new attribute value 731 * @throws Exception if a target instance throws and exception 732 * @throws NoSuchAttributeException if the attribute name is not found in the map 733 */ 734 public void setAttribute(String attributeName, Object value) throws Exception, NoSuchAttributeException { 735 setAttribute(attributeName, value, true); 736 } 737 738 public void setAttribute(String attributeName, Object value, boolean manage) throws Exception, NoSuchAttributeException { 739 GBeanAttribute attribute = getAttributeByName(attributeName); 740 741 // copy target into local variables from within a synchronized block to gaurentee a consistent read 742 int state; 743 Object instance; 744 synchronized (this) { 745 state = instanceState; 746 instance = target; 747 } 748 749 if (state != DESTROYED || attribute.isFramework()) { 750 attribute.setValue(instance, value); 751 } else { 752 attribute.setPersistentValue(value); 753 } 754 if (manage && attribute.isManageable()) { 755 updateManageableAttribute(attribute, value); 756 } 757 } 758 759 private void updateManageableAttribute(GBeanAttribute attribute, Object value) { 760 if (manageableStore == null) { 761 manageableStore = getManageableAttributeStore(); 762 if (manageableStore == null) { 763 return; 764 } 765 } 766 Artifact configName = abstractName.getArtifact(); 767 if (configName != null) { 768 manageableStore.setValue(configName, abstractName, attribute.getAttributeInfo(), value); 769 } else { 770 log.error("Unable to identify Configuration for GBean " + abstractName + ". Manageable attribute " + attribute.getName() + " was not updated in persistent store."); 771 } 772 } 773 774 private ManageableAttributeStore getManageableAttributeStore() { 775 Set set = kernel.listGBeans(new AbstractNameQuery(ManageableAttributeStore.class.getName())); 776 for (Iterator iterator = set.iterator(); iterator.hasNext();) { 777 AbstractName abstractName1 = (AbstractName) iterator.next(); 778 try { 779 return (ManageableAttributeStore) kernel.getGBean(abstractName1); 780 } catch (GBeanNotFoundException e) { 781 // ignored... gbean was unregistered 782 } 783 } 784 return null; 785 } 786 787 private GBeanAttribute getAttributeByName(String name) throws NoSuchAttributeException { 788 Integer index = (Integer) attributeIndex.get(name); 789 if (index == null) { 790 throw new NoSuchAttributeException("Unknown attribute \"" + name + "\" in gbean " + abstractName); 791 } 792 return attributes[index.intValue()]; 793 } 794 795 /** 796 * Invokes an opreation using the operation index. This is the most efficient way to invoke 797 * an operation as it avoids a HashMap lookup. 798 * 799 * @param index the index of the attribute 800 * @param arguments the arguments to the operation 801 * @return the result of the operation 802 * @throws Exception if a target instance throws and exception 803 * @throws IndexOutOfBoundsException if the index is invalid 804 * @throws IllegalStateException if the gbean instance has been destroyed 805 */ 806 public Object invoke(int index, Object[] arguments) throws Exception { 807 GBeanOperation operation = operations[index]; 808 809 // copy target into local variables from within a synchronized block to gaurentee a consistent read 810 int state; 811 Object instance; 812 synchronized (this) { 813 state = instanceState; 814 instance = target; 815 } 816 817 if (state == DESTROYED && !operation.isFramework()) { 818 throw new IllegalStateException("Operations can only be invoke while the GBean instance is running: " + abstractName); 819 } 820 return operation.invoke(instance, arguments); 821 } 822 823 /** 824 * Invokes an operation on the target gbean by method signature. This style if invocation is 825 * inefficient, because the target method must be looked up in a hashmap using a freshly constructed 826 * GOperationSignature object. 827 * 828 * @param operationName the name of the operation to invoke 829 * @param arguments arguments to the operation 830 * @param types types of the operation arguemtns 831 * @return the result of the operation 832 * @throws Exception if a target instance throws and exception 833 * @throws NoSuchOperationException if the operation signature is not found in the map 834 * @throws IllegalStateException if the gbean instance has been destroyed 835 */ 836 public Object invoke(String operationName, Object[] arguments, String[] types) throws Exception, NoSuchOperationException { 837 GOperationSignature signature = new GOperationSignature(operationName, types); 838 Integer index = (Integer) operationIndex.get(signature); 839 if (index == null) { 840 throw new NoSuchOperationException("Unknown operation " + signature); 841 } 842 GBeanOperation operation = operations[index.intValue()]; 843 844 // copy target into local variables from within a synchronized block to gaurentee a consistent read 845 int state; 846 Object instance; 847 synchronized (this) { 848 state = instanceState; 849 instance = target; 850 } 851 852 if (state == DESTROYED && !operation.isFramework()) { 853 throw new IllegalStateException("Operations can only be invoke while the GBean is running: " + abstractName); 854 } 855 return operation.invoke(instance, arguments); 856 } 857 858 private GBeanReference getReferenceByName(String name) { 859 Integer index = (Integer) referenceIndex.get(name); 860 if (index == null) { 861 throw new IllegalArgumentException("Unknown reference " + name); 862 } 863 return references[index.intValue()]; 864 } 865 866 boolean createInstance() throws Exception { 867 synchronized (this) { 868 // first check we are still in the correct state to start 869 if (instanceState == CREATING || instanceState == RUNNING) { 870 // another thread already completed starting 871 return false; 872 } else if (instanceState == DESTROYING) { 873 // this should never ever happen... this method is protected by the GBeanState class which should 874 // prevent stuff like this happening, but check anyway 875 stateReason = "an internal error has occurred. An was made to start an instance that was still stopping which is an illegal state transition."; 876 throw new IllegalStateException("A stopping instance can not be started until fully stopped"); 877 } 878 assert instanceState == DESTROYED; 879 880 stateReason = null; 881 882 // Call all start on every reference. This way the dependecies are held until we can start 883 LinkedHashSet unstarted = new LinkedHashSet(); 884 for (int i = 0; i < dependencies.length; i++) { 885 if (!dependencies[i].start()) { 886 unstarted.add(dependencies[i].getTargetName()); 887 } 888 } 889 for (int i = 0; i < references.length; i++) { 890 if (!references[i].start()) { 891 if (references[i] instanceof GBeanSingleReference) { 892 GBeanSingleReference reference = (GBeanSingleReference) references[i]; 893 unstarted.add(reference.getTargetName()); 894 } 895 } 896 } 897 if (!unstarted.isEmpty()) { 898 if (unstarted.size() == 1) { 899 stateReason = unstarted.iterator().next() + " did not start."; 900 } else { 901 stateReason = "the following dependent services did not start: " + unstarted; 902 } 903 return false; 904 } 905 906 // we are definately going to (try to) start... if this fails the must clean up these variables 907 instanceState = CREATING; 908 startTime = System.currentTimeMillis(); 909 } 910 911 ClassLoader oldCL = Thread.currentThread().getContextClassLoader(); 912 Thread.currentThread().setContextClassLoader(classLoader); 913 Object instance = null; 914 try { 915 GConstructorInfo constructorInfo = gbeanInfo.getConstructor(); 916 Class[] parameterTypes = constructor.getParameterTypes(); 917 918 // create constructor parameter array 919 Object[] parameters = new Object[parameterTypes.length]; 920 Iterator names = constructorInfo.getAttributeNames().iterator(); 921 for (int i = 0; i < parameters.length; i++) { 922 String name = (String) names.next(); 923 if (referenceIndex.containsKey(name)) { 924 parameters[i] = getReferenceByName(name).getProxy(); 925 } else if (attributeIndex.containsKey(name)) { 926 GBeanAttribute attribute = getAttributeByName(name); 927 parameters[i] = attribute.getPersistentValue(); 928 } else { 929 stateReason = "the service constructor definition contained the name '" + name + "' which is not a known attribute or reference of the service."; 930 throw new InvalidConfigurationException("Unknown attribute or reference name in constructor: referenceName=" + name + ", gbean=" + abstractName); 931 } 932 } 933 934 // create instance 935 try { 936 instance = constructor.newInstance(parameters); 937 } catch (InvocationTargetException e) { 938 Throwable targetException = e.getTargetException(); 939 if (targetException instanceof Exception) { 940 throw (Exception) targetException; 941 } else if (targetException instanceof Error) { 942 throw (Error) targetException; 943 } 944 stateReason = "the service constructor threw an exception. \n" + printException(e); 945 throw e; 946 } catch (IllegalArgumentException e) { 947 stateReason = "the service constructor threw an exception due to a parameter type mismatch. \n" + printException(e); 948 log.warn("Constructor mismatch for " + abstractName, e); 949 throw e; 950 } 951 952 // write the target variable in a synchronized block so it is available to all threads 953 // we do this before calling the setters or start method so the bean can be called back 954 // from a setter start method 955 synchronized (this) { 956 target = instance; 957 } 958 959 // inject the persistent attribute value into the new instance 960 for (int i = 0; i < attributes.length; i++) { 961 checkIfShouldFail(); 962 try { 963 attributes[i].inject(instance); 964 } catch (Exception e) { 965 stateReason = "the setter for attribute '" + attributes[i].getName() + "' threw an exception. \n" + printException(e); 966 throw e; 967 } 968 } 969 970 // inject the proxies into the new instance 971 for (int i = 0; i < references.length; i++) { 972 checkIfShouldFail(); 973 try { 974 references[i].inject(instance); 975 } catch (Exception e) { 976 stateReason = "the setter for reference '" + references[i].getName() + "' threw an exception. \n" + printException(e); 977 throw e; 978 } 979 } 980 981 if (instance instanceof GBeanLifecycle) { 982 checkIfShouldFail(); 983 try { 984 ((GBeanLifecycle) instance).doStart(); 985 } catch (Exception e) { 986 stateReason = "the doStart method threw an exception. \n" + printException(e); 987 throw e; 988 } 989 } 990 991 992 // all done... we are now fully running 993 synchronized (this) { 994 checkIfShouldFail(); 995 if (instanceRegistry != null) { 996 instanceRegistry.instanceCreated(instance, this); 997 } 998 instanceState = RUNNING; 999 this.notifyAll(); 1000 } 1001 1002 1003 stateReason = null; 1004 return true; 1005 } catch (Throwable t) { 1006 // something went wrong... we need to destroy this instance 1007 synchronized (this) { 1008 instanceState = DESTROYING; 1009 } 1010 1011 if (instance instanceof GBeanLifecycle) { 1012 try { 1013 ((GBeanLifecycle) instance).doFail(); 1014 } catch (Throwable ignored) { 1015 log.error("Problem in doFail of " + abstractName, ignored); 1016 } 1017 } 1018 1019 // bean has been notified... drop our reference 1020 synchronized (this) { 1021 // stop all of the references 1022 for (int i = 0; i < references.length; i++) { 1023 references[i].stop(); 1024 } 1025 for (int i = 0; i < dependencies.length; i++) { 1026 dependencies[i].stop(); 1027 } 1028 1029 target = null; 1030 instanceState = DESTROYED; 1031 startTime = 0; 1032 this.notifyAll(); 1033 } 1034 1035 if (t instanceof Exception) { 1036 throw (Exception) t; 1037 } else if (t instanceof Error) { 1038 throw (Error) t; 1039 } else { 1040 throw new Error(t); 1041 } 1042 } finally { 1043 Thread.currentThread().setContextClassLoader(oldCL); 1044 } 1045 } 1046 1047 private String printException(Throwable t) { 1048 StringWriter stringWriter = new StringWriter(); 1049 PrintWriter printWriter = new PrintWriter(stringWriter); 1050 t.printStackTrace(printWriter); 1051 printWriter.flush(); 1052 return stringWriter.toString(); 1053 } 1054 1055 private synchronized void checkIfShouldFail() throws Exception { 1056 if (shouldFail) { 1057 shouldFail = false; 1058 throw new Exception("A reference has failed so construction can not complete"); 1059 } 1060 } 1061 1062 boolean destroyInstance(boolean stop) throws Exception { 1063 Object instance; 1064 synchronized (this) { 1065 if (!stop && instanceState == CREATING) { 1066 // signal to the creating thead that it should fail 1067 shouldFail = true; 1068 return false; 1069 } 1070 1071 // if the instance is being created we need to wait 1072 // for it to finish before we can try to stop it 1073 while (instanceState == CREATING) { 1074 // todo should we limit this wait? If so, how do we configure the wait time? 1075 try { 1076 this.wait(); 1077 } catch (InterruptedException e) { 1078 // clear the interrupted flag 1079 Thread.interrupted(); 1080 // rethrow the interrupted exception.... someone was sick of us waiting 1081 throw e; 1082 } 1083 } 1084 1085 if (instanceState == DESTROYING || instanceState == DESTROYED) { 1086 // another thread is already stopping or has already stopped 1087 return false; 1088 } 1089 assert instanceState == RUNNING; 1090 stateReason = null; 1091 1092 // we are definately going to stop... if this fails the must clean up these variables 1093 instanceState = DESTROYING; 1094 instance = target; 1095 } 1096 1097 // update the persistent attributes 1098 // do not update the persistent attibute values in the case of a failure 1099 // failed gbeans may have corrupted attributes that would be persisted 1100 Exception problem = null; 1101 if (stop && instance != null) { 1102 try { 1103 // get all the data but don't update in case there is an exception 1104 Map data = new HashMap(); 1105 for (int i = 0; i < attributes.length; i++) { 1106 GBeanAttribute attribute = attributes[i]; 1107 if (attribute.isPersistent() && attribute.isReadable()) { 1108 // copy the current attribute value to the persistent value 1109 Object value; 1110 try { 1111 value = attribute.getValue(instance); 1112 } catch (Throwable e) { 1113 // There is no reason to create a new Exception sub class as this exception will 1114 // simply be caught and logged on GBeanInstanceState 1115 throw new Exception("Problem while updaing the persistent value of attibute: " + 1116 "Attribute Name: " + attribute.getName() + ", " + 1117 "Type: " + attribute.getType() + ", " + 1118 "GBeanInstance: " + getName(), e); 1119 } 1120 data.put(attribute, value); 1121 } 1122 } 1123 // now we have all the data we can update the persistent values 1124 for (int i = 0; i < attributes.length; i++) { 1125 GBeanAttribute attribute = attributes[i]; 1126 if (attribute.isPersistent() && attribute.isReadable()) { 1127 // copy the current attribute value to the persistent value 1128 Object value = data.get(attribute); 1129 attribute.setPersistentValue(value); 1130 } 1131 } 1132 } catch (Exception e) { 1133 // the getter threw an exception; now we must to fail 1134 stop = false; 1135 problem = e; 1136 } 1137 } 1138 1139 // we notify the bean before removing our reference so the bean can be called back while stopping 1140 ClassLoader oldCL = Thread.currentThread().getContextClassLoader(); 1141 Thread.currentThread().setContextClassLoader(classLoader); 1142 try { 1143 if (instance instanceof GBeanLifecycle) { 1144 if (stop) { 1145 try { 1146 ((GBeanLifecycle) instance).doStop(); 1147 } catch (Throwable ignored) { 1148 log.error("Problem in doStop of " + abstractName, ignored); 1149 } 1150 } else { 1151 try { 1152 ((GBeanLifecycle) instance).doFail(); 1153 } catch (Throwable ignored) { 1154 log.error("Problem in doFail of " + abstractName, ignored); 1155 } 1156 } 1157 } 1158 } finally { 1159 Thread.currentThread().setContextClassLoader(oldCL); 1160 } 1161 1162 // bean has been notified... drop our reference 1163 synchronized (this) { 1164 // stop all of the references 1165 for (int i = 0; i < references.length; i++) { 1166 references[i].stop(); 1167 } 1168 for (int i = 0; i < dependencies.length; i++) { 1169 dependencies[i].stop(); 1170 } 1171 1172 target = null; 1173 instanceState = DESTROYED; 1174 if (instanceRegistry != null) { 1175 instanceRegistry.instanceDestroyed(instance); 1176 } 1177 startTime = 0; 1178 } 1179 1180 if (problem != null) { 1181 throw problem; 1182 } 1183 return true; 1184 } 1185 1186 private void addManagedObjectAttributes(Map attributesMap) { 1187 // 1188 // Special attributes 1189 // 1190 attributesMap.put("abstractName", 1191 GBeanAttribute.createSpecialAttribute((GBeanAttribute) attributesMap.get("abstractName"), 1192 this, 1193 "abstractName", 1194 AbstractName.class, 1195 getAbstractName())); 1196 1197 attributesMap.put("objectName", 1198 GBeanAttribute.createSpecialAttribute((GBeanAttribute) attributesMap.get("objectName"), 1199 this, 1200 "objectName", 1201 String.class, 1202 getObjectName())); 1203 1204 attributesMap.put("classLoader", 1205 GBeanAttribute.createSpecialAttribute((GBeanAttribute) attributesMap.get("classLoader"), 1206 this, 1207 "classLoader", 1208 ClassLoader.class, 1209 classLoader)); 1210 1211 attributesMap.put("kernel", 1212 GBeanAttribute.createSpecialAttribute((GBeanAttribute) attributesMap.get("kernel"), 1213 this, 1214 "kernel", 1215 Kernel.class, 1216 kernel)); 1217 1218 } 1219 1220 private GBeanInfo rebuildGBeanInfo(GConstructorInfo constructor, String j2eeType) { 1221 Set attributeInfos = new HashSet(); 1222 for (int i = 0; i < attributes.length; i++) { 1223 GBeanAttribute attribute = attributes[i]; 1224 attributeInfos.add(attribute.getAttributeInfo()); 1225 } 1226 Set operationInfos = new HashSet(); 1227 for (int i = 0; i < operations.length; i++) { 1228 operationInfos.add(operations[i].getOperationInfo()); 1229 } 1230 1231 Set referenceInfos = new HashSet(); 1232 for (int i = 0; i < references.length; i++) { 1233 referenceInfos.add(references[i].getReferenceInfo()); 1234 } 1235 1236 Set interfaceInfos = new HashSet(); 1237 for (int i = 0; i < interfaces.length; i++) { 1238 interfaceInfos.add(interfaces[i]); 1239 } 1240 1241 return new GBeanInfo(name, 1242 type.getName(), 1243 j2eeType, 1244 attributeInfos, 1245 constructor, 1246 operationInfos, 1247 referenceInfos, 1248 interfaceInfos); 1249 } 1250 1251 public boolean equals(Object obj) { 1252 if (obj == this) return true; 1253 if (!(obj instanceof GBeanInstance)) return false; 1254 return abstractName.equals(((GBeanInstance) obj).abstractName); 1255 } 1256 1257 public int hashCode() { 1258 return abstractName.hashCode(); 1259 } 1260 1261 public String toString() { 1262 return abstractName.toString(); 1263 } 1264 }