Package org.jboss.jms.server.connectionmanager

Source Code of org.jboss.jms.server.connectionmanager.SimpleConnectionManager

/*
  * JBoss, Home of Professional Open Source
  * Copyright 2005, JBoss Inc., and individual contributors as indicated
  * by the @authors tag. See the copyright.txt 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.jms.server.connectionmanager;

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 org.jboss.aop.AspectManager;
import org.jboss.jms.delegate.ConnectionEndpoint;
import org.jboss.jms.server.ConnectionManager;
import org.jboss.jms.server.endpoint.ServerConnectionEndpoint;
import org.jboss.jms.server.endpoint.ServerConnectionFactoryEndpoint;
import org.jboss.jms.server.endpoint.advised.ConnectionAdvised;
import org.jboss.logging.Logger;
import org.jboss.messaging.core.contract.ClusterNotification;
import org.jboss.messaging.core.contract.ClusterNotificationListener;
import org.jboss.messaging.core.contract.Replicator;
import org.jboss.messaging.util.ConcurrentHashSet;
import org.jboss.messaging.util.Util;
import org.jboss.remoting.Client;
import org.jboss.remoting.ClientDisconnectedException;
import org.jboss.remoting.ConnectionListener;
import org.jboss.remoting.callback.InvokerCallbackHandler;
import org.jboss.remoting.callback.ServerInvokerCallbackHandler;

/**
* @author <a href="tim.fox@jboss.com">Tim Fox</a>
* @author <a href="ovidiu@feodorov.com">Ovidiu Feodorov</a>
* @version <tt>$Revision: 6823 $</tt>
*
* $Id: SimpleConnectionManager.java 6823 2009-05-18 07:17:01Z gaohoward $
*/
public class SimpleConnectionManager implements ConnectionManager, ConnectionListener, ClusterNotificationListener
{
   // Constants ------------------------------------------------------------------------------------

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

   // Static ---------------------------------------------------------------------------------------

   private static boolean trace = log.isTraceEnabled();

   // Attributes -----------------------------------------------------------------------------------

   private Map</** VMID */String, Map</** RemoteSessionID */String, ConnectionEndpoint>> jmsClients;
  
   private Map</* VMID */String, Map</* remoting session id */String, InvokerCallbackHandler>> cfHandlers;

   // Map<remotingClientSessionID<String> - jmsClientVMID<String>
   private Map<String, String> remotingSessions;

   // Set<ConnectionEndpoint>
   private Set<ConnectionEndpoint> activeConnectionEndpoints;

   private Replicator replicator;
  
   private Set<ServerConnectionFactoryEndpoint> connectionFactories = new ConcurrentHashSet<ServerConnectionFactoryEndpoint>();
  

   // Constructors ---------------------------------------------------------------------------------

   public SimpleConnectionManager()
   {
      jmsClients = new HashMap<String, Map<String, ConnectionEndpoint>>();
     
      remotingSessions = new HashMap<String, String>();
     
      cfHandlers = new HashMap<String, Map<String, InvokerCallbackHandler>>();
           
      activeConnectionEndpoints = new HashSet<ConnectionEndpoint>();
   }

   // ConnectionManager implementation -------------------------------------------------------------
  
   public synchronized void registerConnection(String jmsClientVMID,
                                               String remotingClientSessionID,
                                               ConnectionEndpoint endpoint)
   {   
      Map<String, ConnectionEndpoint> endpoints = jmsClients.get(jmsClientVMID);
     
      if (endpoints == null)
      {
         endpoints = new HashMap<String, ConnectionEndpoint>();
        
         jmsClients.put(jmsClientVMID, endpoints);
      }
     
      endpoints.put(remotingClientSessionID, endpoint);
     
      remotingSessions.put(remotingClientSessionID, jmsClientVMID);

      activeConnectionEndpoints.add(endpoint);
     
      log.debug("registered connection " + endpoint + " as " +
                Util.guidToString(remotingClientSessionID));
   }
  
