Package org.jboss.jms.server

Source Code of org.jboss.jms.server.ServerPeer$FailoverListener

/*
  * 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;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import javax.jms.InvalidClientIDException;
import javax.management.Attribute;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.transaction.xa.Xid;

import org.jboss.aop.AspectXmlLoader;
import org.jboss.jms.server.connectionfactory.ConnectionFactoryJNDIMapper;
import org.jboss.jms.server.connectionmanager.SimpleConnectionManager;
import org.jboss.jms.server.connectormanager.SimpleConnectorManager;
import org.jboss.jms.server.destination.ManagedQueue;
import org.jboss.jms.server.endpoint.ServerConnectionEndpoint;
import org.jboss.jms.server.endpoint.ServerSessionEndpoint;
import org.jboss.jms.server.messagecounter.MessageCounter;
import org.jboss.jms.server.messagecounter.MessageCounterManager;
import org.jboss.jms.server.plugin.contract.JMSUserManager;
import org.jboss.jms.server.remoting.JMSServerInvocationHandler;
import org.jboss.jms.server.remoting.JMSWireFormat;
import org.jboss.jms.server.security.SecurityMetadataStore;
import org.jboss.jms.util.ExceptionUtil;
import org.jboss.logging.Logger;
import org.jboss.messaging.core.Queue;
import org.jboss.messaging.core.memory.MemoryManager;
import org.jboss.messaging.core.memory.SimpleMemoryManager;
import org.jboss.messaging.core.message.SimpleMessageStore;
import org.jboss.messaging.core.plugin.IDManager;
import org.jboss.messaging.core.plugin.contract.ClusteredPostOffice;
import org.jboss.messaging.core.plugin.contract.MessageStore;
import org.jboss.messaging.core.plugin.contract.PersistenceManager;
import org.jboss.messaging.core.plugin.contract.PostOffice;
import org.jboss.messaging.core.plugin.contract.ReplicationListener;
import org.jboss.messaging.core.plugin.contract.Replicator;
import org.jboss.messaging.core.plugin.postoffice.Binding;
import org.jboss.messaging.core.plugin.postoffice.cluster.DefaultClusteredPostOffice;
import org.jboss.messaging.core.plugin.postoffice.cluster.FailoverStatus;
import org.jboss.messaging.core.tx.TransactionRepository;
import org.jboss.messaging.util.Util;
import org.jboss.mx.loading.UnifiedClassLoader3;
import org.jboss.remoting.ServerInvocationHandler;
import org.jboss.remoting.marshal.MarshalFactory;
import org.jboss.system.ServiceCreator;
import org.jboss.system.ServiceMBeanSupport;
import org.jboss.util.JBossStringBuilder;
import org.w3c.dom.Element;

import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;

/**
* A JMS server peer.
*
* @author <a href="mailto:ovidiu@jboss.org">Ovidiu Feodorov</a>
* @author <a href="mailto:tim.fox@jboss.com">Tim Fox</a>
* @author <a href="mailto:juha@jboss.org">Juha Lindfors</a>
*
* @version <tt>$Revision: 2485 $</tt>
*
* $Id: ServerPeer.java 2485 2007-02-28 02:30:33Z ovidiu.feodorov@jboss.com $
*/
public class ServerPeer extends ServiceMBeanSupport implements ServerPeerMBean
{
   // Constants ------------------------------------------------------------------------------------

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

   // The "subsystem" label this ServerPeer uses to register its ServerInvocationHandler with the
   // Remoting connector
   public static final String REMOTING_JMS_SUBSYSTEM = "JMS";

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

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

   private int serverPeerID;
   private byte[] clientAOPStack;
   private Version version;

   private String defaultQueueJNDIContext;
   private String defaultTopicJNDIContext;

   private boolean started;

   private int objectIDSequence = 1;

   // The default maximum number of delivery attempts before sending to DLQ - can be overridden on
   // the destination
   private int defaultMaxDeliveryAttempts = 10;

   private Object failoverStatusLock;
  
   // Default is 1 minute
   private long failoverStartTimeout = 60 * 1000;
  
   // Default is 5 minutes
   private long failoverCompleteTimeout = 5 * 60 * 1000;
  
   private Map sessions;
  
   private long defaultRedeliveryDelay;
  
   private long queueStatsSamplePeriod = 10000;
  
   private int defaultMessageCounterHistoryDayLimit;
     
   // wired components

   private DestinationJNDIMapper destinationJNDIMapper;
   private SecurityMetadataStore securityStore;
   private ConnectionFactoryJNDIMapper connFactoryJNDIMapper;
   private TransactionRepository txRepository;
   private ConnectionManager connectionManager;
   private ConnectorManager connectorManager;
   private IDManager messageIDManager;
   private IDManager channelIDManager;
   private IDManager transactionIDManager;
   private MemoryManager memoryManager; 
   private MessageStore messageStore;
   private MessageCounterManager messageCounterManager;

   // plugins

   protected ObjectName persistenceManagerObjectName;
   protected PersistenceManager persistenceManager;

   protected ObjectName postOfficeObjectName;
   protected PostOffice postOffice;

   protected ObjectName jmsUserManagerObjectName;
   protected JMSUserManager jmsUserManager;
  
   protected ObjectName defaultDLQObjectName;
   protected Queue defaultDLQ;
  
   protected ObjectName defaultExpiryQueueObjectName;
   protected Queue defaultExpiryQueue;

   //Other stuff

   private JMSServerInvocationHandler handler;

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

