NetworkMessage: introduce _sendersUniqueID, _receivedFrom, fix serialize length issue...
authorEddie <dev@fun2be.me>
Sat, 6 Jun 2015 07:54:03 +0000 (08:54 +0100)
committerEddie <dev@fun2be.me>
Sat, 6 Jun 2015 09:34:15 +0000 (10:34 +0100)
 * introduce _sendersUniqueID, _receivedFrom
 * remove _class
 * fix serialize length issue
 * logging of Exceptions

_sendersUniqueID solves the issue of accurately detecting packets that
have looped back which can happen with UDP multicast unless the socket
is configured with MulitcastSocket.setLoopbackMode(false).
It is set by each object that sends messages. Those objects allocate themselves
a random, final, unique ID during construction.
Loopback was previously detected using _sender but that was fragile and had
unpredictable failures.

Introduced getReceivedFrom()
_receivedFrom is filled in by the receiving network service using the Protocol
IP:port of the sender. This solves a big issue with accurately
maintaining the ServiceAddressMap with correct IP:port tupals for
neighbour service discovery. That functionality sends MessagePresence
objects that are supposed to contain the senders IP:port.
In the case of sender services that bind to their local wildcard address
(0.0.0.0/0.0.0.0) that is the address received in the MessagePresence object,
and does not identify the true sending interface address, only the port number.

_class has been removed since it is no longer used. There is no need to
store a class type descriptor for the enclosed MessageAbstract object
since Java's streamed object serialization deals with that issue
internally.

Introduced deserialize(byte[] bytes, int length)
Serialized length. In some circumstances a NetworkMessage _serializedLength would
be set to the maximum size of a UDP packet payload (65507 bytes) rather
than its true size. This was caused by the backing byte[] buffer being
allocated that amount of memory and the deserialize code not being able
to detect the true size of the object within the buffer.

The new method allows the size of a received DatagramPacket payload to
be passed into the deserialize() method and used to control the size of
the backing buffer of the ByteArrayInputStream. Additional checks are
done to ensure that the size of the deserialized object is obtained from
the number of bytes read out of the ByteArrayInputStream.

Exception logging. This will create a local logger to report the issue.
This could be improved by finding a way to get the application's
logger so that exceptions are logged to the network Log Service when it
is active.

src/uk/ac/ntu/n0521366/wsyd/libs/net/NetworkMessage.java

index dac7c9b..443cd72 100644 (file)
@@ -29,6 +29,8 @@ import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import uk.ac.ntu.n0521366.wsyd.libs.message.MessageAbstract;
 
 /**
@@ -64,9 +66,25 @@ public class NetworkMessage implements Serializable, Cloneable {
      */
     long _key;
 
-    // Class<?> _class;
+    /**
+     * A concrete message sub-class.
+     */
     MessageAbstract _message;
     
+    /**
+    * Sending service unique (random) ID provides loopback detection.
+    */
+    public int _sendersUniqueID;
+    
+    /**
+     * The sending host information tagged on by the receiving service.
+     * 
+     * This information is needed to ensure the ServiceAddressMap is accurate. It cannot be easily
+     * gotten from the sender since services there are usually bound to wildcard IP addresses and
+     * cannot get the IP of the interface the message is sent from.
+     */
+    WSYD_SocketAddress _receivedFrom;
+    
     /**
      * Default constructor.
      */
