Package org.jboss.bootstrap

Source Code of org.jboss.bootstrap.AbstractServerImpl

/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, 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;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.LogManager;

import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;

import org.jboss.Version;
import org.jboss.bootstrap.spi.Bootstrap;
import org.jboss.bootstrap.spi.ServerConfig;
import org.jboss.bootstrap.spi.ServerProcess;
import org.jboss.logging.Logger;
import org.jboss.managed.api.annotation.ManagementProperty;
import org.jboss.net.protocol.URLStreamHandlerFactory;
import org.jboss.util.StopWatch;

/**
* A Server implementation that uses the deployer-beans.xml and ProfileService
* to boot the server.
*
* @author Scott.Stark@jboss.org
* @author Dimitris.Andreadis@jboss.org
* @author adrian@jboss.org
* @version $Revision: 85188 $
*/
public abstract class AbstractServerImpl extends NotificationBroadcasterSupport
   implements ServerProcess, NotificationEmitter
{
   /** Instance logger. */
   protected Logger log;

   /** Container for version information. */
   private final Version version = Version.getInstance();

   /** Package information for org.jboss */
   private final Package jbossPackage = Package.getPackage("org.jboss");

   /** The basic configuration for the server. */
   private BaseServerConfig config;

   /** The optional configuration metadata for the server */
   private Map<String, Object> metadata;

   /** When the server was started. */
   private Date startDate;

   /** Flag to indicate if we are started. */
   private boolean started;
  
   /** A flag indicating if start has been called */
   private boolean isInStart;
  
   /** A flag indicating if shutdown has been called */
   private boolean isInShutdown;

   /** A flag indicating if shutdownServer has been called */
   private boolean isInternalShutdown;
  
   /** The JVM shutdown hook */
   private ShutdownHook shutdownHook;

   /** The JBoss Life Thread */
   private LifeThread lifeThread;
  
   /** The bootstraps */
   private List<Bootstrap> bootstraps = new CopyOnWriteArrayList<Bootstrap>();

   /** The started bootstraps */
   private List<Bootstrap> startedBootstraps = new CopyOnWriteArrayList<Bootstrap>();
  
   /**
    * No-arg constructor for ServerImpl
    */
   public AbstractServerImpl()
   {
   }

   /**
    * Add a bootstrap
    *
    * @param bootstrap the bootstrap
    * @throws IllegalArgumentException for a null bootstrap
    */
   public void addBootstrap(Bootstrap bootstrap)
   {
      if (bootstrap == null)
         throw new IllegalArgumentException("Null bootstrap");
     
      bootstraps.add(bootstrap);
   }

   /**
    * Remove a bootstrap
    *
    * @param bootstrap the bootstrap
    * @throws IllegalArgumentException for a null bootstrap
    */
   public void removeBootstrap(Bootstrap bootstrap)
   {
      if (bootstrap == null)
         throw new IllegalArgumentException("Null bootstrap");
     
      bootstraps.remove(bootstrap);
   }
  
   /**
    * Initialize the server by calling init(props, null);
    */
   public void init(final Properties props)
      throws IllegalStateException, Exception
   {
      init(props, null);
   }
   /**
    * Initialize the Server instance.
    *
    * @param props - The configuration properties for the server.
    * @param metadata configuration metadata for the server
    *
    * @throws IllegalStateException    Already initialized.
    * @throws Exception                Failed to initialize.
    */
   public void init(final Properties props, final Map<String, Object> metadata)
      throws IllegalStateException, Exception
   {
      if (props == null)
         throw new IllegalArgumentException("props is null");
     
      if (config != null)
         throw new IllegalStateException("already initialized");

      if (metadata == null)
         this.metadata = Collections.emptyMap();
      else
         this.metadata = Collections.unmodifiableMap(metadata);

      ClassLoader oldCL = Thread.currentThread().getContextClassLoader();

      try
      {
         Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
         doInit(props);
      }
      finally
      {
         Thread.currentThread().setContextClassLoader(oldCL);
      }
   }

   /**
    * Initialize server configuration and jvm settings.
    *
    * @param props
    * @throws Exception
    */
   private void doInit(final Properties props) throws Exception
   {
      // Create a new config object from the give properties
      this.config = new BaseServerConfig(props);

      // Set the VM temp directory to the server tmp dir
      boolean overrideTmpDir = Boolean.getBoolean("jboss.server.temp.dir.overrideJavaTmpDir");
      if (overrideTmpDir)
      {
         File serverTmpDir = config.getServerTempDir();
         System.setProperty("java.io.tmpdir", serverTmpDir.toString());
      }

      // Initialize the logging layer using the ServerImpl class. The server log
      // directory is initialized prior to this to ensure the jboss.server.log.dir
      //system property is set in case its used by the logging configs.
      config.getServerLogDir();
      log = Logger.getLogger(getClass());

      // Setup URL handlers - do this before initializing the ServerConfig
      initURLHandlers();
      config.initURLs();

      log.info("Starting JBoss (Microcontainer)...");

      if (jbossPackage != null)
      {
         // Show what release this is...
         log.info("Release ID: " +
                  jbossPackage.getImplementationTitle() + " " +
                  jbossPackage.getImplementationVersion());
      }
      else
      {
         log.warn("could not get package info to display release, either the " +
            "jar manifest in jboss-system.jar has been mangled or you're " +
            "running unit tests from ant outside of JBoss itself.");
      }

      log.debug("Using config: " + config);

      // make sure our impl type is exposed
      log.debug("Server type: " + getClass());
     
      // log the boot classloader
      ClassLoader cl = getClass().getClassLoader();
      log.debug("Server loaded through: " + cl.getClass().getName());
     
      // Log the basic configuration elements
      log.info("Bootstrap URL: " + config.getBootstrapURL());
      log.info("Home Dir: " + config.getHomeDir());
      log.info("Home URL: " + config.getHomeURL());
      log.info("Library URL: " + config.getLibraryURL());
      log.info("Patch URL: " + config.getPatchURL());
      log.info("Common Base URL: " + config.getCommonBaseURL());
      log.info("Common Library URL: " + config.getCommonLibraryURL());
      log.info("Server Name: " + config.getServerName());
      log.info("Server Base Dir: " + config.getServerBaseDir());
      log.info("Server Base URL: " + config.getServerBaseURL());
      log.info("Server Config URL: " + config.getServerConfigURL());
      log.info("Server Home Dir: " + config.getServerHomeDir());
      log.info("Server Home URL: " + config.getServerHomeURL());
      log.info("Server Data Dir: " + config.getServerDataDir());
      log.info("Server Library URL: " + config.getServerLibraryURL());
      log.info("Server Log Dir: " + config.getServerLogDir());
      log.info("Server Native Dir: " + config.getServerNativeDir());
      log.info("Server Temp Dir: " + config.getServerTempDir());
      log.info("Server Temp Deploy Dir: " + config.getServerTempDeployDir());
   }

   /**
    * The <code>initURLHandlers</code> method calls
    * internalInitURLHandlers.  if requireJBossURLStreamHandlers is
    * false, any exceptions are logged and ignored.
    *
    * TODO move to the common project alongside URLStreamHandlerFactory
    */
   private void initURLHandlers()
   {
      if (config.getRequireJBossURLStreamHandlerFactory())
      {
         internalInitURLHandlers();
      }
      else
      {
         try
         {
            internalInitURLHandlers();
         }
         catch (SecurityException e)
         {
            log.warn("You do not have permissions to set URLStreamHandlerFactory", e);
         }
         catch (Error e)
         {
            log.warn("URLStreamHandlerFactory already set", e);
         }
      }
   }

   /**
    * Set up our only URLStreamHandlerFactory.
    * This is needed to ensure Sun's version is not used (as it leaves files
    * locked on Win2K/WinXP platforms.
    */
   private void internalInitURLHandlers()
   {
      try
      {
         // Install a URLStreamHandlerFactory that uses the TCL
         URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory());

         // Preload JBoss URL handlers
         URLStreamHandlerFactory.preload();
      }
      catch (Error error)
      { // very naughty but we HAVE to do this or
        // we'll fail if we ever try to do this again
         log.warn("Caught Throwable Error, this probably means " +
            "we've already set the URLStreamHAndlerFactory before");
      }

      // Include the default JBoss protocol handler package
      String handlerPkgs = System.getProperty("java.protocol.handler.pkgs");
      if (handlerPkgs != null)
      {
         handlerPkgs += "|org.jboss.net.protocol";
      }
      else
      {
         handlerPkgs = "org.jboss.net.protocol";
      }
      System.setProperty("java.protocol.handler.pkgs", handlerPkgs);
   }

   /**
    * Get the typed server configuration object which the
    * server has been initalized to use.
    *
    * @return          Typed server configuration object.
    * @throws IllegalStateException    Not initialized.
    */
   @ManagementProperty(managed=true)
   public ServerConfig getConfig() throws IllegalStateException
   {
      if (config == null)
         throw new IllegalStateException("not initialized");

      return config;
   }

   /**
    * Get the optional server configuration metadata
    * @return a possibly empty map of configuration metadata.
    */
   public Map<String, Object> getMetaData()
   {
      return metadata;
   }
   /**
    * Set the server configuration metadata
    * @param metadata
    */
   public void setMetaData(Map<String, Object> metadata)
   {
      if (metadata == null)
         this.metadata = Collections.emptyMap();
      else
         this.metadata = Collections.unmodifiableMap(metadata);
   }
  
   /**
    * Sets the Server Config
    *
    * Package access for use in testing only
    *
    * @param config
    */
   void setConfig(BaseServerConfig config)
   {
      assert config!=null:"Specified " + ServerConfig.class.getSimpleName() + " was null";
      this.config = config;
   }

   /**
    * Check if the server is started.
    *
    * @return   True if the server is started, else false.
    */
   public boolean isStarted()
   {
      return started;
   }

   /**
    * Check if the shutdown operation has been called/is in progress.
    *
    * @return true if shutdown has been called, false otherwise
    */
   public boolean isInShutdown()
   {
      return isInShutdown;
   }

   /**
    * Start the Server instance.
    *
    * @throws IllegalStateException    Already started or not initialized.
    * @throws Exception                Failed to start.
    */
   public void start() throws IllegalStateException, Exception
   {
      synchronized (this)
      {
         if (isInStart == false)
         {
            isInStart = true;
         }
         else
         {
            log.debug("Already in start, ignoring duplicate start");
            return;
         }
      }

      // make sure we are initialized
      ServerConfig config = getConfig();

      // make sure we aren't started yet
      if (started)
         throw new IllegalStateException("already started");

      ClassLoader oldCL = Thread.currentThread().getContextClassLoader();

      try
      {
         ClassLoader myCL = getClass().getClassLoader();
         Thread.currentThread().setContextClassLoader(myCL);

         // See how long it takes us to start up
         StopWatch watch = new StopWatch(true);

         // Remember when we we started
         startDate = new Date();

         // Install the shutdown hook
         shutdownHook = new ShutdownHook();
         shutdownHook.setDaemon(true);
        
         try
         {
            Runtime.getRuntime().addShutdownHook(shutdownHook);
            if (log != null && log.isDebugEnabled())
            {
               log.debug("Shutdown hook added " + shutdownHook);
            }
         }
         catch (Exception e)
         {
            log.warn("Failed to add shutdown hook; ignoring", e);
         }

         // Do the main start
         doStart(watch);

         // TODO Fix the TCL hack used here!
         ClassLoader cl = Thread.currentThread().getContextClassLoader();
         try
         {
            // Run the bootstraps
            for (Bootstrap bootstrap : bootstraps)
            {
               Thread.currentThread().setContextClassLoader(bootstrap.getClass().getClassLoader());
               startedBootstraps.add(0, bootstrap);
               bootstrap.start(this);
            }
         }
         finally
         {
            Thread.currentThread().setContextClassLoader(cl);
         }
        
         if (config.isInstallLifeThread())
         {
            lifeThread = new LifeThread();
            if (log != null && log.isDebugEnabled())
            {
               log.debug("Installing life thread " + lifeThread);
            }
            lifeThread.start();
         }

         started = true;

         // Send a notification that the startup is complete
         Notification msg = new Notification(START_NOTIFICATION_TYPE, this, 1);
         msg.setUserData(new Long(watch.getLapTime()));
         sendNotification(msg);

         watch.stop();

         if (jbossPackage != null)
         {
            // Tell the world how fast it was =)
            if (log != null)
            {
               log.info("JBoss (Microcontainer) [" + jbossPackage.getImplementationVersion() + "] Started in " + watch);
            }
         }
         else
         {
            if (log != null)
            {
               log.info("JBoss (Microcontainer) [unknown version] Started in " + watch);
            }
         } 
      }
      catch (Throwable t)
      {
         if (log != null && log.isDebugEnabled())
         {
            log.debug("Failed to start", t);
         }

         if (t instanceof Exception)
            throw (Exception)t;
         if (t instanceof Error)
            throw (Error)t;

         throw new RuntimeException("Unexpected error", t);
      }
      finally
      {
         Thread.currentThread().setContextClassLoader(oldCL);
         isInStart = false;
      }
   }

   /**
    * Override to perform the start operations
    *
    * @param watch the stop watch
    * @throws Throwable for any error
    */
   protected abstract void doStart(StopWatch watch) throws Throwable;

   /**
    * Override to perform the shutdown
    */
   protected abstract void doShutdown();
  
   /**
    * Shutdown the server
    */
   protected void shutdownServer()
   {
      if (log != null && log.isTraceEnabled())
         log.trace("Shutdown caller:", new Throwable("Here"));
     
      // avoid entering twice; may happen when called directly
      // from AbstractServerImpl.shutdown(), then called again when all
      // non-daemon threads have exited and the ShutdownHook runs.
      if (isInternalShutdown)
         return;
      else
         isInternalShutdown = true;     
     
      // Send a notification that server stop is initiated
      Notification msg = new Notification(STOP_NOTIFICATION_TYPE, this, 2);
      sendNotification(msg);

      // TODO Fix the TCL hack used here!
      ClassLoader cl = Thread.currentThread().getContextClassLoader();
      try
      {
         for (Bootstrap bootstrap : startedBootstraps)
            bootstrap.prepareShutdown(this);
        
         // Do the bootstraps in reverse order
         for (Bootstrap bootstrap : startedBootstraps)
         {
            Thread.currentThread().setContextClassLoader(bootstrap.getClass().getClassLoader());
            try
            {
               bootstrap.shutdown(this);
            }
            catch (Throwable t)
            {
               if (log != null)
               {
                  log.warn("Error shutting down bootstrap: " + bootstrap, t);
               }
            }
         }
      }
      finally
      {
         Thread.currentThread().setContextClassLoader(cl);
      }
     
      try
      {
         doShutdown();
      }
      finally
      {
         // Done
         if (log != null)
         {
            log.info("Shutdown complete");
         }
         System.out.println("Shutdown complete");
      }
   }
  
   /**
    * Shutdown the Server instance and run shutdown hooks.
    *
    * <p>If the exit on shutdown flag is true, then {@link #exit()}
    *    is called, else only the shutdown hook is run.
    *
    * @throws IllegalStateException    No started.
    */
   public void shutdown() throws IllegalStateException
   {
      if (log != null && log.isTraceEnabled())
         log.trace("Shutdown caller:", new Throwable("Here"));
     
      if (!started)
         throw new IllegalStateException("not started");

      if (isInShutdown)
         throw new IllegalStateException("already in shutdown mode");
     
      isInShutdown = true;
      boolean exitOnShutdown = config.getExitOnShutdown();
      boolean blockingShutdown = config.getBlockingShutdown();
      if (log != null)
      {
         log.info("Shutting down the server, blockingShutdown: " + blockingShutdown);
         if (log.isDebugEnabled())
         {
            log.debug("exitOnShutdown: " + exitOnShutdown);
            log.debug("blockingShutdown: " + blockingShutdown);
         }
      }

      if (exitOnShutdown)
      {
         exit(0);
      }
      else
      {
         // signal lifethread to exit; if no non-daemon threads
         // remain, the JVM will eventually exit
         lifeThread.interrupt();

         if (blockingShutdown)
         {
            shutdownServer();
         }
         else
         {
            // start in new thread to give positive
            // feedback to requesting client of success.
            new Thread()
            {
               public void run()
               {
                  // just run the hook, don't call System.exit, as we may
                  // be embeded in a vm that would not like that very much
                  shutdownServer();
               }
            }.start();
         }
      }
   }

   /**
    * Exit the JVM, run shutdown hooks, shutdown the server.
    *
    * @param exitcode   The exit code returned to the operating system.
    */
   public void exit(final int exitcode)
   {
      // exit() in new thread so that we might have a chance to give positive
      // feed back to requesting client of success.     
      new Thread()
      {
         public void run()
         {
            log.info("Server exit(" + exitcode + ") called");
           
            // Set exit code in the shutdown hook, in case halt is enabled
            shutdownHook.setHaltExitCode(exitcode);
           
            // Initiate exiting, shutdown hook will be called
            Runtime.getRuntime().exit(exitcode);
         }
      }.start();
   }

   /**
    * Exit the JVM with code 1, run shutdown hooks, shutdown the server.
    */
   public void exit()
   {
      exit(1);
   }

   /**
    * Forcibly terminates the currently running Java virtual machine.
    *
    * @param exitcode   The exit code returned to the operating system.
    */
   public void halt(final int exitcode)
   {
      // halt() in new thread so that we might have a chance to give positive
      // feed back to requesting client of success.
      new Thread()
      {
         public void run()
         {
            System.err.println("Server halt(" + exitcode + ") called, halting the JVM now!");
            Runtime.getRuntime().halt(exitcode);
         }
      }.start();
   }

   /**
    * Forcibly terminates the currently running Java virtual machine.
    * Halts with code 1.
    */
   public void halt()
   {
      halt(1);
   }


