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}