@@ -75,8 +93,8 @@ public class NetworkMessage implements Serializable, Cloneable {
         _intent = null;
         _serviceSender = null;
         _serviceTarget = null;
-        // _class = null;
         _message = null;
+        _receivedFrom = null;
     }
     
     /**
@@ -86,12 +104,12 @@ public class NetworkMessage implements Serializable, Cloneable {
      */
     @Override
     public String toString() {
-        return new String("_serializedLength:" + _serializeLength + 
-                          ",_intent:" + _intent +
-                          ",_serviceSender:" + _serviceSender +
-                          ",_serviceTarget:" + _serviceTarget +
-                          ",_message:" + _message.toString()
-                          );
+        return "_serializedLength:" + _serializeLength + 
+                ",_intent:" + _intent +
+                ",_serviceSender:" + _serviceSender +
+                ",_serviceTarget:" + _serviceTarget +
+                ",_message:" + (_message != null ? _message.toString() : "") +
+                ", receivedFrom:" + (_receivedFrom != null ? _receivedFrom.toString() : "");
     }
     /**
      * Create a message for passing over the network.
@@ -203,6 +221,13 @@ public class NetworkMessage implements Serializable, Cloneable {
         return _key;
     }
     
+    /**
+     * get the sender details tagged on by the receiving service.
+     * @return the sender's address, port, and protocol
+     */
+    public WSYD_SocketAddress getReceivedFrom() {
+        return _receivedFrom;
+    }
     /**
      * Create a message for passing over the network.
      * 
@@ -244,13 +269,8 @@ public class NetworkMessage implements Serializable, Cloneable {
         if (! (message instanceof Serializable))
             throw new CloneNotSupportedException();
 
-        try {
-            result = deserialize(serialize(message));
-        } catch (IOException | ClassNotFoundException e) {
-            throw new CloneNotSupportedException();
-        }
-
-        return result;
+        byte[] temp = serialize(message);
+        return deserialize(temp);
     }
     
     /**
@@ -271,30 +291,52 @@ public class NetworkMessage implements Serializable, Cloneable {
      * 
      * @param message
      * @return the message as an array of bytes
-     * @throws IOException
      */
-    public static byte[] serialize(NetworkMessage message) throws IOException {
+    public static byte[] serialize(NetworkMessage message) {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        ObjectOutputStream oos = new ObjectOutputStream(baos);
-        oos.writeObject(message);
-        byte[] bytes = baos.toByteArray();
-        message.setSerializeLength(bytes.length);
-        return bytes;
+        ObjectOutputStream oos;
+        try {
+            oos = new ObjectOutputStream(baos);
+            oos.writeObject(message);
+            byte[] bytes = baos.toByteArray();
+            message.setSerializeLength(bytes.length);
+            return bytes;
+        } catch (IOException ex) {
+            Logger.getLogger(NetworkMessage.class.getName()).log(Level.SEVERE, null, ex);
+        }
+        return null;
     }
     
     /**
-     * Deserialize raw data into a NetworkMessage.
+     * Deserialize a number of raw bytes into a NetworkMessage.
      * 
      * @param bytes raw bytes
+     * @param length number of bytes in buffer that should be treated as a serialized object
+     * @return the NetworkMessage object
+     */
+    public static NetworkMessage deserialize(byte[] bytes, int length) {
+        ByteArrayInputStream bais = new ByteArrayInputStream(bytes, 0, length);
+        ObjectInputStream ois;
+        try {
+            ois = new ObjectInputStream(bais);
+            int bytesAvailable = bais.available();
+            NetworkMessage temp = (NetworkMessage) ois.readObject();
+            int bytesRead = bytesAvailable - bais.available();
+            temp.setSerializeLength(bytesRead);
+            return temp;
+        } catch (IOException | ClassNotFoundException ex) {
+            Logger.getLogger(NetworkMessage.class.getName()).log(Level.SEVERE, null, ex);
+        }
+        return null;
+    }
+    
+    /**
+     * Deserialize raw bytes into a NetworkMessage.
+     * 
+     * @param bytes ray bytes
      * @return the NetworkMessage object
-     * @throws IOException
-     * @throws ClassNotFoundException 
      */
-    public static NetworkMessage deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
-        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
-        ObjectInputStream ois = new ObjectInputStream(bais);
-        NetworkMessage temp = (NetworkMessage) ois.readObject();
-        temp.setSerializeLength(bytes.length);
-        return temp;
+    public static NetworkMessage deserialize(byte[] bytes) {
+        return deserialize(bytes, bytes.length);
     }
 }