Package org.jboss.ha.core.channelfactory

Source Code of org.jboss.ha.core.channelfactory.JChannelFactory

/*
* 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.core.channelfactory;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.rmi.dgc.VMID;
import java.rmi.server.UID;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

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

import org.jboss.logging.Logger;
import org.jboss.util.loading.ContextClassLoaderSwitcher;
import org.jgroups.Channel;
import org.jgroups.ChannelException;
import org.jgroups.ChannelListenerAdapter;
import org.jgroups.Global;
import org.jgroups.JChannel;
import org.jgroups.conf.ConfiguratorFactory;
import org.jgroups.conf.ProtocolData;
import org.jgroups.conf.ProtocolParameter;
import org.jgroups.conf.ProtocolStackConfigurator;
import org.jgroups.jmx.JmxConfigurator;
import org.jgroups.protocols.TP;
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;
import org.w3c.dom.Element;

/**
* Implementation of the JGroups <code>ChannelFactory</code> 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>
* <li>Exposes a ProfileService ManagementView interface.</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: 107689 $
*/
@SuppressWarnings("deprecation")
public class JChannelFactory
      implements org.jgroups.ChannelFactory, JChannelFactoryMBean, MBeanRegistration
{
   private static final Logger log = Logger.getLogger(JChannelFactory.class);
  
   /**
    * Prefix prepended to the protocol stack name to create a synthetic
    * transport protocol <code>singleton_name</code> value for channels
    * that don't configure a <code>singleton_name</code>.
    */
   public static final String UNSHARED_TRANSPORT_NAME_BASE = "unnamed_";
  
   /** Default value for property {@link #getDomain() domain}. */
   public static final String DEFAULT_JMX_DOMAIN = "jgroups";

   private static final String[] states = {
      "Stopped", "Stopping", "Starting", "Started", "Failed",
      "Destroyed", "Created", "Unregistered", "Registered"
   };

   /** The Service.stop has completed */
   private static final int STOPPED  = 0;
   /** The Service.stop has been invoked */
   private static final int STOPPING = 1;
   /** The Service.start has been invoked */
   private static final int STARTING = 2;
   /** The Service.start has completed */
   private static final int STARTED  = 3;
   /** There has been an error during some operation */
   private static final int FAILED  = 4;
   /** The Service.destroy has completed */
   private static final int DESTROYED = 5;
   /** The Service.create has completed */
   private static final int CREATED = 6;
   /** The MBean has been created but has not completed MBeanRegistration.postRegister */
   private static final int UNREGISTERED = 7;
   /** The MBean has been created and has completed MBeanRegistration.postRegister */
   private static final int REGISTERED = 8;

   private static final String VALUE_BIND_ADDRESS_ANY = "0.0.0.0";
  
   private InetAddress nodeAddress;
   private String nodeName;
   private int namingServicePort = -1;
   private int state = UNREGISTERED;
   private boolean assignLogicalAddresses = true;
   private boolean manageNewThreadClassLoader = true;
   private boolean manageReleasedThreadClassLoader = false;
   private boolean addMissingSingletonName = true;
   private final ContextClassLoaderSwitcher classLoaderSwitcher;
   private final Map<Channel, ChannelInfo> registeredChannels =
      new ConcurrentHashMap<Channel, ChannelInfo>(16, 0.75f, 2);
  
   private ChannelCloseListener closeListener = new ChannelCloseListener();

   /**
    * Map<String,ProtocolStackConfigurator>. Hashmap which maps stack names to JGroups
    * configurations. Keys are stack names, values are plain JGroups stack
    * configs. This is (re-)populated whenever a setMultiplexerConfig() method
    * is called
    */
   private final Map<String,ProtocolStackConfigInfo> stacks =
      new ConcurrentHashMap<String, ProtocolStackConfigInfo>(16, 0.75f, 2);

   /**
    * Placeholder for stacks injected via {@link #setProtocolStackConfigurations(Map)}
    * until createService is called.
    */
   private Map<String,ProtocolStackConfigInfo> injectedStacks;
  
   /**
    * The MBeanServer to expose JMX management data with (no management data
    * will be available if null)
    */
   private MBeanServer server = null;

   /** To expose the channels and protocols */
   private String domain = DEFAULT_JMX_DOMAIN;
   private boolean domainSet = false;

   /** Whether or not to expose channels via JMX */
   private boolean expose_channels=true;

   /** Whether to expose the factory only, or all protocols as well */
   private boolean expose_protocols=true;

   /**
    * Creates a new JChannelFactory.
    */
   @SuppressWarnings("unchecked")
   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");
   }

   /**
    * Creates a channel by passing <code>properties</code> to the
    * <code>org.jgroups.JChannel</code> constructor.
    *
    * @param properties protocol stack configuration object; can be <code>null</code>
    *                   in which case a default stack will be used
    *
    * @return the channel
    */
   @Override
   public Channel createChannel(Object properties) throws ChannelException
   {
      checkStarted();

      if (properties == null)
         properties = JChannel.DEFAULT_PROTOCOL_STACK;

      ProtocolStackConfigurator config = null;

      try
      {
         ProtocolStackConfigurator c = ConfiguratorFactory.getStackConfigurator(properties);
         config = c;
      }
      catch (Exception x)
      {
         throw new ChannelException("unable to load protocol stack", x);
      }

      JChannel channel = initializeChannel(config, null, false);

      try
      {
         registerChannel(channel, null, null, ProtocolStackUtil.getProtocolData(config));
      }
      catch (ChannelException ce)
      {
         throw ce;
      }
      catch (Exception e)
      {
         throw new ChannelException("unable to register channel", e);
      }

      return channel;
   }

   /**
    * Create a {@link Channel} using the specified stack. Channel will use a
    * shared transport.
    *
    * @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.
    *
    * @return an implementation of Channel configured with a shared transport.
    *
    * @throws IllegalArgumentException if <code>stack_name</code> is
    * <code>null</code> or {@link #getConfig(String)} returns <code>null</code>
    * when <code>stack_name</code> is used.
    *
    * @throws Exception
    */
   @Override
   public Channel createChannel(String stack_name) throws Exception
   {
      return createChannelFromRegisteredStack(stack_name, null, false);
   }
  
   /**
    * 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 {@link #isExposeChannels()} returns <code>true</code>,
    *            in which case, if not <code>null</code>, is used as part of
    *            the <code>ObjectName</code> for the JMX mbeans that represent
    *            the channel and its protocols. Can be <code>null</code>.
    *           
    * @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
   {
      return createChannelFromRegisteredStack(stack_name, id, true);
  
  
   /**
    * 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);
   }
  
   /**
    * {@link #parse(Element) Parses <code>properties</code>} and then adds
    * the resulting protocol stack configurations to the set available for use.
    * Same as
    * {@link #setMultiplexerConfig(Element, boolean) <code>setMultiplexerConfig(properties, true</code>}.
    *
    * @param properties document root node for XML content in the JGroups
    *                   <code>stacks.xml</code> format
    */
   @Override
   public void setMultiplexerConfig(Element properties) throws Exception
   {
      setMultiplexerConfig(properties, true);
   }

   /**
    * {@link #parse(InputStream) Parses} an input stream created from
    * <code>properties</code> and then adds the resulting protocol stack
    * configurations to the set available for use. Same as
    * {@link #setMultiplexerConfig(File, boolean) <code>setMultiplexerConfig(properties, true</code>}.
    *
    * @param properties file which must contain XML content in the JGroups
    *              <code>stacks.xml</code> format
    */
   @Override
   public void setMultiplexerConfig(File properties) throws Exception
   {
      setMultiplexerConfig(properties, true);     
   }

   /**
    * {@link #parse(InputStream) Parses} an input stream created from
    * <code>properties</code> and then adds the resulting protocol stack
    * configurations to the set available for use. Same as
    * {@link #setMultiplexerConfig(Object, boolean) <code>setMultiplexerConfig(properties, true</code>}.
    *
    * @param properties object that can be {@link ConfiguratorFactory#getConfigStream(Object) converted into a stream}
    *                   which must contain XML content in the JGroups
    *                   <code>stacks.xml</code> format
    */
   @Override
   public void setMultiplexerConfig(Object properties) throws Exception
   {
      setMultiplexerConfig(properties, true);
   }

   /**
    * {@link #parse(InputStream) Parses} an input stream created from
    * <code>properties</code> and then adds the resulting protocol stack
    * configurations to the set available for use. Same as
    * {@link #setMultiplexerConfig(String, boolean) <code>setMultiplexerConfig(properties, true</code>}.
    *
    * @param properties string that can be {@link ConfiguratorFactory#getConfigStream(String) converted into a stream}
    *                   which must contain XML content in the JGroups
    *                   <code>stacks.xml</code> format
    */
   @Override
   public void setMultiplexerConfig(String properties) throws Exception
   {
      setMultiplexerConfig(properties, true);
   }

   /**
    * {@link #parse(InputStream) Parses} an input stream created from
    * <code>properties</code> and then adds the resulting protocol stack
    * configurations to the set available for use. Same as
    * {@link #setMultiplexerConfig(URL, boolean) <code>setMultiplexerConfig(properties, true</code>}.
    *
    * @param properties URL which must contain XML content in the JGroups
    *              <code>stacks.xml</code> format
    */
   @Override
   public void setMultiplexerConfig(URL properties) throws Exception
   {
      setMultiplexerConfig(properties, true);
   }

   // -------------------------------------------------------------  Properties

   /**
    * Gets the MBeanServer to use to register mbeans for channels and protocols
    * we create.
    *
    * @return the MBeanServer, or <code>null</code> if one isn't registered
    */
   public MBeanServer getServer()
   {
       return server;
   }

   /**
    * Sets the MBeanServer to use to register mbeans for channels and protocols
    * we create.
    *
    * @param server the MBeanServer. May be <code>null</code>
    */
   public void setServer(MBeanServer server)
   {
       this.server=server;
   }

   /**
    * Gets the domain portion of the JMX ObjectName to use when registering channels and protocols
    *
    * @return the domain. Will not return <code>null</code> after {@link #create()}
    *         has been invoked.
    */
   @Override
   public String getDomain()
   {
       return domain == null ? "jgroups" : domain;
   }
  
   @Override
   public void setDomain(String domain)
   {
      this.domain = domain;
      this.domainSet = true;
   }

   @Override
   public boolean isExposeChannels()
   {
       return expose_channels;
   }

   @Override
   public void setExposeChannels(boolean expose_channels)
   {
       this.expose_channels=expose_channels;
   }

   @Override
   public boolean isExposeProtocols()
   {
       return expose_protocols;
   }

   @Override
   public void setExposeProtocols(boolean expose_protocols)
   {     
       this.expose_protocols=expose_protocols;
       if (expose_protocols)
          this.expose_channels=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;
   }
  
   // -------------------------------------------------------------  Public

   /**
    * {@link #parse(Element) Parses <code>properties</code>} and then adds
    * the resulting protocol stack configurations to the set available for use.
    *
    * @param properties document root node for XML content in the JGroups
    *                   <code>stacks.xml</code> format
    * @param replace <code>true</code> if a configuration with the same
    *                stack name as an already registered configuration should
    *                replace that configuration; <code>false</code> if it
    *                should be discarded.
    */
   public void setMultiplexerConfig(Element properties, boolean replace) throws Exception
   {
      Map<String, ProtocolStackConfigInfo> map = ProtocolStackUtil.parse(properties);
     
      for (Map.Entry<String, ProtocolStackConfigInfo> entry : map.entrySet())
      {
         addConfig(entry.getKey(), entry.getValue(), replace);
      }
   }

   /**
    * {@link #parse(InputStream) Parses} an input stream created from
    * <code>properties</code> and then adds the resulting protocol stack
    * configurations to the set available for use.
    *
    * @param properties file which must contain XML content in the JGroups
    *              <code>stacks.xml</code> format
    * @param replace <code>true</code> if a configuration with the same
    *                stack name as an already registered configuration should
    *                replace that configuration; <code>false</code> if it
    *                should be discarded.
    */
   public void setMultiplexerConfig(File properties, boolean replace) throws Exception
   {
      InputStream input=ConfiguratorFactory.getConfigStream(properties);     
      addConfigs(input, properties.toString(), replace);
   }

   /**
    * {@link #parse(InputStream) Parses} an input stream created from
    * <code>properties</code> and then adds the resulting protocol stack
    * configurations to the set available for use.
    *
    * @param properties object that can be {@link ConfiguratorFactory#getConfigStream(Object) converted into a stream}
    *                   which must contain XML content in the JGroups
    *                   <code>stacks.xml</code> format
    * @param replace <code>true</code> if a configuration with the same
    *                stack name as an already registered configuration should
    *                replace that configuration; <code>false</code> if it
    *                should be discarded.
    */
   public void setMultiplexerConfig(Object properties, boolean replace) throws Exception
   {
      InputStream input=ConfiguratorFactory.getConfigStream(properties);
      addConfigs(input, properties.toString(), replace);  
   }

   /**
    * {@link #parse(InputStream) Parses} an input stream created from
    * <code>properties</code> and then adds the resulting protocol stack
    * configurations to the set available for use.
    *
    * @param properties string that can be {@link ConfiguratorFactory#getConfigStream(String) converted into a stream}
    *                   which must contain XML content in the JGroups
    *                   <code>stacks.xml</code> format
    * @param replace <code>true</code> if a configuration with the same
    *                stack name as an already registered configuration should
    *                replace that configuration; <code>false</code> if it
    *                should be discarded.
    */
   @Override
   public void setMultiplexerConfig(String properties, boolean replace) throws Exception
   {
      InputStream input=ConfiguratorFactory.getConfigStream(properties);     
      addConfigs(input, properties, replace);
   }

   /**
    * {@link #parse(InputStream) Parses} an input stream created from
    * <code>properties</code> and then adds the resulting protocol stack
    * configurations to the set available for use.
    *
    * @param properties URL which must contain XML content in the JGroups
    *              <code>stacks.xml</code> format
    * @param replace <code>true</code> if a configuration with the same
    *                stack name as an already registered configuration should
    *                replace that configuration; <code>false</code> if it
    *                should be discarded.
    */
   public void setMultiplexerConfig(URL url, boolean replace) throws Exception
   {
      InputStream input=ConfiguratorFactory.getConfigStream(url);     
      addConfigs(input, url.toString(), replace);
   }
  

   // --------------------------------------------------------  Management View
  
   /**
    * Gets information on channels created by this factory that are currently
    * open.
    */
   public Set<ChannelInfo> getOpenChannels()
   {
      return new HashSet<ChannelInfo>(registeredChannels.values());
   }
  
   public Map<String, ProtocolStackConfigInfo> getProtocolStackConfigurations()
   {
      return Collections.unmodifiableMap(stacks);
   }
  
   public void setProtocolStackConfigurations(Map<String, ProtocolStackConfigInfo> configs)
   {
      this.injectedStacks = configs;     
     
      if (state == STARTED)
      {
         // We're already running so this must be a ManagedComponent update
         // so apply immediately
         processInjectedStacks();
      }
   }

   // ---------------------------------------------------  JChannelFactoryMBean

   /**
    * {@inheritDoc}
    */
   @Override
   public void clearConfigurations()
   {
      this.stacks.clear();
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public String dumpChannels()
   {
      return "";
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public String dumpConfiguration()
   {
      return stacks.keySet().toString();
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public String getConfig(String stack_name) throws Exception
   {
      ProtocolStackConfigInfo cfg = stacks.get(stack_name);
      if (cfg == null)
         throw new Exception("stack \"" + stack_name + "\" not found in " + stacks.keySet());
      return cfg.getConfigurator().getProtocolStackString();
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public String getMultiplexerConfig()
   {
      StringBuilder sb = new StringBuilder();
      for (Map.Entry<String, ProtocolStackConfigInfo> entry : stacks.entrySet())
      {
         sb.append(entry.getKey()).append(": ").append(entry.getValue().getConfigurator().getProtocolStackString()).append("\n");
      }
      return sb.toString();
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public boolean removeConfig(String stack_name)
   {
      return stack_name != null && this.stacks.remove(stack_name) != null;
   }
  
   // -------------------------------------------------------------  Lifecycle

   /**
    * {@inheritDoc}
    * <p>
    * This method largely directly concerns itself with the {@link #getStateString() state}
    * field, delegating the real work to {@link #createService()}.
    * </p>
    */
   @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
      {
         createService();
         state = CREATED;
      }
      catch (Exception e)
      {
         log.debug("Initialization failed JChannelFactory", e);
         throw e;
      }
     
      log.debug("Created JChannelFactory");
   }

   /**
    * {@inheritDoc}
    * <p>
    * This method largely directly concerns itself with the {@link #getStateString() state}
    * field, delegating the real work to {@link #startService()}.
    * </p>
    */
   @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
      {
         startService();
      }
      catch (Exception e)
      {
         state = FAILED;
         log.debug("Starting failed JChannelFactory", e);
         throw e;
      }

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

   /**
    * {@inheritDoc}
    * <p>
    * This method largely directly concerns itself with the {@link #getStateString() state}
    * field, delegating the real work to {@link #stopService()}.
    * </p>
    */
   @Override
   public void stop()
   {
      if (state != STARTED)
      {
         log.debug("Ignoring stop call; current state is " + getStateString());
         return;
      }
     
      state = STOPPING;
      log.debug("Stopping JChannelFactory");

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

   /**
    * {@inheritDoc}
    * <p>
    * This method largely directly concerns itself with the {@link #getStateString() state}
    * field, delegating the real work to {@link #destroyService()}.
    * </p>
    */
   @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");
     
      try
      {        
         destroyService();
      }
      catch (Throwable t)
      {
         log.warn("Destroying failed JChannelFactory", t);
      }
      state = DESTROYED;
      log.debug("Destroyed JChannelFactory");
   }

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

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

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

   @Override
   public void preDeregister() throws Exception
   {
   }

   @Override
   public void postDeregister()
   {
      setServer(null);
      if (state == DESTROYED)
         state = UNREGISTERED;
   }

   // --------------------------------------------------------------- Protected
  
   /**
    * 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();
   }

   protected void createService() throws Exception
   {
      if(expose_channels)
      {
         if(server == null)
         {
            throw new Exception("No MBeanServer found; JChannelFactory needs to " +
                "be run with an MBeanServer present, or with ExposeChannels " +
                "set to false");
         }
        
         if(domain == null)
         {
            domain= DEFAULT_JMX_DOMAIN;
         }
      }
   }

   /**
    * The actual startup work.
    *
    * @throws Exception
    */
   protected void startService() throws Exception
   {
      // If the ProfileService injected stacks, process them now
      processInjectedStacks();
   }

   /**
    * The actual service stop work. This base implementation does nothing.
    *
    * @throws Exception
    */
   protected void stopService() throws Exception
   {
      // no-op
   }

   /**
    * The actual service destruction work.
    *
    */
   protected void destroyService()
   { 
      for (Channel ch : registeredChannels.keySet())
      {
         unregisterChannel(ch);
      }
   }

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


   private void checkStarted()
   {
      if (state != STARTED)
         throw new IllegalStateException("Cannot use factory; state is " + getStateString());
   }
  
   private void addConfigs(InputStream input, String source, boolean replace) throws Exception
   {
      if(input == null)
      {
         throw new FileNotFoundException(source);
      }
     
      Map<String, ProtocolStackConfigInfo> map = null;
      try
      {
          map = ProtocolStackUtil.parse(input);
      }
      catch(Exception ex)
      {
          throw new Exception("failed parsing " + source, ex);
      }
      finally
      {
          Util.close(input);
      }
     
      for (Map.Entry<String, ProtocolStackConfigInfo> entry : map.entrySet())
      {
         addConfig(entry.getKey(), entry.getValue(), replace);
      }
   }

   private boolean addConfig(String st_name, ProtocolStackConfigInfo val, boolean replace)
   {
      boolean added = replace;
      if (replace)
      {
         stacks.put(st_name, val);
         if (log.isTraceEnabled())
            log.trace("added config '" + st_name + "'");
      }
      else
      {
         if (!stacks.containsKey(st_name))
         {
            stacks.put(st_name, val);
            if (log.isTraceEnabled())
               log.trace("added config '" + st_name + "'");
            added = true;
         }
         else
         {
            if (log.isTraceEnabled())
               log.trace("didn't add config '" + st_name + " because one of the same name already existed");
         }
      }
      return added;     
   }
  
   private synchronized void processInjectedStacks()
   {
      if (injectedStacks != null)
      {
         clearConfigurations();
         stacks.putAll(injectedStacks);
         injectedStacks = null;
      }     
   }

   /**
    * Creates a channel from one of the known stack configurations.
    *
    * @param stack_name the name of the stack config
    * @param id optional id for the channel
    * @param forceSingletonStack <code>true</code> if a singleton_name must be
    *             either configured, or addMissingSingletonName must be true
    *            
    * @return the channel
    *
    * @throws IllegalArgumentException if stack_name is unknown
    * @throws IllegalStateException if forceSingletonStack is <code>true</code>
    *                               but a singleton_name couldn't be configured
    *          
    * @throws Exception
    */
   private Channel createChannelFromRegisteredStack(String stack_name, String id, boolean forceSingletonStack) throws Exception
   {
      checkStarted();
     
      ProtocolStackConfigInfo config = stacks.get(stack_name);
     
      if (config == null)
         throw new IllegalArgumentException("Unknown stack_name " + stack_name);
     
      JChannel channel = initializeChannel(config.getConfigurator(), stack_name, forceSingletonStack);
     
      registerChannel(channel, id, stack_name, ProtocolStackUtil.getProtocolData(config.getConfigurator()));     
     
      return channel;
   }

   /**
    * Construct a JChannel from the given config and then do post-construction
    * processing like fixing up thread managment or setting a unique id.
    *
    * @param config the config
    *
    * @return the channel
    *
    * @throws ChannelException
    */
   private JChannel initializeChannel(ProtocolStackConfigurator config, String stack_name,
         boolean forceSingletonStack) throws ChannelException
  
      Map<String, String> tpProps = getTransportProperties(config);
  
      if (!tpProps.containsKey(Global.SINGLETON_NAME))
      {
         if (addMissingSingletonName && stack_name != null)
         {
            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 if (forceSingletonStack)
         {
            throw new IllegalStateException("Config for " + stack_name + " does not include " +
                      "singleton_name and MuxChannels are not supported.");
         }
      }
      JChannel channel = new MuxChannel(config);
     
      if (manageNewThreadClassLoader || manageReleasedThreadClassLoader)
      {
         fixChannelThreadManagement(channel);
      }
     
      if (assignLogicalAddresses)
      {
         setChannelUniqueId(channel);
      }
     
      return channel;
   }
  
   /**
    * Gets the current runtime lifecycle state (e.g. CREATED, STARTED).
    */
   private String getStateString()
   {
      return states[state];
   }
  
   private void setChannelUniqueId(Channel channel)
   {
      String logical_address_name = channel.getName();
      if (logical_address_name == 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("Assigning unique node id " + nodeName + " to the channel");
        
         channel.setName(this.nodeName);       
      }   
   }

   private synchronized 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

      InetAddress address = 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();
      }
     
      String hostIP = getFastHostName(address);

      // 1st: is JNDI up and running?
      //
      if (namingServicePort > 0)
      {
         // we can proceed with the JNDI trick!
         return hostIP + ":" + namingServicePort;
      }

      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;
   }
  
   /**
    * Tries to determine the hostname of the given InetAddress without
    * triggering a reverse DNS lookup.  Tries to parse a symbolic hostname
    * from {@link InetAddress.toString()}, which is documented to return a
    * String of the form "symbolicname/ipaddress" with 'symbolicname' blank
    * if not stored in the object.
    * <p/>
    * If the symbolic name cannot be determined from InetAddress.toString(),
    * the value of {@link InetAddress.getHostAddress()} is returned.
    */
   private static String getFastHostName(InetAddress address)
   {
      String result = null;
     
      String hostAddress = address.getHostAddress();
     
      String inetAddr = address.toString();
      int idx = inetAddr.lastIndexOf('/');
      int idx1 = inetAddr.indexOf(hostAddress);
      if (idx1 == idx + 1)
      {
         if (idx == 0)
            result = hostAddress;
         else
            result = inetAddr.substring(0, idx);
      }
      else
      {
         // Doesn't follow the toString() contract!
         result = hostAddress;
      }
      return result;
   }
  
   private static InetAddress fixRemoteAddress(InetAddress address)
   {
      try
      {
         if (address == null
               || InetAddress.getByName(VALUE_BIND_ADDRESS_ANY).equals(address))
            return InetAddress.getLocalHost();
      }
      catch (UnknownHostException ignored)
      {
      }
      return address;
   }
  
   private Map<String, String> getTransportProperties(ProtocolStackConfigurator config)
   {
      Map<String, String> tpProps = null;
      ProtocolData[] protocols= ProtocolStackUtil.getProtocolData(config);
      ProtocolData transport=protocols[0];
      @SuppressWarnings("unchecked")
      Map<String,ProtocolParameter> tmp=transport.getParameters();
      tpProps = new HashMap<String,String>();
      for(Map.Entry<String,ProtocolParameter> entry: tmp.entrySet())
      {
          tpProps.put(entry.getKey(), entry.getValue().getValue());
      }
     
      return tpProps;
   }
 
   private ProtocolStackConfigurator addSingletonName(ProtocolStackConfigurator orig, String singletonName)
      throws ChannelException
   {
      ProtocolStackConfigurator result = null;
      try
      {
         ProtocolData[] protocols=orig.getProtocolStack();
         ProtocolData transport=protocols[0];
         ProtocolParameter singletonParam = new ProtocolParameter(Global.SINGLETON_NAME, singletonName);
         transport.override(new ProtocolParameter[]{ singletonParam});
         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)
   {
      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
    */
   void setDefaultThreadContextClassLoader(Thread thread, ClassLoader classLoader)
   {
      classLoaderSwitcher.setContextClassLoader(thread, classLoader);
   }
  
   private void registerChannel(JChannel ch, String channelId, String stackName, ProtocolData[] config) throws Exception
   {
      // Register for channel closed notification so we can unregister
      ch.addChannelListener(closeListener);
     
      ObjectName chName = null;
      List<ObjectName> protNames = null;
      List<ObjectName> allNames = registerInJmx(ch, channelId);
      if (allNames != null && allNames.size() > 0)
      {
         chName = allNames.get(0);
         if (allNames.size() > 1)
         {
            protNames = allNames.subList(1, allNames.size());
         }
      }
     
      ChannelInfo info = new ChannelInfo(channelId, stackName, ch, config, chName, protNames);
      registeredChannels.put(ch, info);
   }
  
   private List<ObjectName> registerInJmx(JChannel ch, String channelId) throws Exception
   {
      List<ObjectName> allNames = null;
     
      if(isExposeChannels() && getServer() != null && channelId != null && channelId.length() > 0)
      {
         allNames = new ArrayList<ObjectName>();
         ObjectName channelName = new ObjectName(getDomain() + ":type=channel,cluster=" + channelId);
         JmxConfigurator conf = new JmxConfigurator();
         getServer().registerMBean(conf.asDynamicMBean(ch), channelName);
         allNames.add(channelName);
         if (isExposeProtocols())
         {
            String baseName = getDomain() + ":type=protocol,cluster=" + channelId;
            ProtocolStack stack=ch.getProtocolStack();
            List<Protocol> protocols=stack.getProtocols();
           
            for(Protocol prot : protocols)
            {                               
                ObjectName prot_name=new ObjectName(baseName + ",protocol=" + prot.getName());
                server.registerMBean(conf.asDynamicMBean(prot), prot_name);
                allNames.add(prot_name);
            }
         }
      }
     
      return allNames;
   }
  
   private void unregisterChannel(Channel ch)
   {
      ChannelInfo info = registeredChannels.remove(ch);
      if (info == null)
      {
         log.warn("Unknown channel " + ch.getClusterName());
      }
      else
      {
         unregisterFromJmx(info);
      }
     
      ch.removeChannelListener(closeListener);
   }
  
   private void unregisterFromJmx(ChannelInfo info)
   {
      ObjectName oname = info.getChannelObjectName();
      MBeanServer mbs = getServer();
      if(info != null && mbs != null)
      {
         try
         {
            mbs.unregisterMBean(oname);
         }
         catch(Exception e)
         {
            log.error("failed unregistering " + oname, e);
         }
        
         List<ObjectName> onames = info.getProtocolObjectNames();
         if (onames != null)
         {
            for (ObjectName protName : onames)
            {
               try
               {
                  mbs.unregisterMBean(protName);
               }
               catch(Exception e)
               {
                  log.error("failed unregistering " + protName, e);
               }
              
            }
         }
      }
   }
  
   private class ThreadDecoratorImpl implements ThreadDecorator
   {
      private final ClassLoader classloader;
      private ThreadDecorator parent;
     
      ThreadDecoratorImpl(ClassLoader classloader)
      {
         this.classloader = classloader;
      }

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

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

      @SuppressWarnings("unused")
      public ThreadDecorator getParent()
      {
         return parent;
      }

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

   class ChannelCloseListener extends ChannelListenerAdapter
   {
      @SuppressWarnings("synthetic-access")
      @Override
      public void channelClosed(Channel channel)
      {
         unregisterChannel(channel);
      }           
   }
}
TOP

Related Classes of org.jboss.ha.core.channelfactory.JChannelFactory

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.