   public ServerPeer(int serverPeerID,
                     String defaultQueueJNDIContext,
                     String defaultTopicJNDIContext) throws Exception
   {
      if (serverPeerID < 0)
      {
         throw new IllegalArgumentException("ID cannot be negative");
      }
     
      log.info(this + " creating server peer with ID " + serverPeerID);

      this.serverPeerID = serverPeerID;
      this.defaultQueueJNDIContext = defaultQueueJNDIContext;
      this.defaultTopicJNDIContext = defaultTopicJNDIContext;

      // Some wired components need to be started here
      securityStore = new SecurityMetadataStore();

      version = Version.instance();
     
      failoverStatusLock = new Object();
     
      sessions = new ConcurrentReaderHashMap();

      started = false;
   }     

   // ServiceMBeanSupport overrides ----------------------------------------------------------------

   public synchronized void startService() throws Exception
   {
      try
      {
         log.debug("starting ServerPeer");

         if (started)
         {
            return;
         }

         log.debug(this + " starting");

         loadClientAOPConfig();

         loadServerAOPConfig();

         MBeanServer mbeanServer = getServer();

         // Acquire references to plugins. Each plug-in will be accessed directly via a reference
         // circumventing the MBeanServer. However, they are installed as services to take advantage
         // of their automatically-creating management interface.

         persistenceManager = (PersistenceManager)mbeanServer.
            getAttribute(persistenceManagerObjectName, "Instance");

         jmsUserManager = (JMSUserManager)mbeanServer.
            getAttribute(jmsUserManagerObjectName, "Instance");

         // We get references to some plugins lazily to avoid problems with circular MBean
         // dependencies

         // Create the wired components
         messageIDManager = new IDManager("MESSAGE_ID", 4096, persistenceManager);
         channelIDManager = new IDManager("CHANNEL_ID", 10, persistenceManager);
         transactionIDManager = new IDManager("TRANSACTION_ID", 1024, persistenceManager);
         destinationJNDIMapper = new DestinationJNDIMapper(this);
         connFactoryJNDIMapper = new ConnectionFactoryJNDIMapper(this);
         connectionManager = new SimpleConnectionManager();
         connectorManager = new SimpleConnectorManager();
         memoryManager = new SimpleMemoryManager();
         messageStore = new SimpleMessageStore();
         txRepository =
            new TransactionRepository(persistenceManager, messageStore, transactionIDManager);
         messageCounterManager = new MessageCounterManager(queueStatsSamplePeriod);

         // Start the wired components

         messageIDManager.start();
         channelIDManager.start();
         transactionIDManager.start();
         destinationJNDIMapper.start();
         connFactoryJNDIMapper.start();
         connectionManager.start();
         connectorManager.start();
         memoryManager.start();
         messageStore.start();
         securityStore.start();
         txRepository.start();
        
         // Note we do not start the message counter manager by default. This must be done
         // explicitly by the user by calling enableMessageCounters(). This is because message
         // counter history takes up growing memory to store the stats and could theoretically
         // eventually cause the server to run out of RAM
        
         txRepository.loadPreparedTransactions();
        
         initializeRemoting(mbeanServer);

         started = true;

         log.info("JBoss Messaging " + getVersion().getProviderVersion() + " server [" +
            getServerPeerID()+ "] started");
      }
      catch (Throwable t)
      {
         throw ExceptionUtil.handleJMXInvocation(t, this + " startService");
      }
   }

   public synchronized void stopService() throws Exception
   {
      try
      {
         if (!started)
         {
            return;
         }

         log.debug(this + " stopping");

         started = false;

         // Stop the wired components

         messageIDManager.stop();
         messageIDManager = null;
         channelIDManager.stop();
         channelIDManager = null;
         transactionIDManager.stop();
         transactionIDManager = null;
         destinationJNDIMapper.stop();
         destinationJNDIMapper = null;
         connFactoryJNDIMapper.stop();
         connFactoryJNDIMapper = null;
         connectionManager.stop();
         connectionManager = null;
         connectorManager.start();
         connectorManager = null;
         memoryManager.stop();
         memoryManager = null;
         messageStore.stop();
         messageStore = null;
         securityStore.stop();
         securityStore = null;
         txRepository.stop();
         txRepository = null;
         messageCounterManager.stop();
         messageCounterManager = null;

         unloadServerAOPConfig();

         // TODO unloadClientAOPConfig();

         MessagingTimeoutFactory.instance.reset();

         log.info("JMS " + this + " stopped");
      }
      catch (Throwable t)
      {
         throw ExceptionUtil.handleJMXInvocation(t, this + " stopService");
      }
   }

   // JMX Attributes -------------------------------------------------------------------------------

   // Plugins
  
   public synchronized ObjectName getPersistenceManager()
   {
      return persistenceManagerObjectName;
   }

   public synchronized void setPersistenceManager(ObjectName on)
   {
      if (started)
      {
         log.warn("Cannot set persistence manager on server peer when server peer is started");
         return;        
      }
      persistenceManagerObjectName = on;
   }

   public synchronized ObjectName getPostOffice()
   {
      return postOfficeObjectName;
   }

   public synchronized void setPostOffice(ObjectName on)
   {
      if (started)
      {
         log.warn("Cannot set post office on server peer when server peer is started");
         return;        
      }
      postOfficeObjectName = on;
   }

   public synchronized ObjectName getJmsUserManager()
   {
      return jmsUserManagerObjectName;
   }

   public synchronized void setJMSUserManager(ObjectName on)
   {
      if (started)
      {
         log.warn("Cannot set jms user manager on server peer when server peer is started");
         return;        
      }
      jmsUserManagerObjectName = on;
   }
  
   public synchronized ObjectName getDefaultDLQ()
   {
      return defaultDLQObjectName;
   }

   public synchronized void setDefaultDLQ(ObjectName on)
   {
      defaultDLQObjectName = on;
   }
  
   public synchronized ObjectName getDefaultExpiryQueue()
   {
      return defaultExpiryQueueObjectName;
   }

   public synchronized void setDefaultExpiryQueue(ObjectName on)
   {
      this.defaultExpiryQueueObjectName = on;
   }    
     
   // Instance access

   public Object getInstance()
   {
      return this;
   }
  
   //read only JMX attributes

   public String getJMSVersion()
   {
      return version.getJMSVersion();
   }

   public int getJMSMajorVersion()
   {
      return version.getJMSMajorVersion();
   }

   public int getJMSMinorVersion()
   {
      return version.getJMSMinorVersion();
   }

   public String getJMSProviderName()
   {
      return version.getJMSProviderName();
   }

   public String getProviderVersion()
   {
      return version.getProviderVersion();
   }

   public int getProviderMajorVersion()
   {
      return version.getProviderMajorVersion();
   }

   public int getProviderMinorVersion()
   {
      return version.getProviderMinorVersion();
   }

   public int getServerPeerID()
   {
      return serverPeerID;
   }

   public String getDefaultQueueJNDIContext()
   {
      return defaultQueueJNDIContext;
   }

   public String getDefaultTopicJNDIContext()
   {
      return defaultTopicJNDIContext;
   }
  
   //Read - write attributes

   public synchronized void setSecurityDomain(String securityDomain) throws Exception
   {
      try
      {
         securityStore.setSecurityDomain(securityDomain);
      }
      catch (Throwable t)
      {
         throw ExceptionUtil.handleJMXInvocation(t, this + " setSecurityDomain");
      }
   }

   public synchronized String getSecurityDomain()
   {
      return securityStore.getSecurityDomain();
   }

   public synchronized void setDefaultSecurityConfig(Element conf) throws Exception
   {
      securityStore.setDefaultSecurityConfig(conf);
   }

   public synchronized Element getDefaultSecurityConfig()
   {
      return securityStore.getDefaultSecurityConfig();
   }
       
   public synchronized long getFailoverStartTimeout()
   {
      return this.failoverStartTimeout;
   }
  
   public synchronized void setFailoverStartTimeout(long timeout)
   {
      this.failoverStartTimeout = timeout;
   }
  
   public synchronized long getFailoverCompleteTimeout()
   {
      return this.failoverCompleteTimeout;
   }
  
   public synchronized void setFailoverCompleteTimeout(long timeout)
   {
      this.failoverCompleteTimeout = timeout;
   }
  
   public synchronized int getDefaultMaxDeliveryAttempts()
   {
      return defaultMaxDeliveryAttempts;
   }

   public synchronized void setDefaultMaxDeliveryAttempts(int attempts)
   {
      this.defaultMaxDeliveryAttempts = attempts;
   }
  
   public synchronized long getQueueStatsSamplePeriod()
   {
      return queueStatsSamplePeriod;
   }

   public synchronized void setQueueStatsSamplePeriod(long newPeriod)
   {
      if (newPeriod < 1000)
      {
         throw new IllegalArgumentException("Cannot set QueueStatsSamplePeriod < 1000 ms");
      }
     
      if (messageCounterManager != null && newPeriod != queueStatsSamplePeriod)
      {
         messageCounterManager.reschedule(newPeriod);
      }           
     
      this.queueStatsSamplePeriod = newPeriod;
   }
  
   public synchronized long getDefaultRedeliveryDelay()
   {
      return defaultRedeliveryDelay;
   }
  
   public synchronized void setDefaultRedeliveryDelay(long delay)
   {
      this.defaultRedeliveryDelay = delay;
   }
  
   public synchronized int getDefaultMessageCounterHistoryDayLimit()
   {
      return defaultMessageCounterHistoryDayLimit;
   }
  
   public void setDefaultMessageCounterHistoryDayLimit(int limit)
   {
      if (limit < -1)
      {
         limit = -1;
      }
     
      this.defaultMessageCounterHistoryDayLimit = limit;
   }
  
   public void enableMessageCounters()
   {     
      messageCounterManager.start();
   }
  
   public void disableMessageCounters()
   {
      messageCounterManager.stop();
     
      messageCounterManager.resetAllCounters();
     
      messageCounterManager.resetAllCounterHistories();
   }
  
   // JMX Operations -------------------------------------------------------------------------------

   public String deployQueue(String name, String jndiName) throws Exception
   {
      try
      {
         return deployDestinationDefault(true, name, jndiName);
      }
      catch (Throwable t)
      {
         throw ExceptionUtil.handleJMXInvocation(t, this + " createQueue");
      }
   }

   public String deployQueue(String name, String jndiName, int fullSize, int pageSize, int downCacheSize) throws Exception
   {
      try
      {
         return deployDestination(true, name, jndiName, fullSize, pageSize, downCacheSize);
      }
      catch (Throwable t)
      {
         throw ExceptionUtil.handleJMXInvocation(t, this + " createQueue(2)");
      }
   }

   public boolean destroyQueue(String name) throws Exception
   {
      try
      {
         return destroyDestination(true, name);       
      }
      catch (Throwable t)
      {
         throw ExceptionUtil.handleJMXInvocation(t, this + " destroyQueue");
      }
   }
  
   public boolean undeployQueue(String name) throws Exception
   {
      try
      {
         return undeployDestination(true, name);
      }
      catch (Throwable t)
      {
         throw ExceptionUtil.handleJMXInvocation(t, this + " destroyQueue");
      }
   }

   public String deployTopic(String name, String jndiName) throws Exception
   {
      try
      {
         return deployDestinationDefault(false, name, jndiName);
      }
      catch (Throwable t)
      {
         throw ExceptionUtil.handleJMXInvocation(t, this + " createTopic");
      }
   }

   public String deployTopic(String name, String jndiName, int fullSize, int pageSize, int downCacheSize) throws Exception
   {
      try
      {
         return deployDestination(false, name, jndiName, fullSize, pageSize, downCacheSize);
      }
      catch (Throwable t)
      {
         throw ExceptionUtil.handleJMXInvocation(t, this + " createTopic(2)");
      }
   }

   public boolean destroyTopic(String name) throws Exception
   {
      try
      {
         return destroyDestination(false, name);
      }
      catch (Throwable t)
      {
         throw ExceptionUtil.handleJMXInvocation(t, this + " destroyTopic");
      }
   }
  
   public boolean undeployTopic(String name) throws Exception
   {
      try
      {
         return undeployDestination(false, name);
      }
      catch (Throwable t)
      {
         throw ExceptionUtil.handleJMXInvocation(t, this + " destroyTopic");
      }
   }

   public Set getDestinations() throws Exception
   {
      try
      {
         return destinationJNDIMapper.getDestinations();
      }
      catch (Throwable t)
      {
         throw ExceptionUtil.handleJMXInvocation(t, this + " getDestinations");
      }
   }
  
   public List getMessageCounters() throws Exception
   {
      Collection counters = messageCounterManager.getMessageCounters();
     
      return new ArrayList(counters);
   }

   public List getMessageStatistics() throws Exception
   {
      return MessageCounter.getMessageStatistics(getMessageCounters());
   }

   public String listMessageCountersAsHTML() throws Exception
   {
      List counters = getMessageCounters();

      String ret =
         "<table width=\"100%\" border=\"1\" cellpadding=\"1\" cellspacing=\"1\">"
            + "<tr>"
            + "<th>Type</th>"
            + "<th>Name</th>"
            + "<th>Subscription</th>"
            + "<th>Durable</th>"
            + "<th>Count</th>"
            + "<th>CountDelta</th>"
            + "<th>Depth</th>"
            + "<th>DepthDelta</th>"
            + "<th>Last Add</th>"
            + "</tr>";

      String strNameLast = null;
      String strTypeLast = null;
      String strDestLast = null;

      String destData = "";
      int destCount = 0;

      int countTotal = 0;
      int countDeltaTotal = 0;
      int depthTotal = 0;
      int depthDeltaTotal = 0;

      int i = 0; // define outside of for statement, so variable
      // still exists after for loop, because it is
      // needed during output of last module data string

      Iterator iter = counters.iterator();
     
      while (iter.hasNext())
      {
         MessageCounter counter = (MessageCounter)iter.next();
        
         // get counter data
         StringTokenizer tokens = new StringTokenizer(counter.getCounterAsString(), ",");

         String strType = tokens.nextToken();
         String strName = tokens.nextToken();
         String strSub = tokens.nextToken();
         String strDurable = tokens.nextToken();

         String strDest = strType + "-" + strName;

         String strCount = tokens.nextToken();
         String strCountDelta = tokens.nextToken();
         String strDepth = tokens.nextToken();
         String strDepthDelta = tokens.nextToken();
         String strDate = tokens.nextToken();

         // update total count / depth values
         countTotal += Integer.parseInt(strCount);
         depthTotal += Integer.parseInt(strDepth);

         countDeltaTotal += Integer.parseInt(strCountDelta);
         depthDeltaTotal += Integer.parseInt(strDepthDelta);

         if (strCountDelta.equalsIgnoreCase("0"))
            strCountDelta = "-"; // looks better

         if (strDepthDelta.equalsIgnoreCase("0"))
            strDepthDelta = "-"; // looks better

         // output destination counter data as HTML table row
         // ( for topics with multiple subscriptions output
         //   type + name field as rowspans, looks better )
         if (strDestLast != null && strDestLast.equals(strDest))
         {
            // still same destination -> append destination subscription data
            destData += "<tr bgcolor=\"#" + ((i % 2) == 0 ? "FFFFFF" : "F0F0F0") + "\">";
            destCount += 1;
         }
         else
         {
            // start new destination data
            if (strDestLast != null)
            {
               // store last destination data string
               ret += "<tr bgcolor=\"#"
                  + ((i % 2) == 0 ? "FFFFFF" : "F0F0F0")
                  + "\"><td rowspan=\""
                  + destCount
                  + "\">"
                  + strTypeLast
                  + "</td><td rowspan=\""
                  + destCount
                  + "\">"
                  + strNameLast
                  + "</td>"
                  + destData;

               destData = "";
            }

            destCount = 1;
         }

         // counter data row
         destData += "<td>"
            + strSub
            + "</td>"
            + "<td>"
            + strDurable
            + "</td>"
            + "<td>"
            + strCount
            + "</td>"
            + "<td>"
            + strCountDelta
            + "</td>"
            + "<td>"
            + strDepth
            + "</td>"
            + "<td>"
            + strDepthDelta
            + "</td>"
            + "<td>"
            + strDate
            + "</td>";

         // store current destination data for change detection
         strTypeLast = strType;
         strNameLast = strName;
         strDestLast = strDest;
      }

      if (strDestLast != null)
      {
         // store last module data string
         ret += "<tr bgcolor=\"#"
            + ((i % 2) == 0 ? "FFFFFF" : "F0F0F0")
            + "\"><td rowspan=\""
            + destCount
            + "\">"
            + strTypeLast
            + "</td><td rowspan=\""
            + destCount
            + "\">"
            + strNameLast
            + "</td>"
            + destData;
      }

      // append summation info
      ret += "<tr>"
         + "<td><![CDATA[ ]]></td><td><![CDATA[ ]]></td>"
         + "<td><![CDATA[ ]]></td><td><![CDATA[ ]]></td><td>"
         + countTotal
         + "</td><td>"
         + (countDeltaTotal == 0 ? "-" : Integer.toString(countDeltaTotal))
         + "</td><td>"
         + depthTotal
         + "</td><td>"
         + (depthDeltaTotal == 0 ? "-" : Integer.toString(depthDeltaTotal))
         + "</td><td>Total</td></tr></table>";

      return ret;
   }