//////////////////////////////////////////////////////////////////////////   /
//                               Runtime Access                             //
//////////////////////////////////////////////////////////////////////////   /

   /**
    * A simple helper used to log the Runtime memory information.
    *
    * @param rt the runtime
    */
   private void logMemoryUsage(final Runtime rt)
   {
      log.info("Total/free memory: " + rt.totalMemory() + "/" + rt.freeMemory());
   }

   /**
    * Hint to the JVM to run the garbage collector.
    */
   public void runGarbageCollector()
   {
      Runtime rt = Runtime.getRuntime();

      logMemoryUsage(rt);
      rt.gc();
      log.info("Hinted to the JVM to run garbage collection");
      logMemoryUsage(rt);
   }

   /**
    * Hint to the JVM to run any pending object finalizations.
    */
   public void runFinalization()
   {
      Runtime.getRuntime().runFinalization();
      log.info("Hinted to the JVM to run any pending object finalizations");
   }

   /**
    * Enable or disable tracing method calls at the Runtime level.
    *
    * @param flag whether to enable trace
    */
   public void traceMethodCalls(final Boolean flag)
   {
      Runtime.getRuntime().traceMethodCalls(flag.booleanValue());
   }

   /**
    * Enable or disable tracing instructions the Runtime level.
    *
    * @param flag whether to enable trace
    */
   public void traceInstructions(final Boolean flag)
   {
      Runtime.getRuntime().traceInstructions(flag.booleanValue());
   }

