Package org.hornetq.core.cluster.impl

Source Code of org.hornetq.core.cluster.impl.DiscoveryGroupImpl

/*
* Copyright 2009 Red Hat, Inc.
* Red Hat licenses this file to you under the Apache License, version
* 2.0 (the "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*    http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.  See the License for the specific language governing
* permissions and limitations under the License.
*/

package org.hornetq.core.cluster.impl;

import java.io.InterruptedIOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.hornetq.api.core.HornetQBuffer;
import org.hornetq.api.core.HornetQBuffers;
import org.hornetq.api.core.Pair;
import org.hornetq.api.core.SimpleString;
import org.hornetq.api.core.TransportConfiguration;
import org.hornetq.api.core.management.NotificationType;
import org.hornetq.core.cluster.DiscoveryEntry;
import org.hornetq.core.cluster.DiscoveryGroup;
import org.hornetq.core.cluster.DiscoveryListener;
import org.hornetq.core.logging.Logger;
import org.hornetq.core.server.management.Notification;
import org.hornetq.core.server.management.NotificationService;
import org.hornetq.utils.TypedProperties;

/**
* A DiscoveryGroupImpl
*
* @author <a href="mailto:tim.fox@jboss.com">Tim Fox</a>
*
* Created 17 Nov 2008 13:21:45
*
*/
public class DiscoveryGroupImpl implements Runnable, DiscoveryGroup
{
   private static final Logger log = Logger.getLogger(DiscoveryGroupImpl.class);

   private static final int SOCKET_TIMEOUT = 500;

   private MulticastSocket socket;

   private final List<DiscoveryListener> listeners = new ArrayList<DiscoveryListener>();

   private final String name;

   private Thread thread;

   private boolean received;

   private final Object waitLock = new Object();

   private final Map<String, DiscoveryEntry> connectors = new HashMap<String, DiscoveryEntry>();

   private final long timeout;

   private volatile boolean started;

   private final String nodeID;
  
   private final InetAddress localBindAddress;

   private final InetAddress groupAddress;

   private final int groupPort;

   private final Map<String, String> uniqueIDMap = new HashMap<String, String>();

   private NotificationService notificationService;

   public DiscoveryGroupImpl(final String nodeID,
                             final String name,
                             final InetAddress localBindAddress,
                             final InetAddress groupAddress,
                             final int groupPort,
                             final long timeout) throws Exception
   {
      this.nodeID = nodeID;

      this.name = name;

      this.timeout = timeout;
     
      this.localBindAddress = localBindAddress;

      this.groupAddress = groupAddress;

      this.groupPort = groupPort;
   }

   public void setNotificationService(final NotificationService notificationService)
   {
      this.notificationService = notificationService;
   }

   public synchronized void start() throws Exception
   {
      if (started)
      {
         return;
      }

      socket = new MulticastSocket(groupPort);

      if (localBindAddress != null)
      {
         socket.setInterface(localBindAddress);
      }

      socket.joinGroup(groupAddress);

      socket.setSoTimeout(DiscoveryGroupImpl.SOCKET_TIMEOUT);

      started = true;

      thread = new Thread(this, "hornetq-discovery-group-thread-" + name);

      thread.setDaemon(true);

      thread.start();

      if (notificationService != null)
      {
         TypedProperties props = new TypedProperties();
        
         props.putSimpleStringProperty(new SimpleString("name"), new SimpleString(name));
        
         Notification notification = new Notification(nodeID, NotificationType.DISCOVERY_GROUP_STARTED, props);
        
         notificationService.sendNotification(notification);
      }
   }

   public void stop()
   {
      synchronized (this)
      {
         if (!started)
         {
            return;
         }

         started = false;
      }

      try
      {
         thread.join();
      }
      catch (InterruptedException e)
      {
      }

      socket.close();

      socket = null;

      thread = null;

      if (notificationService != null)
      {
         TypedProperties props = new TypedProperties();
         props.putSimpleStringProperty(new SimpleString("name"), new SimpleString(name));
         Notification notification = new Notification(nodeID, NotificationType.DISCOVERY_GROUP_STOPPED, props);
         try
         {
            notificationService.sendNotification(notification);
         }
         catch (Exception e)
         {
            DiscoveryGroupImpl.log.warn("unable to send notification when discovery group is stopped", e);
         }
      }
   }

   public boolean isStarted()
   {
      return started;
   }

   public String getName()
   {
      return name;
   }

   public synchronized Map<String, DiscoveryEntry> getDiscoveryEntryMap()
   {
      return new HashMap<String, DiscoveryEntry>(connectors);
   }

