If you want to compose your own routing module, you have to derive a new class from the ActiveRouter class and rewrite the update() method and probably the replicate() method too. In this article, we look into these two methods.
Let's take the DirectDeliveryRouter for an example. The following snippet is the function body of its DirectDeliveryRouter::update() method.
2 public void update() {
3 super .update();
4 if (isTransferring() || ! canStartTransfer()) {
5 return ; // can't start a new transfer
6 }
7
8 // Try only the messages that can be delivered to final recipient
9 if (exchangeDeliverableMessages() != null ) {
10 return ; // started a transfer
11 }
12 }
It firstly call the update method of is parent (ActiveRouter). Then if the router is transferring right now it just returns. Here is the ActiveRouter::isTransferring method:
2 * Returns true if this router is transferring something at the moment or
3 * some transfer has not been finalized.
4 * @return true if this router is transferring something
5 */
6 public boolean isTransferring() {
7 if ( this .sendingConnections.size() > 0 ) {
8 return true ; // sending something
9 }
10
11 if ( this .getHost().getConnections().size() == 0 ) {
12 return false ; // not connected
13 }
14
15 List < Connection > connections = getConnections();
16 for ( int i = 0 , n = connections.size(); i < n; i ++ ) {
17 Connection con = connections.get(i);
18 if ( ! con.isReadyForTransfer()) {
19 return true ; // a connection isn't ready for new transfer(it's busy)
20 }
21 }
22
23 return false;
24 }
The function Connection::isReadyForTransfer:
2 * Returns true if the connection is ready to transfer a message (connection
3 * is up and there is no message being transferred).
4 * @return true if the connection is ready to transfer a message
5 */
6 public boolean isReadyForTransfer() {
7 return this .isUp && this .msgOnFly == null;
8 }
The function body of ActiveRouter::canStartTransfer is:
2 * Makes rudimentary checks (that we have at least one message and one
3 * connection) about can this router start transfer.
4 * @return True if router can start transfer, false if not
5 */
6 protected boolean canStartTransfer() {
7 if ( this .getNrofMessages() == 0 ) {
8 return false ;
9 }
10 if ( this .getConnections().size() == 0 ) {
11 return false ;
12 }
13
14 return true;
15 }
The function body of DirectDeliveryRouter::exchangeDeliverableMessages:
2 * Exchanges deliverable (to final recipient) messages between this host
3 * and all hosts this host is currently connected to. First all messages
4 * from this host are checked and then all other hosts are asked for
5 * messages to this host. If a transfer is started, the search ends.
6 * @return A connection that started a transfer or null if no transfer
7 * was started
8 */
9 protected Connection exchangeDeliverableMessages() {
10 List < Connection > connections = getConnections();
11
12 if (connections.size() == 0 ) {
13 return null ;
14 }
15
16 @SuppressWarnings(value = " unchecked " )
17 Tuple < Message, Connection > t =
18 tryMessagesForConnected(sortByQueueMode(getMessagesForConnected()));
19
20 if (t != null ) {
21 return t.getValue(); // started transfer
22 }
23
24 // didn't start transfer to any node -> ask messages from connected
25 for (Connection con : connections) {
26 if (con.getOtherNode(getHost()).requestDeliverableMessages(con)) {
27 return con;
28 }
29 }
30
31 return null;
32 }
The function body of ActiveRouter::tryMessagesForConnected:
2 * Tries to send messages for the connections that are mentioned
3 * in the Tuples in the order they are in the list until one of
4 * the connections starts transferring or all tuples have been tried.
5 * @param tuples The tuples to try
6 * @return The tuple whose connection accepted the message or null if
7 * none of the connections accepted the message that was meant for them.
8 */
9 protected Tuple < Message, Connection > tryMessagesForConnected(
10 List < Tuple < Message, Connection >> tuples) {
11 if (tuples.size() == 0 ) {
12 return null ;
13 }
14
15 for (Tuple < Message, Connection > t : tuples) {
16 Message m = t.getKey();
17 Connection con = t.getValue();
18 if (startTransfer(m, con) == RCV_OK) {
19 return t;
20 }
21 }
22
23 return null;
24 }
The function body of MessageRouter::sortByQueueMode:
2 * Sorts/shuffles the given list according to the current sending queue
3 * mode. The list can contain either Message or Tuple<Message, Connection>
4 * objects. Other objects cause error.
5 * @param list The list to sort or shuffle
6 * @return The sorted/shuffled list
7 */
8 @SuppressWarnings(value = " unchecked " ) /* ugly way to make this generic */
9 protected List sortByQueueMode(List list) {
10 switch (sendQueueMode) {
11 case Q_MODE_RANDOM:
12 Collections.shuffle(list, new Random(SimClock.getIntTime()));
13 break ;
14 case Q_MODE_FIFO:
15 Collections.sort(list,
16 new Comparator() {
17 /** Compares two tuples by their messages' receiving time */
18 public int compare(Object o1, Object o2) {
19 double diff;
20 Message m1, m2;
21
22 if (o1 instanceof Tuple) {
23 m1 = ((Tuple < Message, Connection > )o1).getKey();
24 m2 = ((Tuple < Message, Connection > )o2).getKey();
25 }
26 else if (o1 instanceof Message) {
27 m1 = (Message)o1;
28 m2 = (Message)o2;
29 }
30 else {
31 throw new SimError( " Invalid type of objects in " +
32 " the list " );
33 }
34
35 diff = m1.getReceiveTime() - m2.getReceiveTime();
36 if (diff == 0 ) {
37 return 0 ;
38 }
39 return (diff < 0 ? - 1 : 1 );
40 }
41 });
42 break ;
43 /* add more queue modes here */
44 default :
45 throw new SimError( " Unknown queue mode " + sendQueueMode);
46 }
47
48 return list;
49 }
The function body of ActiveRouter::getMessagesForConnected:
2 * Returns a list of message-connections tuples of the messages whose
3 * recipient is some host that we're connected to at the moment.
4 * @return a list of message-connections tuples
5 */
6 protected List < Tuple < Message, Connection >> getMessagesForConnected() {
7 if (getNrofMessages() == 0 || getConnections().size() == 0 ) {
8 /* no messages -> empty list */
9 return new ArrayList < Tuple < Message, Connection >> ( 0 );
10 }
11
12 List < Tuple < Message, Connection >> forTuples =
13 new ArrayList < Tuple < Message, Connection >> ();
14 for (Message m : getMessageCollection()) {
15 for (Connection con : getConnections()) {
16 DTNHost to = con.getOtherNode(getHost());
17 if (m.getTo() == to) {
18 forTuples.add( new Tuple < Message, Connection > (m,con));
19 }
20 }
21 }
22
23 return forTuples;
24 }
The function body of MessageRouter::requestDeliverableMessages :
2 * Requests for deliverable message from this router to be sent trough a
3 * connection.
4 * @param con The connection to send the messages trough
5 * @return True if this router started a transfer, false if not
6 */
7 public boolean requestDeliverableMessages(Connection con) {
8 return false ; // default behavior is to not start -- subclasses override
9 }