   public synchronized void registerConnectionFactoryCallback(String JVMID,
                                                               String remotingSessionID,
                                                               InvokerCallbackHandler handler)
   {
      Map<String, InvokerCallbackHandler> handlers = cfHandlers.get(JVMID);
     
      if (handlers == null)
      {
         handlers = new HashMap<String, InvokerCallbackHandler>();
        
         cfHandlers.put(JVMID, handlers);
      }
     
      handlers.put(remotingSessionID, handler);
     
      remotingSessions.put(remotingSessionID, JVMID);

      log.debug("registered cf callback handler " + handler + " as " +
                Util.guidToString(remotingSessionID));
   }

   public synchronized ConnectionEndpoint unregisterConnection(String jmsClientVMId,
                                                               String remotingClientSessionID)
   {
      Map<String, ConnectionEndpoint> endpoints = this.jmsClients.get(jmsClientVMId);
     
      if (endpoints != null)
      {
         ConnectionEndpoint e = endpoints.remove(remotingClientSessionID);

         if (e != null)
         {
            activeConnectionEndpoints.remove(e);
         }

         log.debug("unregistered connection " + e + " with remoting session ID " +
               Util.guidToString(remotingClientSessionID));
        
         if (endpoints.isEmpty())
         {
            jmsClients.remove(jmsClientVMId);
         }
        
         remotingSessions.remove(remotingClientSessionID);
        
         return e;
      }
     
      return null;
   }
  
   public synchronized void unregisterConnectionFactoryCallback(String JVMID, String remotingSessionID)
   {
      Map<String, InvokerCallbackHandler> handlers = cfHandlers.get(JVMID);
     
      if (handlers != null)
      {
         handlers.remove(remotingSessionID);
        
         if (handlers.isEmpty())
         {
            cfHandlers.remove(JVMID);
         }
        
         remotingSessions.remove(remotingSessionID);
      }
   }

   public synchronized List getActiveConnections()
   {
      // I will make a copy to avoid ConcurrentModification
      List<ConnectionEndpoint> list = new ArrayList<ConnectionEndpoint>();
      list.addAll(activeConnectionEndpoints);
      return list;
   }
     
   public synchronized void handleClientFailure(String remotingSessionID)
   {
      cleanupForSessionID(remotingSessionID);
   }
  
   public void registerConnectionFactory(ServerConnectionFactoryEndpoint cf)
   {
      connectionFactories.add(cf);
   }
     
   public void unregisterConnectionFactory(ServerConnectionFactoryEndpoint cf)
   {
      connectionFactories.remove(cf);
   }
  
   // ConnectionListener implementation ------------------------------------------------------------

   /**
    * Be aware that ConnectionNotifier uses to call this method with null Throwables.
    *
    * @param t - plan for it to be null!
    */
   public void handleConnectionException(Throwable t, Client client)
   { 
      if (t instanceof ClientDisconnectedException)
      {
         if (log.isTraceEnabled())
         {
            log.trace("Connection is closed normally: " + client);
         }
      }
      else
      {
         if (log.isTraceEnabled())
         {
            log.trace("Connection is closed on failure event: " + client);
         }
      }
      String remotingSessionID = client.getSessionId();

      if (remotingSessionID != null)
      {
         handleClientFailure(remotingSessionID);
      }
   }
    
   // ClusterNotificationListener implementation ---------------------------------------------------

