Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/geronimo/gbean/runtime/GBeanInstance.java


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