Package org.jboss.bootstrap.impl.base.server

Source Code of org.jboss.bootstrap.impl.base.server.AbstractServer$StartServerTask

/*
* JBoss, Home of Professional Open Source.
* Copyright 2009, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.jboss.bootstrap.impl.base.server;

import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.management.Notification;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationBroadcasterSupport;

import org.jboss.bootstrap.api.config.InvalidConfigurationException;
import org.jboss.bootstrap.api.config.ServerConfig;
import org.jboss.bootstrap.api.lifecycle.LifecycleEventException;
import org.jboss.bootstrap.api.lifecycle.LifecycleEventHandler;
import org.jboss.bootstrap.api.lifecycle.LifecycleState;
import org.jboss.bootstrap.api.server.Server;
import org.jboss.bootstrap.spi.Bootstrap;
import org.jboss.bootstrap.spi.config.ConfigurationInitializer;
import org.jboss.bootstrap.spi.config.ConfigurationValidator;
import org.jboss.bootstrap.spi.server.ServerInitializer;
import org.jboss.bootstrap.spi.server.ServerProvider;
import org.jboss.logging.Logger;
import org.jboss.util.StopWatch;

/**
* AbstractServer
*
* Generic support for Server implementations
*
* @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a>
* @version $Revision: $
*/
public abstract class AbstractServer<K extends Server<K, T>, T extends ServerConfig<T>>
      extends
         NotificationBroadcasterSupport implements ServerProvider<K, T>, NotificationBroadcaster
{

   //-------------------------------------------------------------------------------------||
   // Class Members ----------------------------------------------------------------------||
   //-------------------------------------------------------------------------------------||

   private static final Logger log = Logger.getLogger(AbstractServer.class);

   //-------------------------------------------------------------------------------------||
   // Instance Members -------------------------------------------------------------------||
   //-------------------------------------------------------------------------------------||

   /**
    * Actual class used in casting to type K
    */
   private Class<K> actualClass;

   /**
    * Current state of the server.  Synchronized on "this".
    * Volatile so we can return the current state without blocking.
    */
   private volatile LifecycleState state;

   /**
    * Underlying configuration.  Must be a Thread-safe implementation
    * as it's exported
    */
   private volatile T configuration;

   /**
    * Validator for the configuration.  Synchronized on "this".  Requires
    * Thread-safe impl (as it's exported)
    */
   private ConfigurationValidator<T> validator;

   /**
    * Initializer for the configuration.  Synchronized on "this".  Requires
    * Thread-safe impl (as it's exported).  Volatile for immediate
    * accessor return.
    */
   private volatile ConfigurationInitializer<T> configInitializer;

   /**
    * Server initializer.  Synchronized on "this".  Requires
    * Thread-safe impl (as it's exported).  Volatile for immediate
    * accessor return.
    */
   private volatile ServerInitializer<K, T> serverInitializer;

   /**
    * The list of bootstraps to run upon start, requires Thread-safe impl.
    */
   private final List<Bootstrap<K, T>> bootstraps = new CopyOnWriteArrayList<Bootstrap<K, T>>();

   /**
    * The bootstraps that have been started, requires Thread-safe impl.
    */
   private final List<Bootstrap<K, T>> startedBootstraps = new CopyOnWriteArrayList<Bootstrap<K, T>>();

   /**
    * Event handlers for each lifecycle state change
    */
   private final Map<LifecycleState, Set<LifecycleEventHandler>> eventHandlers = new ConcurrentHashMap<LifecycleState, Set<LifecycleEventHandler>>();

   /**
    * Thread used for startup.  Part of the server's internal state so we can interrupt
    * this if necessary.  Volatile so we don't need to block in order to
    * get the correct view.
    */
   private volatile Thread startupThread = null;

   //-------------------------------------------------------------------------------------||
   // Constructors -----------------------------------------------------------------------||
   //-------------------------------------------------------------------------------------||

   /**
    * Constructor
    *
    * Creates a new Server using an uninitialized, default configuration
    * (which is an instance of the Class specified by
    * {@link AbstractServer#getDefaultServerConfigClass()}
    * @throws IllegalArgumentException If the actual class was not supplied
    */
   protected AbstractServer(final Class<K> actualClass) throws IllegalArgumentException
   {
      // Pass it to the other ctor
      this(actualClass, null);
   }

   /**
    * Constructor
    *
    * Creates a new Server, specifying the configuration to be used.
    *
    * @param configuration The configuration to set.  If null a new default
    *    configuration will be made (which is an instance of the Class specified by
    *    {@link AbstractServer#getDefaultServerConfigClass()}
    * @throws IllegalArgumentException If the configuration or actual class has not been supplied
    */
   protected AbstractServer(final Class<K> actualClass, final T configuration) throws IllegalArgumentException
   {
      // Precondition check
      if (actualClass == null)
      {
         throw new IllegalArgumentException("Actual class must be specified");
      }

      // Initialize
      T configToSet = configuration;

      // Check that a configuration has been supplied
      if (configToSet == null)
      {

         /*
          *  Make a new Configuration
          */
         log.debug("No configuration has been supplied, so making a default one");
         T newConfiguration = null;

         // Get the default config class
         final Class<? extends T> configClass = this.getDefaultServerConfigClass();
         try
         {
            // Create
            newConfiguration = SecurityActions.newInstance(configClass);
            log.debug("Created new default configuration: " + newConfiguration);
         }
         catch (final Throwable t)
         {
            throw new RuntimeException("Could not create default configuration of type " + configClass, t);
         }

         // Set
         configToSet = newConfiguration;
      }

      // Set properties
      this.setActualClass(actualClass);
      this.setConfiguration(configToSet);
      // Set the state directly (ie. bypass callback contract of setState()),
      // this isn't *really* a state change so much as an initialization
      this.state = LifecycleState.INSTANCIATED;
   }

   //-------------------------------------------------------------------------------------||
   // Required Implementations -----------------------------------------------------------||
   //-------------------------------------------------------------------------------------||

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#getConfiguration()
    */
   public T getConfiguration()
   {
      return this.configuration;
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#setConfiguration(org.jboss.bootstrap.spi.config.ServerConfig)
    */
   // Synchronized on "this" to ensure we don't set the config in the middle of some
   // other lifecycle operation
   public synchronized void setConfiguration(final T configuration)
   {
      // Log and set
      log.tracef("Set configuration: %s", configuration);
      this.configuration = configuration;
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#getState()
    */
   public final LifecycleState getState()
   {
      final LifecycleState state = this.state;
      if (state == null)
      {
         throw new IllegalStateException("null state");
      }
      return state;
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#stop()
    */
   public void stop() throws IllegalStateException, Exception
   {
      // Alias to shutdown()
      this.shutdown();
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#shutdown()
    */
   public void shutdown() throws IllegalStateException, Exception
   {
      // Log
      log.trace("Received request to shutdown");

      // If we're in startup
      final Thread startupThread = this.startupThread;
      if (startupThread != null)
      {
         // Interrupt startup
         startupThread.interrupt();

         // Wait until it's done
         try
         {
            startupThread.join();
         }
         catch (final InterruptedException ie)
         {
            // Warn and clear the flag
            log.warn("Interrupted while shutdown is waiting for server startup to complete");
            Thread.interrupted();
         }
      }

      // Synchronized for atomicity
      synchronized (this)
      {
         // Ensure running
         final LifecycleState required = LifecycleState.STARTED;
         final LifecycleState actual = this.getState();
         this.checkState(required, actual);

         // Initiate shutdown sequence
         log.info("Stopping: " + this);
         final StopWatch watch = new StopWatch(true);
         this.setState(LifecycleState.STOPPING);

         // Send JMX Notification
         this.sendStopJmxNotification();

         // Shutdown the Bootstraps
         log.trace("Shutting down bootstraps");
         this.shutdownBootstraps();

         // Shutdown
         log.trace("Calling implementation class shutdown...");
         this.doShutdown();

         // Let the initializer clean up
         final ServerInitializer<K, T> serverInitializer = this.getServerInitializer();
         if (serverInitializer != null)
         {
            log.tracef("Calling to clean up for shutdown: %s", serverInitializer);
            serverInitializer.cleanup(this.covarientReturn());
         }

         // Done and set states
         log.infof("Stopped: %s in %s", this, watch);
         // So we fire "stopped" events and draw the difference
         // between IDLE (which may also designate pre-start)
         this.setState(LifecycleState.STOPPED);
         this.setState(LifecycleState.IDLE);
      }
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#start()
    */
   public void start() throws IllegalStateException, Exception
   {
      // Log
      log.trace("Received request to start");

      // Invoke init() if necessary
      if (getState().equals(LifecycleState.INSTANCIATED))
      {
         log.debug("Invoking implicit initialization from start()");
         AbstractServer.this.initialize();
      }

      // Make a new Thread to run startup, so we may interrupt it
      // if necessary for shutdown
      final StartServerTask task = new StartServerTask();
      final Thread thread = new Thread(task);
      this.startupThread = thread;
      thread.start();

      // Wait on startup to complete
      try
      {
         thread.join();
      }
      catch (final InterruptedException ie)
      {
         // Clear the flag
         Thread.interrupted();
      }

      // Check for Exceptions encountered during run()
      final Exception exceptionOnStart = task.getException();
      if (exceptionOnStart != null)
      {
         // Throw up the stack
         throw exceptionOnStart;
      }
   }

   /**
    * StartServerTask
    *
    * Task to start the server in a new Thread of execution.  This will
    * allow the calling Thread to interrupt the startup process
    * in the case the server is requested to shutdown during start.
    *
    * JBBOOT-75
    *
    * @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a>
    * @version $Revision: $
    */
   class StartServerTask implements Runnable
   {
      /**
       * Any exception encountered while
       * the task was run
       */
      private Exception exception;

      /*
       * (non-Javadoc)
       * @see java.lang.Runnable#run()
       */
      public void run()
      {
         // Get a reference to this of the outer class
         final AbstractServer<?, ?> thisRef = AbstractServer.this;

         /*
          * Synchronized for atomicity
          */
         synchronized (thisRef)
         {
            try
            {
               // Ensure idle
               final LifecycleState required = LifecycleState.IDLE;
               final LifecycleState actual = getState();
               checkState(required, actual);

               // Initiate start sequence
               log.infof("Starting: %s", thisRef);
               final StopWatch watch = new StopWatch(true);
               setState(LifecycleState.STARTING);

               // Start
               log.trace("Entering implementation class start...");
               doStart();

               // Run the bootstraps
               log.trace("Starting bootstraps...");
               startBootstraps();

               // Send JMX Start Notification
               sendStartJmxNotification();

               // Done
               log.infof("%s Started in %s", thisRef, watch);
               setState(LifecycleState.STARTED);
            }
            catch (final Exception e)
            {
               log.debugf("Encountered exception while starting, caching it to be thrown later: %s", e);
               this.exception = e;
               return;
            }
            finally
            {
               // Clear the startup Thread
               AbstractServer.this.startupThread = null;
            }
         }
      }

      /**
       * Obtains the exception encountered during start, or null
       * if none was thrown
       *
       * @return
       */
      Exception getException()
      {
         return this.exception;
      }

   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#addBootstrap(org.jboss.bootstrap.spi.Bootstrap)
    */
   public void addBootstrap(final Bootstrap<K, T> bootstrap) throws IllegalStateException, IllegalArgumentException
   {
      // Precondition check
      if (bootstrap == null)
      {
         throw new IllegalArgumentException("Supplied bootstrap was null");
      }

      // Add
      this.getBootstraps().add(bootstrap);
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#removeBootstrap(org.jboss.bootstrap.spi.Bootstrap)
    */
   public void removeBootstrap(final Bootstrap<K, T> bootstrap) throws IllegalStateException, IllegalArgumentException
   {
      // Remove
      boolean removed = bootstraps.remove(bootstrap);

      // Postcondition check
      if (!removed)
      {
         throw new IllegalArgumentException(
               "Specified bootstrap could not be removed because it is not present in the list");
      }
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#getValidator()
    */
   public ConfigurationValidator<T> getValidator()
   {
      return this.validator;
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#initialize()
    */
   // Synchronized for atomicity
   public final synchronized void initialize() throws IllegalStateException, InvalidConfigurationException,
         LifecycleEventException
   {
      /*
       * Precondition checks
       */

      // Config must be in place
      final T config = this.getConfiguration();
      if (config == null)
      {
         throw new IllegalStateException("Configuration must be supplied before server is initialized");
      }

      // State must be instanciated
      final LifecycleState state = this.getState();
      if (!state.equals(LifecycleState.INSTANCIATED))
      {
         throw new IllegalStateException("Cannot initialize an already initialized server, state is: " + state);
      }

      // Set state to pre-init to fire lifecycle callbacks
      this.setState(LifecycleState.PRE_INIT);

      // Log
      log.debugf("Initializing server: %s", this);

      // Do the actual initialization logic
      this.doInitialize();

      // Freeze config
      config.freeze();

      // Set state (firing lifecycle callbacks along the way)
      this.setState(LifecycleState.INITIALIZED);
      this.setState(LifecycleState.IDLE);
   }

   /**
    * Implementation-specific initialization logic; invoked by
    * {@link AbstractServer#initialize()}
    *
    * @throws IllegalStateException
    * @throws InvalidConfigurationException
    * @throws LifecycleEventException
    */
   protected void doInitialize() throws IllegalStateException, InvalidConfigurationException, LifecycleEventException
   {

      // Config must be in place
      final T config = this.getConfiguration();
      if (config == null)
      {
         throw new IllegalStateException("Configuration must be supplied before server is initialized");
      }

      // If there's a configuration initializer, use it
      final ConfigurationInitializer<T> configInitializer = this.getConfigInitializer();
      if (configInitializer != null)
      {
         log.trace("Performing configuration initialization...");
         configInitializer.initialize(config);
      }
      else
      {
         log.trace("No configuration initializer supplied, skipping");
      }

      // Validate
      log.trace("Validating config...");
      this.validate(config);

      /*
       * If there's an initializer, use it
       */
      final ServerInitializer<K, T> serverInitializer = this.getServerInitializer();
      if (serverInitializer != null)
      {
         serverInitializer.initialize(this.covarientReturn());
      }
      else
      {
         log.debugf("No initializer defined, skipping initialization of %s", this);
      }
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#getInitializer()
    */
   public final ServerInitializer<K, T> getServerInitializer()
   {
      return this.serverInitializer;
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#setInitializer(org.jboss.bootstrap.spi.server.ServerInitializer)
    */
   public synchronized final void setServerInitializer(final ServerInitializer<K, T> serverInitializer)
         throws IllegalStateException
   {
      // Precondition check
      this.checkMutable(this.getState());

      this.serverInitializer = serverInitializer;
      log.debugf("Set server initializer to %s", serverInitializer);
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#getConfigInitializer()
    */
   public final ConfigurationInitializer<T> getConfigInitializer()
   {
      return this.configInitializer;
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#setConfigInitializer(org.jboss.bootstrap.spi.config.ConfigurationInitializer)
    */
   public synchronized final void setConfigInitializer(ConfigurationInitializer<T> configInitializer)
   {
      // Precondition check
      this.checkMutable(this.getState());

      this.configInitializer = configInitializer;
      log.debugf("Set config initializer to %s", configInitializer);
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#setConfigurationValidator(org.jboss.bootstrap.spi.config.ConfigurationValidator)
    */
   public synchronized final void setValidator(final ConfigurationValidator<T> validator)
   {
      // Precondition check
      this.checkMutable(this.getState());

      log.debugf("Setting validator to: %s", validator);
      this.validator = validator;
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#registerEventHandlers(org.jboss.bootstrap.spi.lifecycle.LifecycleState, org.jboss.bootstrap.spi.lifecycle.LifecycleEventHandler)
    */
   public void registerEventHandler(final LifecycleState state, final LifecycleEventHandler handler)
         throws IllegalArgumentException
   {
      // Delegate
      this.registerEventHandlers(state, handler);
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#registerEventHandler(org.jboss.bootstrap.spi.lifecycle.LifecycleEventHandler, java.util.EnumSet)
    */
   public void registerEventHandler(final LifecycleEventHandler handler, final EnumSet<LifecycleState> states)
         throws IllegalArgumentException
   {
      // Precondition checks
      if (handler == null)
      {
         throw new IllegalArgumentException("handler is required");
      }
      if (states == null)
      {
         throw new IllegalArgumentException("states is required");
      }

      // Delegate
      for (final LifecycleState state : states)
      {
         this.registerEventHandler(state, handler);
      }
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#registerEventHandler(org.jboss.bootstrap.spi.lifecycle.LifecycleEventHandler, org.jboss.bootstrap.spi.lifecycle.LifecycleState[])
    */
   public void registerEventHandler(final LifecycleEventHandler handler, final LifecycleState... states)
         throws IllegalArgumentException
   {
      // Precondition checks
      if (handler == null)
      {
         throw new IllegalArgumentException("handler is required");
      }
      if (states == null)
      {
         throw new IllegalArgumentException("states is required");
      }

      // Delegate
      for (final LifecycleState state : states)
      {
         this.registerEventHandler(state, handler);
      }
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#registerEventHandler(org.jboss.bootstrap.spi.lifecycle.LifecycleState, org.jboss.bootstrap.spi.lifecycle.LifecycleEventHandler)
    */
   public void registerEventHandlers(final LifecycleState state, final LifecycleEventHandler... handlers)
         throws IllegalArgumentException
   {
      // Precondition checks
      if (handlers == null)
      {
         throw new IllegalArgumentException("At least one handler is required");
      }
      if (state == null)
      {
         throw new IllegalArgumentException("state is required");
      }

      // Get existing handlers for this state change
      final Set<LifecycleEventHandler> handlersForEvent = this.getHandlersForEvent(state);

      // Add the state change
      for (final LifecycleEventHandler handler : handlers)
      {
         handlersForEvent.add(handler);
         log.debugf("Added lifecycle handler %s to fire upon state change to %s for %s", handler, state, this);
      }
   }

   /* (non-Javadoc)
    * @see org.jboss.bootstrap.spi.server.Server#unregisterEventHandler(org.jboss.bootstrap.spi.lifecycle.LifecycleEventHandler,org.jboss.bootstrap.spi.lifecycle.LifecycleState)
    */
   public boolean unregisterEventHandler(final LifecycleEventHandler handler, final LifecycleState state)
         throws IllegalArgumentException
   {
      // Get all handlers for this state change
      final Set<LifecycleEventHandler> handlers = this.getHandlersForEvent(state);

      // Remove and return
      final boolean removed = handlers.remove(handler);
      log.debugf("Removed lifecycle handler %s from firing upon state change to %s for %s", handler, state, this);
      return removed;
   }

   //-------------------------------------------------------------------------------------||
   // Contracts --------------------------------------------------------------------------||
   //-------------------------------------------------------------------------------------||

   /**
    * Implementation-specific start hook
    *
    * @throws Exception
    */
   protected abstract void doStart() throws Exception;

   /**
    * Implementation-specific shutdown hook
    *
    * @throws Exception
    */
   protected abstract void doShutdown() throws Exception;

   /**
    * Obtains the Class used in constructing a new default
    * Server Configuration if one is not supplied to the constructor
    *
    * @return
    */
   protected abstract Class<? extends T> getDefaultServerConfigClass();

   //-------------------------------------------------------------------------------------||
   // Functional Methods -----------------------------------------------------------------||
   //-------------------------------------------------------------------------------------||

   /**
    * Sends the JMX Notification with type signaling the start of the server
    */
   protected void sendStartJmxNotification()
   {
      this.sendNotificationWithCurrentTimeUserData(START_NOTIFICATION_TYPE, 1);
   }

   /**
    * Sends the JMX Notification with type signaling the stop of the server
    */
   protected void sendStopJmxNotification()
   {
      this.sendNotificationWithCurrentTimeUserData(STOP_NOTIFICATION_TYPE, 2);
   }

   /**
    * Sends a JMX Notification with specified type and sequence number,
    * setting user data to the number of milliseconds since the epoch.
    *
    * @param type
    * @param sequenceNumber
    */
   protected final void sendNotificationWithCurrentTimeUserData(final String type, final int sequenceNumber)
   {
      // Send JMX Notification
      final Notification startNotification = new Notification(type, this, sequenceNumber);
      startNotification.setUserData(System.currentTimeMillis());
      this.sendNotification(startNotification);
      log.debugf("Sent JMX Notification: %s", type);
   }

   /**
    * Starts the bootstraps
    *
    * @throws Exception
    */
   protected void startBootstraps() throws Exception
   {
      // Get the proper type for this server
      final K typedServer = this.covarientReturn();

      // Run the bootstraps
      for (Bootstrap<K, T> bootstrap : this.getBootstraps())
      {
         // Add in reverse order
         this.getStartedBootstraps().add(0, bootstrap);

         // Remember TCCL
         @Deprecated
         final ClassLoader oldCl = SecurityActions.getThreadContextClassLoader();

         // Get new TCCL
         @Deprecated
         final ClassLoader bootstrapCl = bootstrap.getClass().getClassLoader();

         try
         {
            // Set the bootstrap CL
            // FIXME This is a carryover hack from the legacy implementation,
            // still required for proper visibility 
            SecurityActions.setThreadContextClassLoader(bootstrapCl);

            // Start
            bootstrap.start(typedServer);
         }
         finally
         {
            // Reset the TCCL
            SecurityActions.setThreadContextClassLoader(oldCl);
         }
      }
   }

   /**
    * Shuts down the bootstraps that have been started
    */
   protected void shutdownBootstraps()
   {
      // Initialize
      final List<Bootstrap<K, T>> startedBootstraps = this.getStartedBootstraps();

      // Get the proper type for this server
      final K typedServer = this.covarientReturn();

      // Signal
      for (final Bootstrap<K, T> bootstrap : startedBootstraps)
      {
         bootstrap.prepareShutdown(typedServer);
      }

      // Do the bootstraps in reverse order (they were added in reverse order)
      for (final Bootstrap<K, T> bootstrap : startedBootstraps)
      {
         try
         {
            bootstrap.shutdown(typedServer);
            // Remove bootstrap from started list
            if (!startedBootstraps.remove(bootstrap))
            {
               log.warnf("Failed to remove bootstrap after shutdown: %s", bootstrap);
            }
         }
         catch (final Throwable t)
         {
            log.warn("Error shutting down bootstrap: " + bootstrap, t);
         }
      }
   }

   //-------------------------------------------------------------------------------------||
   // Internal Helper Methods ------------------------------------------------------------||
   //-------------------------------------------------------------------------------------||

   /**
    * Returns this instance as a typed server (as specified by K),
    * throwing a descriptive error message in case the server is not assignable
    */
   protected final K covarientReturn()
   {
      // Get the actual class
      final Class<K> actualClass = this.getActualClass();

      // Cast
      try
      {
         return actualClass.cast(this);
      }
      catch (ClassCastException cce)
      {
         throw new RuntimeException("Actual class is incorrect and " + actualClass
               + " was not assignable to this instance: " + this, cce);
      }
   }

   /**
    * Obtains a list of handlers for a lifecycle event change into the specified state.
    * No nulls will be returned, but rather an empty List in the case no events are
    * registered.  All requests to the event handlers should be proxied through here
    * to prevent encountering a null backing List.
    *
    * @param state The target state change for the event
    * @return
    * @throws IllegalArgumentException If the state was not specified
    */
   private Set<LifecycleEventHandler> getHandlersForEvent(final LifecycleState state) throws IllegalArgumentException
   {
      // Precondition check
      if (state == null)
      {
         throw new IllegalArgumentException("state is required");
      }

      // Initialize
      Set<LifecycleEventHandler> handlers = this.eventHandlers.get(state);

      // Adjust to empty List if null
      if (handlers == null)
      {
         handlers = new CopyOnWriteArraySet<LifecycleEventHandler>();
         // Put this new list into the backing Map to prevent null access
         this.eventHandlers.put(state, handlers);
         log.tracef("Placed empty backing map for lifecycle event handers upon state change into %s for: %s", state,
               this);
      }

      // Return
      return handlers;
   }

   /**
    * Ensures the actual state matches the required, throwing {@link IllegalStateException}
    * if not
    *
    * @param required
    * @param actual
    * @throws IllegalStateException If the actual state does not match required
    */
   private void checkState(final LifecycleState required, final LifecycleState actual) throws IllegalStateException
   {
      // Check State
      if (required != actual)
      {
         throw new IllegalStateException("Server must be in " + LifecycleState.class.getSimpleName() + " " + required
               + "; is instead: " + actual);
      }
   }

   /**
    * Ensures the actual state matches at least one of the required, throwing {@link IllegalStateException}
    * if not
    *
    * @param required
    * @param actual
    * @throws IllegalStateException If the actual state does not match one of those required
    */
   private void checkState(final LifecycleState[] required, final LifecycleState actual) throws IllegalStateException
   {
      for (final LifecycleState current : required)
      {
         if (current.equals(actual))
         {
            // Match, so return OK
            return;
         }
      }
      // No match found
      throw new IllegalStateException("Server state must be in one of " + Arrays.asList(required) + "; is instead: "
            + actual);
   }

   /**
    * Ensures the server state is mutable, defined by being either in {@link LifecycleState#INSTANCIATED}
    * or {@link LifecycleState#PRE_INIT}
    *
    * @throws IllegalStateException If the specified state does not allow for mutable operations
    */
   private void checkMutable(final LifecycleState state)
   {
      this.checkState(new LifecycleState[]
      {LifecycleState.INSTANCIATED, LifecycleState.PRE_INIT}, state);
   }

   /**
    * If {@link Server#getValidator()} is non-null, will
    * assert the configuration is valid using the supplied
    * validator
    *
    * @param configuration
    * @throws InvalidConfigurationException If the configuration is invalid
    * @throws IllegalArgumentException If the confirguation has not been set
    */
   private void validate(final T configuration) throws InvalidConfigurationException, IllegalArgumentException
   {
      // Precondition check
      if (configuration == null)
      {
         throw new IllegalArgumentException("Configuration was not specified");
      }

      // Get the validator
      final ConfigurationValidator<T> validator = this.getValidator();

      // Is specified, validate
      if (validator != null)
      {
         log.debugf("Validating configuration using: %s", validator);
         validator.validate(this.getConfiguration());
      }
      else
      {
         log.trace("No validator defined, skipping validation upon configuration");
      }
   }

   //-------------------------------------------------------------------------------------||
   // Accessors / Mutators ---------------------------------------------------------------||
   //-------------------------------------------------------------------------------------||

   /**
    * @return the bootstraps
    */
   private List<Bootstrap<K, T>> getBootstraps()
   {
      return bootstraps;
   }

   /**
    * @return the startedBootstraps
    */
   List<Bootstrap<K, T>> getStartedBootstraps()
   {
      return startedBootstraps;
   }

   /**
    * @return the actualClass
    */
   protected final Class<K> getActualClass()
   {
      return actualClass;
   }

   /**
    * @param actualClass the actualClass to set
    */
   private void setActualClass(final Class<K> actualClass)
   {
      this.actualClass = actualClass;
   }

   /**
    * Sets the state of the server, firing the appropriate lifecycle
    * events to the registered handlers
    *
    * @param state the state to set
    * @throws Exception If some error was encountered while firing the handlers
    * @throws IllegalArgumentException If no state was specified
    */
   private synchronized final void setState(final LifecycleState state) throws LifecycleEventException,
         IllegalArgumentException
   {
      // Precondition check
      if (state == null)
      {
         throw new IllegalArgumentException("State was not specified");
      }

      // Log and set
      log.tracef("Setting %s to: %s", LifecycleState.class.getSimpleName(), state);
     
      this.state = state;

      // Fire handlers registered for event changes to this state
      final Set<LifecycleEventHandler> handlers = this.getHandlersForEvent(state);
      for (final LifecycleEventHandler handler : handlers)
      {
         try
         {
            // Log
            log.tracef("Firing event handler for state change to %s: %s", state, handler);

            // Fire
            handler.handleEvent(state);
         }
         catch (final LifecycleEventException t)
         {
            // Log and rethrow
            log.error("Encountered problem in firing event handler " + handler + " for state change to " + state
                  + " in " + this, t);
            throw t;
         }
      }
   }

}
TOP

Related Classes of org.jboss.bootstrap.impl.base.server.AbstractServer$StartServerTask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.