   public void resetAllMessageCounters()
   {
      messageCounterManager.resetAllCounters();
   }
  
   public void resetAllMessageCounterHistories()
   {
      messageCounterManager.resetAllCounterHistories();
   }
  
   public List retrievePreparedTransactions()
   {
      return txRepository.getPreparedTransactions();
   }

   public String showPreparedTransactionsAsHTML()
   {
      List txs = txRepository.getPreparedTransactions();
      JBossStringBuilder buffer = new JBossStringBuilder();
      buffer.append("<table width=\"100%\" border=\"1\" cellpadding=\"1\" cellspacing=\"1\">");
      buffer.append("<tr><th>Xid</th></tr>");
      for (Iterator i = txs.iterator(); i.hasNext();)
      {
         Xid xid = (Xid)i.next();
         if (xid != null)
         {
            buffer.append("<tr><td>");
            buffer.append(xid);
            buffer.append("</td></tr>");
         }
      }
      buffer.append("</table>");
      return buffer.toString();
   }
  
   // Public ---------------------------------------------------------------------------------------
  
   public byte[] getClientAOPStack()
   {
      return clientAOPStack;
   }
  
   public MessageCounterManager getMessageCounterManager()
   {
      return messageCounterManager;
   }
  
   public IDManager getMessageIDManager()
   {
      return messageIDManager;
   }

   public IDManager getChannelIDManager()
   {
      return channelIDManager;
   }

   public ServerInvocationHandler getInvocationHandler()
   {
      return handler;
   }
  
   public ServerSessionEndpoint getSession(Integer sessionID)
   {
      return (ServerSessionEndpoint)sessions.get(sessionID);
   }
  
   public void addSession(Integer id, ServerSessionEndpoint session)
   {
      sessions.put(id, session);     
   }
  
   public void removeSession(Integer id)
   {
      if (sessions.remove(id) == null)
      {
         throw new IllegalStateException("Cannot find session with id " + id + " to remove");
      }
   }

   public synchronized Queue getDefaultDLQInstance() throws Exception
   {
      Queue dlq = null;
     
      if (defaultDLQObjectName != null)
      {
         ManagedQueue dest = null;
        
         try
         {        
            dest = (ManagedQueue)getServer().
               getAttribute(defaultDLQObjectName, "Instance");
         }
         catch (InstanceNotFoundException e)
         {
            //Ok
         }
        
         if (dest != null)
         {           
            PostOffice po = getPostOfficeInstance();
           
            Binding binding = po.getBindingForQueueName(dest.getName());
           
            if (binding != null && binding.getQueue().isActive())
            {
               dlq =  binding.getQueue();
            }
         }
      }
     
      return dlq;
   }
  
   public synchronized Queue getDefaultExpiryQueueInstance() throws Exception
   {
      Queue expiryQueue = null;
     
      if (defaultExpiryQueueObjectName != null)
      {
         ManagedQueue dest = null;
        
         try
         {        
            dest = (ManagedQueue)getServer().
               getAttribute(defaultExpiryQueueObjectName, "Instance");
         }
         catch (InstanceNotFoundException e)
         {
            //Ok
         }

         if (dest != null)
         {           
            PostOffice po = getPostOfficeInstance();
           
            Binding binding = po.getBindingForQueueName(dest.getName());
           
            if (binding != null && binding.getQueue().isActive())
            {
               expiryQueue =  binding.getQueue();
            }
         }
      }
     
      return expiryQueue;
   }

   public TransactionRepository getTxRepository()
   {
      return txRepository;
   }

   public synchronized boolean isStarted()
   {
      return started;
   }

   public Version getVersion()
   {
      return version;
   }

   // access to hard-wired server extensions

   public SecurityManager getSecurityManager()
   {
      return securityStore;
   }

   public DestinationManager getDestinationManager()
   {
      return destinationJNDIMapper;
   }

   public ConnectionFactoryManager getConnectionFactoryManager()
   {
      return connFactoryJNDIMapper;
   }

   public ConnectionManager getConnectionManager()
   {
      return connectionManager;
   }

   public ConnectorManager getConnectorManager()
   {
      return connectorManager;
   }

   public MessageStore getMessageStore()
   {
      return messageStore;
   }

   public MemoryManager getMemoryManager()
   {
      return memoryManager;
   }

   // access to plugin references

