
org.apache.geronimo.gbean.runtime.GBeanInstance Maven / Gradle / Ivy
The newest version!
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.geronimo.gbean.runtime;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.management.ObjectName;
import org.apache.geronimo.gbean.ServiceInterfaces;
import org.osgi.framework.ServiceRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.geronimo.gbean.AbstractName;
import org.apache.geronimo.gbean.AbstractNameQuery;
import org.apache.geronimo.gbean.GAttributeInfo;
import org.apache.geronimo.gbean.GBeanData;
import org.apache.geronimo.gbean.GBeanInfo;
import org.apache.geronimo.gbean.GBeanLifecycle;
import org.apache.geronimo.gbean.GConstructorInfo;
import org.apache.geronimo.gbean.GOperationInfo;
import org.apache.geronimo.gbean.GOperationSignature;
import org.apache.geronimo.gbean.GReferenceInfo;
import org.apache.geronimo.gbean.InvalidConfigurationException;
import org.apache.geronimo.gbean.ReferencePatterns;
import org.apache.geronimo.kernel.DependencyManager;
import org.apache.geronimo.kernel.GBeanNotFoundException;
import org.apache.geronimo.kernel.Kernel;
import org.apache.geronimo.kernel.NoSuchAttributeException;
import org.apache.geronimo.kernel.NoSuchOperationException;
import org.apache.xbean.osgi.bundle.util.BundleClassLoader;
import org.apache.xbean.osgi.bundle.util.equinox.EquinoxBundleClassLoader;
import org.apache.geronimo.kernel.config.ManageableAttributeStore;
import org.apache.geronimo.kernel.management.State;
import org.apache.geronimo.kernel.management.StateManageable;
import org.apache.geronimo.kernel.osgi.FrameworkUtils;
import org.apache.geronimo.kernel.repository.Artifact;
import org.apache.xbean.recipe.ConstructionException;
import org.apache.xbean.recipe.ObjectRecipe;
import org.apache.xbean.recipe.Option;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
/**
* A GBeanInstance is a J2EE Management Managed Object, and is standard base for Geronimo services.
*
* @version $Rev:385718 $ $Date: 2011-08-01 10:24:43 +0800 (Mon, 01 Aug 2011) $
*/
public final class GBeanInstance implements StateManageable {
private static final Logger log = LoggerFactory.getLogger(GBeanInstance.class);
private static final int MAX_DEPENDENCY_STATE_REASON_NUM = Integer.getInteger("org.apache.geronimo.gbean.runtime.max_state_reason_count", 5);
private static final String ABSTRACT_NAME_PROPERTY = "org.apache.geronimo.abstractName";
private static final String OSGI_JNDI_NAME_PROPERTY = "osgi.jndi.service.name";
private static final int DESTROYED = 0;
private static final int CREATING = 1;
private static final int RUNNING = 2;
private static final int DESTROYING = 3;
/**
* Attribute name used to retrieve the RawInvoker for the GBean
*/
public static final String RAW_INVOKER = "$$RAW_INVOKER$$";
/**
* The kernel in which this server is registered.
*/
private final Kernel kernel;
/**
* The ManageableAttributeStore notified of any changes to manageable
* attributes. This is lazy-loaded as manageable attributes are set.
*/
private ManageableAttributeStore manageableStore;
/**
* the abstract name of this service
*/
private final AbstractName abstractName;
/**
* This handles all state transiitions for this instance.
*/
private final GBeanInstanceState gbeanInstanceState;
/**
* The objectRecipe used to create the instance
*/
private final ObjectRecipe objectRecipe;
/**
* A fast index based raw invoker for this GBean.
*/
private final RawInvoker rawInvoker;
/**
* The single listener to which we broadcast lifecycle change events.
*/
private final LifecycleBroadcaster lifecycleBroadcaster;
/**
* Interfaces for this GBean
*/
private final String[] interfaces;
/**
* Attributes lookup table
*/
private final GBeanAttribute[] attributes;
/**
* Attributes supported by this GBeanMBean by (String) name.
*/
private final Map attributeIndex = new HashMap();
/**
* References lookup table
*/
private final GBeanReference[] references;
/**
* References supported by this GBeanMBean by (String) name.
*/
private final Map referenceIndex = new HashMap();
/**
* Dependencies supported by this GBean.
*/
private final GBeanDependency[] dependencies;
/**
* Operations lookup table
*/
private final GBeanOperation[] operations;
/**
* Operations supported by this GBeanMBean by (GOperationSignature) name.
*/
private final Map operationIndex = new HashMap();
/**
* The classloader used for all invocations and creating targets.
*/
private final ClassLoader classLoader;
private final BundleContext bundleContext;
/**
* Metadata describing the attributes, operations and references of this GBean
*/
private final GBeanInfo gbeanInfo;
/**
* Our name
*/
private final String name;
/**
* Java type of the wrapped GBean class
*/
private final Class type;
/**
* Has this instance been destroyed?
*/
private boolean dead = false;
/**
* The state of the internal gbean instance that we are wrapping.
*/
private int instanceState = DESTROYED;
/**
* Target instance of this GBean wrapper
*/
private Object target;
/**
* The time this application started.
*/
private long startTime;
/**
* This is used to signal the creating thread that it should
* fail when it returns from usercode. This is set when a
* reference has gone offline during construction.
*/
private boolean shouldFail = false;
/**
* Used to track instance
*/
private InstanceRegistry instanceRegistry;
private String stateReason;
private String[] serviceInterfaces;
private Dictionary serviceProperties;
private ServiceRegistration serviceRegistration;
/**
* Construct a GBeanMBean using the supplied GBeanData and bundle
*
* @param gbeanData the data for the new GBean including GBeanInfo, intial attribute values, and reference patterns
* @param bundleContext
* @throws org.apache.geronimo.gbean.InvalidConfigurationException
* if the gbeanInfo is inconsistent with the actual java classes, such as
* mismatched attribute types or the intial data cannot be set
*/
public GBeanInstance(GBeanData gbeanData, Kernel kernel, DependencyManager dependencyManager, LifecycleBroadcaster lifecycleBroadcaster, BundleContext bundleContext) throws InvalidConfigurationException {
this.abstractName = gbeanData.getAbstractName();
this.kernel = kernel;
this.lifecycleBroadcaster = lifecycleBroadcaster;
this.gbeanInstanceState = new GBeanInstanceState(abstractName, kernel, dependencyManager, this, lifecycleBroadcaster);
this.bundleContext = bundleContext;
GBeanInfo gbeanInfo = gbeanData.getGBeanInfo();
try {
type = bundleContext.getBundle().loadClass(gbeanInfo.getClassName());
} catch (ClassNotFoundException e) {
throw new InvalidConfigurationException("Could not load GBeanInfo class from classloader: " + bundleContext +
" className=" + gbeanInfo.getClassName(), e);
}
if (FrameworkUtils.useURLClassLoader() && FrameworkUtils.isEquinox()) {
this.classLoader = new EquinoxBundleClassLoader(bundleContext.getBundle());
} else {
this.classLoader = new BundleClassLoader(bundleContext.getBundle());
}
name = gbeanInfo.getName();
// interfaces
interfaces = gbeanInfo.getInterfaces().toArray(new String[gbeanInfo.getInterfaces().size()]);
// attributes
attributes = buildAttributes(gbeanInfo);
for (int i = 0; i < attributes.length; i++) {
attributeIndex.put(attributes[i].getName(), i);
}
// references
Set referencesSet = new HashSet();
Set dependencySet = new HashSet();
buildReferencesAndDependencies(gbeanData, gbeanInfo, referencesSet, dependencySet);
references = referencesSet.toArray(new GBeanReference[referencesSet.size()]);
for (int i = 0; i < references.length; i++) {
referenceIndex.put(references[i].getName(), i);
}
//dependencies
for (ReferencePatterns referencePatterns : gbeanData.getDependencies()) {
AbstractName dependencyName = referencePatterns.getAbstractName();
dependencySet.add(new GBeanDependency(this, dependencyName, kernel));
}
dependencies = dependencySet.toArray(new GBeanDependency[dependencySet.size()]);
// framework operations -- all framework operations have currently been removed
// operations
Map operationsMap = new HashMap();
for (GOperationInfo operationInfo : gbeanInfo.getOperations()) {
GOperationSignature signature = new GOperationSignature(operationInfo.getName(), operationInfo.getParameterList());
// do not allow overriding of framework operations
if (!operationsMap.containsKey(signature)) {
GBeanOperation operation = new GBeanOperation(this, operationInfo);
operationsMap.put(signature, operation);
}
}
operations = new GBeanOperation[operationsMap.size()];
int opCounter = 0;
for (Map.Entry entry : operationsMap.entrySet()) {
operations[opCounter] = entry.getValue();
operationIndex.put(entry.getKey(), opCounter);
opCounter++;
}
// rebuild the gbean info based on the current attributes, operations, and references because
// the above code add new attributes and operations
this.gbeanInfo = rebuildGBeanInfo(gbeanInfo.getConstructor(), gbeanInfo.getJ2eeType(), gbeanInfo.getPriority(), gbeanInfo.isOsgiService(), gbeanInfo.getServiceInterfaces());
objectRecipe = newObjectRecipe(gbeanData);
// create the raw invokers
rawInvoker = new RawInvoker(this);
//Add the reference to all applicable reference collections before possibly starting the gbean having an
//explicit reference to the reference.
for (int i = 0; i < references.length; i++) {
references[i].online();
}
for (int i = 0; i < dependencies.length; i++) {
dependencies[i].online();
}
this.serviceInterfaces = gbeanData.getServiceInterfaces();
this.serviceProperties = gbeanData.getServiceProperties();
}
protected ObjectRecipe newObjectRecipe(GBeanData gbeanData) {
GBeanInfo beanInfo = gbeanData.getGBeanInfo();
List cstrNames = beanInfo.getConstructor().getAttributeNames();
Class[] cstrTypes = new Class[cstrNames.size()];
for (int i = 0; i < cstrTypes.length; i++) {
String argumentName = cstrNames.get(i);
if (referenceIndex.containsKey(argumentName)) {
Integer index = referenceIndex.get(argumentName);
GBeanReference reference = references[index];
cstrTypes[i] = reference.getProxyType();
} else if (attributeIndex.containsKey(argumentName)) {
Integer index = attributeIndex.get(argumentName);
GBeanAttribute attribute = attributes[index];
cstrTypes[i] = attribute.getType();
}
}
ObjectRecipe objectRecipe = new ObjectRecipe(type, cstrNames.toArray(new String[0]), cstrTypes);
objectRecipe.allow(Option.IGNORE_MISSING_PROPERTIES);
// set the initial attribute values
Map dataAttributes = gbeanData.getAttributes();
for (GAttributeInfo attributeInfo : beanInfo.getAttributes()) {
Integer integer = attributeIndex.get(attributeInfo.getName());
GBeanAttribute attribute = attributes[integer];
String attributeName = attribute.getName();
if (attribute.isPersistent() || attribute.isDynamic()) {
Object attributeValue = dataAttributes.get(attributeName);
if (null != attributeValue) {
attribute.setPersistentValue(attributeValue);
}
if (attribute.isPersistent() && null != attributeValue && !attribute.isDynamic()) {
objectRecipe.setProperty(attributeName, attribute.getPersistentValue());
}
} else if (attribute.isSpecial() && (attribute.isWritable() || cstrNames.contains(attributeName))) {
objectRecipe.setProperty(attributeName, attribute.getPersistentValue());
}
}
return objectRecipe;
}
protected void buildReferencesAndDependencies(GBeanData gbeanData,
GBeanInfo gbeanInfo,
Set referencesSet,
Set dependencySet) {
Map dataReferences = gbeanData.getReferences();
for (GReferenceInfo referenceInfo : gbeanInfo.getReferences()) {
String referenceName = referenceInfo.getName();
ReferencePatterns referencePatterns = dataReferences.remove(referenceName);
if (referenceInfo.getProxyType().equals(Collection.class.getName())) {
referencesSet.add(new GBeanCollectionReference(this, referenceInfo, kernel, referencePatterns));
} else {
referencesSet.add(new GBeanSingleReference(this, referenceInfo, kernel, referencePatterns));
if (referencePatterns != null) {
dependencySet.add(new GBeanDependency(this, referencePatterns.getAbstractName(), kernel));
}
}
}
if (!dataReferences.isEmpty()) {
throw new IllegalStateException("Attempting to set unknown references: " + dataReferences.keySet());
}
}
protected GBeanAttribute[] buildAttributes(GBeanInfo gbeanInfo) {
Map attributesMap = new HashMap();
for (GAttributeInfo attributeInfo : gbeanInfo.getAttributes()) {
attributesMap.put(attributeInfo.getName(), new GBeanAttribute(this, attributeInfo));
}
addManagedObjectAttributes(attributesMap);
return attributesMap.values().toArray(new GBeanAttribute[attributesMap.size()]);
}
public void die() throws GBeanNotFoundException {
synchronized (this) {
if (dead) {
// someone beat us to the punch... this instance should have never been found in the first place
throw new GBeanNotFoundException(abstractName);
}
dead = true;
}
// if the bean is already stopped or failed, this will do nothing; otherwise it will shutdown the bean
gbeanInstanceState.fail();
for (int i = 0; i < references.length; i++) {
references[i].offline();
}
for (int i = 0; i < dependencies.length; i++) {
dependencies[i].offline();
}
// tell everyone we are done
lifecycleBroadcaster.fireUnloadedEvent();
manageableStore = null;
}
public synchronized void setInstanceRegistry(InstanceRegistry instanceRegistry) {
this.instanceRegistry = instanceRegistry;
}
/**
* Gets the name of the GBean as defined in the gbean info.
*
* @return the gbean name
*/
public String getName() {
return name;
}
/**
* The bundle used to build this gbean.
*
* @return the bundle used to build this gbean
*/
public Bundle getBundle() {
return bundleContext.getBundle();
}
/**
* Has this gbean instance been destroyed. An destroyed gbean can no longer be used.
*
* @return true if the gbean has been destroyed
*/
public synchronized boolean isDead() {
return dead;
}
/**
* Gets the reason we are in the current state.
*
* @return the reason we are in the current state
*/
public String getStateReason() {
return stateReason;
}
/**
* Sets the reason we are in the current state.
*
* @param reason The reason we are in the current state
*/
public void setStateReason(String reason) {
stateReason = reason;
}
/**
* The java type of the wrapped gbean instance
*
* @return the java type of the gbean
*/
public Class getType() {
return type;
}
public synchronized Object getTarget() {
return target;
}
public final String getObjectName() {
return abstractName.getObjectName().getCanonicalName();
}
public final ObjectName getObjectNameObject() {
return abstractName.getObjectName();
}
public final AbstractName getAbstractName() {
return abstractName;
}
public synchronized final long getStartTime() {
return startTime;
}
public int getState() {
return gbeanInstanceState.getState();
}
public final State getStateInstance() {
return gbeanInstanceState.getStateInstance();
}
/**
* Gets an unmodifiable map from attribute names to index number (Integer). This index number
* can be used to efficiently set or retrieve an attribute value.
*
* @return an unmodifiable map of attribute indexes by name
*/
public Map getAttributeIndex() {
return Collections.unmodifiableMap(new HashMap(attributeIndex));
}
/**
* Gets an unmodifiable map from operation signature (GOperationSignature) to index number (Integer).
* This index number can be used to efficciently invoke the operation.
*
* @return an unmodifiable map of operation indexec by signature
*/
public Map getOperationIndex() {
return Collections.unmodifiableMap(new HashMap(operationIndex));
}
/**
* Gets the GBeanInfo used to build this gbean.
*
* @return the GBeanInfo used to build this gbean
*/
public GBeanInfo getGBeanInfo() {
return gbeanInfo;
}
/**
* Moves this GBeanInstance to the starting state and then attempts to move this MBean immediately
* to the running state.
*
* @throws IllegalStateException If the gbean is disabled
*/
public final void start() {
synchronized (this) {
if (dead) {
throw new IllegalStateException("A dead GBean can not be started: abstractName=" + abstractName);
}
}
gbeanInstanceState.start();
}
/**
* Starts this GBeanInstance and then attempts to start all of its start dependent children.
*
* @throws IllegalStateException If the gbean is disabled
*/
public final void startRecursive() {
synchronized (this) {
if (dead) {
throw new IllegalStateException("A dead GBean can not be started: abstractName=" + abstractName);
}
}
gbeanInstanceState.startRecursive();
}
/**
* Moves this GBeanInstance to the STOPPING state, calls stop on all start dependent children, and then attempt
* to move this MBean to the STOPPED state.
*/
public final void stop() {
gbeanInstanceState.stop();
}
/**
* Moves this GBeanInstance to the FAILED state. There are no calls to dependent children, but they will be
* notified using standard J2EE management notification.
*/
final void referenceFailed() {
gbeanInstanceState.fail();
}
/**
* Gets the gbean data for the gbean held by this gbean mbean.
*
* @return the gbean data
*/
public GBeanData getGBeanData() {
GBeanData gbeanData = new GBeanData(abstractName, gbeanInfo);
// copy target into local variables from within a synchronized block to gaurentee a consistent read
int state;
Object instance;
synchronized (this) {
state = instanceState;
instance = target;
}
// add the attributes
for (int i = 0; i < attributes.length; i++) {
GBeanAttribute attribute = attributes[i];
if (attribute.isPersistent()) {
String name = attribute.getName();
Object value;
if ((state != DESTROYED || attribute.isFramework()) && attribute.isReadable()) {
try {
value = attribute.getValue(instance);
} catch (Throwable throwable) {
value = attribute.getPersistentValue();
if(log.isDebugEnabled()) {
log.debug("Could not get the current value of persistent attribute. The persistent " +
"attribute will not reflect the current state attribute. " + attribute.getDescription(), throwable);
}
}
} else {
value = attribute.getPersistentValue();
}
gbeanData.setAttribute(name, value);
}
}
// add the references
for (int i = 0; i < references.length; i++) {
GBeanReference reference = references[i];
String name = reference.getName();
if (reference instanceof GBeanSingleReference) {
AbstractName abstractName = ((GBeanSingleReference) reference).getTargetName();
if (abstractName != null) {
gbeanData.setReferencePattern(name, abstractName);
}
} else if (reference instanceof GBeanCollectionReference) {
Set patterns = ((GBeanCollectionReference) reference).getPatterns();
if (patterns != null) {
gbeanData.setReferencePatterns(name, patterns);
}
} else {
throw new IllegalStateException("Unrecognized GBeanReference '" + reference.getClass().getName() + "'");
}
}
//TODO copy the dependencies??
return gbeanData;
}
/**
* Gets the attribute value using the attribute index. This is the most efficient way to get
* an attribute as it avoids a HashMap lookup.
*
* @param index the index of the attribute
* @return the attribute value
* @throws Exception if a target instance throws and exception
* @throws IndexOutOfBoundsException if the index is invalid
*/
public Object getAttribute(int index) throws Exception {
GBeanAttribute attribute = attributes[index];
// copy target into local variables from within a synchronized block to gaurentee a consistent read
int state;
Object instance;
synchronized (this) {
state = instanceState;
instance = target;
}
if (state != DESTROYED || attribute.isFramework()) {
return attribute.getValue(instance);
} else {
if (attribute.isPersistent()) {
return attribute.getPersistentValue();
} else {
throw new IllegalStateException("Cannot retrieve the value for non-persistent attribute \"" + attribute.getName() + "\" when GBeanInstance is DESTROYED");
}
}
}
/**
* Gets an attribute's value by name. This get style is less efficient becuse the attribute must
* first be looked up in a HashMap.
*
* @param attributeName the name of the attribute to retrieve
* @return the attribute value
* @throws Exception if a problem occurs while getting the value
* @throws NoSuchAttributeException if the attribute name is not found in the map
*/
public Object getAttribute(String attributeName) throws NoSuchAttributeException, Exception {
GBeanAttribute attribute;
try {
attribute = getAttributeByName(attributeName);
} catch (NoSuchAttributeException e) {
if (attributeName.equals(RAW_INVOKER)) {
return rawInvoker;
}
throw e;
}
// copy target into local variables from within a synchronized block to gaurentee a consistent read
int state;
Object instance;
synchronized (this) {
state = instanceState;
instance = target;
}
if (state != DESTROYED || attribute.isFramework()) {
return attribute.getValue(instance);
} else {
if (attribute.isPersistent()) {
return attribute.getPersistentValue();
} else {
throw new IllegalStateException("Cannot retrieve the value for non-persistent attribute " + attributeName + " when gbean has been destroyed: " + abstractName);
}
}
}
/**
* Sets the attribute value using the attribute index. This is the most efficient way to set
* an attribute as it avoids a HashMap lookup.
*
* @param index the index of the attribute
* @param value the new value of attribute value
* @throws Exception if a target instance throws and exception
* @throws IndexOutOfBoundsException if the index is invalid
*/
public void setAttribute(int index, Object value) throws Exception, IndexOutOfBoundsException {
setAttribute(index, value, true);
}
private void setAttribute(int index, Object value, boolean manage) throws Exception, IndexOutOfBoundsException {
GBeanAttribute attribute = attributes[index];
// copy target into local variables from within a synchronized block to gaurentee a consistent read
int state;
Object instance;
synchronized (this) {
state = instanceState;
instance = target;
}
if (state != DESTROYED || attribute.isFramework()) {
attribute.setValue(instance, value);
} else {
attribute.setPersistentValue(value);
}
if (manage && attribute.isManageable()) {
updateManageableAttribute(attribute, value);
}
}
/**
* Sets an attribute's value by name. This set style is less efficient becuse the attribute must
* first be looked up in a HashMap.
*
* @param attributeName the name of the attribute to retrieve
* @param value the new attribute value
* @throws Exception if a target instance throws and exception
* @throws NoSuchAttributeException if the attribute name is not found in the map
*/
public void setAttribute(String attributeName, Object value) throws Exception, NoSuchAttributeException {
setAttribute(attributeName, value, true);
}
public void setAttribute(String attributeName, Object value, boolean manage) throws Exception, NoSuchAttributeException {
GBeanAttribute attribute = getAttributeByName(attributeName);
// copy target into local variables from within a synchronized block to gaurentee a consistent read
int state;
Object instance;
synchronized (this) {
state = instanceState;
instance = target;
}
if (state != DESTROYED || attribute.isFramework()) {
attribute.setValue(instance, value);
} else {
attribute.setPersistentValue(value);
}
if (manage && attribute.isManageable()) {
updateManageableAttribute(attribute, value);
}
}
private void updateManageableAttribute(GBeanAttribute attribute, Object value) {
if (manageableStore == null) {
manageableStore = getManageableAttributeStore();
if (manageableStore == null) {
return;
}
}
Artifact configName = abstractName.getArtifact();
if (configName != null) {
manageableStore.setValue(configName, abstractName, attribute.getAttributeInfo(), value, getBundle());
} else {
log.error("Unable to identify Configuration for GBean " + abstractName + ". Manageable attribute " + attribute.getName() + " was not updated in persistent store.");
}
}
private ManageableAttributeStore getManageableAttributeStore() {
Set set = kernel.listGBeans(new AbstractNameQuery(ManageableAttributeStore.class.getName()));
for (Iterator iterator = set.iterator(); iterator.hasNext();) {
AbstractName abstractName1 = (AbstractName) iterator.next();
try {
return (ManageableAttributeStore) kernel.getGBean(abstractName1);
} catch (GBeanNotFoundException e) {
// ignored... gbean was unregistered
}
}
return null;
}
private GBeanAttribute getAttributeByName(String name) throws NoSuchAttributeException {
Integer index = attributeIndex.get(name);
if (index == null) {
throw new NoSuchAttributeException("Unknown attribute \"" + name + "\" in gbean " + abstractName);
}
return attributes[index.intValue()];
}
/**
* Invokes an opreation using the operation index. This is the most efficient way to invoke
* an operation as it avoids a HashMap lookup.
*
* @param index the index of the attribute
* @param arguments the arguments to the operation
* @return the result of the operation
* @throws Exception if a target instance throws and exception
* @throws IndexOutOfBoundsException if the index is invalid
* @throws IllegalStateException if the gbean instance has been destroyed
*/
public Object invoke(int index, Object[] arguments) throws Exception {
GBeanOperation operation = operations[index];
// copy target into local variables from within a synchronized block to gaurentee a consistent read
int state;
Object instance;
synchronized (this) {
state = instanceState;
instance = target;
}
if (state == DESTROYED && !operation.isFramework()) {
throw new IllegalStateException("Operations can only be invoke while the GBean instance is running: " + abstractName);
}
return operation.invoke(instance, arguments);
}
/**
* Invokes an operation on the target gbean by method signature. This style if invocation is
* inefficient, because the target method must be looked up in a hashmap using a freshly constructed
* GOperationSignature object.
*
* @param operationName the name of the operation to invoke
* @param arguments arguments to the operation
* @param types types of the operation arguemtns
* @return the result of the operation
* @throws Exception if a target instance throws and exception
* @throws NoSuchOperationException if the operation signature is not found in the map
* @throws IllegalStateException if the gbean instance has been destroyed
*/
public Object invoke(String operationName, Object[] arguments, String[] types) throws Exception, NoSuchOperationException {
GOperationSignature signature = new GOperationSignature(operationName, types);
Integer index = operationIndex.get(signature);
if (index == null) {
throw new NoSuchOperationException("Unknown operation " + signature);
}
GBeanOperation operation = operations[index.intValue()];
// copy target into local variables from within a synchronized block to gaurentee a consistent read
int state;
Object instance;
synchronized (this) {
state = instanceState;
instance = target;
}
if (state == DESTROYED && !operation.isFramework()) {
throw new IllegalStateException("Operations can only be invoke while the GBean is running: " + abstractName);
}
return operation.invoke(instance, arguments);
}
boolean createInstance() throws Exception {
synchronized (this) {
// first check we are still in the correct state to start
if (instanceState == CREATING || instanceState == RUNNING) {
// another thread already completed starting
return false;
} else if (instanceState == DESTROYING) {
// this should never ever happen... this method is protected by the GBeanState class which should
// prevent stuff like this happening, but check anyway
stateReason = "an internal error has occurred. An attempt was made to start an instance that was still stopping which is an illegal state transition.";
throw new IllegalStateException("A stopping instance can not be started until fully stopped");
}
assert instanceState == DESTROYED;
stateReason = null;
// Call all start on every reference. This way the dependecies are held until we can start
LinkedHashSet unstarted = new LinkedHashSet();
int actualUnstartedSize = 0;
for (int i = 0; i < dependencies.length; i++) {
if (dependencies[i].start()) {
continue;
}
if (unstarted.size() < MAX_DEPENDENCY_STATE_REASON_NUM || MAX_DEPENDENCY_STATE_REASON_NUM == -1) {
unstarted.add(dependencies[i].getTargetName());
}
actualUnstartedSize++;
}
for (int i = 0; i < references.length; i++) {
if (references[i].start()) {
continue;
}
if (references[i] instanceof GBeanSingleReference) {
if (unstarted.size() < MAX_DEPENDENCY_STATE_REASON_NUM || MAX_DEPENDENCY_STATE_REASON_NUM == -1) {
GBeanSingleReference reference = (GBeanSingleReference) references[i];
unstarted.add(reference.getTargetName());
}
actualUnstartedSize++;
}
}
if (!unstarted.isEmpty()) {
if (unstarted.size() == 1) {
stateReason = unstarted.iterator().next() + " did not start.";
} else {
if (actualUnstartedSize == unstarted.size()) {
stateReason = "the following dependent services did not start: " + unstarted;
} else {
stateReason = "there are " + actualUnstartedSize + " dependent services did not start, and the first " + unstarted.size() + " are recored: \n" + unstarted
+ ". \n You might configure the system property org.apache.geronimo.gbean.runtime.max_state_reason_count to show more service names or -1 to show all the unstarted service names";
}
}
return false;
}
// we are definately going to (try to) start... if this fails the must clean up these variables
instanceState = CREATING;
startTime = System.currentTimeMillis();
}
for (GBeanReference reference : references) {
Object value = reference.getProxy();
if (null != value) {
objectRecipe.setProperty(reference.getName(), value);
}
}
ClassLoader oldCL = Thread.currentThread().getContextClassLoader();
// Thread.currentThread().setContextClassLoader(classLoader);
Object instance = null;
try {
try {
//TODO OSGI NO!
instance = objectRecipe.create(classLoader);
} catch (ConstructionException e) {
Throwable targetException = e.getCause();
if (targetException instanceof Exception) {
stateReason = "the service constructor threw an exception. \n" + printException(targetException);
throw (Exception) targetException;
} else if (targetException instanceof Error) {
stateReason = "the service constructor threw an exception. \n" + printException(targetException);
throw (Error) targetException;
}
stateReason = "the service constructor threw an exception. \n" + printException(e);
throw e;
}
Map unsetProperties = objectRecipe.getUnsetProperties();
if (unsetProperties.size() > 0) {
throw new ConstructionException("Error creating gbean of class: " + gbeanInfo.getClassName() + ", attempting to set nonexistent properties: " + unsetProperties.keySet());
}
// write the target variable in a synchronized block so it is available to all threads
// we do this before calling the setters or start method so the bean can be called back
// from a setter start method
synchronized (this) {
target = instance;
}
// inject the persistent attribute value into the new instance
for (GBeanAttribute attribute : attributes) {
checkIfShouldFail();
if (!attribute.isDynamic()) {
continue;
}
try {
attribute.inject(target);
} catch (Exception e) {
stateReason = "the setter for attribute '" + attribute.getName() + "' threw an exception. \n" + printException(e);
throw e;
}
}
if (instance instanceof GBeanLifecycle) {
checkIfShouldFail();
try {
((GBeanLifecycle) instance).doStart();
} catch (Exception e) {
stateReason = "the doStart method threw an exception. \n" + printException(e);
throw e;
}
}
// all done... we are now fully running
synchronized (this) {
checkIfShouldFail();
if (instanceRegistry != null) {
instanceRegistry.instanceCreated(instance, this);
}
if (gbeanInfo.isOsgiService()) {
String[] serviceInterfaces;
if (this.serviceInterfaces != null) {
serviceInterfaces = this.serviceInterfaces;
} else if (instance instanceof ServiceInterfaces) {
serviceInterfaces = ((ServiceInterfaces)instance).getServiceInterfaces();
} else if (gbeanInfo.getServiceInterfaces().length > 0) {
serviceInterfaces = gbeanInfo.getServiceInterfaces();
} else {
Set classes = new HashSet(gbeanInfo.getInterfaces());
classes.add(gbeanInfo.getClassName());
serviceInterfaces = classes.toArray(new String[classes.size()]);
}
Dictionary serviceProperties;
if (this.serviceProperties != null) {
serviceProperties = this.serviceProperties;
} else {
serviceProperties = new Hashtable();
}
serviceProperties.put(ABSTRACT_NAME_PROPERTY, abstractName.toString());
if (serviceProperties.get(OSGI_JNDI_NAME_PROPERTY) == null) {
serviceProperties.put(OSGI_JNDI_NAME_PROPERTY, kernel.getNaming().toOsgiJndiName(abstractName));
}
serviceRegistration = bundleContext.registerService(serviceInterfaces, instance, serviceProperties);
if (log.isDebugEnabled()) {
log.debug("Registered gbean " + abstractName + " as osgi service under interfaces " + Arrays.asList(serviceInterfaces) + " with properties " + serviceProperties);
}
}
instanceState = RUNNING;
this.notifyAll();
}
stateReason = null;
return true;
} catch (Throwable t) {
stateReason = "Throwable during start of gbean: \n" + printException(t);
// something went wrong... we need to destroy this instance
synchronized (this) {
instanceState = DESTROYING;
}
if (instance instanceof GBeanLifecycle) {
try {
((GBeanLifecycle) instance).doFail();
} catch (Throwable ignored) {
log.error("Problem in doFail of " + abstractName, ignored);
}
}
// bean has been notified... drop our reference
synchronized (this) {
// stop all of the references
for (int i = 0; i < references.length; i++) {
references[i].stop();
}
for (int i = 0; i < dependencies.length; i++) {
dependencies[i].stop();
}
target = null;
instanceState = DESTROYED;
startTime = 0;
this.notifyAll();
}
if (t instanceof Exception) {
throw (Exception) t;
} else if (t instanceof Error) {
throw (Error) t;
} else {
throw new Error(t);
}
} finally {
Thread.currentThread().setContextClassLoader(oldCL);
}
}
private String printException(Throwable t) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
t.printStackTrace(printWriter);
printWriter.flush();
return stringWriter.toString();
}
private synchronized void checkIfShouldFail() throws Exception {
if (shouldFail) {
shouldFail = false;
throw new Exception("A reference has failed so construction can not complete");
}
}
boolean destroyInstance(boolean stop) throws Exception {
Object instance;
synchronized (this) {
if (!stop && instanceState == CREATING) {
// signal to the creating thead that it should fail
shouldFail = true;
return false;
}
// if the instance is being created we need to wait
// for it to finish before we can try to stop it
while (instanceState == CREATING) {
// todo should we limit this wait? If so, how do we configure the wait time?
try {
this.wait();
} catch (InterruptedException e) {
// clear the interrupted flag
Thread.interrupted();
// rethrow the interrupted exception.... someone was sick of us waiting
throw e;
}
}
if (instanceState == DESTROYING || instanceState == DESTROYED) {
// another thread is already stopping or has already stopped
return false;
}
assert instanceState == RUNNING;
stateReason = null;
// we are definately going to stop... if this fails the must clean up these variables
instanceState = DESTROYING;
instance = target;
}
// update the persistent attributes
// do not update the persistent attibute values in the case of a failure
// failed gbeans may have corrupted attributes that would be persisted
Exception problem = null;
if (stop && instance != null) {
try {
// get all the data but don't update in case there is an exception
Map data = new HashMap();
for (int i = 0; i < attributes.length; i++) {
GBeanAttribute attribute = attributes[i];
if (attribute.isPersistent() && attribute.isReadable()) {
// copy the current attribute value to the persistent value
Object value;
try {
value = attribute.getValue(instance);
} catch (Throwable e) {
// There is no reason to create a new Exception sub class as this exception will
// simply be caught and logged on GBeanInstanceState
throw new Exception("Problem while updating the persistent value of attibute: " +
"Attribute Name: " + attribute.getName() + ", " +
"Type: " + attribute.getType() + ", " +
"GBeanInstance: " + getName(), e);
}
data.put(attribute, value);
}
}
// now we have all the data we can update the persistent values
for (int i = 0; i < attributes.length; i++) {
GBeanAttribute attribute = attributes[i];
if (attribute.isPersistent() && attribute.isReadable()) {
// copy the current attribute value to the persistent value
Object value = data.get(attribute);
attribute.setPersistentValue(value);
}
}
} catch (Exception e) {
// the getter threw an exception; now we must to fail
stop = false;
problem = e;
}
}
// we notify the bean before removing our reference so the bean can be called back while stopping
ClassLoader oldCL = Thread.currentThread().getContextClassLoader();
// Thread.currentThread().setContextClassLoader(classLoader);
try {
if (instance instanceof GBeanLifecycle) {
if (stop) {
try {
((GBeanLifecycle) instance).doStop();
} catch (Throwable ignored) {
log.error("Problem in doStop of " + abstractName, ignored);
}
} else {
try {
((GBeanLifecycle) instance).doFail();
} catch (Throwable ignored) {
log.error("Problem in doFail of " + abstractName, ignored);
}
}
}
} finally {
Thread.currentThread().setContextClassLoader(oldCL);
}
// bean has been notified... drop our reference
synchronized (this) {
// stop all of the references
for (int i = 0; i < references.length; i++) {
references[i].stop();
}
for (int i = 0; i < dependencies.length; i++) {
dependencies[i].stop();
}
target = null;
instanceState = DESTROYED;
if (instanceRegistry != null) {
instanceRegistry.instanceDestroyed(instance);
}
if (serviceRegistration != null) {
serviceRegistration.unregister();
serviceRegistration = null;
if (log.isDebugEnabled()) {
log.debug("unregistered gbean as osgi service: " + abstractName);
}
}
startTime = 0;
}
if (problem != null) {
throw problem;
}
return true;
}
private void addManagedObjectAttributes(Map attributesMap) {
//
// Special attributes
//
attributesMap.put("abstractName",
GBeanAttribute.createSpecialAttribute(attributesMap.get("abstractName"),
this,
"abstractName",
AbstractName.class,
getAbstractName()));
attributesMap.put("objectName",
GBeanAttribute.createSpecialAttribute(attributesMap.get("objectName"),
this,
"objectName",
String.class,
getObjectName()));
attributesMap.put("classLoader",
GBeanAttribute.createSpecialAttribute(attributesMap.get("classLoader"),
this,
"classLoader",
ClassLoader.class,
classLoader));
attributesMap.put("bundle",
GBeanAttribute.createSpecialAttribute(attributesMap.get("bundle"),
this,
"bundle",
Bundle.class,
bundleContext.getBundle()));
attributesMap.put("bundleContext",
GBeanAttribute.createSpecialAttribute(attributesMap.get("bundleContext"),
this,
"bundleContext",
BundleContext.class,
bundleContext));
attributesMap.put("kernel",
GBeanAttribute.createSpecialAttribute(attributesMap.get("kernel"),
this,
"kernel",
Kernel.class,
kernel));
}
private GBeanInfo rebuildGBeanInfo(GConstructorInfo constructor,
String j2eeType,
int priority,
boolean osgiService,
String[] serviceInterfaces) {
Set attributeInfos = new HashSet();
for (int i = 0; i < attributes.length; i++) {
GBeanAttribute attribute = attributes[i];
attributeInfos.add(attribute.getAttributeInfo());
}
Set operationInfos = new HashSet();
for (int i = 0; i < operations.length; i++) {
operationInfos.add(operations[i].getOperationInfo());
}
Set referenceInfos = new HashSet();
for (int i = 0; i < references.length; i++) {
referenceInfos.add(references[i].getReferenceInfo());
}
Set interfaceInfos = new HashSet();
for (int i = 0; i < interfaces.length; i++) {
interfaceInfos.add(interfaces[i]);
}
return new GBeanInfo(null,
name,
type.getName(),
j2eeType,
attributeInfos,
constructor,
operationInfos,
referenceInfos,
interfaceInfos,
priority,
osgiService,
serviceInterfaces);
}
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof GBeanInstance)) return false;
return abstractName.equals(((GBeanInstance) obj).abstractName);
}
public int hashCode() {
return abstractName.hashCode();
}
public String toString() {
return abstractName.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy