0001: /**
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */package org.apache.geronimo.gbean.runtime;
0017:
0018: import java.io.PrintWriter;
0019: import java.io.StringWriter;
0020: import java.lang.reflect.Constructor;
0021: import java.lang.reflect.InvocationTargetException;
0022: import java.util.Collection;
0023: import java.util.Collections;
0024: import java.util.HashMap;
0025: import java.util.HashSet;
0026: import java.util.Iterator;
0027: import java.util.LinkedHashSet;
0028: import java.util.List;
0029: import java.util.Map;
0030: import java.util.Set;
0031: import java.util.Arrays;
0032:
0033: import javax.management.ObjectName;
0034:
0035: import org.apache.commons.logging.Log;
0036: import org.apache.commons.logging.LogFactory;
0037: import org.apache.geronimo.gbean.AbstractName;
0038: import org.apache.geronimo.gbean.AbstractNameQuery;
0039: import org.apache.geronimo.gbean.GAttributeInfo;
0040: import org.apache.geronimo.gbean.GBeanData;
0041: import org.apache.geronimo.gbean.GBeanInfo;
0042: import org.apache.geronimo.gbean.GBeanLifecycle;
0043: import org.apache.geronimo.gbean.GConstructorInfo;
0044: import org.apache.geronimo.gbean.GOperationInfo;
0045: import org.apache.geronimo.gbean.GOperationSignature;
0046: import org.apache.geronimo.gbean.GReferenceInfo;
0047: import org.apache.geronimo.gbean.InvalidConfigurationException;
0048: import org.apache.geronimo.gbean.ReferencePatterns;
0049: import org.apache.geronimo.kernel.DependencyManager;
0050: import org.apache.geronimo.kernel.GBeanNotFoundException;
0051: import org.apache.geronimo.kernel.Kernel;
0052: import org.apache.geronimo.kernel.NoSuchAttributeException;
0053: import org.apache.geronimo.kernel.NoSuchOperationException;
0054: import org.apache.geronimo.kernel.repository.Artifact;
0055: import org.apache.geronimo.kernel.config.ManageableAttributeStore;
0056: import org.apache.geronimo.kernel.management.State;
0057: import org.apache.geronimo.kernel.management.StateManageable;
0058:
0059: /**
0060: * A GBeanInstance is a J2EE Management Managed Object, and is standard base for Geronimo services.
0061: *
0062: * @version $Rev:385718 $ $Date: 2008-01-04 16:47:14 -0800 (Fri, 04 Jan 2008) $
0063: */
0064: public final class GBeanInstance implements StateManageable {
0065: private static final Log log = LogFactory
0066: .getLog(GBeanInstance.class);
0067:
0068: private static final int DESTROYED = 0;
0069: private static final int CREATING = 1;
0070: private static final int RUNNING = 2;
0071: private static final int DESTROYING = 3;
0072:
0073: /**
0074: * Attribute name used to retrieve the RawInvoker for the GBean
0075: */
0076: public static final String RAW_INVOKER = "$$RAW_INVOKER$$";
0077:
0078: /**
0079: * The kernel in which this server is registered.
0080: */
0081: private final Kernel kernel;
0082:
0083: /**
0084: * The ManageableAttributeStore notified of any changes to manageable
0085: * attributes. This is lazy-loaded as manageable attributes are set.
0086: */
0087: private ManageableAttributeStore manageableStore;
0088:
0089: /**
0090: * the abstract name of this service
0091: */
0092: private final AbstractName abstractName;
0093:
0094: /**
0095: * This handles all state transiitions for this instance.
0096: */
0097: private final GBeanInstanceState gbeanInstanceState;
0098:
0099: /**
0100: * The constructor used to create the instance
0101: */
0102: private final Constructor constructor;
0103:
0104: /**
0105: * A fast index based raw invoker for this GBean.
0106: */
0107: private final RawInvoker rawInvoker;
0108:
0109: /**
0110: * The single listener to which we broadcast lifecycle change events.
0111: */
0112: private final LifecycleBroadcaster lifecycleBroadcaster;
0113:
0114: /**
0115: * Interfaces for this GBean
0116: */
0117: private final String[] interfaces;
0118:
0119: /**
0120: * Attributes lookup table
0121: */
0122: private final GBeanAttribute[] attributes;
0123:
0124: /**
0125: * Attributes supported by this GBeanMBean by (String) name.
0126: */
0127: private final Map attributeIndex = new HashMap();
0128:
0129: /**
0130: * References lookup table
0131: */
0132: private final GBeanReference[] references;
0133:
0134: /**
0135: * References supported by this GBeanMBean by (String) name.
0136: */
0137: private final Map referenceIndex = new HashMap();
0138:
0139: /**
0140: * Dependencies supported by this GBean.
0141: */
0142: private final GBeanDependency[] dependencies;
0143:
0144: /**
0145: * Operations lookup table
0146: */
0147: private final GBeanOperation[] operations;
0148:
0149: /**
0150: * Operations supported by this GBeanMBean by (GOperationSignature) name.
0151: */
0152: private final Map operationIndex = new HashMap();
0153:
0154: /**
0155: * The classloader used for all invocations and creating targets.
0156: */
0157: private final ClassLoader classLoader;
0158:
0159: /**
0160: * Metadata describing the attributes, operations and references of this GBean
0161: */
0162: private final GBeanInfo gbeanInfo;
0163:
0164: /**
0165: * Our name
0166: */
0167: private final String name;
0168:
0169: /**
0170: * Java type of the wrapped GBean class
0171: */
0172: private final Class type;
0173:
0174: /**
0175: * Has this instance been destroyed?
0176: */
0177: private boolean dead = false;
0178:
0179: /**
0180: * The state of the internal gbean instance that we are wrapping.
0181: */
0182: private int instanceState = DESTROYED;
0183:
0184: /**
0185: * Target instance of this GBean wrapper
0186: */
0187: private Object target;
0188:
0189: /**
0190: * The time this application started.
0191: */
0192: private long startTime;
0193:
0194: /**
0195: * This is used to signal the creating thread that it should
0196: * fail when it returns from usercode. This is set when a
0197: * reference has gone offline during construction.
0198: */
0199: private boolean shouldFail = false;
0200:
0201: /**
0202: * Used to track instance
0203: */
0204: private InstanceRegistry instanceRegistry;
0205:
0206: private String stateReason;
0207:
0208: /**
0209: * Construct a GBeanMBean using the supplied GBeanData and class loader
0210: *
0211: * @param gbeanData the data for the new GBean including GBeanInfo, intial attribute values, and reference patterns
0212: * @param classLoader the class loader used to load the gbean instance and attribute/reference types
0213: * @throws org.apache.geronimo.gbean.InvalidConfigurationException
0214: * if the gbeanInfo is inconsistent with the actual java classes, such as
0215: * mismatched attribute types or the intial data cannot be set
0216: */
0217: public GBeanInstance(GBeanData gbeanData, Kernel kernel,
0218: DependencyManager dependencyManager,
0219: LifecycleBroadcaster lifecycleBroadcaster,
0220: ClassLoader classLoader)
0221: throws InvalidConfigurationException {
0222: this .abstractName = gbeanData.getAbstractName();
0223: this .kernel = kernel;
0224: this .lifecycleBroadcaster = lifecycleBroadcaster;
0225: this .gbeanInstanceState = new GBeanInstanceState(abstractName,
0226: kernel, dependencyManager, this , lifecycleBroadcaster);
0227: this .classLoader = classLoader;
0228:
0229: GBeanInfo gbeanInfo = gbeanData.getGBeanInfo();
0230: try {
0231: type = classLoader.loadClass(gbeanInfo.getClassName());
0232: } catch (ClassNotFoundException e) {
0233: throw new InvalidConfigurationException(
0234: "Could not load GBeanInfo class from classloader: "
0235: + classLoader + " className="
0236: + gbeanInfo.getClassName(), e);
0237: }
0238:
0239: name = gbeanInfo.getName();
0240:
0241: //
0242: Set constructorArgs = new HashSet(gbeanInfo.getConstructor()
0243: .getAttributeNames());
0244:
0245: // interfaces
0246: interfaces = (String[]) gbeanInfo.getInterfaces().toArray(
0247: new String[0]);
0248:
0249: // attributes
0250: Map attributesMap = new HashMap();
0251: for (Iterator iterator = gbeanInfo.getAttributes().iterator(); iterator
0252: .hasNext();) {
0253: GAttributeInfo attributeInfo = (GAttributeInfo) iterator
0254: .next();
0255: attributesMap.put(attributeInfo.getName(),
0256: new GBeanAttribute(this , attributeInfo,
0257: constructorArgs.contains(attributeInfo
0258: .getName())));
0259: }
0260: addManagedObjectAttributes(attributesMap);
0261: attributes = (GBeanAttribute[]) attributesMap.values().toArray(
0262: new GBeanAttribute[attributesMap.size()]);
0263: for (int i = 0; i < attributes.length; i++) {
0264: attributeIndex.put(attributes[i].getName(), new Integer(i));
0265: }
0266:
0267: // references
0268: Set referencesSet = new HashSet();
0269: Set dependencySet = new HashSet();
0270: // add the references
0271: Map dataReferences = gbeanData.getReferences();
0272: for (Iterator iterator = gbeanInfo.getReferences().iterator(); iterator
0273: .hasNext();) {
0274: GReferenceInfo referenceInfo = (GReferenceInfo) iterator
0275: .next();
0276: String referenceName = referenceInfo.getName();
0277: ReferencePatterns referencePatterns = (ReferencePatterns) dataReferences
0278: .remove(referenceName);
0279: if (referenceInfo.getProxyType().equals(
0280: Collection.class.getName())) {
0281: referencesSet.add(new GBeanCollectionReference(this ,
0282: referenceInfo, kernel, referencePatterns));
0283:
0284: } else {
0285: referencesSet.add(new GBeanSingleReference(this ,
0286: referenceInfo, kernel, referencePatterns));
0287: if (referencePatterns != null) {
0288: dependencySet
0289: .add(new GBeanDependency(
0290: this ,
0291: referencePatterns.getAbstractName(),
0292: kernel));
0293: }
0294: }
0295: }
0296: if (!dataReferences.isEmpty()) {
0297: throw new IllegalStateException(
0298: "Attempting to set unknown references: "
0299: + dataReferences.keySet());
0300: }
0301:
0302: references = (GBeanReference[]) referencesSet
0303: .toArray(new GBeanReference[referencesSet.size()]);
0304: for (int i = 0; i < references.length; i++) {
0305: referenceIndex.put(references[i].getName(), new Integer(i));
0306: }
0307:
0308: //dependencies
0309: for (Iterator iterator = gbeanData.getDependencies().iterator(); iterator
0310: .hasNext();) {
0311: AbstractName dependencyName = ((ReferencePatterns) iterator
0312: .next()).getAbstractName();
0313: dependencySet.add(new GBeanDependency(this , dependencyName,
0314: kernel));
0315: }
0316:
0317: dependencies = (GBeanDependency[]) dependencySet
0318: .toArray(new GBeanDependency[dependencySet.size()]);
0319:
0320: // framework operations -- all framework operations have currently been removed
0321:
0322: // operations
0323: Map operationsMap = new HashMap();
0324: for (Iterator iterator = gbeanInfo.getOperations().iterator(); iterator
0325: .hasNext();) {
0326: GOperationInfo operationInfo = (GOperationInfo) iterator
0327: .next();
0328: GOperationSignature signature = new GOperationSignature(
0329: operationInfo.getName(), operationInfo
0330: .getParameterList());
0331: // do not allow overriding of framework operations
0332: if (!operationsMap.containsKey(signature)) {
0333: GBeanOperation operation = new GBeanOperation(this ,
0334: operationInfo);
0335: operationsMap.put(signature, operation);
0336: }
0337: }
0338: operations = new GBeanOperation[operationsMap.size()];
0339: int opCounter = 0;
0340: for (Iterator iterator = operationsMap.entrySet().iterator(); iterator
0341: .hasNext();) {
0342: Map.Entry entry = (Map.Entry) iterator.next();
0343: operations[opCounter] = (GBeanOperation) entry.getValue();
0344: operationIndex.put(entry.getKey(), new Integer(opCounter));
0345: opCounter++;
0346: }
0347:
0348: // get the constructor
0349: List arguments = gbeanInfo.getConstructor().getAttributeNames();
0350: Class[] parameterTypes = new Class[arguments.size()];
0351: for (int i = 0; i < parameterTypes.length; i++) {
0352: String argumentName = (String) arguments.get(i);
0353: if (referenceIndex.containsKey(argumentName)) {
0354: Integer index = (Integer) referenceIndex
0355: .get(argumentName);
0356: GBeanReference reference = references[index.intValue()];
0357: parameterTypes[i] = reference.getProxyType();
0358: } else if (attributeIndex.containsKey(argumentName)) {
0359: Integer index = (Integer) attributeIndex
0360: .get(argumentName);
0361: GBeanAttribute attribute = attributes[index.intValue()];
0362: parameterTypes[i] = attribute.getType();
0363: }
0364: }
0365: try {
0366: constructor = type.getConstructor(parameterTypes);
0367: } catch (NoSuchMethodException e) {
0368: StringBuffer buf = new StringBuffer(
0369: "Could not find a valid constructor for GBean: ")
0370: .append(gbeanInfo.getName()).append("\n");
0371: buf.append("ParameterTypes: ").append(
0372: Arrays.asList(parameterTypes)).append("\n");
0373: Constructor[] constructors = type.getConstructors();
0374: for (int i = 0; i < constructors.length; i++) {
0375: Constructor testConstructor = constructors[i];
0376: buf.append("constructor types: ").append(
0377: Arrays.asList(testConstructor
0378: .getParameterTypes())).append("\n");
0379: if (testConstructor.getParameterTypes().length == parameterTypes.length) {
0380: Class[] testParameterTypes = testConstructor
0381: .getParameterTypes();
0382: for (int k = 0; k < testParameterTypes.length; k++) {
0383: Class testParameterType = testParameterTypes[k];
0384: if (parameterTypes[k].getName().equals(
0385: testParameterType.getName())) {
0386: if (parameterTypes[k].getClassLoader() != testParameterType
0387: .getClassLoader()) {
0388: buf
0389: .append(
0390: "different classloaders in position: ")
0391: .append(k).append(
0392: " class name: ")
0393: .append(
0394: testParameterType
0395: .getName())
0396: .append("\n");
0397: buf
0398: .append(
0399: "parameter type classloader: ")
0400: .append(
0401: parameterTypes[k]
0402: .getClassLoader())
0403: .append("\n");
0404: buf
0405: .append(
0406: "constructor type classloader: ")
0407: .append(
0408: testParameterType
0409: .getClassLoader())
0410: .append("\n");
0411: }
0412: } else {
0413: buf.append("different type in position: ")
0414: .append(k).append("\n");
0415: }
0416: }
0417: }
0418: }
0419: throw new InvalidConfigurationException(buf.toString());
0420: } catch (NoClassDefFoundError e) {
0421: throw new InvalidConfigurationException(e);
0422: }
0423:
0424: // rebuild the gbean info based on the current attributes, operations, and references because
0425: // the above code add new attributes and operations
0426: this .gbeanInfo = rebuildGBeanInfo(gbeanInfo.getConstructor(),
0427: gbeanInfo.getJ2eeType());
0428:
0429: // create the raw invokers
0430: rawInvoker = new RawInvoker(this );
0431:
0432: // set the initial attribute values
0433: try {
0434: // set the attributes
0435: Map dataAttributes = gbeanData.getAttributes();
0436: for (Iterator iterator = dataAttributes.entrySet()
0437: .iterator(); iterator.hasNext();) {
0438: Map.Entry entry = (Map.Entry) iterator.next();
0439: String attributeName = (String) entry.getKey();
0440: Object attributeValue = entry.getValue();
0441: if (entry.getValue() != null) {
0442: setAttribute(attributeName, attributeValue, false);
0443: }
0444: }
0445:
0446: } catch (Exception e) {
0447: throw new InvalidConfigurationException(
0448: "Could not inject configuration data into the GBean "
0449: + abstractName, e);
0450: }
0451:
0452: //Add the reference to all applicable reference collections before possibly starting the gbean having an
0453: //explicit reference to the reference.
0454: for (int i = 0; i < references.length; i++) {
0455: references[i].online();
0456: }
0457: for (int i = 0; i < dependencies.length; i++) {
0458: dependencies[i].online();
0459: }
0460: }
0461:
0462: public void die() throws GBeanNotFoundException {
0463: synchronized (this ) {
0464: if (dead) {
0465: // someone beat us to the punch... this instance should have never been found in the first place
0466: throw new GBeanNotFoundException(abstractName);
0467: }
0468: dead = true;
0469: }
0470:
0471: // if the bean is already stopped or failed, this will do nothing; otherwise it will shutdown the bean
0472: gbeanInstanceState.fail();
0473:
0474: for (int i = 0; i < references.length; i++) {
0475: references[i].offline();
0476: }
0477: for (int i = 0; i < dependencies.length; i++) {
0478: dependencies[i].offline();
0479: }
0480:
0481: // tell everyone we are done
0482: lifecycleBroadcaster.fireUnloadedEvent();
0483:
0484: manageableStore = null;
0485: }
0486:
0487: public synchronized void setInstanceRegistry(
0488: InstanceRegistry instanceRegistry) {
0489: this .instanceRegistry = instanceRegistry;
0490: }
0491:
0492: /**
0493: * Gets the name of the GBean as defined in the gbean info.
0494: *
0495: * @return the gbean name
0496: */
0497: public String getName() {
0498: return name;
0499: }
0500:
0501: /**
0502: * The class loader used to build this gbean. This class loader is set into the thread context
0503: * class loader before callint the target instace.
0504: *
0505: * @return the class loader used to build this gbean
0506: */
0507: public ClassLoader getClassLoader() {
0508: return classLoader;
0509: }
0510:
0511: /**
0512: * Has this gbean instance been destroyed. An destroyed gbean can no longer be used.
0513: *
0514: * @return true if the gbean has been destroyed
0515: */
0516: public synchronized boolean isDead() {
0517: return dead;
0518: }
0519:
0520: /**
0521: * Gets the reason we are in the current state.
0522: * @return the reason we are in the current state
0523: */
0524: public String getStateReason() {
0525: return stateReason;
0526: }
0527:
0528: /**
0529: * Sets the reason we are in the current state.
0530: * @param reason The reason we are in the current state
0531: */
0532: public void setStateReason(String reason) {
0533: stateReason = reason;
0534: }
0535:
0536: /**
0537: * The java type of the wrapped gbean instance
0538: *
0539: * @return the java type of the gbean
0540: */
0541: public Class getType() {
0542: return type;
0543: }
0544:
0545: public synchronized Object getTarget() {
0546: return target;
0547: }
0548:
0549: public final String getObjectName() {
0550: return abstractName.getObjectName().getCanonicalName();
0551: }
0552:
0553: public final ObjectName getObjectNameObject() {
0554: return abstractName.getObjectName();
0555: }
0556:
0557: public final AbstractName getAbstractName() {
0558: return abstractName;
0559: }
0560:
0561: public synchronized final long getStartTime() {
0562: return startTime;
0563: }
0564:
0565: public int getState() {
0566: return gbeanInstanceState.getState();
0567: }
0568:
0569: public final State getStateInstance() {
0570: return gbeanInstanceState.getStateInstance();
0571: }
0572:
0573: /**
0574: * Gets an unmodifiable map from attribute names to index number (Integer). This index number
0575: * can be used to efficiently set or retrieve an attribute value.
0576: *
0577: * @return an unmodifiable map of attribute indexes by name
0578: */
0579: public Map getAttributeIndex() {
0580: return Collections.unmodifiableMap(new HashMap(attributeIndex));
0581: }
0582:
0583: /**
0584: * Gets an unmodifiable map from operation signature (GOperationSignature) to index number (Integer).
0585: * This index number can be used to efficciently invoke the operation.
0586: *
0587: * @return an unmodifiable map of operation indexec by signature
0588: */
0589: public Map getOperationIndex() {
0590: return Collections.unmodifiableMap(new HashMap(operationIndex));
0591: }
0592:
0593: /**
0594: * Gets the GBeanInfo used to build this gbean.
0595: *
0596: * @return the GBeanInfo used to build this gbean
0597: */
0598: public GBeanInfo getGBeanInfo() {
0599: return gbeanInfo;
0600: }
0601:
0602: /**
0603: * Moves this GBeanInstance to the starting state and then attempts to move this MBean immediately
0604: * to the running state.
0605: *
0606: * @throws IllegalStateException If the gbean is disabled
0607: */
0608: public final void start() {
0609: synchronized (this ) {
0610: if (dead) {
0611: throw new IllegalStateException(
0612: "A dead GBean can not be started: abstractName="
0613: + abstractName);
0614: }
0615: }
0616: gbeanInstanceState.start();
0617: }
0618:
0619: /**
0620: * Starts this GBeanInstance and then attempts to start all of its start dependent children.
0621: *
0622: * @throws IllegalStateException If the gbean is disabled
0623: */
0624: public final void startRecursive() {
0625: synchronized (this ) {
0626: if (dead) {
0627: throw new IllegalStateException(
0628: "A dead GBean can not be started: abstractName="
0629: + abstractName);
0630: }
0631: }
0632: gbeanInstanceState.startRecursive();
0633: }
0634:
0635: /**
0636: * Moves this GBeanInstance to the STOPPING state, calls stop on all start dependent children, and then attempt
0637: * to move this MBean to the STOPPED state.
0638: */
0639: public final void stop() {
0640: gbeanInstanceState.stop();
0641: }
0642:
0643: /**
0644: * Moves this GBeanInstance to the FAILED state. There are no calls to dependent children, but they will be
0645: * notified using standard J2EE management notification.
0646: */
0647: final void referenceFailed() {
0648: gbeanInstanceState.fail();
0649: }
0650:
0651: /**
0652: * Gets the gbean data for the gbean held by this gbean mbean.
0653: *
0654: * @return the gbean data
0655: */
0656: public GBeanData getGBeanData() {
0657: GBeanData gbeanData = new GBeanData(abstractName, gbeanInfo);
0658:
0659: // copy target into local variables from within a synchronized block to gaurentee a consistent read
0660: int state;
0661: Object instance;
0662: synchronized (this ) {
0663: state = instanceState;
0664: instance = target;
0665: }
0666:
0667: // add the attributes
0668: for (int i = 0; i < attributes.length; i++) {
0669: GBeanAttribute attribute = attributes[i];
0670: if (attribute.isPersistent()) {
0671: String name = attribute.getName();
0672: Object value;
0673: if ((state != DESTROYED || attribute.isFramework())
0674: && attribute.isReadable()) {
0675: try {
0676: value = attribute.getValue(instance);
0677: } catch (Throwable throwable) {
0678: value = attribute.getPersistentValue();
0679: log
0680: .debug(
0681: "Could not get the current value of persistent attribute. The persistent "
0682: + "attribute will not reflect the current state attribute. "
0683: + attribute
0684: .getDescription(),
0685: throwable);
0686: }
0687: } else {
0688: value = attribute.getPersistentValue();
0689: }
0690: gbeanData.setAttribute(name, value);
0691: }
0692: }
0693:
0694: // add the references
0695: for (int i = 0; i < references.length; i++) {
0696: GBeanReference reference = references[i];
0697: String name = reference.getName();
0698: if (reference instanceof GBeanSingleReference) {
0699: AbstractName abstractName = ((GBeanSingleReference) reference)
0700: .getTargetName();
0701: if (abstractName != null) {
0702: gbeanData.setReferencePattern(name, abstractName);
0703: }
0704: } else if (reference instanceof GBeanCollectionReference) {
0705: Set patterns = ((GBeanCollectionReference) reference)
0706: .getPatterns();
0707: if (patterns != null) {
0708: gbeanData.setReferencePatterns(name, patterns);
0709: }
0710: } else {
0711: throw new IllegalStateException(
0712: "Unrecognized GBeanReference '"
0713: + reference.getClass().getName() + "'");
0714: }
0715: }
0716: //TODO copy the dependencies??
0717: return gbeanData;
0718: }
0719:
0720: /**
0721: * Gets the attribute value using the attribute index. This is the most efficient way to get
0722: * an attribute as it avoids a HashMap lookup.
0723: *
0724: * @param index the index of the attribute
0725: * @return the attribute value
0726: * @throws Exception if a target instance throws and exception
0727: * @throws IndexOutOfBoundsException if the index is invalid
0728: */
0729: public Object getAttribute(int index) throws Exception {
0730: GBeanAttribute attribute = attributes[index];
0731:
0732: // copy target into local variables from within a synchronized block to gaurentee a consistent read
0733: int state;
0734: Object instance;
0735: synchronized (this ) {
0736: state = instanceState;
0737: instance = target;
0738: }
0739:
0740: if (state != DESTROYED || attribute.isFramework()) {
0741: return attribute.getValue(instance);
0742: } else {
0743: if (attribute.isPersistent()) {
0744: return attribute.getPersistentValue();
0745: } else {
0746: throw new IllegalStateException(
0747: "Cannot retrieve the value for non-persistent attribute \""
0748: + attribute.getName()
0749: + "\" when GBeanInstance is DESTROYED");
0750: }
0751: }
0752: }
0753:
0754: /**
0755: * Gets an attribute's value by name. This get style is less efficient becuse the attribute must
0756: * first be looked up in a HashMap.
0757: *
0758: * @param attributeName the name of the attribute to retrieve
0759: * @return the attribute value
0760: * @throws Exception if a problem occurs while getting the value
0761: * @throws NoSuchAttributeException if the attribute name is not found in the map
0762: */
0763: public Object getAttribute(String attributeName)
0764: throws NoSuchAttributeException, Exception {
0765: GBeanAttribute attribute;
0766: try {
0767: attribute = getAttributeByName(attributeName);
0768: } catch (NoSuchAttributeException e) {
0769: if (attributeName.equals(RAW_INVOKER)) {
0770: return rawInvoker;
0771: }
0772: throw e;
0773: }
0774:
0775: // copy target into local variables from within a synchronized block to gaurentee a consistent read
0776: int state;
0777: Object instance;
0778: synchronized (this ) {
0779: state = instanceState;
0780: instance = target;
0781: }
0782:
0783: if (state != DESTROYED || attribute.isFramework()) {
0784: return attribute.getValue(instance);
0785: } else {
0786: if (attribute.isPersistent()) {
0787: return attribute.getPersistentValue();
0788: } else {
0789: throw new IllegalStateException(
0790: "Cannot retrieve the value for non-persistent attribute "
0791: + attributeName
0792: + " when gbean has been destroyed: "
0793: + abstractName);
0794: }
0795: }
0796: }
0797:
0798: /**
0799: * Sets the attribute value using the attribute index. This is the most efficient way to set
0800: * an attribute as it avoids a HashMap lookup.
0801: *
0802: * @param index the index of the attribute
0803: * @param value the new value of attribute value
0804: * @throws Exception if a target instance throws and exception
0805: * @throws IndexOutOfBoundsException if the index is invalid
0806: */
0807: public void setAttribute(int index, Object value) throws Exception,
0808: IndexOutOfBoundsException {
0809: setAttribute(index, value, true);
0810: }
0811:
0812: private void setAttribute(int index, Object value, boolean manage)
0813: throws Exception, IndexOutOfBoundsException {
0814: GBeanAttribute attribute = attributes[index];
0815:
0816: // copy target into local variables from within a synchronized block to gaurentee a consistent read
0817: int state;
0818: Object instance;
0819: synchronized (this ) {
0820: state = instanceState;
0821: instance = target;
0822: }
0823:
0824: if (state != DESTROYED || attribute.isFramework()) {
0825: attribute.setValue(instance, value);
0826: } else {
0827: attribute.setPersistentValue(value);
0828: }
0829: if (manage && attribute.isManageable()) {
0830: updateManageableAttribute(attribute, value);
0831: }
0832: }
0833:
0834: /**
0835: * Sets an attribute's value by name. This set style is less efficient becuse the attribute must
0836: * first be looked up in a HashMap.
0837: *
0838: * @param attributeName the name of the attribute to retrieve
0839: * @param value the new attribute value
0840: * @throws Exception if a target instance throws and exception
0841: * @throws NoSuchAttributeException if the attribute name is not found in the map
0842: */
0843: public void setAttribute(String attributeName, Object value)
0844: throws Exception, NoSuchAttributeException {
0845: setAttribute(attributeName, value, true);
0846: }
0847:
0848: public void setAttribute(String attributeName, Object value,
0849: boolean manage) throws Exception, NoSuchAttributeException {
0850: GBeanAttribute attribute = getAttributeByName(attributeName);
0851:
0852: // copy target into local variables from within a synchronized block to gaurentee a consistent read
0853: int state;
0854: Object instance;
0855: synchronized (this ) {
0856: state = instanceState;
0857: instance = target;
0858: }
0859:
0860: if (state != DESTROYED || attribute.isFramework()) {
0861: attribute.setValue(instance, value);
0862: } else {
0863: attribute.setPersistentValue(value);
0864: }
0865: if (manage && attribute.isManageable()) {
0866: updateManageableAttribute(attribute, value);
0867: }
0868: }
0869:
0870: private void updateManageableAttribute(GBeanAttribute attribute,
0871: Object value) {
0872: if (manageableStore == null) {
0873: manageableStore = getManageableAttributeStore();
0874: if (manageableStore == null) {
0875: return;
0876: }
0877: }
0878: Artifact configName = abstractName.getArtifact();
0879: if (configName != null) {
0880: manageableStore.setValue(configName, abstractName,
0881: attribute.getAttributeInfo(), value, classLoader);
0882: } else {
0883: log.error("Unable to identify Configuration for GBean "
0884: + abstractName + ". Manageable attribute "
0885: + attribute.getName()
0886: + " was not updated in persistent store.");
0887: }
0888: }
0889:
0890: private ManageableAttributeStore getManageableAttributeStore() {
0891: Set set = kernel.listGBeans(new AbstractNameQuery(
0892: ManageableAttributeStore.class.getName()));
0893: for (Iterator iterator = set.iterator(); iterator.hasNext();) {
0894: AbstractName abstractName1 = (AbstractName) iterator.next();
0895: try {
0896: return (ManageableAttributeStore) kernel
0897: .getGBean(abstractName1);
0898: } catch (GBeanNotFoundException e) {
0899: // ignored... gbean was unregistered
0900: }
0901: }
0902: return null;
0903: }
0904:
0905: private GBeanAttribute getAttributeByName(String name)
0906: throws NoSuchAttributeException {
0907: Integer index = (Integer) attributeIndex.get(name);
0908: if (index == null) {
0909: throw new NoSuchAttributeException("Unknown attribute \""
0910: + name + "\" in gbean " + abstractName);
0911: }
0912: return attributes[index.intValue()];
0913: }
0914:
0915: /**
0916: * Invokes an opreation using the operation index. This is the most efficient way to invoke
0917: * an operation as it avoids a HashMap lookup.
0918: *
0919: * @param index the index of the attribute
0920: * @param arguments the arguments to the operation
0921: * @return the result of the operation
0922: * @throws Exception if a target instance throws and exception
0923: * @throws IndexOutOfBoundsException if the index is invalid
0924: * @throws IllegalStateException if the gbean instance has been destroyed
0925: */
0926: public Object invoke(int index, Object[] arguments)
0927: throws Exception {
0928: GBeanOperation operation = operations[index];
0929:
0930: // copy target into local variables from within a synchronized block to gaurentee a consistent read
0931: int state;
0932: Object instance;
0933: synchronized (this ) {
0934: state = instanceState;
0935: instance = target;
0936: }
0937:
0938: if (state == DESTROYED && !operation.isFramework()) {
0939: throw new IllegalStateException(
0940: "Operations can only be invoke while the GBean instance is running: "
0941: + abstractName);
0942: }
0943: return operation.invoke(instance, arguments);
0944: }
0945:
0946: /**
0947: * Invokes an operation on the target gbean by method signature. This style if invocation is
0948: * inefficient, because the target method must be looked up in a hashmap using a freshly constructed
0949: * GOperationSignature object.
0950: *
0951: * @param operationName the name of the operation to invoke
0952: * @param arguments arguments to the operation
0953: * @param types types of the operation arguemtns
0954: * @return the result of the operation
0955: * @throws Exception if a target instance throws and exception
0956: * @throws NoSuchOperationException if the operation signature is not found in the map
0957: * @throws IllegalStateException if the gbean instance has been destroyed
0958: */
0959: public Object invoke(String operationName, Object[] arguments,
0960: String[] types) throws Exception, NoSuchOperationException {
0961: GOperationSignature signature = new GOperationSignature(
0962: operationName, types);
0963: Integer index = (Integer) operationIndex.get(signature);
0964: if (index == null) {
0965: throw new NoSuchOperationException("Unknown operation "
0966: + signature);
0967: }
0968: GBeanOperation operation = operations[index.intValue()];
0969:
0970: // copy target into local variables from within a synchronized block to gaurentee a consistent read
0971: int state;
0972: Object instance;
0973: synchronized (this ) {
0974: state = instanceState;
0975: instance = target;
0976: }
0977:
0978: if (state == DESTROYED && !operation.isFramework()) {
0979: throw new IllegalStateException(
0980: "Operations can only be invoke while the GBean is running: "
0981: + abstractName);
0982: }
0983: return operation.invoke(instance, arguments);
0984: }
0985:
0986: private GBeanReference getReferenceByName(String name) {
0987: Integer index = (Integer) referenceIndex.get(name);
0988: if (index == null) {
0989: throw new IllegalArgumentException("Unknown reference "
0990: + name);
0991: }
0992: return references[index.intValue()];
0993: }
0994:
0995: boolean createInstance() throws Exception {
0996: synchronized (this ) {
0997: // first check we are still in the correct state to start
0998: if (instanceState == CREATING || instanceState == RUNNING) {
0999: // another thread already completed starting
1000: return false;
1001: } else if (instanceState == DESTROYING) {
1002: // this should never ever happen... this method is protected by the GBeanState class which should
1003: // prevent stuff like this happening, but check anyway
1004: stateReason = "an internal error has occurred. An attempt was made to start an instance that was still stopping which is an illegal state transition.";
1005: throw new IllegalStateException(
1006: "A stopping instance can not be started until fully stopped");
1007: }
1008: assert instanceState == DESTROYED;
1009:
1010: stateReason = null;
1011:
1012: // Call all start on every reference. This way the dependecies are held until we can start
1013: LinkedHashSet unstarted = new LinkedHashSet();
1014: for (int i = 0; i < dependencies.length; i++) {
1015: if (!dependencies[i].start()) {
1016: unstarted.add(dependencies[i].getTargetName());
1017: }
1018: }
1019: for (int i = 0; i < references.length; i++) {
1020: if (!references[i].start()) {
1021: if (references[i] instanceof GBeanSingleReference) {
1022: GBeanSingleReference reference = (GBeanSingleReference) references[i];
1023: unstarted.add(reference.getTargetName());
1024: }
1025: }
1026: }
1027: if (!unstarted.isEmpty()) {
1028: if (unstarted.size() == 1) {
1029: stateReason = unstarted.iterator().next()
1030: + " did not start.";
1031: } else {
1032: stateReason = "the following dependent services did not start: "
1033: + unstarted;
1034: }
1035: return false;
1036: }
1037:
1038: // we are definately going to (try to) start... if this fails the must clean up these variables
1039: instanceState = CREATING;
1040: startTime = System.currentTimeMillis();
1041: }
1042:
1043: ClassLoader oldCL = Thread.currentThread()
1044: .getContextClassLoader();
1045: Thread.currentThread().setContextClassLoader(classLoader);
1046: Object instance = null;
1047: try {
1048: GConstructorInfo constructorInfo = gbeanInfo
1049: .getConstructor();
1050: Class[] parameterTypes = constructor.getParameterTypes();
1051:
1052: // create constructor parameter array
1053: Object[] parameters = new Object[parameterTypes.length];
1054: Iterator names = constructorInfo.getAttributeNames()
1055: .iterator();
1056: for (int i = 0; i < parameters.length; i++) {
1057: String name = (String) names.next();
1058: if (referenceIndex.containsKey(name)) {
1059: parameters[i] = getReferenceByName(name).getProxy();
1060: } else if (attributeIndex.containsKey(name)) {
1061: GBeanAttribute attribute = getAttributeByName(name);
1062: parameters[i] = attribute.getPersistentValue();
1063: } else {
1064: stateReason = "the service constructor definition contained the name '"
1065: + name
1066: + "' which is not a known attribute or reference of the service.";
1067: throw new InvalidConfigurationException(
1068: "Unknown attribute or reference name in constructor: referenceName="
1069: + name + ", gbean=" + abstractName);
1070: }
1071: }
1072:
1073: // create instance
1074: try {
1075: instance = constructor.newInstance(parameters);
1076: } catch (InvocationTargetException e) {
1077: Throwable targetException = e.getTargetException();
1078: if (targetException instanceof Exception) {
1079: stateReason = "the service constructor threw an exception. \n"
1080: + printException(targetException);
1081: throw (Exception) targetException;
1082: } else if (targetException instanceof Error) {
1083: stateReason = "the service constructor threw an exception. \n"
1084: + printException(targetException);
1085: throw (Error) targetException;
1086: }
1087: stateReason = "the service constructor threw an exception. \n"
1088: + printException(e);
1089: throw e;
1090: } catch (IllegalArgumentException e) {
1091: stateReason = "the service constructor threw an exception due to a parameter type mismatch. \n"
1092: + printException(e);
1093: log.warn("Constructor mismatch for " + abstractName, e);
1094: throw e;
1095: }
1096:
1097: // write the target variable in a synchronized block so it is available to all threads
1098: // we do this before calling the setters or start method so the bean can be called back
1099: // from a setter start method
1100: synchronized (this ) {
1101: target = instance;
1102: }
1103:
1104: // inject the persistent attribute value into the new instance
1105: for (int i = 0; i < attributes.length; i++) {
1106: checkIfShouldFail();
1107: try {
1108: attributes[i].inject(instance);
1109: } catch (Exception e) {
1110: stateReason = "the setter for attribute '"
1111: + attributes[i].getName()
1112: + "' threw an exception. \n"
1113: + printException(e);
1114: throw e;
1115: }
1116: }
1117:
1118: // inject the proxies into the new instance
1119: for (int i = 0; i < references.length; i++) {
1120: checkIfShouldFail();
1121: try {
1122: references[i].inject(instance);
1123: } catch (Exception e) {
1124: stateReason = "the setter for reference '"
1125: + references[i].getName()
1126: + "' threw an exception. \n"
1127: + printException(e);
1128: throw e;
1129: }
1130: }
1131:
1132: if (instance instanceof GBeanLifecycle) {
1133: checkIfShouldFail();
1134: try {
1135: ((GBeanLifecycle) instance).doStart();
1136: } catch (Exception e) {
1137: stateReason = "the doStart method threw an exception. \n"
1138: + printException(e);
1139: throw e;
1140: }
1141: }
1142:
1143: // all done... we are now fully running
1144: synchronized (this ) {
1145: checkIfShouldFail();
1146: if (instanceRegistry != null) {
1147: instanceRegistry.instanceCreated(instance, this );
1148: }
1149: instanceState = RUNNING;
1150: this .notifyAll();
1151: }
1152:
1153: stateReason = null;
1154: return true;
1155: } catch (Throwable t) {
1156: stateReason = "Throwable during start of gbean: \n"
1157: + printException(t);
1158: // something went wrong... we need to destroy this instance
1159: synchronized (this ) {
1160: instanceState = DESTROYING;
1161: }
1162:
1163: if (instance instanceof GBeanLifecycle) {
1164: try {
1165: ((GBeanLifecycle) instance).doFail();
1166: } catch (Throwable ignored) {
1167: log.error("Problem in doFail of " + abstractName,
1168: ignored);
1169: }
1170: }
1171:
1172: // bean has been notified... drop our reference
1173: synchronized (this ) {
1174: // stop all of the references
1175: for (int i = 0; i < references.length; i++) {
1176: references[i].stop();
1177: }
1178: for (int i = 0; i < dependencies.length; i++) {
1179: dependencies[i].stop();
1180: }
1181:
1182: target = null;
1183: instanceState = DESTROYED;
1184: startTime = 0;
1185: this .notifyAll();
1186: }
1187:
1188: if (t instanceof Exception) {
1189: throw (Exception) t;
1190: } else if (t instanceof Error) {
1191: throw (Error) t;
1192: } else {
1193: throw new Error(t);
1194: }
1195: } finally {
1196: Thread.currentThread().setContextClassLoader(oldCL);
1197: }
1198: }
1199:
1200: private String printException(Throwable t) {
1201: StringWriter stringWriter = new StringWriter();
1202: PrintWriter printWriter = new PrintWriter(stringWriter);
1203: t.printStackTrace(printWriter);
1204: printWriter.flush();
1205: return stringWriter.toString();
1206: }
1207:
1208: private synchronized void checkIfShouldFail() throws Exception {
1209: if (shouldFail) {
1210: shouldFail = false;
1211: throw new Exception(
1212: "A reference has failed so construction can not complete");
1213: }
1214: }
1215:
1216: boolean destroyInstance(boolean stop) throws Exception {
1217: Object instance;
1218: synchronized (this ) {
1219: if (!stop && instanceState == CREATING) {
1220: // signal to the creating thead that it should fail
1221: shouldFail = true;
1222: return false;
1223: }
1224:
1225: // if the instance is being created we need to wait
1226: // for it to finish before we can try to stop it
1227: while (instanceState == CREATING) {
1228: // todo should we limit this wait? If so, how do we configure the wait time?
1229: try {
1230: this .wait();
1231: } catch (InterruptedException e) {
1232: // clear the interrupted flag
1233: Thread.interrupted();
1234: // rethrow the interrupted exception.... someone was sick of us waiting
1235: throw e;
1236: }
1237: }
1238:
1239: if (instanceState == DESTROYING
1240: || instanceState == DESTROYED) {
1241: // another thread is already stopping or has already stopped
1242: return false;
1243: }
1244: assert instanceState == RUNNING;
1245: stateReason = null;
1246:
1247: // we are definately going to stop... if this fails the must clean up these variables
1248: instanceState = DESTROYING;
1249: instance = target;
1250: }
1251:
1252: // update the persistent attributes
1253: // do not update the persistent attibute values in the case of a failure
1254: // failed gbeans may have corrupted attributes that would be persisted
1255: Exception problem = null;
1256: if (stop && instance != null) {
1257: try {
1258: // get all the data but don't update in case there is an exception
1259: Map data = new HashMap();
1260: for (int i = 0; i < attributes.length; i++) {
1261: GBeanAttribute attribute = attributes[i];
1262: if (attribute.isPersistent()
1263: && attribute.isReadable()) {
1264: // copy the current attribute value to the persistent value
1265: Object value;
1266: try {
1267: value = attribute.getValue(instance);
1268: } catch (Throwable e) {
1269: // There is no reason to create a new Exception sub class as this exception will
1270: // simply be caught and logged on GBeanInstanceState
1271: throw new Exception(
1272: "Problem while updaing the persistent value of attibute: "
1273: + "Attribute Name: "
1274: + attribute.getName()
1275: + ", " + "Type: "
1276: + attribute.getType()
1277: + ", " + "GBeanInstance: "
1278: + getName(), e);
1279: }
1280: data.put(attribute, value);
1281: }
1282: }
1283: // now we have all the data we can update the persistent values
1284: for (int i = 0; i < attributes.length; i++) {
1285: GBeanAttribute attribute = attributes[i];
1286: if (attribute.isPersistent()
1287: && attribute.isReadable()) {
1288: // copy the current attribute value to the persistent value
1289: Object value = data.get(attribute);
1290: attribute.setPersistentValue(value);
1291: }
1292: }
1293: } catch (Exception e) {
1294: // the getter threw an exception; now we must to fail
1295: stop = false;
1296: problem = e;
1297: }
1298: }
1299:
1300: // we notify the bean before removing our reference so the bean can be called back while stopping
1301: ClassLoader oldCL = Thread.currentThread()
1302: .getContextClassLoader();
1303: Thread.currentThread().setContextClassLoader(classLoader);
1304: try {
1305: if (instance instanceof GBeanLifecycle) {
1306: if (stop) {
1307: try {
1308: ((GBeanLifecycle) instance).doStop();
1309: } catch (Throwable ignored) {
1310: log.error("Problem in doStop of "
1311: + abstractName, ignored);
1312: }
1313: } else {
1314: try {
1315: ((GBeanLifecycle) instance).doFail();
1316: } catch (Throwable ignored) {
1317: log.error("Problem in doFail of "
1318: + abstractName, ignored);
1319: }
1320: }
1321: }
1322: } finally {
1323: Thread.currentThread().setContextClassLoader(oldCL);
1324: }
1325:
1326: // bean has been notified... drop our reference
1327: synchronized (this ) {
1328: // stop all of the references
1329: for (int i = 0; i < references.length; i++) {
1330: references[i].stop();
1331: }
1332: for (int i = 0; i < dependencies.length; i++) {
1333: dependencies[i].stop();
1334: }
1335:
1336: target = null;
1337: instanceState = DESTROYED;
1338: if (instanceRegistry != null) {
1339: instanceRegistry.instanceDestroyed(instance);
1340: }
1341: startTime = 0;
1342: }
1343:
1344: if (problem != null) {
1345: throw problem;
1346: }
1347: return true;
1348: }
1349:
1350: private void addManagedObjectAttributes(Map attributesMap) {
1351: //
1352: // Special attributes
1353: //
1354: attributesMap.put("abstractName", GBeanAttribute
1355: .createSpecialAttribute((GBeanAttribute) attributesMap
1356: .get("abstractName"), this , "abstractName",
1357: AbstractName.class, getAbstractName()));
1358:
1359: attributesMap.put("objectName", GBeanAttribute
1360: .createSpecialAttribute((GBeanAttribute) attributesMap
1361: .get("objectName"), this , "objectName",
1362: String.class, getObjectName()));
1363:
1364: attributesMap.put("classLoader", GBeanAttribute
1365: .createSpecialAttribute((GBeanAttribute) attributesMap
1366: .get("classLoader"), this , "classLoader",
1367: ClassLoader.class, classLoader));
1368:
1369: attributesMap.put("kernel", GBeanAttribute
1370: .createSpecialAttribute((GBeanAttribute) attributesMap
1371: .get("kernel"), this , "kernel", Kernel.class,
1372: kernel));
1373:
1374: }
1375:
1376: private GBeanInfo rebuildGBeanInfo(GConstructorInfo constructor,
1377: String j2eeType) {
1378: Set attributeInfos = new HashSet();
1379: for (int i = 0; i < attributes.length; i++) {
1380: GBeanAttribute attribute = attributes[i];
1381: attributeInfos.add(attribute.getAttributeInfo());
1382: }
1383: Set operationInfos = new HashSet();
1384: for (int i = 0; i < operations.length; i++) {
1385: operationInfos.add(operations[i].getOperationInfo());
1386: }
1387:
1388: Set referenceInfos = new HashSet();
1389: for (int i = 0; i < references.length; i++) {
1390: referenceInfos.add(references[i].getReferenceInfo());
1391: }
1392:
1393: Set interfaceInfos = new HashSet();
1394: for (int i = 0; i < interfaces.length; i++) {
1395: interfaceInfos.add(interfaces[i]);
1396: }
1397:
1398: return new GBeanInfo(name, type.getName(), j2eeType,
1399: attributeInfos, constructor, operationInfos,
1400: referenceInfos, interfaceInfos);
1401: }
1402:
1403: public boolean equals(Object obj) {
1404: if (obj == this )
1405: return true;
1406: if (!(obj instanceof GBeanInstance))
1407: return false;
1408: return abstractName.equals(((GBeanInstance) obj).abstractName);
1409: }
1410:
1411: public int hashCode() {
1412: return abstractName.hashCode();
1413: }
1414:
1415: public String toString() {
1416: return abstractName.toString();
1417: }
1418: }
|