====== HelloGroup ====== {{ :hellogroupext.png?nolink&400|}} In this tutorial we will exemplify SDDL's group communication capabilities. In particular, we will show how mobile clients can join a group and communicate through it. Similarly to one-to-one (unicast) communication (see Tutorial [[HelloMobile]]), also group communication is asynchronous, which means that message delivery is based on callback method invocation. Our sample application will consist of two peer executing in mobile nodes: a group peer that will act as a client and another that act as a receiver, as illustrated in the figure aside. The application code share much similarity with the previous tutorials, with the addition that now the nodes implement the ''GroupMembershipListener'', that trigger events for joining, leaving, and receiving messages from Groups. We start the tutorial explaining the concept of Groups, that in SDDL can be explicit or implicit. After that, we explain the changes we made in the mobile client applications to accommodate those concepts. The tutorial then displayis the the modifications that allow a peer ''HelloGroupClient'' to explicity join a Group and send a message to it. The ''HelloGroupPeer'' peer will join this group to receive the message sent. After this example, we show an implicit group communication, which requires a mobile node application and a processing node ''GroupDefiner'' to map nodes to groups. You do not need to copy the source code snippets shown in this tutorial. At the end of each section there is a link to download the entire node source code. Moreover, all examples are included in the virtual machine made available. ===== Group Concept ===== In SDDL, groups are created/defined according to the group selection logic implemented in the ''GroupDefiner'', and mobile clients can only become members of (any set of) existing groups. Group membership means that the mobile client will receive all the messages posted to its group. A mobile client may become member of any group in any of following two ways: - Implicit: Some of its application message attribute satisfies the membership condition in the group selection logic. For example, its ''UUID'' is included in a static set of group member IDs, or otherwise, its geographic coordinate falls inside a specific region boundary; - Explicit: The mobile client explicitly joins an existing group through the ''join()'' operation of the [[ClientLib]] GroupAPI. It is worth noting that membership is the conjunction of the implicit and the explicit case: i.e., a mobile client which is an implicit member of a group (case 1) will remain so even after making the leave() operation, and a client that is not an implicit member of a group, can control its group membership through the ''join()'' and ''leave()'' operations of the GroupAPI. Moreover, in SDDL groups are open. This means that regarding the sending of groupcast messages there is no need to be a group member. Instead, it is sufficient to use the correct ''GroupID'' as the message destination. ===== HelloGroupClient ===== The ''HelloGroupClient'' application adds some modifications to the previous clients. Our client now specify a group that we want to join and communicate with it. In SDDL, each group is identified by its type and id, precisely, an integer pair. In our sample application, we want to communicate with the group that has group type of ''250'' and id of ''300''. To communicate with groups using the ''GroupAPI'', we need to instantiate a group manager in a node <=> gateway connection. This is done usually in the connection event function that is triggered when the node connects to the gateway. After that, we execute a join call in the manager passing the group that we want to communicate as parameter. After that, we can send messages to the group members (groupcast messages) using the group manager. Finally, we added a code in our ''newMessageReceived'' method to exemplify that sending to the group, all nodes receives the message, including the sender. The code is shown below: package br.pucrio.inf.lac.helloworld; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import lac.cnclib.net.NodeConnection; import lac.cnclib.net.NodeConnectionListener; import lac.cnclib.net.groups.Group; import lac.cnclib.net.groups.GroupCommunicationManager; import lac.cnclib.net.groups.GroupMembershipListener; import lac.cnclib.net.mrudp.MrUdpNodeConnection; import lac.cnclib.sddl.message.ApplicationMessage; import lac.cnclib.sddl.message.Message; public class HelloGroupClient implements NodeConnectionListener, GroupMembershipListener { private static String gatewayIP = "127.0.0.1"; private static int gatewayPort = 5500; private GroupCommunicationManager groupManager; private Group aGroup; public HelloGroupClient() { InetSocketAddress address = new InetSocketAddress(gatewayIP, gatewayPort); try { MrUdpNodeConnection connection = new MrUdpNodeConnection(); connection.connect(address); connection.addNodeConnectionListener(this); aGroup = new Group(250, 300); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { Logger.getLogger("").setLevel(Level.OFF); new HelloGroupClient(); } @Override public void connected(NodeConnection remoteCon) { groupManager = new GroupCommunicationManager(remoteCon); groupManager.addMembershipListener(this); try { groupManager.joinGroup(aGroup); ApplicationMessage appMsg = new ApplicationMessage(); appMsg.setContentObject("Group Message"); groupManager.sendGroupcastMessage(appMsg, aGroup); } catch (IOException e) { e.printStackTrace(); } } @Override public void reconnected(NodeConnection remoteCon, SocketAddress endPoint, boolean wasHandover, boolean wasMandatory) {} @Override public void disconnected(NodeConnection remoteCon) {} @Override public void newMessageReceived(NodeConnection remoteCon, Message message) { System.out.println("Group sender also received: " + message.getContentObject()); } @Override public void unsentMessages(NodeConnection remoteCon, List unsentMessages) {} @Override public void internalException(NodeConnection remoteCon, Exception e) {} @Override public void enteringGroups(List groups) { System.out.println("Entered in the group"); } @Override public void leavingGroups(List groups) {} } ===== HelloGroupPeer ===== Since group clients and peers are identical, the ''HelloGroupPeer'' receiver code is pretty much the same. With the exception that this client does not send the message, only receives. The complete code is below: package br.pucrio.inf.lac.helloworld; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import lac.cnclib.net.NodeConnection; import lac.cnclib.net.NodeConnectionListener; import lac.cnclib.net.groups.Group; import lac.cnclib.net.groups.GroupCommunicationManager; import lac.cnclib.net.groups.GroupMembershipListener; import lac.cnclib.net.mrudp.MrUdpNodeConnection; import lac.cnclib.sddl.message.Message; public class HelloGroupPeer implements NodeConnectionListener, GroupMembershipListener { private static String gatewayIP = "127.0.0.1"; private static int gatewayPort = 5500; private GroupCommunicationManager groupManager; private Group aGroup; public HelloGroupPeer() { InetSocketAddress address = new InetSocketAddress(gatewayIP, gatewayPort); try { MrUdpNodeConnection connection = new MrUdpNodeConnection(); connection.connect(address); connection.addNodeConnectionListener(this); aGroup = new Group(250, 300); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { Logger.getLogger("").setLevel(Level.OFF); new HelloGroupPeer(); } @Override public void connected(NodeConnection remoteCon) { groupManager = new GroupCommunicationManager(remoteCon); groupManager.addMembershipListener(this); try { groupManager.joinGroup(aGroup); } catch (IOException e) { e.printStackTrace(); } } @Override public void reconnected(NodeConnection remoteCon, SocketAddress endPoint, boolean wasHandover, boolean wasMandatory) {} @Override public void disconnected(NodeConnection remoteCon) {} @Override public void newMessageReceived(NodeConnection remoteCon, Message message) { System.out.println("Group Peer received: " + message.getContentObject()); } @Override public void unsentMessages(NodeConnection remoteCon, List unsentMessages) {} @Override public void internalException(NodeConnection remoteCon, Exception e) {} @Override public void enteringGroups(List groups) { System.out.println("Entered in the group"); } @Override public void leavingGroups(List groups) {} } ===== HelloGroupExecution ===== The execution of our application is identical to the others example, if there is no gateway executing we need to create one to instantiate the core infrastructure. To do that, open a shell, and run the following command: $ gateway 127.0.0.1 5500 After that, we instantiate the mobiles nodes. First run the ''HelloGroupPeer'' class in Eclipse as a Java application. Finally, execute the ''HelloGroupClient'' class in Eclipse to start the mobile node application. You will see the printed messages in your console. ===== GroupDefiner ===== The GroupDefiner concept allows applications to implicitly aggregate nodes in groups. When used, the SDDL core redirect the mobile nodes messages to the GroupDefiner, that apply some application logic, which returns the group set of that node. To exercise this concept, in this tutorial, we are developing a simple logic that separates the nodes based in their identifier UUID. If their UUID least significant bits is even they are assigned to group 2, otherwise (it is odd) they are assigned to group 3. By default we also associate them to our application default group, which we consider to be group 1. In addition, every GroupDefiner only process information of a given group type. Therefore, every GroupDefiner must implement the ''GroupSelector'' interface, which contains two methods: * ''getGroupType()'' - which returns the definer group type * ''processGroup(Message message)'' - which classify and returns the node’s group ID The entire code of our GroupDefiner and GroupSelector is listed below: package br.pucrio.inf.lac.helloworld; import java.util.HashSet; import java.util.Set; import lac.cnet.groupdefiner.components.groupselector.GroupSelector; import lac.cnet.sddl.objects.Message; public class HelloGroupSelector implements GroupSelector { @Override public int getGroupType() { return 3; } @Override public Set processGroups(Message nodeMessage) { System.out.println("STARTED CLASSIFYING GROUP MESSAGE"); // Add the node to two groups HashSet groupCollection = new HashSet(2, 1); // Some default group (ID = 1) groupCollection.add(1); // Even (2) or Odd Group (3) if (nodeMessage.getSenderId().getLeastSignificantBits() % 2 == 0) { groupCollection.add(2); } else { groupCollection.add(3); } System.out.println("ENDED CLASSIFYING GROUP MESSAGE\n"); return groupCollection; } } package br.pucrio.inf.lac.helloworld; import lac.cnet.groupdefiner.components.GroupDefiner; import lac.cnet.groupdefiner.components.groupselector.GroupSelector; public class HelloGroupDefiner { public static void main(String[] args) { GroupSelector groupSelector = new HelloGroupSelector(); new GroupDefiner(groupSelector); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); } } } ===== HelloGroupReceiver ===== To test our GroupDefiner, we will implement two mobile nodes receiver. One will have its UUID least significant bit as an even number, while the other one has an odd one. They will respectively be classified in the groups ID ''<1, 2>'' and ''<1, 3>'' of group type ''3''. Each node will send a simple message just so the definer can classify them. After that, each node waits for the messages from their implicit group. The code of both mobile node receivers are below. Note, that the only difference is the line where the UUID is generated. We enforce that the number is even or odd accordingly to the given node. package br.pucrio.inf.lac.helloworld; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.List; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import lac.cnclib.net.NodeConnection; import lac.cnclib.net.NodeConnectionListener; import lac.cnclib.net.groups.Group; import lac.cnclib.net.groups.GroupCommunicationManager; import lac.cnclib.net.groups.GroupMembershipListener; import lac.cnclib.net.mrudp.MrUdpNodeConnection; import lac.cnclib.sddl.message.ApplicationMessage; import lac.cnclib.sddl.message.Message; public class HelloGroupDefinerEvenReceiver implements NodeConnectionListener, GroupMembershipListener { private final static String gatewayIP = "127.0.0.1"; private final static int gatewayPort = 5500; private GroupCommunicationManager groupManager; public HelloGroupDefinerEvenReceiver() { UUID uuid = UUID.randomUUID(); while (uuid.getLeastSignificantBits() % 2 != 0) { uuid = UUID.randomUUID(); } InetSocketAddress address = new InetSocketAddress(gatewayIP, gatewayPort); try { MrUdpNodeConnection connection = new MrUdpNodeConnection(uuid); connection.connect(address); connection.addNodeConnectionListener(this); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { Logger.getLogger("").setLevel(Level.OFF); new HelloGroupDefinerEvenReceiver(); } @Override public void connected(NodeConnection remoteCon) { groupManager = new GroupCommunicationManager(remoteCon); groupManager.addMembershipListener(this); try { ApplicationMessage message = new ApplicationMessage(); String serializableContent = "Bogus Message for Group Definer"; message.setContentObject(serializableContent); remoteCon.sendMessage(message); } catch (Exception e) { e.printStackTrace(); } } @Override public void reconnected(NodeConnection remoteCon, SocketAddress endPoint, boolean wasHandover, boolean wasMandatory) {} @Override public void disconnected(NodeConnection remoteCon) {} @Override public void newMessageReceived(NodeConnection remoteCon, Message message) { System.out.println(message.getContentObject()); } @Override public void unsentMessages(NodeConnection remoteCon, List unsentMessages) {} @Override public void internalException(NodeConnection remoteCon, Exception e) {} @Override public void enteringGroups(List groups) {} @Override public void leavingGroups(List groups) {} } package br.pucrio.inf.lac.helloworld; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.List; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import lac.cnclib.net.NodeConnection; import lac.cnclib.net.NodeConnectionListener; import lac.cnclib.net.groups.Group; import lac.cnclib.net.groups.GroupCommunicationManager; import lac.cnclib.net.groups.GroupMembershipListener; import lac.cnclib.net.mrudp.MrUdpNodeConnection; import lac.cnclib.sddl.message.ApplicationMessage; import lac.cnclib.sddl.message.Message; public class HelloGroupDefinerOddReceiver implements NodeConnectionListener, GroupMembershipListener { private final static String gatewayIP = "127.0.0.1"; private final static int gatewayPort = 5500; private GroupCommunicationManager groupManager; public HelloGroupDefinerOddReceiver() { UUID uuid = UUID.randomUUID(); while (uuid.getLeastSignificantBits() % 2 == 0) { uuid = UUID.randomUUID(); } InetSocketAddress address = new InetSocketAddress(gatewayIP, gatewayPort); try { MrUdpNodeConnection connection = new MrUdpNodeConnection(uuid); connection.connect(address); connection.addNodeConnectionListener(this); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { Logger.getLogger("").setLevel(Level.OFF); new HelloGroupDefinerOddReceiver(); } @Override public void connected(NodeConnection remoteCon) { groupManager = new GroupCommunicationManager(remoteCon); groupManager.addMembershipListener(this); try { ApplicationMessage message = new ApplicationMessage(); String serializableContent = "Bogus Message for Group Definer"; message.setContentObject(serializableContent); remoteCon.sendMessage(message); } catch (Exception e) { e.printStackTrace(); } } @Override public void reconnected(NodeConnection remoteCon, SocketAddress endPoint, boolean wasHandover, boolean wasMandatory) {} @Override public void disconnected(NodeConnection remoteCon) {} @Override public void newMessageReceived(NodeConnection remoteCon, Message message) { System.out.println(message.getContentObject()); } @Override public void unsentMessages(NodeConnection remoteCon, List unsentMessages) {} @Override public void internalException(NodeConnection remoteCon, Exception e) {} @Override public void enteringGroups(List groups) {} @Override public void leavingGroups(List groups) {} } ===== HelloGroupDefinerClient ===== Our client will act slightly the same as the ''HelloGroupClient''. With the difference, that we now send messages to the group type 3, and groups ID 1, 2, and 3. The ''HelloGroupDefinerEvenReceiver'' will receives the messages of group ID 1 and 2, while the ''HelloGroupDefinerOddReceiver'' will receives the messages of groups ID 1 and 3. The complete code of our client is shown below: package br.pucrio.inf.lac.helloworld; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import lac.cnclib.net.NodeConnection; import lac.cnclib.net.NodeConnectionListener; import lac.cnclib.net.groups.Group; import lac.cnclib.net.groups.GroupCommunicationManager; import lac.cnclib.net.groups.GroupMembershipListener; import lac.cnclib.net.mrudp.MrUdpNodeConnection; import lac.cnclib.sddl.message.ApplicationMessage; import lac.cnclib.sddl.message.Message; public class HelloGroupClient implements NodeConnectionListener, GroupMembershipListener { private final static String gatewayIP = "127.0.0.1"; private final static int gatewayPort = 5500; private GroupCommunicationManager groupManager; public HelloGroupClient() { InetSocketAddress address = new InetSocketAddress(gatewayIP, gatewayPort); try { MrUdpNodeConnection connection = new MrUdpNodeConnection(); connection.connect(address); connection.addNodeConnectionListener(this); try { Thread.sleep(30000); } catch (InterruptedException e) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { Logger.getLogger("").setLevel(Level.OFF); new HelloGroupClient(); } @Override public void connected(NodeConnection remoteCon) { groupManager = new GroupCommunicationManager(remoteCon); groupManager.addMembershipListener(this); try { // i = 1 Default // i = 2 EVEN // i = 3 ODD for (int i = 1; i < 4; i++) { Group group = new Group(3, i); ApplicationMessage appMsg = new ApplicationMessage(); appMsg.setContentObject("Message GroupID: " + i); groupManager.sendGroupcastMessage(appMsg, group); } } catch (IOException e) { e.printStackTrace(); } } @Override public void reconnected(NodeConnection remoteCon, SocketAddress endPoint, boolean wasHandover, boolean wasMandatory) {} @Override public void disconnected(NodeConnection remoteCon) {} @Override public void newMessageReceived(NodeConnection remoteCon, Message message) { System.out.println(message.getContentObject()); } @Override public void unsentMessages(NodeConnection remoteCon, List unsentMessages) {} @Override public void internalException(NodeConnection remoteCon, Exception e) {} @Override public void enteringGroups(List groups) {} @Override public void leavingGroups(List groups) {} } ===== HelloGroupDefinerExecution ===== The execution of our application is identical to the others example, if there is no gateway executing we need to create one to instantiate the core infrastructure. To do that, open a shell, and run the following command: $ gateway 127.0.0.1 5500 After that, we instantiate the GroupDefiner, followed by the mobile nodes. First run the ''HelloGroupDefiner'' class in Eclipse as a Java application. After that, execute both the ''HelloGroupDefinerEvenReceiver'' and ''HelloGroupDefinerOddReceiver'' classes. Finally, execute the ''HelloGroupClient'' class in Eclipse to start the mobile node application. You will see the printed messages in your console.