   public boolean waitForBroadcast(final long timeout)
   {
      synchronized (waitLock)
      {
         long start = System.currentTimeMillis();

         long toWait = timeout;

         while (!received && toWait > 0)
         {
            try
            {
               waitLock.wait(toWait);
            }
            catch (InterruptedException e)
            {
            }

            long now = System.currentTimeMillis();

            toWait -= now - start;

            start = now;
         }

         boolean ret = received;

         received = false;

         return ret;
      }
   }

   /*
    * This is a sanity check to catch any cases where two different nodes are broadcasting the same node id either
    * due to misconfiguration or problems in failover
    */
   private void checkUniqueID(final String originatingNodeID, final String uniqueID)
   {
      String currentUniqueID = uniqueIDMap.get(originatingNodeID);

      if (currentUniqueID == null)
      {
         uniqueIDMap.put(originatingNodeID, uniqueID);
      }
      else
      {
         if (!currentUniqueID.equals(uniqueID))
         {           
            log.warn("There are more than one servers on the network broadcasting the same node id. " +
                     "You will see this message exactly once (per node) if a node is restarted, in which case it can be safely " +
                     "ignored. But if it is logged continuously it means you really do have more than one node on the same network " +
                     "active concurrently with the same node id. This could occur if you have a backup node active at the same time as " +
                     "its live node.");
            uniqueIDMap.put(originatingNodeID, uniqueID);
         }
      }
   }

   public void run()
   {
      try
      {
         // TODO - can we use a smaller buffer size?
         final byte[] data = new byte[65535];

         while (true)
         {
            if (!started)
            {
               return;
            }

            final DatagramPacket packet = new DatagramPacket(data, data.length);

            try
            {
               socket.receive(packet);
            }
            catch (InterruptedIOException e)
            {
               if (!started)
               {
                  return;
               }
               else
               {
                  continue;
               }
            }

            HornetQBuffer buffer = HornetQBuffers.wrappedBuffer(data);

            String originatingNodeID = buffer.readString();

            String uniqueID = buffer.readString();

            checkUniqueID(originatingNodeID, uniqueID);
           
            if (nodeID.equals(originatingNodeID))
            {
               // Ignore traffic from own node
               continue;
            }

            int size = buffer.readInt();

            boolean changed = false;
           
            synchronized (this)
            {
               for (int i = 0; i < size; i++)
               {
                  TransportConfiguration connector = new TransportConfiguration();

                  connector.decode(buffer);

                  boolean existsBackup = buffer.readBoolean();

                  TransportConfiguration backupConnector = null;

                  if (existsBackup)
                  {
                     backupConnector = new TransportConfiguration();

                     backupConnector.decode(buffer);
                  }

                  Pair<TransportConfiguration, TransportConfiguration> connectorPair = new Pair<TransportConfiguration, TransportConfiguration>(connector,
                                                                                                                                                backupConnector);

                  DiscoveryEntry entry = new DiscoveryEntry(connectorPair, System.currentTimeMillis());

                  DiscoveryEntry oldVal = connectors.put(originatingNodeID, entry);

                  if (oldVal == null)
                  {
                     changed = true;
                  }
               }

               long now = System.currentTimeMillis();

               Iterator<Map.Entry<String, DiscoveryEntry>> iter = connectors.entrySet().iterator();

               // Weed out any expired connectors

               while (iter.hasNext())
               {
                  Map.Entry<String, DiscoveryEntry> entry = iter.next();

                  if (entry.getValue().getLastUpdate() + timeout <= now)
                  {
                     iter.remove();

                     changed = true;
                  }
               }
            }

            if (changed)
            {
               callListeners();
            }

            synchronized (waitLock)
            {
               received = true;

               waitLock.notify();
            }
         }
      }
      catch (Exception e)
      {
         DiscoveryGroupImpl.log.error("Failed to receive datagram", e);
      }
   }

   public synchronized void registerListener(final DiscoveryListener listener)
   {
      listeners.add(listener);

      if (!connectors.isEmpty())
      {
         listener.connectorsChanged();
      }
   }

   public synchronized void unregisterListener(final DiscoveryListener listener)
   {
      listeners.remove(listener);
   }

   private void callListeners()
   {
      for (DiscoveryListener listener : listeners)
      {
         try
         {
            listener.connectorsChanged();
         }
         catch (Throwable t)
         {
            // Catch it so exception doesn't prevent other listeners from running
            DiscoveryGroupImpl.log.error("Failed to call discovery listener", t);
         }
      }
   }

}
TOP

Related Classes of org.hornetq.core.cluster.impl.DiscoveryGroupImpl

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.