Home » geronimo-2.2-source-release » org.apache.geronimo.gbean.runtime » [javadoc | source]

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

Home » geronimo-2.2-source-release » org.apache.geronimo.gbean.runtime » [javadoc | source]