View Javadoc

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