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