Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » system » [javadoc | source]
    1   /*
    2   * JBoss, Home of Professional Open Source
    3   * Copyright 2005, JBoss Inc., and individual contributors as indicated
    4   * by the @authors tag. See the copyright.txt in the distribution for a
    5   * full listing of individual contributors.
    6   *
    7   * This is free software; you can redistribute it and/or modify it
    8   * under the terms of the GNU Lesser General Public License as
    9   * published by the Free Software Foundation; either version 2.1 of
   10   * the License, or (at your option) any later version.
   11   *
   12   * This software is distributed in the hope that it will be useful,
   13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   15   * Lesser General Public License for more details.
   16   *
   17   * You should have received a copy of the GNU Lesser General Public
   18   * License along with this software; if not, write to the Free
   19   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
   20   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
   21   */
   22   package org.jboss.system;
   23   
   24   import javax.management.AttributeChangeNotification;
   25   import javax.management.JMException;
   26   import javax.management.MBeanInfo;
   27   import javax.management.MBeanOperationInfo;
   28   import javax.management.MBeanRegistration;
   29   import javax.management.MBeanServer;
   30   import javax.management.MalformedObjectNameException;
   31   import javax.management.ObjectName;
   32   
   33   import org.jboss.beans.metadata.api.annotations.Create;
   34   import org.jboss.beans.metadata.api.annotations.Destroy;
   35   import org.jboss.beans.metadata.api.annotations.Start;
   36   import org.jboss.beans.metadata.api.annotations.Stop;
   37   import org.jboss.dependency.spi.Controller;
   38   import org.jboss.dependency.spi.ControllerState;
   39   import org.jboss.deployment.DeploymentInfo;
   40   import org.jboss.deployment.SARDeployerMBean;
   41   import org.jboss.kernel.spi.dependency.KernelControllerContext;
   42   import org.jboss.kernel.spi.dependency.KernelControllerContextAware;
   43   import org.jboss.logging.Logger;
   44   import org.jboss.mx.util.JBossNotificationBroadcasterSupport;
   45   
   46   /**
   47    * An abstract base class JBoss services can subclass to implement a
   48    * service that conforms to the ServiceMBean interface. Subclasses must
   49    * override {@link #getName} method and should override 
   50    * {@link #startService}, and {@link #stopService} as approriate.
   51    *
   52    * @see ServiceMBean
   53    * 
   54    * @author <a href="mailto:rickard.oberg@telkel.com">Rickard Öberg</a>
   55    * @author Scott.Stark@jboss.org
   56    * @author <a href="mailto:andreas@jboss.org">Andreas Schaefer</a>
   57    * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
   58    * @version $Revision: 74097 $
   59    */
   60   public class ServiceMBeanSupport
   61      extends JBossNotificationBroadcasterSupport
   62      implements ServiceMBean, MBeanRegistration, KernelControllerContextAware
   63   {
   64      /** The signature for service controller operations */
   65      public static final String[] SERVICE_CONTROLLER_SIG = new String[] { ObjectName.class.getName() };
   66      
   67      /**
   68       * The instance logger for the service.  Not using a class logger
   69       * because we want to dynamically obtain the logger name from
   70       * concreate sub-classes.
   71       */
   72      protected Logger log;
   73      
   74      /** The MBeanServer which we have been register with. */
   75      protected MBeanServer server;
   76   
   77      /** The object name which we are registsred under. */
   78      protected ObjectName serviceName;
   79   
   80      /** The current state this service is in. */
   81      private int state = UNREGISTERED;
   82   
   83      /** For backwards compatibility */
   84      private boolean isJBossInternalLifecycleExposed = false;
   85   
   86      /** The controller context */
   87      private KernelControllerContext controllerContext;
   88      
   89      /**
   90       * Construct a <t>ServiceMBeanSupport</tt>.
   91       *
   92       * <p>Sets up logging.
   93       */
   94      public ServiceMBeanSupport()
   95      {
   96         // can not call this(Class) because we need to call getClass()
   97         this.log = Logger.getLogger(getClass().getName());
   98         log.trace("Constructing");
   99      }
  100   
  101      /**
  102       * Construct a <t>ServiceMBeanSupport</tt>.
  103       *
  104       * <p>Sets up logging.
  105       *
  106       * @param type   The class type to determine category name from.
  107       */
  108      @SuppressWarnings("unchecked")
  109      public ServiceMBeanSupport(final Class type)
  110      {
  111         this(type.getName());
  112      }
  113      
  114      /**
  115       * Construct a <t>ServiceMBeanSupport</tt>.
  116       *
  117       * <p>Sets up logging.
  118       *
  119       * @param category   The logger category name.
  120       */
  121      public ServiceMBeanSupport(final String category)
  122      {
  123         this(Logger.getLogger(category));
  124      }
  125   
  126      /**
  127       * Construct a <t>ServiceMBeanSupport</tt>.
  128       *
  129       * @param log   The logger to use.
  130       */
  131      public ServiceMBeanSupport(final Logger log)
  132      {
  133         this.log = log;
  134         log.trace("Constructing");
  135      }
  136      
  137      public void setKernelControllerContext(KernelControllerContext controllerContext) throws Exception
  138      {
  139         this.controllerContext = controllerContext;
  140      }
  141   
  142      public void unsetKernelControllerContext(KernelControllerContext controllerContext) throws Exception
  143      {
  144         this.controllerContext = null;
  145      }
  146   
  147      /**
  148       * Use the short class name as the default for the service name.
  149       * 
  150       * @return a description of the mbean
  151       */
  152      public String getName()
  153      {
  154         // TODO: Check if this gets called often, if so cache this or remove if not used
  155         // use the logger so we can better be used as a delegate instead of sub-class
  156         return org.jboss.util.Classes.stripPackageName(log.getName());
  157      }
  158      
  159      public ObjectName getServiceName()
  160      {
  161         return serviceName;
  162      }
  163   
  164      /**
  165       * Provide access to the service DeploymentInfo. This is only available
  166       * after the service has passed its create step.
  167       * 
  168       * @return The service DeploymentInfo if found registered under the SARDeployer.
  169       * @throws JMException - thrown on failure to invoke
  170       *    SARDeployer.getService(ObjectName)
  171       */ 
  172      public DeploymentInfo getDeploymentInfo()
  173         throws JMException
  174      {
  175         Object[] args = {serviceName};
  176         String[] sig = {serviceName.getClass().getName()};
  177         DeploymentInfo sdi = (DeploymentInfo) server.invoke(SARDeployerMBean.OBJECT_NAME,
  178            "getService", args, sig);
  179         return sdi;
  180      }
  181   
  182      public MBeanServer getServer()
  183      {
  184         return server;
  185      }
  186      
  187      public int getState()
  188      {
  189         return state;
  190      }
  191      
  192      public String getStateString()
  193      {
  194         return states[state];
  195      }
  196      
  197      public Logger getLog()
  198      {
  199         return log;
  200      }
  201   
  202   
  203      ///////////////////////////////////////////////////////////////////////////
  204      //                             State Mutators                            //
  205      ///////////////////////////////////////////////////////////////////////////
  206   
  207      @Create
  208      public void pojoCreate() throws Exception
  209      {
  210         jbossInternalCreate();
  211      }
  212      
  213      @Start
  214      public void pojoStart() throws Exception
  215      {
  216         jbossInternalStart();
  217      }
  218   
  219      @Stop
  220      public void pojoStop() throws Exception
  221      {
  222         jbossInternalStop();
  223      }
  224      
  225      @Destroy
  226      public void pojoDestroy() throws Exception
  227      {
  228         jbossInternalDestroy();
  229      }
  230   
  231      protected void pojoChange(ControllerState state)
  232      {
  233         Controller controller = controllerContext.getController();
  234         try
  235         {
  236            controller.change(controllerContext, state);
  237         }
  238         catch (RuntimeException e)
  239         {
  240            throw e;
  241         }
  242         catch (Error e)
  243         {
  244            throw e;
  245         }
  246         catch (Throwable t)
  247         {
  248            throw new RuntimeException("Error changing state of " + controllerContext.getName() + " to " + state.getStateString(), t);
  249         }
  250      }
  251      
  252      public void create() throws Exception
  253      {
  254         if (controllerContext != null)
  255            pojoChange(ControllerState.CREATE);
  256         else if (serviceName != null && isJBossInternalLifecycleExposed)
  257            server.invoke(ServiceController.OBJECT_NAME, "create", new Object[] { serviceName }, SERVICE_CONTROLLER_SIG);
  258         else
  259            jbossInternalCreate();
  260      }
  261      
  262      public void start() throws Exception
  263      {
  264         if (controllerContext != null)
  265            pojoChange(ControllerState.START);
  266         else if (serviceName != null && isJBossInternalLifecycleExposed)
  267            server.invoke(ServiceController.OBJECT_NAME, "start", new Object[] { serviceName }, SERVICE_CONTROLLER_SIG);
  268         else
  269            jbossInternalStart();
  270      }
  271      
  272      public void stop()
  273      {
  274         try
  275         {
  276            if (controllerContext != null)
  277               pojoChange(ControllerState.CREATE);
  278            else if (serviceName != null && isJBossInternalLifecycleExposed)
  279               server.invoke(ServiceController.OBJECT_NAME, "stop", new Object[] { serviceName }, SERVICE_CONTROLLER_SIG);
  280            else
  281               jbossInternalStop();
  282         }
  283         catch (Throwable t)
  284         {
  285            log.warn("Error in stop " + jbossInternalDescription(), t);
  286         }
  287      }
  288      
  289      public void destroy()
  290      {
  291         try
  292         {
  293            if (controllerContext != null)
  294               pojoChange(ControllerState.CONFIGURED);
  295            else if (serviceName != null && isJBossInternalLifecycleExposed)
  296               server.invoke(ServiceController.OBJECT_NAME, "destroy", new Object[] { serviceName }, SERVICE_CONTROLLER_SIG);
  297            else
  298               jbossInternalDestroy();
  299         }
  300         catch (Throwable t)
  301         {
  302            log.warn("Error in destroy " + jbossInternalDescription(), t);
  303         }
  304      }
  305      
  306      protected String jbossInternalDescription()
  307      {
  308         if (serviceName != null)
  309            return serviceName.toString();
  310         else
  311            return getName();
  312      }
  313      
  314      public void jbossInternalLifecycle(String method) throws Exception
  315      {
  316         if (method == null)
  317            throw new IllegalArgumentException("Null method name");
  318         
  319         if (method.equals("create"))
  320            jbossInternalCreate();
  321         else if (method.equals("start"))
  322            jbossInternalStart();
  323         else if (method.equals("stop"))
  324            jbossInternalStop();
  325         else if (method.equals("destroy"))
  326            jbossInternalDestroy();
  327         else
  328            throw new IllegalArgumentException("Unknown lifecyle method " + method);
  329      }
  330      
  331      protected void jbossInternalCreate() throws Exception
  332      {
  333         if (state == CREATED || state == STARTING || state == STARTED
  334            || state == STOPPING || state == STOPPED)
  335         {
  336            log.debug("Ignoring create call; current state is " + getStateString());
  337            return;
  338         }
  339         
  340         log.debug("Creating " + jbossInternalDescription());
  341         
  342         try
  343         {
  344            createService();
  345            state = CREATED;
  346         }
  347         catch (Exception e)
  348         {
  349            log.debug("Initialization failed " + jbossInternalDescription(), e);
  350            throw e;
  351         }
  352         
  353         log.debug("Created " + jbossInternalDescription());
  354      }
  355   
  356      protected void jbossInternalStart() throws Exception
  357      {
  358         if (state == STARTING || state == STARTED || state == STOPPING)
  359         {
  360            log.debug("Ignoring start call; current state is " + getStateString());
  361            return;
  362         }
  363         
  364         if (state != CREATED && state != STOPPED && state != FAILED)
  365         {
  366            log.debug("Start requested before create, calling create now");         
  367            create();
  368         }
  369         
  370         state = STARTING;
  371         sendStateChangeNotification(STOPPED, STARTING, getName() + " starting", null);
  372         log.debug("Starting " + jbossInternalDescription());
  373   
  374         try
  375         {
  376            startService();
  377         }
  378         catch (Exception e)
  379         {
  380            state = FAILED;
  381            sendStateChangeNotification(STARTING, FAILED, getName() + " failed", e);
  382            log.debug("Starting failed " + jbossInternalDescription(), e);
  383            throw e;
  384         }
  385   
  386         state = STARTED;
  387         sendStateChangeNotification(STARTING, STARTED, getName() + " started", null);
  388         log.debug("Started " + jbossInternalDescription());
  389      }
  390      
  391      protected void jbossInternalStop()
  392      {
  393         if (state != STARTED)
  394         {
  395            log.debug("Ignoring stop call; current state is " + getStateString());
  396            return;
  397         }
  398         
  399         state = STOPPING;
  400         sendStateChangeNotification(STARTED, STOPPING, getName() + " stopping", null);
  401         log.debug("Stopping " + jbossInternalDescription());
  402   
  403         try
  404         {
  405            stopService();
  406         }
  407         catch (Throwable e)
  408         {
  409            state = FAILED;
  410            sendStateChangeNotification(STOPPING, FAILED, getName() + " failed", e);
  411            log.warn("Stopping failed " + jbossInternalDescription(), e);
  412            return;
  413         }
  414         
  415         state = STOPPED;
  416         sendStateChangeNotification(STOPPING, STOPPED, getName() + " stopped", null);
  417         log.debug("Stopped " + jbossInternalDescription());
  418      }
  419   
  420      protected void jbossInternalDestroy()
  421      {
  422         if (state == DESTROYED)
  423         {
  424            log.debug("Ignoring destroy call; current state is " + getStateString());
  425            return;
  426         }
  427         
  428         if (state == STARTED)
  429         {
  430            log.debug("Destroy requested before stop, calling stop now");
  431            stop();
  432         }
  433         
  434         log.debug("Destroying " + jbossInternalDescription());
  435         
  436         try
  437         {
  438            destroyService();
  439         }
  440         catch (Throwable t)
  441         {
  442            log.warn("Destroying failed " + jbossInternalDescription(), t);
  443         }
  444         state = DESTROYED;
  445         log.debug("Destroyed " + jbossInternalDescription());
  446      }
  447   
  448   
  449      ///////////////////////////////////////////////////////////////////////////
  450      //                                JMX Hooks                              //
  451      ///////////////////////////////////////////////////////////////////////////
  452      
  453      /**
  454       * Callback method of {@link MBeanRegistration}
  455       * before the MBean is registered at the JMX Agent.
  456       * 
  457       * <p>
  458       * <b>Attention</b>: Always call this method when you overwrite it in a subclass
  459       *                   because it saves the Object Name of the MBean.
  460       *
  461       * @param server    Reference to the JMX Agent this MBean is registered on
  462       * @param name      Name specified by the creator of the MBean. Note that you can
  463       *                  overwrite it when the given ObjectName is null otherwise the
  464       *                  change is discarded (maybe a bug in JMX-RI).
  465       * @return the ObjectName
  466       * @throws Exception for any error
  467       */
  468      public ObjectName preRegister(MBeanServer server, ObjectName name)
  469         throws Exception
  470      {
  471         this.server = server;
  472   
  473         serviceName = getObjectName(server, name);
  474         
  475         return serviceName;
  476      }
  477      
  478      public void postRegister(Boolean registrationDone)
  479      {
  480         if (!registrationDone.booleanValue())
  481         {
  482            log.info( "Registration is not done -> stop" );
  483            stop();
  484         }
  485         else
  486         {
  487            state = REGISTERED;
  488            // This is for backwards compatibility - see whether jbossInternalLifecycle is exposed
  489            try
  490            {
  491               MBeanInfo info = server.getMBeanInfo(serviceName);
  492               MBeanOperationInfo[] ops = info.getOperations();
  493               for (int i = 0; i < ops.length; ++i)
  494               {
  495                  if (ops[i] != null && ServiceController.JBOSS_INTERNAL_LIFECYCLE.equals(ops[i].getName()))
  496                  {
  497                     isJBossInternalLifecycleExposed = true;
  498                     break;
  499                  }
  500               }
  501            }
  502            catch (Throwable t)
  503            {
  504               log.warn("Unexcepted error accessing MBeanInfo for " + serviceName, t);
  505            }
  506         }
  507      }
  508   
  509      public void preDeregister() throws Exception
  510      {
  511      }
  512      
  513      public void postDeregister()
  514      {
  515         server = null;
  516         serviceName = null;
  517         state = UNREGISTERED;
  518      }
  519   
  520      /**
  521       * The <code>getNextNotificationSequenceNumber</code> method returns 
  522       * the next sequence number for use in notifications.
  523       *
  524       * @return a <code>long</code> value
  525       */
  526      protected long getNextNotificationSequenceNumber()
  527      {
  528         return nextNotificationSequenceNumber();
  529      }
  530   
  531   
  532      ///////////////////////////////////////////////////////////////////////////
  533      //                       Concrete Service Overrides                      //
  534      ///////////////////////////////////////////////////////////////////////////
  535   
  536      /**
  537       * Sub-classes should override this method if they only need to set their
  538       * object name during MBean pre-registration.
  539       * 
  540       * @param server the mbeanserver
  541       * @param name the suggested name, maybe null
  542       * @return the object name
  543       * @throws MalformedObjectNameException for a bad object name
  544       */
  545      protected ObjectName getObjectName(MBeanServer server, ObjectName name)
  546         throws MalformedObjectNameException
  547      {
  548         return name;
  549      }
  550   
  551      /**
  552       * Sub-classes should override this method to provide
  553       * custum 'create' logic.
  554       *
  555       * <p>This method is empty, and is provided for convenience
  556       *    when concrete service classes do not need to perform
  557       *    anything specific for this state change.
  558       * 
  559       * @throws Exception for any error
  560       */
  561      protected void createService() throws Exception {}
  562      
  563      /**
  564       * Sub-classes should override this method to provide
  565       * custum 'start' logic.
  566       * 
  567       * <p>This method is empty, and is provided for convenience
  568       *    when concrete service classes do not need to perform
  569       *    anything specific for this state change.
  570       * 
  571       * @throws Exception for any error
  572       */
  573      protected void startService() throws Exception {}
  574      
  575      /**
  576       * Sub-classes should override this method to provide
  577       * custum 'stop' logic.
  578       * 
  579       * <p>This method is empty, and is provided for convenience
  580       *    when concrete service classes do not need to perform
  581       *    anything specific for this state change.
  582       * 
  583       * @throws Exception for any error
  584       */
  585      protected void stopService() throws Exception {}
  586      
  587      /**
  588       * Sub-classes should override this method to provide
  589       * custum 'destroy' logic.
  590       * 
  591       * <p>This method is empty, and is provided for convenience
  592       *    when concrete service classes do not need to perform
  593       *    anything specific for this state change.
  594       * 
  595       * @throws Exception for any error
  596       */
  597      protected void destroyService() throws Exception {}
  598      
  599      // Private -------------------------------------------------------
  600      
  601      /**
  602       * Helper for sending out state change notifications
  603       */
  604      private void sendStateChangeNotification(int oldState, int newState, String msg, Throwable t)
  605      {
  606         long now = System.currentTimeMillis();
  607         
  608         AttributeChangeNotification stateChangeNotification = new AttributeChangeNotification(
  609            this,
  610            getNextNotificationSequenceNumber(), now, msg,
  611            "State", "java.lang.Integer",
  612            new Integer(oldState), new Integer(newState)
  613            );
  614         stateChangeNotification.setUserData(t);
  615         
  616         sendNotification(stateChangeNotification);      
  617      }
  618   }

Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » system » [javadoc | source]