   /**
    * Closing connections that are coming from a failed node
    * @param notification
    */
   public void notify(ClusterNotification notification)
   { 
      if (notification.type == ClusterNotification.TYPE_NODE_LEAVE)
      {

         log.trace("SimpleConnectionManager was notified about node leaving from node " +
                    notification.nodeID);
        
         try
         {
            //We remove any consumers with the same JVMID as the node that just failed
            //This will remove any message suckers from a failed node
            //This is important to workaround a remoting bug where sending messages down a broken connection
            //can cause a deadlock with the bisocket transport
           
            //Get the jvm id for the failed node
           
            Map ids = replicator.get(Replicator.JVM_ID_KEY);
           
            if (ids == null)
            {
               log.trace("Cannot find jvmid map");
               throw new IllegalStateException("Cannot find jvmid map");
            }
           
            int failedNodeID = notification.nodeID;
           
            String clientVMID = (String)ids.get(new Integer(failedNodeID));
              
            if (clientVMID == null)
            {
               log.error("Cannot find ClientVMID for failed node " + failedNodeID);
               throw new IllegalStateException("Cannot find clientVMID for failed node " + failedNodeID);
            }
           
            //Close the consumers corresponding to that VM

            log.trace("Closing consumers for clientVMID=" + clientVMID);

            if (clientVMID != null)
            {
               cleanupForVMID(clientVMID);
            }
         }
         catch (Exception e)
         {
            log.error("Failed to process failover start", e);
         }
      }    
   }
  
   // MessagingComponent implementation ------------------------------------------------------------
  
   public void start() throws Exception
   {
      //NOOP
   }
  
   public void stop() throws Exception
   {
      //NOOP
   }

   // Public ---------------------------------------------------------------------------------------

   /*
    * Used in testing only
    */
   public synchronized boolean containsRemotingSession(String remotingClientSessionID)
   {
      return remotingSessions.containsKey(remotingClientSessionID);
   }

   /*
    * Used in testing only
    */
   public synchronized Map getClients()
   {
      return Collections.unmodifiableMap(jmsClients);
   }
  
   public void injectReplicator(Replicator replicator)
   {
      this.replicator = replicator;
   }


   public String toString()
   {
      return "ConnectionManager[" + Integer.toHexString(hashCode()) + "]";
   }

   // Package protected ----------------------------------------------------------------------------

   // Protected ------------------------------------------------------------------------------------

   // Private --------------------------------------------------------------------------------------
 
   private synchronized void cleanupForVMID(String jmsClientID)
   {
      Map<String, ConnectionEndpoint> endpoints = jmsClients.get(jmsClientID);

      if (endpoints != null)
      {
         //Copy to prevent ConcurrentModificationException
         List<ConnectionEndpoint> sces = new ArrayList<ConnectionEndpoint>(endpoints.values());

         // Now close the end points - this will result in a callback into unregisterConnection
         // to remove the data from the jmsClients and sessions maps.

         for(ConnectionEndpoint sce: sces )
         {
            log.debug("clearing up state for connection " + sce);

            // sce could also be a mock test.. so this test is required
            if (sce instanceof ServerConnectionEndpoint)
            {
               //Remoting is dumb and doesn't clean up it's state after itself - so we have to do it.
               ((ServerConnectionEndpoint)sce).closeCallbackClient();
            }
            // wrap the Endpoint in the ConnectionAdvised so that we get a proper call back for the close
            // TODO:  What we really need to do is to wrap the End point in registerConnection, when we
            // get the connection.  But, I'm hesitant, because there are are many of instanceof checks that would
            // fail if I wrapped it during registerConneciton.
            // Need to synchronized to prevent a deadlock
            // See http://jira.jboss.com/jira/browse/JBMESSAGING-797
            synchronized (AspectManager.instance())
            {      
               sce = new ConnectionAdvised(sce);
            }
            try
            {
               sce.closing(-1);
            }
            catch (Throwable ignore)
            {                
            }
            try
            {
               sce.close();
            }
            catch (Throwable ignore)
            {                
            }
            log.debug("cleared up state for connection " + sce);     
         }
      }
     
      Map<String, InvokerCallbackHandler> handlers = cfHandlers.remove(jmsClientID);
           
      if (handlers != null)
      {       
         Map<String, InvokerCallbackHandler> handlersClone = new HashMap<String, InvokerCallbackHandler>(handlers);
                 
         for (Map.Entry<String, InvokerCallbackHandler> entry: handlersClone.entrySet())
         {
            try
            {
               ((ServerInvokerCallbackHandler)entry.getValue()).getCallbackClient().disconnect();
            }
            catch (Throwable ignore)
            {
            }

            try
            {
               ((ServerInvokerCallbackHandler)entry.getValue()).shutdown();
            }
            catch (Throwable ignore)
            {
            }          
           
            for (ServerConnectionFactoryEndpoint ep: connectionFactories)
            {
               ep.removeCallbackhandler((ServerInvokerCallbackHandler)entry.getValue());
            }
           
            unregisterConnectionFactoryCallback(jmsClientID, entry.getKey());
         }
      }
   }
        
