Java tutorial
/* * Copyright (C) 2008 Yohan Liyanage. * * Licensed 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.nebulaframework.discovery.multicast; import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.MulticastSocket; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.util.Arrays; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nebulaframework.grid.Grid; import org.nebulaframework.grid.cluster.manager.ClusterManager; import org.nebulaframework.util.net.NetUtils; /** * Implementation of Multicast Discovery. This class provides necessary * mechanisms to discover clusters using multicast messages. All * ClusterManager's will start a multicast discovery service which listens * to multicast requests for a specific IP Address. When such a message * is received, it will respond on a designated response channel (another * multicast IP Address). * * @author Yohan Liyanage * @version 1.0 */ public class MulticastDiscovery { private static Log log = LogFactory.getLog(MulticastDiscovery.class); /** Multicast Service Port */ public static final int SERVICE_PORT = 8787; /** Request Channel IP Address */ public static final InetAddress SERVICE_REQUEST_IP; /** Response Channel IP Address*/ public static final InetAddress SERVICE_RESPONSE_IP; /** Default Greeting Message */ public static final String GREET_MSG = "ALOHA"; /** Multicast Discovery Timout */ public static final long TIMEOUT = 10000L; private String cluster = null; /** * Static initilization to resolve Multicast * service IP Address. */ static { try { SERVICE_REQUEST_IP = InetAddress.getByName("230.0.0.1"); SERVICE_RESPONSE_IP = InetAddress.getByName("230.0.0.2"); } catch (UnknownHostException e) { // Should not happen throw new AssertionError(e); } } /** * Private Constructor. No External Instantiation. */ private MulticastDiscovery() { // No External Instantiation } /** * Starts Multicast Discovery Service. This can only * be invoked by a Nebula ClusterManager. * * @throws IOException if occurred during operation * @throws UnsupportedOperationException if invoked by non-ClusterManager nodes. */ public static void startService() throws IOException, UnsupportedOperationException { // Only allowed for ClusterManagers if (!Grid.isClusterManager()) { throw new UnsupportedOperationException( "Multicast Discovery Service can be enabled only for ClusterManagers"); } // Start Service Thread t = new Thread(new Runnable() { public void run() { try { // Start Multicast Socket and listen for Requests final MulticastSocket mSock = new MulticastSocket(SERVICE_PORT); mSock.joinGroup(SERVICE_REQUEST_IP); // Infinite Loop while (true) { // Buffer (for Greeting Message) byte[] msg = new byte[GREET_MSG.getBytes("UTF-8").length]; // Create Datagram Packet DatagramPacket packet = new DatagramPacket(msg, msg.length); // Wait and Receive Request mSock.receive(packet); log.debug("[MulticastDiscovery] Received Discovery Request"); // Check if Greeting Message is valid try { String greet = new String(packet.getData()); if (!greet.equals(GREET_MSG)) { throw new IllegalArgumentException("Invalid Greeting"); } } catch (Exception e) { log.debug("Malformed Multicast Message Igonored"); continue; } // Respond doRespond(); } } catch (IOException e) { log.error("[MulticastDiscovery] Service Failed on Receive", e); } } }); t.setDaemon(true); // Run as Daemon thread t.start(); // Start Service log.debug("[MulticastDiscovery] Service Started"); } /** * Responds to a Multicast Discovery Request by publishing * the IP Address of Service into response channel. */ protected static void doRespond() { // Only allowed for ClusterManagers if (!Grid.isClusterManager()) { throw new UnsupportedOperationException( "Multicast Discovery Service can be enabled only for ClusterManagers"); } try { // Get Broker Service URL String serviceUrl = ClusterManager.getInstance().getClusterInfo().getServiceUrl(); byte[] hostInfo = NetUtils.getHostInfoAsBytes(serviceUrl); // Create Response Packet DatagramPacket response = new DatagramPacket(hostInfo, hostInfo.length, SERVICE_RESPONSE_IP, SERVICE_PORT); // Create Multicast Socket MulticastSocket resSock = new MulticastSocket(); // Send response resSock.send(response); log.debug("[MulticastDiscovery] Responded Discovery Request"); } catch (Exception e) { log.error("[MulticastDiscovery] Service Failed to Reply", e); } } /** * Invoked by GridNodes to detect Clusters with in multicast * reachable network range. The discovery process returns * without results if no cluster was discovered with in * a fixed duration, set by {@link #TIMEOUT}. * * @return IP Address and Port of detected Cluster (String) */ public static String discoverCluster() { log.debug("[MulticastDiscovery] Attempting to discover cluster"); // Synchronization Mutex final Object mutex = new Object(); // Create Instance of MulticastDiscovery final MulticastDiscovery mDisc = new MulticastDiscovery(); // Execute on a separate Thread new Thread(new Runnable() { public void run() { try { // Attempt Discovery mDisc.doDiscover(); } catch (IOException e) { log.debug("[MulticastDiscovery] Failed to Discover", e); } // Notify waiting Threads synchronized (mutex) { mutex.notifyAll(); } } }).start(); // Wait till response come or timeout happens synchronized (mutex) { try { mutex.wait(TIMEOUT); } catch (InterruptedException e) { // Should not happen throw new AssertionError(e); } } if (mDisc.cluster != null) { log.info("[MulticastDiscovery] Discovered Cluster at " + mDisc.cluster); } return mDisc.cluster; } /** * Discovery Process to identify Clusters with in * network. * * @throws IOException if occurred during operation */ private void doDiscover() throws IOException { // Send Request byte[] greet = GREET_MSG.getBytes("UTF-8"); DatagramPacket request = new DatagramPacket(greet, greet.length, SERVICE_REQUEST_IP, SERVICE_PORT); MulticastSocket reqSock = new MulticastSocket(); reqSock.send(request); // Wait for Response MulticastSocket resSock = new MulticastSocket(SERVICE_PORT); resSock.joinGroup(SERVICE_RESPONSE_IP); // 9 = # of bytes for an IP Address + 5 byte port DatagramPacket response = new DatagramPacket(new byte[9], 9); // Receive resSock.setSoTimeout((int) TIMEOUT); try { resSock.receive(response); } catch (SocketTimeoutException e) { log.debug("[MulticastDiscovery] Receive Timeout"); return; } byte[] data = response.getData(); byte[] ipBytes = Arrays.copyOfRange(data, 0, 4); byte[] portBytes = Arrays.copyOfRange(data, 4, 9); InetAddress ip = InetAddress.getByAddress(ipBytes); StringBuilder sb = new StringBuilder(ip.getHostAddress()); sb.append(":"); for (byte b : portBytes) { sb.append(b); } this.cluster = sb.toString(); } /** * Attempts to discover peer clusters using Multicast * Discovery. Each discovered Cluster will be notified * to the {@code PeerClusterService} of the * ClusterManager. * * @throws IOException if occurred during process */ public static void discoverPeerClusters() throws IOException { // Only allowed for ClusterManagers if (!Grid.isClusterManager()) { throw new UnsupportedOperationException( "Multicast Discovery Service can be enabled only for ClusterManagers"); } log.info("[MulticastDiscovery] Discovering Peer Clusters..."); // Send Request byte[] greet = GREET_MSG.getBytes("UTF-8"); DatagramPacket request = new DatagramPacket(greet, greet.length, SERVICE_REQUEST_IP, SERVICE_PORT); MulticastSocket reqSock = new MulticastSocket(); reqSock.send(request); // Response Socket MulticastSocket resSock = new MulticastSocket(SERVICE_PORT); resSock.joinGroup(SERVICE_RESPONSE_IP); // 9 = # of bytes for an IP Address + 5 byte port DatagramPacket response = new DatagramPacket(new byte[9], 9); // Set Socket Timeout resSock.setSoTimeout((int) TIMEOUT); try { // Loop until Socket Timeout Occurs while (true) { // Receive resSock.receive(response); processPeerResponse(response.getData()); } } catch (SocketTimeoutException e) { log.debug("[MulticastDiscovery] Receive Timeout"); return; } finally { log.info("[MulticastDiscovery] Peer Cluster Discovery Complete"); } } /** * Support routine which processes a response received for a discovery * request for peer clusters. Each invocation will be handled * in a separate thread. * * @param data response data */ private static void processPeerResponse(final byte[] data) { new Thread(new Runnable() { @Override public void run() { try { byte[] ipBytes = Arrays.copyOfRange(data, 0, 4); byte[] portBytes = Arrays.copyOfRange(data, 4, 9); InetAddress ip = null; ip = InetAddress.getByAddress(ipBytes); StringBuilder sb = new StringBuilder(ip.getHostAddress()); sb.append(":"); for (byte b : portBytes) { sb.append(b); } // Add Peer Cluster ClusterManager.getInstance().getPeerService().addCluster(sb.toString()); } catch (Exception e) { log.warn("[Discovery] Unable to resolve peer"); } } }).start(); } }