Package org.jboss.ha.framework.server

Source Code of org.jboss.ha.framework.server.JChannelFactory$ThreadDecoratorImpl

/*
* 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.ha.framework.server;

import java.net.InetAddress;
import java.rmi.dgc.VMID;
import java.rmi.server.UID;
import java.security.AccessController;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;

import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.ObjectName;

import org.jboss.bootstrap.spi.util.ServerConfigUtil;
import org.jboss.logging.Logger;
import org.jboss.system.ServiceMBean;
import org.jboss.util.loading.ContextClassLoaderSwitcher;
import org.jgroups.Channel;
import org.jgroups.ChannelException;
import org.jgroups.ChannelListenerAdapter;
import org.jgroups.Event;
import org.jgroups.Global;
import org.jgroups.JChannel;
import org.jgroups.conf.ConfiguratorFactory;
import org.jgroups.conf.ProtocolData;
import org.jgroups.conf.ProtocolStackConfigurator;
import org.jgroups.jmx.JmxConfigurator;
import org.jgroups.protocols.TP;
import org.jgroups.stack.IpAddress;
import org.jgroups.stack.Protocol;
import org.jgroups.stack.ProtocolStack;
import org.jgroups.util.DefaultThreadFactory;
import org.jgroups.util.LazyThreadFactory;
import org.jgroups.util.ThreadDecorator;
import org.jgroups.util.ThreadFactory;
import org.jgroups.util.ThreadManager;
import org.jgroups.util.Util;

/**
* Extension to the JGroups JChannelFactory that supports a number of
* JBoss AS-specific behaviors:
* <p>
* <ul>
* <li>Passing a config event to newly created channels containing
* "additional_data" that will be associated with the JGroups
* <code>IpAddress</code> for the peer. Used to provide logical addresses
* to cluster peers that remain consistent across channel and server restarts.</li>
* <li>Never returns instances of {@link org.jgroups.mux.MuxChannel} from
* the <code>createMultiplexerChannel</code> methods.  Instead always returns
* a channel with a shared transport protocol.</li>
* <li>Configures the channel's thread pools and thread factories to ensure
* that application thread context classloaders don't leak to the channel
* threads.</li>
* </ul>
* </p>
*
* @author <a href="mailto://brian.stansberry@jboss.com">Brian Stansberry</a>
* @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a>
*
* @version $Revision: 86596 $
*/
public class JChannelFactory extends org.jgroups.JChannelFactory
      implements JChannelFactoryMBean, MBeanRegistration
{
   protected static final Logger log = Logger.getLogger(JChannelFactory.class);
  
   public static final String UNSHARED_TRANSPORT_NAME_BASE = "unnamed_";
  
   private static final int CREATED = ServiceMBean.CREATED;
   private static final int STARTING = ServiceMBean.STARTING;
   private static final int STARTED = ServiceMBean.STARTED;
   private static final int STOPPING = ServiceMBean.STOPPING;
   private static final int STOPPED = ServiceMBean.STOPPED;
   private static final int DESTROYED = ServiceMBean.DESTROYED;
   private static final int FAILED = ServiceMBean.FAILED;
  
   private InetAddress nodeAddress;
   private String nodeName;
   private int namingServicePort = -1;
   private int state = ServiceMBean.UNREGISTERED;
   private boolean assignLogicalAddresses = true;
   private boolean manageNewThreadClassLoader = true;
   private boolean manageReleasedThreadClassLoader = false;
   private boolean addMissingSingletonName = true;
   private boolean domainSet;
   private final ContextClassLoaderSwitcher classLoaderSwitcher;
   private final Set<String> registeredChannels = new HashSet<String>();

   public JChannelFactory()
   {
      this.classLoaderSwitcher = (ContextClassLoaderSwitcher) AccessController.doPrivileged(ContextClassLoaderSwitcher.INSTANTIATOR);
   }  

   /**
    * Always throws <code>ChannelException</code>; this method is not supported.
    */  
   @Override
   public Channel createChannel() throws ChannelException
   {
      throw new ChannelException("No-arg createChannel() is not supported");
   }

   @Override
   public Channel createChannel(Object properties) throws ChannelException
   {
      checkStarted();
     
      @SuppressWarnings("deprecation")     
      Channel channel = super.createChannel(properties);
     
      if (manageNewThreadClassLoader || manageReleasedThreadClassLoader)
      {
         fixChannelThreadManagement(channel);
      }
     
      if (assignLogicalAddresses)
      {
         setChannelUniqueId(channel);
      }
     
      // can't register in JMX as we don't have a channel name
     
      return channel;
   }

   /**
    * Create a {@link Channel} using the specified stack. Channel will use a
    * shared transport if the <code>singleton-name</code> attribute is
    * set on the stack's transport protocol.
    *
    * @param stack_name the name of the stack
    * @return the channel
    *
    * @throws Exception
    */
   @Override
   public Channel createChannel(String stack_name) throws Exception
   {
      checkStarted();
     
      String props=stack_name != null? getConfig(stack_name) : null;
      if (props == null)
      {
         log.warn("No protocol stack found with name " + stack_name +
                  "; creating default channel");
         return createChannel();
      }
     
      JChannel channel = new JChannel(props);
     
      if (manageNewThreadClassLoader || manageReleasedThreadClassLoader)
      {
         fixChannelThreadManagement(channel);
      }
     
      if (assignLogicalAddresses)
      {
         setChannelUniqueId(channel);
      }
     
      // can't register in JMX as we don't have a channel name
     
      return channel;
   }
  
   /**
    * Creates and returns a shared transport Channel configured with the specified
    * {@link #getConfig(String) protocol stack configuration}.
    * <p>
    * <emphasis>NOTE:</emphasis> The implementation of this method is somewhat
    * different from what is described in
    * {@link org.jgroups.ChannelFactory#createMultiplexerChannel(String, String)}.
    * The returned channel will not be an instance of
    * <code>org.jgroups.mux.MuxChannel</code>; rather a channel that uses a
    * shared transport will be returned.  This will be the case whether or
    * not the protocol stack specified by <code>stack_name</code> includes
    * a <code>singleton_name</code> attribute in its
    * {@link org.jgroups.protocols.TP transport protocol} configuration. If no
    * <code>singleton_name</code> attribute is present, this factory will create
    * a synthetic one by prepending "unnamed_" to the provided
    * <code>id</code> param and will use that for the returned channel's
    * transport protocol. (Note this will not effect the protocol stack
    * configuration named by <code>stack_name</code>; i.e. another request
    * that passes the same <code>stack_name</code> will not inherit the
    * synthetic singleton name.)
    *
    * @param stack_name
    *            The name of the stack to be used. All stacks are defined in
    *            the configuration with which the factory is configured (see
    *            {@link #setMultiplexerConfig(Object)} for example. If
    *            clients attempt to create a Channel for an undefined stack
    *            name an Exception will be thrown.
    * @param id  Only used if the transport protocol configuration for the
    *            specified stack does not include the <code>singleton_name</code>
    *            attribute; then it is used to create a synthetic singleton-name
    *            for the channel's protocol stack.
    *           
    * @return An implementation of Channel configured with a shared transport.
    *        
    * @throws IllegalStateException if the specified protocol stack does not
    *                               declare a <code>singleton_name</code> and
    *                               {@link #getAddMissingSingletonName()} returns
    *                               <code>false</code>.
    * @throws ChannelException
    */
   @Override
   public Channel createMultiplexerChannel(String stack_name, String id) throws Exception
   {
      checkStarted();
     
      String configStr = getConfig(stack_name);
     
      if (configStr == null)
         throw new IllegalStateException("Unknown stack_name " + stack_name);
     
      ProtocolStackConfigurator config = ConfiguratorFactory.getStackConfigurator(configStr);
      Map<String, String> tpProps = getTransportProperties(config);
     
      if (!tpProps.containsKey(Global.SINGLETON_NAME))
      {
         if (addMissingSingletonName)
         {
            String singletonName = UNSHARED_TRANSPORT_NAME_BASE + stack_name;
           
            log.warn("Config for " + stack_name + " does not include " +
                      "singleton_name; adding a name of " + singletonName +
                      ". You should configure a singleton_name for this stack.");
           
            config = addSingletonName(config, singletonName);
            log.debug("Stack config after adding singleton_name is " + config.getProtocolStackString());
            tpProps = getTransportProperties(config);                      
         }
         else
         {
            throw new IllegalStateException("Config for " + stack_name + " does not include " +
                      "singleton_name and MuxChannels are not supported.");
         }
      }
     
      JChannel channel = new JChannel(config);
     
      if (manageNewThreadClassLoader || manageReleasedThreadClassLoader)
      {
         fixChannelThreadManagement(channel);
      }
     
      if (assignLogicalAddresses)
      {
         setChannelUniqueId(channel);
      }
     
      if (isExposeChannels() && id != null && id.length() > 0)
      {
         registerChannel(channel, id);
      }
     
      return channel;
  

  
   /**
    * Creates and returns a shared transport Channel configured with the specified
    * {@link #getConfig(String) protocol stack configuration}.
    *
    * See {@link #createMultiplexerChannel(String, String)}; the additional
    * attributes specified in this overloaded version of that method are ignored.
    *
    * @param register_for_state_transfer ignored in JBoss AS. Treated as <code>false</code>.
    *
    * @param substate_id ignored in JBoss AS
    *           
    * @return An implementation of Channel configured with a shared transport.
    *        
    *        
    * @throws IllegalStateException if the specified protocol stack does not
    *                               declare a <code>singleton_name</code> and
    *                               {@link #getAddMissingSingletonName()} returns
    *                               <code>false</code>.
    * @throws ChannelException
    */
   @Override
   public Channel createMultiplexerChannel(String stack_name, String id, boolean register_for_state_transfer, String substate_id) throws Exception
   {
      return createMultiplexerChannel(stack_name, id);
   }
  
   @Override
   public void setDomain(String domain)
   {
      super.setDomain(domain);
      domainSet = true;
   }

   /**
    * Get any logical name assigned to this server; if not null this value
    * will be the value of the
    * {@link #setAssignLogicalAddresses(boolean) logical address} assigned
    * to the channels this factory creates.
    *
    * @return the logical name for this server, or <code>null</code>.
    */
   public String getNodeName()
   {
      return nodeName;
   }

   /**
    * Sets the logical name assigned to this server; if not null this value
    * will be the value of the
    * {@link #setAssignLogicalAddresses(boolean) logical address} assigned
    * to the channels this factory creates.
    *
    * @param nodeName the logical name for this server, or <code>null</code>.
    */
   public void setNodeName(String nodeName)
   {
      this.nodeName = nodeName;
   }
  
   /**
    * Gets the address to which this server is bound; typically the value
    * passed to <code>-b</code> when JBoss is started. Used in combination
    * with {@link #getNamingServicePort() the naming service port} to create
    * a logical name for this server if no {@link #SetNodeName(String) node name}
    * is specified.
    *
    * @return the address to which this server is bound, or <code>null</code>
    *         if not set
    */
   public InetAddress getNodeAddress()
   {
      return nodeAddress;
   }
  
   /**
    * Sets the address to which this server is bound; typically the value
    * passed to <code>-b</code> when JBoss is started. Used in combination
    * with {@link #getNamingServicePort() the naming service port} to create
    * a logical name for this server if no {@link #SetNodeName(String) node name}
    * is specified.
    *
    * @param nodeAddress the address to which this server is bound,
    *                    or <code>null</code>
    */
   public void setNodeAddress(InetAddress nodeAddress)
   {
      this.nodeAddress = nodeAddress;
   }

   /**
    * Gets the port on which this server's naming service is listening. Used in
    * combination with {@link #getNodeAddress() the server bind address} to create
    * a logical name for this server if no {@link #SetNodeName(String) node name}
    * is specified.
    *
    * @return the port on which JNDI is listening, or <code>-1</code> if not set.
    */
   public int getNamingServicePort()
   {
      return namingServicePort;
   }

   /**
    * Sets the port on which this server's naming service is listening. Used in
    * combination with {@link #getNodeAddress() the server bind address} to create
    * a logical name for this server if no {@link #SetNodeName(String) node name}
    * is specified.
    *
    * @param jndiPort the port on which JNDI is listening.
    */
   public void setNamingServicePort(int jndiPort)
   {
      this.namingServicePort = jndiPort;
   }
  
   /**
    * Gets whether this factory should create a "logical address" (or use
    * one set via {@link #setNodeName(String)} and assign it to
    * any newly created <code>Channel</code> as JGroups "additional_data".
    *
    * @see #setAssignLogicalAddresses(boolean)
    */
   public boolean getAssignLogicalAddresses()
   {
      return assignLogicalAddresses;
   }

   /**
    * Sets whether this factory should create a "logical address" (or use
    * one set via {@link #setNodeName(String)} and assign it to
    * any newly created <code>Channel</code> as JGroups "additional_data".
    * <p>
    * Any such logical address will be used by <code>HAPartition</code>
    * to assign a name to the <code>ClusterNode</code> object representing
    * this node. If a logical address is not set, the <code>ClusterNode</code>
    * will use the address and port JGroups is using to receive messages to
    * create its name.
    * </p>
    * <p>
    * Default is <code>true</code>.
    * </p>
    */
   public void setAssignLogicalAddresses(boolean logicalAddresses)
   {
      this.assignLogicalAddresses = logicalAddresses;
   }

   /**
    * Gets whether this factory should update the standard JGroups
    * thread factories to ensure application classloaders do not leak to
    * newly created channel threads.
    *
    * @return <code>true</code> if the factories should be updated.
    *         Default is <code>true</code>.
    */
   public boolean getManageNewThreadClassLoader()
   {
      return manageNewThreadClassLoader;
   }

   /**
    * Sets whether this factory should update the standard JGroups
    * thread factories to ensure application classloaders do not leak to
    * newly created channel threads. This should only be set to <code>false</code>
    * if a JGroups release is used that itself prevents such classloader leaks.
    *
    * @param manage <code>true</code> if the factories should be updated.
    */
   public void setManageNewThreadClassLoader(boolean manage)
   {
      this.manageNewThreadClassLoader = manage;
   }

   /**
    * Gets whether this factory should update the standard JGroups
    * thread pools to ensure application classloaders have not leaked to
    * threads returned to the pool.
    *
    * @return <code>true</code> if the pools should be updated.
    *         Default is <code>false</code>.
    */
   public boolean getManageReleasedThreadClassLoader()
   {
      return manageReleasedThreadClassLoader;
   }

   /**
    * Sets whether this factory should update the standard JGroups
    * thread pools to ensure application classloaders have not leaked to
    * threads returned to the pool.
    * <p>
    * There is a small performance cost to enabling this, and applications
    * can prevent any need to enable it by properly restoring the thread
    * context classloader if they change it.  Therefore, by default this
    * is set to <code>false</code>.
    * </p>
    *
    * @param manage <code>true</code> if the factories should be updated.
    */
   public void setManageReleasedThreadClassLoader(boolean manage)
   {
      this.manageReleasedThreadClassLoader = manage;
   }

   /**
    * Gets whether {@link #createMultiplexerChannel(String, String)} should
    * create a synthetic singleton name attribute for a channel's transport
    * protocol if one isn't configured.  If this is <code>false</code> and
    * no <code>singleton_name</code> is configured,
    * {@link #createMultiplexerChannel(String, String)} will throw an
    * <code>IllegalStateException</code>.
    *
    * @return <code>true</code> if synthetic singleton names should be created.
    *         Default is <code>true</code>.
    */
   public boolean getAddMissingSingletonName()
   {
      return addMissingSingletonName;
   }

   /**
    * Sets whether {@link #createMultiplexerChannel(String, String)} should
    * create a synthetic singleton name attribute for a channel's transport
    * protocol if one isn't configured.
    *
    * @param addMissingSingletonName <code>true</code> if synthetic singleton
    *                                names should be created.
    */
   public void setAddMissingSingletonName(boolean addMissingSingletonName)
   {
      this.addMissingSingletonName = addMissingSingletonName;
   }

   @Override
   public void create() throws Exception
   {

      if (state == CREATED || state == STARTING || state == STARTED
         || state == STOPPING || state == STOPPED)
      {
         log.debug("Ignoring create call; current state is " + getStateString());
         return;
      }
     
      log.debug("Creating JChannelFactory");
     
      try
      {
         super.create();
         state = CREATED;
      }
      catch (Exception e)
      {
         log.debug("Initialization failed JChannelFactory", e);
         throw e;
      }
     
      log.debug("Created JChannelFactory");
   }

   @Override
   public void start() throws Exception
   {
      if (state == STARTING || state == STARTED || state == STOPPING)
      {
         log.debug("Ignoring start call; current state is " + getStateString());
         return;
      }
     
      if (state != CREATED && state != STOPPED && state != FAILED)
      {
         log.debug("Start requested before create, calling create now");        
         create();
      }
     
      state = STARTING;
      log.debug("Starting JChannelFactory");

      try
      {
         super.start();
      }
      catch (Exception e)
      {
         state = FAILED;
         log.debug("Starting failed JChannelFactory", e);
         throw e;
      }

      state = STARTED;
      log.debug("Started JChannelFactory");
     
   }

   @Override
   public void stop()
   {
      if (state != STARTED)
      {
         log.debug("Ignoring stop call; current state is " + getStateString());
         return;
      }
     
      state = STOPPING;
      log.debug("Stopping JChannelFactory");

      try
      {
         super.stop();
      }
      catch (Throwable e)
      {
         state = FAILED;
         log.warn("Stopping failed JChannelFactory", e);
         return;
      }
     
      state = STOPPED;
      log.debug("Stopped JChannelFactory");
   }

   @Override
   public void destroy()
   {
      if (state == DESTROYED)
      {
         log.debug("Ignoring destroy call; current state is " + getStateString());
         return;
      }
     
      if (state == STARTED)
      {
         log.debug("Destroy requested before stop, calling stop now");
         stop();
      }
     
      log.debug("Destroying JChannelFactory");
     
      // DON'T call super.destroy() as that may deregister the JMX proxy
      // to this pojo service, leading to ugliness when the proxy is destroyed
      try
      {
        
         Set<String> toUnregister = null;
         synchronized (registeredChannels)
         {
            toUnregister = new HashSet<String>(registeredChannels);
         }
        
         for (String channelId : toUnregister)
         {
            unregister(channelId);
         }
      }
      catch (Throwable t)
      {
         log.warn("Destroying failed JChannelFactory", t);
      }
      state = DESTROYED;
      log.debug("Destroyed JChannelFactory");
   }

   // ------------------------------------------------------- MBeanRegistration

   public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception
   {
      setServer(server);
      if (!domainSet || getDomain() == null)
      {
         setDomain(name.getDomain());
      }
      return name;
   }


   public void postRegister(Boolean registrationDone)
   {
      if (registrationDone != null && registrationDone.booleanValue()
            && state == ServiceMBean.UNREGISTERED)
      {
         state = ServiceMBean.REGISTERED;
      }
   }


   public void preDeregister() throws Exception
   {
   }

   public void postDeregister()
   {
      setServer(null);
      if (state == ServiceMBean.DESTROYED)
         state = ServiceMBean.UNREGISTERED;
   }
  
   /**
    * Gets the classloader that channel threads should be set to if
    * {@link #getManageNewThreadClassloader()} or {@link #getManageReleasedThreadClassLoader()}
    * are <code>true</code>.
    * <p>
    * This implementation returns this class' classloader.
    *
    * @return the classloader.
    */
   protected ClassLoader getDefaultChannelThreadContextClassLoader()
   {
      return getClass().getClassLoader();
   }

   // ----------------------------------------------------------------- Private


   private void checkStarted()
   {
      if (state != ServiceMBean.STARTED)
         throw new IllegalStateException("Cannot use factory; state is " + getStateString());
   }
  
   private void setChannelUniqueId(Channel channel)
   {
      IpAddress address = (IpAddress) channel.getLocalAddress();
      if (address == null)
      {
         // We push the independent name in the protocol stack before connecting to the cluster
         if (this.nodeName == null || "".equals(this.nodeName)) {
            this.nodeName = generateUniqueNodeName();
         }
        
         log.debug("Passing unique node id " + nodeName + " to the channel as additional data");
        
         HashMap<String, byte[]> staticNodeName = new HashMap<String, byte[]>();
         staticNodeName.put("additional_data", this.nodeName.getBytes());
         channel.down(new Event(Event.CONFIG, staticNodeName));
        
      }
      else if (address.getAdditionalData() == null)
      {
         if (channel.isConnected())
         {
            throw new IllegalStateException("Underlying JChannel was " +
                    "connected before additional_data was set");
         }
      }
      else if (this.nodeName == null || "".equals(this.nodeName))
      {        
         this.nodeName = new String(address.getAdditionalData());
         log.warn("Field nodeName was not set but mux channel already had " +
                "additional data -- setting nodeName to " + nodeName);
      }
   }
  
   private String getStateString()
   {
      return ServiceMBean.states[state];
   }

   private String generateUniqueNodeName ()
   {
      // we first try to find a simple meaningful name:
      // 1st) "local-IP:JNDI_PORT" if JNDI is running on this machine
      // 2nd) "local-IP:JMV_GUID" otherwise
      // 3rd) return a fully GUID-based representation
      //

      // Before anything we determine the local host IP (and NOT name as this could be
      // resolved differently by other nodes...)

      // But use the specified node address for multi-homing

      String hostIP = null;
      InetAddress address = ServerConfigUtil.fixRemoteAddress(nodeAddress);
      if (address == null)
      {
         log.debug ("unable to create a GUID for this cluster, check network configuration is correctly setup (getLocalHost has returned an exception)");
         log.debug ("using a full GUID strategy");
         return new VMID().toString();
      }
      else
      {
         hostIP = address.getHostAddress();
      }

      // 1st: is JNDI up and running?
      //
      if (namingServicePort > 0)
      {
         // we can proceed with the JNDI trick!
         return hostIP + ":" + namingServicePort;
      }
      else
      {
         log.warn("JNDI has been found but the service wasn't started. Most likely, " +
               "HAPartition bean is missing dependency on JBoss Naming. " +
               "Instead using host based UID strategy for defining a node " +
               "GUID for the cluster.");
      }

      // 2nd: host-GUID strategy
      //
      String uid = new UID().toString();
      return hostIP + ":" + uid;
   }
  
   private Map<String, String> getTransportProperties(ProtocolStackConfigurator config)
   {
      Map<String, String> tpProps = null;
      try
      {
         ProtocolData[] protocols=config.getProtocolStack();
         ProtocolData transport=protocols[0];
         tpProps = transport.getParameters();
      }
      catch (UnsupportedOperationException uoe)
      {
         // JGroups version hasn't implemented ProtocolStackConfigurator.getProtocolStack()
         // So we do it ourselves
         String configStr = config.getProtocolStackString();
         String tpConfigStr = configStr.substring(configStr.indexOf('(') + 1, configStr.indexOf(')'));
         String[] params = tpConfigStr.split(";");
         tpProps = new HashMap<String, String>();
         for (String param : params)
         {
            String[] keyVal = param.split("=");
            if (keyVal.length != 2)
               throw new IllegalStateException("Invalid parameter " + param + " in stack " + configStr);
            tpProps.put(keyVal[0], keyVal[1]);
         }
      }
     
      return tpProps;
   }
 
   private ProtocolStackConfigurator addSingletonName(ProtocolStackConfigurator orig, String singletonName)
      throws ChannelException
   {
      ProtocolStackConfigurator result = null;
      try
      {
         ProtocolData[] protocols=orig.getProtocolStack();
         ProtocolData transport=protocols[0];
         Map<String, String> tpProps = transport.getParameters();
         tpProps.put(Global.SINGLETON_NAME, singletonName);
         // we've now updated the state of orig; just return it
         result = orig;
      }
      catch (UnsupportedOperationException uoe)
      {
         // JGroups version hasn't implemented ProtocolStackConfigurator.getProtocolStack()
         // So we do things manually via string manipulation        
         String config = orig.getProtocolStackString();
         int idx = config.indexOf('(') + 1;
         StringBuilder builder = new StringBuilder(config.substring(0, idx));
         builder.append(Global.SINGLETON_NAME);
         builder.append('=');
         builder.append(singletonName);
         builder.append(';');
         builder.append(config.substring(idx));
        
         result = ConfiguratorFactory.getStackConfigurator(builder.toString());
      }
     
      return result;
   }
  
   private void fixChannelThreadManagement(Channel channel) throws ChannelException
   {
      if (!(channel instanceof JChannel))
      {
         log.debug("Cannot fix thread pools for unknown Channel type " + channel.getClass().getName());
         return;
      }
     
      JChannel jchannel = (JChannel) channel;
     
      ProtocolStack stack = jchannel.getProtocolStack();
      List<Protocol> protocols = stack.getProtocols();
      TP tp = null;
      for (int i = protocols.size() - 1; i >= 0; i--)
      {
         if (protocols.get(i) instanceof TP)
         {
            tp = (TP) protocols.get(i);
            break;
         }
      }
     
      ClassLoader defaultTCCL = getDefaultChannelThreadContextClassLoader();
      ThreadDecoratorImpl threadDecorator = new ThreadDecoratorImpl(defaultTCCL);
      if (manageNewThreadClassLoader)
      {
         fixProtocolThreadFactories(tp, threadDecorator);
      }
     
      if (manageReleasedThreadClassLoader)
      {
         fixTransportThreadPools(tp, threadDecorator);
      }
   }

   private void fixProtocolThreadFactories(TP tp, ThreadDecoratorImpl threadDecorator)
   {
      ThreadFactory stackFactory = tp.getThreadFactory();
      if (stackFactory == null)
      {
         stackFactory = new DefaultThreadFactory(Util.getGlobalThreadGroup(), "", false);
         tp.setThreadFactory(stackFactory);
      }
      fixThreadManager(stackFactory, threadDecorator, "TP.getThreadFactory()");
     
      log.debug("Fixed thread factory for " + tp);
     
      ThreadFactory timerFactory = tp.getTimerThreadFactory();
      if (timerFactory == null)
      {
         timerFactory = new LazyThreadFactory(Util.getGlobalThreadGroup(), "Timer", true, true);
         tp.setTimerThreadFactory(timerFactory);           
      }
      fixThreadManager(timerFactory, threadDecorator, "TP.getTimerThreadFactory()");
     
      log.debug("Fixed timer thread factory for " + tp);
     
      ThreadGroup pool_thread_group = null;
      if (tp.isDefaulThreadPoolEnabled())
      {
         ThreadFactory defaultPoolFactory = tp.getDefaultThreadPoolThreadFactory();
         if (defaultPoolFactory == null)
         {
            pool_thread_group=new ThreadGroup(Util.getGlobalThreadGroup(), "Thread Pools");
            defaultPoolFactory = new DefaultThreadFactory(pool_thread_group, "Incoming", false, true);
            tp.setThreadFactory(defaultPoolFactory);
         }
         fixThreadManager(defaultPoolFactory, threadDecorator, "TP.getDefaultThreadPoolThreadFactory()");
        
         log.debug("Fixed default pool thread factory for " + tp);
      }
     
      if (tp.isOOBThreadPoolEnabled())
      {
         ThreadFactory oobPoolFactory = tp.getOOBThreadPoolThreadFactory();
         if (oobPoolFactory == null)
         {
            if (pool_thread_group == null)
               pool_thread_group=new ThreadGroup(Util.getGlobalThreadGroup(), "Thread Pools");
            oobPoolFactory = new DefaultThreadFactory(pool_thread_group, "OOB", false, true);
            tp.setThreadFactory(oobPoolFactory);
         }
         fixThreadManager(oobPoolFactory, threadDecorator, "TP.getOOBThreadPoolThreadFactory()");
        
         log.debug("Fixed oob pool thread factory for " + tp);
      }
     
      Map<ThreadFactory, Protocol> factories= new HashMap<ThreadFactory, Protocol>();
      Protocol tmp=tp.getUpProtocol();
      while(tmp != null) {
        ThreadFactory f=tmp.getThreadFactory();
         if(f != null && !factories.containsKey(f))
         {
            factories.put(f, tmp);
         }
         tmp=tmp.getUpProtocol();
      }
     
      for (Map.Entry<ThreadFactory, Protocol> entry : factories.entrySet())
      {
         fixThreadManager(entry.getKey(), threadDecorator, entry.getValue().getClass().getSimpleName() + ".getThreadFactory()");
      }
     
      log.debug("Fixed Protocol thread factories");
   }

   private void fixTransportThreadPools(TP tp, ThreadDecoratorImpl threadDecorator)
   {
      Executor threadPool = tp.getDefaultThreadPool();
      if (tp.isDefaulThreadPoolEnabled())
      {
         fixThreadManager(threadPool, threadDecorator, "TP.getDefaultThreadPool()");
        
         log.debug("Fixed default thread pool for " + tp);
      }
     
      threadPool = tp.getOOBThreadPool();
      if (tp.isOOBThreadPoolEnabled())
      {
         fixThreadManager(threadPool, threadDecorator, "TP.getOOBThreadPool()");
        
         log.debug("Fixed OOB thread pool for " + tp);
      }
   }
  
   private void fixThreadManager(Object manager, ThreadDecoratorImpl decorator, String managerSource)
   {
      if (manager instanceof ThreadManager)
      {
         ThreadManager threadManager = (ThreadManager) manager;
        
         ThreadDecorator existing = threadManager.getThreadDecorator();
         if (existing instanceof ThreadDecoratorImpl)
         {
            // already been handled
            return;
         }
         else if (existing != null)
         {
            // someone else has added one; integrate with it
            decorator.setParent(existing);
         }
         threadManager.setThreadDecorator(decorator);
      }
      else
      {
         log.warn(managerSource + " is not a ThreadManager");
      }
   }
  
   /**
    * Sets the context class loader on <code>thread</code> to the classloader
    * in effect when this factory was instantiated.
    *
    * @param thread the thread to set
    */
   private void setDefaultThreadContextClassLoader(Thread thread, ClassLoader classLoader)
   {
      classLoaderSwitcher.setContextClassLoader(thread, classLoader);
   }
  
   private void registerChannel(JChannel ch, String channelId) throws Exception
   {
      if(getServer() != null)
      {
         // Register for channel closed notification so we can unregister
         JmxDeregistrationChannelListener listener = new JmxDeregistrationChannelListener(channelId);
         ch.addChannelListener(listener);
         JmxConfigurator.registerChannel(ch, getServer(), getDomain(), channelId, isExposeProtocols());
         synchronized (registeredChannels)
         {
            registeredChannels.add(channelId);
         }
      }
   }
  
   private void unregister(String channelId)
   {
      if(getServer() != null && registeredChannels.contains(channelId))
      {
         String oname = getDomain() + ":type=channel,cluster=" + channelId;
         try
         {
            getServer().unregisterMBean(new ObjectName(oname));
            oname = getDomain() + ":type=protocol,cluster=" + channelId + ",*";
            JmxConfigurator.unregister(getServer(), oname);
            synchronized (registeredChannels)
            {
               registeredChannels.remove(channelId);
            }
         }
         catch(Exception e)
         {
            log.error("failed unregistering " + oname, e);
         }
      }
   }
  
   private class ThreadDecoratorImpl implements ThreadDecorator
   {
      private final ClassLoader classloader;
      private ThreadDecorator parent;
     
      private ThreadDecoratorImpl(ClassLoader classloader)
      {
         this.classloader = classloader;
      }

      public void threadCreated(Thread thread)
      {
         if (parent != null)
            parent.threadCreated(thread);
         setDefaultThreadContextClassLoader(thread, classloader);
      }

      public void threadReleased(Thread thread)
      {
         if (parent != null)
            parent.threadCreated(thread);
         setDefaultThreadContextClassLoader(thread, classloader);
      }

      public ThreadDecorator getParent()
      {
         return parent;
      }

      public void setParent(ThreadDecorator parent)
      {
         this.parent = parent;
      }
     
   }

   private class JmxDeregistrationChannelListener extends ChannelListenerAdapter
   {
      private final String channelId;
     
      JmxDeregistrationChannelListener(String channelId)
      {
         this.channelId = channelId;
      }
     
      public void channelClosed(Channel channel)
      {
         unregister(channelId);
      }           
   }
  
}
TOP

Related Classes of org.jboss.ha.framework.server.JChannelFactory$ThreadDecoratorImpl

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.