   private void cleanupForSessionID(String jmsSessionID)
   {     
      String jmsClientID = remotingSessions.get(jmsSessionID);
     
      log.trace("A problem has been detected " +
                    "with the connection to remote client " +
                     jmsSessionID + ", jmsClientID=" + jmsClientID + ". It is possible the client has exited without closing " +
                  "its connection(s) or the network has failed. All associated connection resources will be cleaned up.");
     
      if (jmsClientID != null)
      {       
         Map<String, ConnectionEndpoint> endpoints = jmsClients.get(jmsClientID);
  
         if (endpoints != null)
         {
            ConnectionEndpoint conn = null;
        
            for (Map.Entry<String, ConnectionEndpoint> entry: endpoints.entrySet())
            {
               if (entry.getKey().equals(jmsSessionID))
               {  
                  conn = entry.getValue();
                 
                  break;
               }
            }
           
            if (conn != null)
            {
              
              
               // sce could also be a mock test.. so this test is required
               if (conn instanceof ServerConnectionEndpoint)
               {
                  //Remoting is dumb and doesn't clean up it's state after itself - so we have to do it.
                  ((ServerConnectionEndpoint)conn).closeCallbackClient();
               }
               // wrap the Endpoint in the ConnectionAdvised so that we get a proper call back for the close
               // TODO:  What we really need to do is to wrap the Endpoint in registerConnection, when we
               // get the connection.  Then anyone
               // Need to synchronized to prevent a deadlock
               // See http://jira.jboss.com/jira/browse/JBMESSAGING-797
               synchronized (AspectManager.instance())
               {      
                  conn = new ConnectionAdvised(conn);
               }
               try
               {
                  conn.closing(-1);
               }
               catch (Throwable ignore)
               {             
               }
               try
               {
                  conn.close();
               }
               catch (Throwable ignore)
               {             
               }
               return;
            }
         }
      }

      Map<String, InvokerCallbackHandler> handlers = cfHandlers.get(jmsClientID);
     
      if (handlers != null)
      {       
         boolean found = false;
        
         for (Map.Entry<String, InvokerCallbackHandler> entry: handlers.entrySet())
         {
            if (entry.getKey().equals(jmsSessionID))
            {                            
               try
               {
                  ((ServerInvokerCallbackHandler)entry.getValue()).getCallbackClient().disconnect();
               }
               catch (Throwable ignore)
               {
               }
  
               try
               {
                  ((ServerInvokerCallbackHandler)entry.getValue()).shutdown();
               }
               catch (Throwable ignore)
               {
               }          
              
               for (ServerConnectionFactoryEndpoint ep: connectionFactories)
               {
                  ep.removeCallbackhandler(entry.getValue());
               }
              
               found = true;
              
               break;
            }
         }
        
         if (found)
         {
            unregisterConnectionFactoryCallback(jmsClientID, jmsSessionID);
         }
      }

   }

   // Inner classes --------------------------------------------------------------------------------

}
TOP

Related Classes of org.jboss.jms.server.connectionmanager.SimpleConnectionManager

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.