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