   public PersistenceManager getPersistenceManagerInstance()
   {
      return persistenceManager;
   }

   public JMSUserManager getJmsUserManagerInstance()
   {
      return jmsUserManager;
   }

   public PostOffice getPostOfficeInstance() throws Exception
   {
      // We get the reference lazily to avoid problems with MBean circular dependencies
      if (postOffice == null)
      {
         postOffice = (PostOffice)getServer().getAttribute(postOfficeObjectName, "Instance");

         // We also inject the replicator dependency into the ConnectionFactoryJNDIMapper. This is
         // a bit messy but we have a circular dependency POJOContainer should be able to help us
         // here. Yes, this is nasty.

         if (!postOffice.isLocal())
         {
            Replicator rep = (Replicator)postOffice;
            connFactoryJNDIMapper.injectReplicator(rep);           
            rep.registerListener(new FailoverListener());
         }
        
         // Also need to inject into txRepository
         txRepository.injectPostOffice(postOffice);
      }
      return postOffice;
   }

   public Replicator getReplicator() throws Exception
   {
      PostOffice postOffice = getPostOfficeInstance();
      if (!(postOffice instanceof Replicator))
      {
         throw new  IllegalAccessException("This operations is only legal on clustering configurations");
      }
      return (Replicator)postOffice;
   }

   public synchronized int getNextObjectID()
   {
      return objectIDSequence++;
   }

   /*
    * Wait for failover from the specified node to complete.
    */
   public int waitForFailover(int failedNodeID) throws Exception
   {
      // This node may be failing over for another node - in which case we must wait for that to be
      // complete.
     
      log.debug(this + " waiting for server-side failover for failed node " + failedNodeID + " to complete");
     
      Replicator replicator = getReplicator();

      long startToWait = getFailoverStartTimeout();
      long completeToWait = getFailoverCompleteTimeout();
                    
      // Must lock here
      synchronized (failoverStatusLock)
      {        
         while (true)
         {        
            //TODO we shouldn't have a dependency on DefaultClusteredPostOffice - where should we put the constants?

            Map replicants = replicator.get(DefaultClusteredPostOffice.FAILED_OVER_FOR_KEY);
           
            boolean foundEntry = false;
                       
            if (replicants != null)
            {
               for(Iterator i = replicants.entrySet().iterator(); i.hasNext(); )
               {
                  Map.Entry entry = (Map.Entry)i.next();
                  Integer nid = (Integer)entry.getKey();
                  FailoverStatus status = (FailoverStatus)entry.getValue();
                 
                  if (status.isFailedOverForNode(failedNodeID))
                  {
                     log.debug(this + ": failover is complete on node " + nid);
                     return nid.intValue();
                  }
                  else if (status.isFailingOverForNode(failedNodeID))
                  {
                     log.debug(this + ": fail over is in progress on node " + nid);
                    
                     // A server has started failing over for the failed node, but not completed.
                     // If it's not this node then we immediately return so the connection can be
                     // redirected to another node.
                     if (nid.intValue() != this.getServerPeerID())
                     {
                        return nid.intValue();
                     }
                    
                     // Otherwise we wait for failover to complete
                    
                     if (completeToWait <= 0)
                     {
                        // Give up now
                        log.debug(this + " already waited long enough for failover to complete, giving up");
                        return -1;
                     }
                    
                     // Note - we have to count the time since other unrelated nodes may fail and
                     // wake up the lock - in this case we don't want to give up too early.
                     long start = System.currentTimeMillis();

                     try
                     {
                        log.debug(this + " blocking on the failover lock, waiting for failover to complete");
                        failoverStatusLock.wait(completeToWait);
                        log.debug(this + " releasing the failover lock, checking again whether failover completed ...");
                     }
                     catch (InterruptedException ignore)
                     {                 
                     }
                     completeToWait -= System.currentTimeMillis() - start;
                     foundEntry = true;
                  }
               }       
            }
           
            if (!foundEntry)
            {             
               // No trace of failover happening so we wait a maximum of FAILOVER_START_TIMEOUT for
               // some replicated data to arrive. This should arrive fairly quickly since this is
               // added at the beginning of the failover process. If it doesn't arrive it would
               // imply that no failover has actually happened on the server or the timeout is too
               // short. It is possible that no failover has actually happened on the server, if for
               // example there is a problem with the client side network but the server side
               // network is ok.
  
               if (startToWait <= 0)
               {
                  // Don't want to wait again
                  log.debug(this + " already waited long enough for failover to start, giving up");
                  return -1;
               }
              
               // Note - we have to count the time since other unrelated nodes may fail and wake
               // up the lock - in this case we don't want to give up too early.
               long start = System.currentTimeMillis();
               try
               {
                  log.debug(this + " blocking on the failover lock, waiting for failover to start");
                  failoverStatusLock.wait(startToWait);
                  log.debug(this + " releasing the failover lock, checking again whether failover started ...");
               }
               catch (InterruptedException ignore)
               {                 
               }
               startToWait -= System.currentTimeMillis() - start;             
            }
         }       
      }
   }
  
   public void checkClientID(String clientID) throws Exception
   {  
      // verify the clientID is unique
  
      // JMS 1.1 Specifications, Section 4.3.2:
      // "By definition, the client state identified by a client identifier can be 'in use' by
      // only one client at a time. A JMS provider must prevent concurrently executing clients
      // from using it."
     
      if (clientID != null)
      {           
         List conns = connectionManager.getActiveConnections();
     
         for(Iterator i = conns.iterator(); i.hasNext(); )
         {
            ServerConnectionEndpoint sce = (ServerConnectionEndpoint)i.next();
            if (clientID != null && clientID.equals(sce.getClientID()))
            {
               throw new InvalidClientIDException(
                  "Client ID '" + clientID + "' already used by " + sce);
            }
         }
      }
   }
  