///////////////////////////////////////////////////////////////////////////
//                             Server Information                        //
///////////////////////////////////////////////////////////////////////////

   @ManagementProperty(description="The server start time")
   public Date getStartDate()
   {
      return startDate;
   }

   @ManagementProperty(description="The server version string")
   public String getVersion()
   {
      return version.toString();
   }

   @ManagementProperty(description="The server version name")
   public String getVersionName()
   {
      return version.getName();
   }

   @ManagementProperty(description="The server version number string")
   public String getVersionNumber()
   {
      return version.getVersionNumber();
   }

   @ManagementProperty(description="The server build number")
   public String getBuildNumber()
   {
      return version.getBuildNumber();
   }

   @ManagementProperty(description="The server build JVM")
   public String getBuildJVM()
   {
      return version.getBuildJVM();
   }

   @ManagementProperty(description="The server build OS")
   public String getBuildOS()
   {
      return version.getBuildOS();
   }

   @ManagementProperty(description="The server build ID")
   public String getBuildID()
   {
      return version.getBuildID();
   }

   /**
    * The server build date
    * @return server build date
    */
   @ManagementProperty(description="The server build date")
   public String getBuildDate()
   {
      return version.getBuildDate();
   }

   ///////////////////////////////////////////////////////////////////////////
   //                             Lifecycle Thread                          //
   ///////////////////////////////////////////////////////////////////////////
  
   /** A simple thread that keeps the vm alive in the event there are no
    * other threads started.
    */
   private class LifeThread extends Thread
   {
      Object lock = new Object();
     
      LifeThread()
      {
         super("JBossLifeThread");
      }
     
      public void run()
      {
         synchronized (lock)
         {
            try
            {
               lock.wait();
            }
            catch (InterruptedException ignore)
            {
            }
         }
         log.info("LifeThread.run() exits!");
      }
   }

   ///////////////////////////////////////////////////////////////////////////
   //                             Shutdown Hook                             //
   ///////////////////////////////////////////////////////////////////////////
  
   private class ShutdownHook extends Thread
   {
      /** Whether to halt the JMV at the end of the shutdown hook */     
      private boolean forceHalt = true;

      /** The exit code to use if forceHalt is enabled */
      private int haltExitCode;
     
      public ShutdownHook()
      {
         super("JBoss Shutdown Hook");

         String value = SecurityActions.getSystemProperty("jboss.shutdown.forceHalt", null);
         if (value != null)
         {
            forceHalt = Boolean.valueOf(value).booleanValue();
         }
      }

      public void setHaltExitCode(int haltExitCode)
      {
         this.haltExitCode = haltExitCode;
      }
     
      public void run()
      {
         log.info("Runtime shutdown hook called, forceHalt: " + forceHalt);
        
         // shutdown the server
         shutdownServer();
        
         // Execute the jdk JBossJDKLogManager doReset via reflection
         LogManager lm = LogManager.getLogManager();
         try
         {
            Class<?>[] sig = {};
            Method doReset = lm.getClass().getDeclaredMethod("doReset", sig);
            Object[] args = {};
            doReset.invoke(lm, args);
         }
         catch(Exception e)
         {
            if (log.isTraceEnabled())
               log.trace("No doReset found?", e);
         }
        
         // later bitch - other shutdown hooks may be killed
         if (forceHalt)
         {
            System.out.println("Halting VM");
            Runtime.getRuntime().halt(haltExitCode);
         }
      }
   }
}
TOP

Related Classes of org.jboss.bootstrap.AbstractServerImpl

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.