package jeffaschenk.commons.frameworks.cnxidx.resiliency.jgroups; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.InetAddress; import java.net.DatagramPacket; import java.net.MulticastSocket; import java.net.NetworkInterface; import java.net.UnknownHostException; import java.util.Enumeration; import java.util.LinkedList; import java.util.Properties; import java.util.Vector; import jeffaschenk.commons.frameworks.cnxidx.utility.ldap.idxCMDReturnCodes; import jeffaschenk.commons.frameworks.cnxidx.resiliency.ldap.IRRChangeLogRestoreService; import jeffaschenk.commons.frameworks.cnxidx.shell.Shell; import org.jgroups.util.Util; /** * Multicast Network Verification Utility allows an Instance to perform * Sending, Receiving or Discovering Members of the Multicast Group. * <p/> * These facilities have been adapted from the Test Utilities found in the * JGroups Library, these are: * org.jgroups.tests.McastDiscovery1_4 * org.jgroups.tests.McastReceiverTest1_4 * org.jgroups.tests.McastSenderTest1_4 * <p/> * Bascially all of the information for driving the Verification Utility will * come from the Service Properties, which should have been set up prior * to using this utility. * * @author jeff.schenk * @version 4.4 $Revision * Developed 2006 */ public class McastNetworkVerificationUtility extends Shell implements idxCMDReturnCodes { // ******************************* // Common Logging Facility. public static final String CLASSNAME = McastNetworkVerificationUtility.class.getName(); // ************************************** // BackEnd Information public static final String BACKEND = "MULTICAST"; // ******************************** // Default IP Addresses. public static final String IPV4_LOOPBACK_ADDRESS = "127.0.0.1"; public static final String IPV6_LOOPBACK_ADDRESS = "0:0:0:0:0:0:0:1"; public static final String IPV6_UNSPECIFIED_ADDRESS = "0:0:0:0:0:0:0:0"; public static final String IPV6_SHORT_LOOPBACK_ADDRESS = "::1"; public static final String IPV6_SHORT_UNSPECIFIED_ADDRESS = "::"; // ******************************** // Default Shell Prompt public static final String shell_name = "idxmcast"; public static final String shell_prompt = shell_name + "> "; public static final String discover_shell_prompt = shell_name + " Discover> "; public static final String receive_shell_prompt = shell_name + " Receive> "; public static final String send_shell_prompt = shell_name + " Send> "; // ******************************** // Static Values. public static final String ALL = "All"; // ******************************** // Global Fields. public static final String DEFAULT_MULTICAST_ADDRESS = "224.0.0.35"; private String MULTICAST_ADDRESS = null; public static final int DEFAULT_MULTICAST_PORT = 45566; private String MULTICAST_PORT = null; public static final String DEFAULT_BIND_ADDRESS = ALL; private String BIND_ADDRESS = null; private boolean PROPERTIES_NOT_FOUND = false; private boolean PROERTIES_FAILED_TO_LOAD = false; // ***************************** // Time to Live public static final int DEFAULT_TTL = 32; private int TTL = DEFAULT_TTL; // ***************************** // Discovery Internal. // in Milliseconds. public static final long DEFAULT_DISCOVERY_INTERVAL = 2000; private long DISCOVERY_INTERVAL = 2000; /** * Default Constructor. */ public McastNetworkVerificationUtility() { super(false); super.setBackEnd(McastNetworkVerificationUtility.BACKEND); super.setPrompt(McastNetworkVerificationUtility.shell_prompt); } // End of Constructor. /** * Constructor, providing all necessary input fields * to begin a verification process. */ public McastNetworkVerificationUtility(String MULTICAST_ADDRESS, String MULTICAST_PORT, boolean PROPERTIES_NOT_FOUND, boolean PROERTIES_FAILED_TO_LOAD) { super(false); super.setBackEnd(McastNetworkVerificationUtility.BACKEND); super.setPrompt(McastNetworkVerificationUtility.shell_prompt); // ********************** // Save our defaults. this.MULTICAST_ADDRESS = MULTICAST_ADDRESS; this.MULTICAST_PORT = MULTICAST_PORT; this.PROPERTIES_NOT_FOUND = PROPERTIES_NOT_FOUND; this.PROERTIES_FAILED_TO_LOAD = PROERTIES_FAILED_TO_LOAD; } // End of Constructor. /** * Command Line Interactive Mode Shell. */ public int CMDprocess(LinkedList _cmdargs) throws InterruptedException, IOException { // ******************** // Do we have any // Command at All? if (_cmdargs.size() < 1) { return (0); } // ******************** // Check for Command String COMMAND = (String) _cmdargs.get(0); if (COMMAND.equalsIgnoreCase("discover")) { return (CMDdiscover()); } if (COMMAND.equalsIgnoreCase("receive")) { return (CMDreceive()); } if (COMMAND.equalsIgnoreCase("send")) { return (CMDsend()); } if (COMMAND.equalsIgnoreCase("set")) { return (CMDset(_cmdargs)); } if (COMMAND.equalsIgnoreCase("show")) { return (CMDshow()); } if (COMMAND.equalsIgnoreCase("showver")) { return (CMDshowver()); } if (COMMAND.equalsIgnoreCase("echo")) { return (CMDecho(_cmdargs)); } // ******************************* // Check for a Help/usage Command. if ((COMMAND.equalsIgnoreCase("help")) || (COMMAND.equalsIgnoreCase("usage")) || (COMMAND.startsWith("?"))) { return (CMDusage()); } // *************************** // Check for an Exit Command. if ((COMMAND.equalsIgnoreCase("exit")) || (COMMAND.equalsIgnoreCase("end")) || (COMMAND.equalsIgnoreCase("quit"))) { return (SHELL_RETURN_CODE_EXIT); } // *************************** // If we fell through then we // have an invalid command. displayMsg("Specified Command '" + COMMAND + "', is Invalid."); displayMsg("Please use 'help' for Commands and Usage."); // ******************** // Return return (0); } // End of CMDprocess Method. // ************************************************************** // Commands // ************************************************************** /** * usage command to simply display valid commands. */ public int CMDusage() { // ********************** // Show Usage. displayMsg("FRAMEWORK Data Resiliency Multicast Network Verification Utility"); displayMsg("Usage: " + CLASSNAME); displayMsg("\tAvailable Functions:"); displayMsg("\tdiscover - Discover Multicast Members in Group."); displayMsg("\treceive - Receive Data from Multicast Members in Group."); displayMsg("\tsend - Send Data To Multicast Members in Group."); displayMsg("\tset mcastaddr - Sets Multicast Address."); displayMsg("\tset mcastport - Sets Multicast Port."); displayMsg("\tset bindaddr - Sets Local Network Interface to use or All."); displayMsg("\tset ttl - Sets Time to Live."); displayMsg("\tset interval - Sets Send or Discovery Interval."); displayMsg("\tshow - Shows Current Settings for Multicast address and Port."); displayMsg("\tshowver - Shows JGroups Version in current CLASSPATH."); // ********************** // Return return (0); } // End of usage Command. /** * echo command to simply echo back information to the requestor. */ public int CMDecho(LinkedList _cmdargs) { String echoMsg = ""; for (int i = 1; i < _cmdargs.size(); i++) { if ((echoMsg == null) || (echoMsg.equalsIgnoreCase(""))) { echoMsg = (String) _cmdargs.get(i); } else { echoMsg = echoMsg + " " + (String) _cmdargs.get(i); } } // End of For Loop. // ********************** // Perform the Echo. displayMsg(echoMsg); // ********************** // Return return (0); } // End of echo Command. /** * Show Current Multicast Settings. */ public int CMDshow() { // ***************************************** // Show our current Settings. displayMsg(" MULTICAST ADDRESS:[" + this.MULTICAST_ADDRESS + "], " + " MULTICAST PORT:[" + this.MULTICAST_PORT + "], " + " LOCAL BIND ADDRESS:[" + this.showLocalBindAddr() + "]."); // ********************** // Return return (0); } // End of show Command. /** * Show JGroups Version. */ public int CMDshowver() { // ***************************************** // Show the JGroups Version Information Tag. displayMsg(org.jgroups.Version.printVersion()); // ********************** // Return return (0); } // End of showver Command. /** * Set command to set various parameters for running utilities. */ public int CMDset(LinkedList _cmdargs) { String echoMsg = ""; for (int i = 1; i < _cmdargs.size(); i++) { if ((echoMsg == null) || (echoMsg.equalsIgnoreCase(""))) { echoMsg = (String) _cmdargs.get(i); } else { echoMsg = echoMsg + " " + (String) _cmdargs.get(i); } } // End of For Loop. // ********************** // Perform the Echo. displayMsg(echoMsg); // ********************** // Return return (0); } // End of set Command. /** * Discover. */ public int CMDdiscover() { // ************************************** // Set our new Prompt. super.setPrompt(discover_shell_prompt); // ****************************** // Instantiate the Discovery thread. try { // ***************************** // Show Initial Message for User. displayMsg("Do you wish to begin Discovery of the specified MultiCast Group? (Y|N) "); if (super.CMDPrompt()) { // ***************************** // Start the Discovery. new McastDiscovery( InetAddress.getByName(this.MULTICAST_ADDRESS), Integer.parseInt(this.MULTICAST_PORT), DISCOVERY_INTERVAL, TTL).start(); } // End of Check to Proceed. } catch (Exception ex) { ex.printStackTrace(); } // End of Exception Processing. // *************************** // Always ensure we have // the right prompt. super.setPrompt(McastNetworkVerificationUtility.shell_prompt); // ********************** // Show the End. displayMsg("End of Interactive Discover Session."); // ********************** // Return return (0); } // End of discover Command. /** * Receive. */ public int CMDreceive() { // ***************************************** // Array of Receiver Threads. McastAckReceiverThread[] ack_receiver = null; // ********************************************* // Initialize Local Variables for Send Function. MulticastSocket[] sockets = null; InetAddress bind_addr = null; // ************************************** // Set our new Prompt. super.setPrompt(receive_shell_prompt); // ****************************************** // Now build Array of Multicast Sockets to // use. try { if (this.showLocalBindAddr().equalsIgnoreCase( McastNetworkVerificationUtility.ALL)) { Vector<InetAddress> v = new Vector<>(); for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) { NetworkInterface intf = (NetworkInterface) en.nextElement(); for (Enumeration e2 = intf.getInetAddresses(); e2.hasMoreElements(); ) { bind_addr = (InetAddress) e2.nextElement(); // ********************************* // Accept only valid Addresses on // Interfaces. if (this.isValidInterface(bind_addr)) { v.addElement(bind_addr); } } // End of Inner For Loop. } // End of Outer For Loop. // *************************************** // Size our Arrays Accordingly. sockets = new MulticastSocket[v.size()]; ack_receiver = new McastAckReceiverThread[v.size()]; // *************************************** // Loop through created Vector and // instantiate the Multicast socket for // each interface. for (int i = 0; i < v.size(); i++) { sockets[i] = new MulticastSocket(this.getMulticastPortInteger()); sockets[i].setTimeToLive(this.TTL); sockets[i].setInterface((InetAddress) v.elementAt(i)); sockets[i].joinGroup(this.getMulticastInetAddress()); ack_receiver[i] = new McastAckReceiverThread(sockets[i]); ack_receiver[i].start(); } // End of For Loop. } else { // *************************************** // Resize our Arrays Accordingly. sockets = new MulticastSocket[1]; ack_receiver = new McastAckReceiverThread[1]; // ************************************* // Ok, we have a specific interface to // bind to so use that address. sockets[0] = new MulticastSocket(this.getMulticastPortInteger()); sockets[0].setTimeToLive(this.TTL); bind_addr = this.getBindInetAddress(); if (bind_addr == null) { sockets[0] = null; } else { sockets[0].setInterface(bind_addr); sockets[0].joinGroup(this.getMulticastInetAddress()); ack_receiver[0] = new McastAckReceiverThread(sockets[0]); ack_receiver[0].start(); } // End of Inner Else. } // End of Else. // ********************************* // Show all Interfaces. for (int i = 0; i < sockets.length; i++) { if (sockets[i] == null) { continue; } displayMsg("Socket #" + (i + 1) + '=' + sockets[i].getLocalAddress() + ':' + sockets[i].getLocalPort() + ", ttl=" + sockets[i].getTimeToLive() + ", bind interface=" + sockets[i].getInterface()); } // End of For Loop. // ***************************** // Now loop until the user ends. displayMsg("Will Receive from MultiCast Group, " + "enter \042quit\042 or \042exit\042 to end function."); while (true) { // ******************************************** // Prompt and verify we got something. super.displayPrompt(); String svalue = this.CMDPromptForString(); if ((svalue == null) || (svalue.trim().equalsIgnoreCase(""))) { continue; } // ******************************************** // Check to see if the data entered indicates // we should end? if (svalue.startsWith("quit") || svalue.startsWith("exit")) { break; } } // End of While Loop. } catch (Exception e) { System.err.println(e); e.printStackTrace(); } // End of Exception Processing. // *************************** // Always ensure we have // the right prompt. super.setPrompt(McastNetworkVerificationUtility.shell_prompt); // ********************** // Show the End. displayMsg("End of Interactive Send Session."); // ********************** // Return return (0); } // End of receive Command. /** * Send. */ public int CMDsend() { // ***************************************** // Array of Receiver Threads. McastAckReceiverThread[] ack_receiver = null; // ********************************************* // Initialize Local Variables for Send Function. MulticastSocket[] sockets = null; InetAddress bind_addr = null; DatagramPacket packet; byte[] buf = new byte[0]; // ************************************** // Set our new Prompt. super.setPrompt(send_shell_prompt); // ****************************************** // Now build Array of Multicast Sockets to // use. try { if (this.showLocalBindAddr().equalsIgnoreCase( McastNetworkVerificationUtility.ALL)) { Vector<InetAddress> v = new Vector<>(); for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) { NetworkInterface intf = (NetworkInterface) en.nextElement(); for (Enumeration e2 = intf.getInetAddresses(); e2.hasMoreElements(); ) { bind_addr = (InetAddress) e2.nextElement(); // ********************************* // Accept only valid Addresses on // Interfaces. if (this.isValidInterface(bind_addr)) { v.addElement(bind_addr); } } // End of Inner For Loop. } // End of Outer For Loop. // *************************************** // Size our Arrays Accordingly. sockets = new MulticastSocket[v.size()]; ack_receiver = new McastAckReceiverThread[v.size()]; // *************************************** // Loop through created Vector and // instantiate the Multicast socket for // each interface. for (int i = 0; i < v.size(); i++) { sockets[i] = new MulticastSocket(this.getMulticastPortInteger()); sockets[i].setTimeToLive(this.TTL); sockets[i].setInterface((InetAddress) v.elementAt(i)); sockets[i].joinGroup(this.getMulticastInetAddress()); ack_receiver[i] = new McastAckReceiverThread(sockets[i], McastNetworkVerificationUtility.send_shell_prompt, false); ack_receiver[i].start(); } // End of For Loop. } else { // *************************************** // Resize our Arrays Accordingly. sockets = new MulticastSocket[1]; ack_receiver = new McastAckReceiverThread[1]; // ************************************* // Ok, we have a specific interface to // bind to so use that address. sockets[0] = new MulticastSocket(this.getMulticastPortInteger()); sockets[0].setTimeToLive(this.TTL); bind_addr = this.getBindInetAddress(); if (bind_addr == null) { sockets[0] = null; } else { sockets[0].setInterface(bind_addr); sockets[0].joinGroup(this.getMulticastInetAddress()); ack_receiver[0] = new McastAckReceiverThread(sockets[0], McastNetworkVerificationUtility.send_shell_prompt, false); ack_receiver[0].start(); } // End of Inner Else. } // End of Else. // ********************************* // Show all Interfaces. for (int i = 0; i < sockets.length; i++) { if (sockets[i] == null) { continue; } displayMsg("Socket #" + (i + 1) + '=' + sockets[i].getLocalAddress() + ':' + sockets[i].getLocalPort() + ", ttl=" + sockets[i].getTimeToLive() + ", bind interface=" + sockets[i].getInterface()); } // End of For Loop. // ***************************** // Now loop until the user ends. displayMsg("Enter Data to Send to MultiCast Group, " + "enter \042quit\042 or \042exit\042 to end function."); while (true) { // ******************************************** // Prompt and verify we got something. super.displayPrompt(); String svalue = this.CMDPromptForString(); if ((svalue == null) || (svalue.trim().equalsIgnoreCase(""))) { continue; } // ******************************************** // Check to see if the data entered indicates // we should end? if (svalue.startsWith("quit") || svalue.startsWith("exit")) { break; } // ******************************************************** // Send the Serialized Object Data to all members of Group. buf = Util.objectToByteBuffer(svalue); packet = new DatagramPacket(buf, buf.length, this.getMulticastInetAddress(), this.getMulticastPortInteger()); send(packet, sockets); // send on all interfaces } // End of While Loop. } catch (Exception e) { System.err.println(e); e.printStackTrace(); } // End of Exception Processing. // *************************** // Always ensure we have // the right prompt. super.setPrompt(McastNetworkVerificationUtility.shell_prompt); // ********************** // Show the End. displayMsg("End of Interactive Send Session."); // ********************** // Return return (0); } // End of send Command. /** * Main to provide immediate use of utility Function invoked from * Shell Script. * * @param args */ public static void main(String[] args) { // **************************************** // Local Objects Properties RunConfig = new Properties(); // **************************************** // Obtain the RunTime Properties from // a local Property file. boolean DEFAULTS_FAILED_TO_LOAD = false; boolean DEFAULTS_NOT_FOUND = false; try { String PFileName = System.getProperty( IRRChangeLogRestoreService.FRAMEWORK_CONFIG_PROPERTIES, IRRChangeLogRestoreService.DEFAULT_PROPERTIES_FILENAME); File PF = new File(PFileName); if (PF.exists()) { // ************************************* // Try to load the Properties. RunConfig.load(new FileInputStream(PF)); } else { DEFAULTS_NOT_FOUND = true; } // End of Else. } catch (Exception e) { DEFAULTS_FAILED_TO_LOAD = true; } // End of Exception. // ************************** // Instantiate our Object. McastNetworkVerificationUtility shell = new McastNetworkVerificationUtility( RunConfig.getProperty( IRRChangeLogRestoreService.MULTICAST_ADDRESS_PNAME), RunConfig.getProperty( IRRChangeLogRestoreService.MULTICAST_PORT_PNAME), DEFAULTS_NOT_FOUND, DEFAULTS_FAILED_TO_LOAD); // ******************************** // Begin the Interactive Session. System.exit(shell.CMDInteractiveShell()); } // End of Main. // ******************************************************************* // PRIVATE METHODS // ******************************************************************* /** * Show Local Bind Address for Display. */ private String showLocalBindAddr() { if ((this.BIND_ADDRESS == null) || (this.BIND_ADDRESS.equalsIgnoreCase(""))) { return McastNetworkVerificationUtility.DEFAULT_BIND_ADDRESS; } else { return this.BIND_ADDRESS; } } // End of showLocalBindAddr private method. /** * Get Multicast port as an Integer. * * @return int Port in Integer Form or Default. */ private int getMulticastPortInteger() { try { return Integer.parseInt(this.MULTICAST_PORT); } catch (NumberFormatException nfe) { return DEFAULT_MULTICAST_PORT; } // End of Exception processing. } // End of getMulticastPortInteger private Method. /** * Get Multicast Address Object. * * @return InnetAddress */ private InetAddress getMulticastInetAddress() { try { if ((this.MULTICAST_ADDRESS == null) || (this.MULTICAST_ADDRESS.equalsIgnoreCase(""))) { this.MULTICAST_ADDRESS = DEFAULT_MULTICAST_ADDRESS; } return InetAddress.getByName(this.MULTICAST_ADDRESS); } catch (Exception e) { this.displayErrorMsg("Unable to obtain InetAddress Object for " + "Specified Multicast Address."); return null; } // End of Exception processing. } // End of getMulticastPortInteger private Method. /** * Get Bind Address Object. * * @return InnetAddress */ private InetAddress getBindInetAddress() { try { if ((this.BIND_ADDRESS == null) || (this.BIND_ADDRESS.equalsIgnoreCase(""))) { return null; } return InetAddress.getByName(this.BIND_ADDRESS); } catch (Exception e) { this.displayErrorMsg("Unable to obtain InetAddress Object for " + "Specified Bind Address."); return null; } // End of Exception processing. } // End of getBindInetAddress private Method. /** * Performs a send of a Data Packet across Multicast sockets interfaces. * * @param packet * @param sockets */ private static void send(DatagramPacket packet, MulticastSocket[] sockets) { if (packet == null || sockets == null) { return; } // ********************************* // Loop Through array of Multicast // Sockets. for (int i = 0; i < sockets.length; i++) { try { if (sockets[i] != null) { sockets[i].send(packet); } } catch (Exception ex) { System.err.println("** Send Failure: " + ex); } // End of Exception processing. } // End of For Loop. } // End of static send method. /** * Check to make sure the Network Interface Address is valid and * should be included to send/receive processing. * * @param bind_addr * @return boolean indicator whether to include or exclude this * interface from being included to send or receove on. */ private boolean isValidInterface(InetAddress bind_addr) { // ********************************** // Check for Null incoming parameter. if (bind_addr == null) { return false; } // ********************************* // Ignore any Link Local Addresses. if (bind_addr.isLinkLocalAddress()) { return false; } // ********************************* // Now get the Interface Address and // determine if we should use this or // not. If we find a IPV6 Loopback, // we ignore since we will already // have a IPV4 Loopback address. // If not we will get additional // packets due to the duel local // addresses. // try { String saddr = InetAddress.getByAddress(bind_addr.getAddress()).toString(); if ((saddr == null) || (saddr.equalsIgnoreCase(""))) { return false; } // *************************** // Strip of a leading // slah if there is one. if (saddr.startsWith("/")) { saddr = saddr.substring(1); } // ***************************** // Now Ignore a IPV6 Loopback // or unspecified IPV6 Address. if ((saddr.equalsIgnoreCase(McastNetworkVerificationUtility.IPV6_LOOPBACK_ADDRESS)) || (saddr.equalsIgnoreCase(McastNetworkVerificationUtility.IPV6_SHORT_LOOPBACK_ADDRESS)) || (saddr.equalsIgnoreCase(McastNetworkVerificationUtility.IPV6_UNSPECIFIED_ADDRESS)) || (saddr.equalsIgnoreCase(McastNetworkVerificationUtility.IPV6_SHORT_UNSPECIFIED_ADDRESS))) { return false; } } catch (UnknownHostException uhe) { return false; } // End of Exception processing. // ******************************* // Return indicating we can use // this interface/address. return true; } // End of isValidInterface private Method. } ///:> End of Class.