   public String toString()
   {
      return "ServerPeer[" + getServerPeerID() + "]";
   }

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

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

   // Private --------------------------------------------------------------------------------------
 
   private void initializeRemoting(MBeanServer mbeanServer) throws Exception
   {
      JMSWireFormat wf = new JMSWireFormat();

      MarshalFactory.addMarshaller("jms", wf, wf);

      handler = new JMSServerInvocationHandler();
   }

   private void loadServerAOPConfig() throws Exception
   {
      URL url = this.getClass().getClassLoader().getResource("aop-messaging-server.xml");
      AspectXmlLoader.deployXML(url);
   }

   private void unloadServerAOPConfig() throws Exception
   {
      URL url = this.getClass().getClassLoader().getResource("aop-messaging-server.xml");
      AspectXmlLoader.undeployXML(url);
   }

   private void loadClientAOPConfig() throws Exception
   {
      // Note the file is called aop-messaging-client.xml NOT messaging-client-aop.xml. This is
      // because the JBoss will automatically deploy any files ending with aop.xml; we do not want
      // this to happen for the client config

      URL url = this.getClass().getClassLoader().getResource("aop-messaging-client.xml");
      InputStream is = null;
      ByteArrayOutputStream os = new ByteArrayOutputStream();
      try
      {
         is = url.openStream();
         int b;
         while ((b = is.read()) != -1)
         {
            os.write(b);
         }
         os.flush();
         clientAOPStack = os.toByteArray();
      }
      finally
      {
         if (is != null)
         {
            is.close();
         }
         if (os != null)
         {
            os.close();
         }
      }
   }

   private String deployDestinationDefault(boolean isQueue, String name, String jndiName)
      throws Exception
   {
      //
      // TODO - THIS IS A TEMPORARY IMPLEMENTATION; WILL BE REPLACED WITH INTEGRATION-CONSISTENT ONE
      // TODO - if I find a way not using UnifiedClassLoader3 directly, then get rid of
      //        <path refid="jboss.jmx.classpath"/> from jms/build.xml dependentmodule.classpath
      //

      //TODO - Yes this is super-ugly - there must be an easier way of doing it
      //also in LocalTestServer is doing the same thing in a slightly different way
      //this should be combined

      String destType = isQueue ? "Queue" : "Topic";
      String className = "org.jboss.jms.server.destination." + destType + "Service";
      String ons ="jboss.messaging.destination:service="+ destType + ",name=" + name;
      ObjectName on = new ObjectName(ons);

      String destinationMBeanConfig =
         "<mbean code=\"" + className + "\" " +
         "       name=\"" + ons + "\" " +
         "       xmbean-dd=\"xmdesc/" + destType + "-xmbean.xml\">\n" +
         "    <constructor>" +
         "        <arg type=\"boolean\" value=\"true\"/>" +
         "    </constructor>" +
         "</mbean>";

      return deployDestinationInternal(destinationMBeanConfig, on, jndiName, false, -1, -1, -1);
   }
  
   private String deployDestination(boolean isQueue, String name, String jndiName, int fullSize,
            int pageSize, int downCacheSize) throws Exception
   {
      //   
      //    TODO - THIS IS A TEMPORARY IMPLEMENTATION; WILL BE REPLACED WITH INTEGRATION-CONSISTENT ONE
      //    TODO - if I find a way not using UnifiedClassLoader3 directly, then get rid of
      //    <path refid="jboss.jmx.classpath"/> from jms/build.xml dependentmodule.classpath
      //   
     
      String destType = isQueue ? "Queue" : "Topic";
      String className = "org.jboss.jms.server.destination." + destType + "Service";
     
      String ons ="jboss.messaging.destination:service="+ destType + ",name=" + name;
      ObjectName on = new ObjectName(ons);
     
      String destinationMBeanConfig =
         "<mbean code=\"" + className + "\" " +
         "       name=\"" + ons + "\" " +
         "       xmbean-dd=\"xmdesc/" + destType + "-xmbean.xml\">\n" +
         "    <constructor>" +
         "        <arg type=\"boolean\" value=\"true\"/>" +
         "    </constructor>" +
         "    <attribute name=\"FullSize\">" + fullSize + "</attribute>" +
         "    <attribute name=\"PageSize\">" + pageSize + "</attribute>" +
         "    <attribute name=\"DownCacheSize\">" + downCacheSize + "</attribute>" +
         "</mbean>";
     
      return deployDestinationInternal(destinationMBeanConfig, on, jndiName, true, fullSize,
               pageSize, downCacheSize);
   }

   private String deployDestinationInternal(String destinationMBeanConfig, ObjectName on,
                                            String jndiName, boolean params, int fullSize,
                                            int pageSize, int downCacheSize) throws Exception
   {
      MBeanServer mbeanServer = getServer();

      Element element = Util.stringToElement(destinationMBeanConfig);

      ServiceCreator sc = new ServiceCreator(mbeanServer);

      ClassLoader cl = this.getClass().getClassLoader();
      ObjectName loaderObjectName = null;
      if (cl instanceof UnifiedClassLoader3)
      {
         loaderObjectName = ((UnifiedClassLoader3)cl).getObjectName();
      }

      sc.install(on, loaderObjectName, element);

      // inject dependencies
      mbeanServer.setAttribute(on, new Attribute("ServerPeer", getServiceName()));
      mbeanServer.setAttribute(on, new Attribute("JNDIName", jndiName));
      if (params)
      {
         mbeanServer.setAttribute(on, new Attribute("FullSize", new Integer(fullSize)));
         mbeanServer.setAttribute(on, new Attribute("PageSize", new Integer(pageSize)));
         mbeanServer.setAttribute(on, new Attribute("DownCacheSize", new Integer(downCacheSize)));
      }
      mbeanServer.invoke(on, "create", new Object[0], new String[0]);
      mbeanServer.invoke(on, "start", new Object[0], new String[0]);

      return (String)mbeanServer.getAttribute(on, "JNDIName");

      //
      // end of TODO
      //
   }

  

   /*
    * Undeploy the MBean but don't delete the underlying data
    */
   private boolean undeployDestination(boolean isQueue, String name) throws Exception
   {
      String destType = isQueue ? "Queue" : "Topic";
      String ons ="jboss.messaging.destination:service=" + destType + ",name=" + name;
      ObjectName on = new ObjectName(ons);

      MBeanServer mbeanServer = getServer();

      // we can only undeploy destinations that exist AND that have been created programatically
      if (!mbeanServer.isRegistered(on))
      {
         return false;
      }
      Boolean b = (Boolean)mbeanServer.getAttribute(on, "CreatedProgrammatically");
      if (!b.booleanValue())
      {
         log.warn("Cannot undeploy a destination that has not been created programatically");
         return false;
      }
      mbeanServer.invoke(on, "stop", new Object[0], new String[0]);
      mbeanServer.invoke(on, "destroy", new Object[0], new String[0]);
      mbeanServer.unregisterMBean(on);
      return true;
   }
  
   /*
    * Undeploy the MBean and delete the underlying data
    */
   private boolean destroyDestination(boolean isQueue, String name) throws Exception
   {
      String destType = isQueue ? "Queue" : "Topic";
      String ons ="jboss.messaging.destination:service=" + destType + ",name=" + name;
      ObjectName on = new ObjectName(ons);

      MBeanServer mbeanServer = getServer();

      // we can only destroy destinations that exist AND that have been created programatically
      if (!mbeanServer.isRegistered(on))
      {
         return false;
      }
                 
      //First deactivate
     
      if (isQueue)
      {
         Binding binding = postOffice.getBindingForQueueName(name);
        
         if (binding != null)
         {
            binding.getQueue().deactivate();
         }
      }
      else
      {
         JMSCondition topicCond = new JMSCondition(false, name);   
        
         Collection bindings = postOffice.getBindingsForCondition(topicCond);
        
         Iterator iter = bindings.iterator();
         while (iter.hasNext())           
         {
            Binding binding = (Binding)iter.next();
           
            binding.getQueue().deactivate();
         }
      }
           
      //Delete any message data
     
      mbeanServer.invoke(on, "removeAllMessages", null, null);
     
      //undeploy the mbean
      if (!undeployDestination(isQueue, name))
      {
         return false;
      }
           
      //Unbind from the post office
     
      if (isQueue)
      {
         Binding binding = postOffice.getBindingForQueueName(name);
        
         if (binding != null)
         {
            try
            {
               Queue queue = binding.getQueue();
               if (!queue.isClustered())
               {
                  postOffice.unbindQueue(queue.getName());
               }
               else
               {
                  ((ClusteredPostOffice)postOffice).unbindClusteredQueue(queue.getName());
               }
            }
            catch (Throwable t)
            {
               throw new Exception("Failed to unbind queue", t);
            }
         }
      }
      else
      {
         JMSCondition topicCond = new JMSCondition(false, name);   
        
         Collection bindings = postOffice.getBindingsForCondition(topicCond);
        
         Iterator iter = bindings.iterator();
         while (iter.hasNext())           
         {
            Binding binding = (Binding)iter.next();
           
            try
            {
               postOffice.unbindQueue(binding.getQueue().getName());
            }
            catch (Throwable t)
            {
               throw new Exception("Failed to unbind queue", t);
            }
         }
      }
      return true;
   }
  

   // Inner classes --------------------------------------------------------------------------------
  
   private class FailoverListener implements ReplicationListener
   {
      public void onReplicationChange(Serializable key, Map updatedReplicantMap,
                                      boolean added, int originatingNodeId)
      {
         if (key.equals(DefaultClusteredPostOffice.FAILED_OVER_FOR_KEY))
         {
            if (updatedReplicantMap != null && originatingNodeId == serverPeerID)
            {
               FailoverStatus status =
                  (FailoverStatus)updatedReplicantMap.get(new Integer(serverPeerID));
              
               if (status != null && status.isFailedOver())
               {                    
                  // We prompt txRepository to load any prepared txs - so we can take over
                  // responsibility for in doubt transactions from other nodes
                  try
                  {
                     txRepository.loadPreparedTransactions();
                  }
                  catch (Exception e)
                  {
                     log.error("Failed to load prepared transactions", e);
                  }
               }
            }
           
            synchronized (failoverStatusLock)
            {
               log.debug(ServerPeer.this +
                  ".FailoverListener got failover event, notifying those waiting on lock");
              
               failoverStatusLock.notifyAll();
            }
         }        
      }     
   }
}
TOP

Related Classes of org.jboss.jms.server.ServerPeer$FailoverListener

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.