Add Registration functionality and tidy up
[WeStealzYourDataz.git] / src / uk / ac / ntu / n0521366 / wsyd / libs / net / NetworkMessage.java
1 /*
2  * The MIT License
3  *
4  * Copyright 2015 Eddie Berrisford-Lynch <dev@fun2be.me>.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 package uk.ac.ntu.n0521366.wsyd.libs.net;
25
26 import java.io.IOException;
27 import java.io.Serializable;
28 import java.io.ByteArrayInputStream;
29 import java.io.ByteArrayOutputStream;
30 import java.io.ObjectInputStream;
31 import java.io.ObjectOutputStream;
32 import uk.ac.ntu.n0521366.wsyd.libs.message.MessageAbstract;
33
34 /**
35  * Wraps an object and intent label together for passing over a network.
36  * 
37  * @see NetworkMessageEvent
38  * @author Eddie Berrisford-Lynch <dev@fun2be.me>
39  */
40 public class NetworkMessage implements Serializable, Cloneable {
41     /**
42      * number of bytes used by serialized object
43      */
44     int _serializeLength;
45     
46     /**
47      * Key describing purpose of message; receiver uses this to dispatch to the correct
48      * consumer method.
49      */
50     String _intent;
51
52     /**
53      * Filled in by the network server service that sends the message
54      */
55     String _serviceSender;
56
57     /**
58      * Filled in by the message originator with the title of the destination service
59      */
60     String _serviceTarget;
61     
62     /**
63      * Optional user key of a NetworkStream to enable TCP to send userID on connection
64      */
65     long _key;
66
67     // Class<?> _class;
68     MessageAbstract _message;
69     
70     /**
71      * Default constructor.
72      */
73     NetworkMessage() {
74         _serializeLength = -1;
75         _intent = null;
76         _serviceSender = null;
77         _serviceTarget = null;
78         // _class = null;
79         _message = null;
80     }
81     
82     /**
83      * Make the NetworkMessage printable.
84      * 
85      * @return human readable text
86      */
87     @Override
88     public String toString() {
89         return new String("_serializedLength:" + _serializeLength + 
90                           ",_intent:" + _intent +
91                           ",_serviceSender:" + _serviceSender +
92                           ",_serviceTarget:" + _serviceTarget +
93                           ",_message:" + _message.toString()
94                           );
95     }
96     /**
97      * Create a message for passing over the network.
98      * 
99      * @param intent The purpose of the attached object
100      * @param target The title of the target service
101      * @param message The Message being passed. Must implement java.io.Serialize or only
102      * contain native types that are serializable
103      * @throws IllegalArgumentException 
104      */
105     public NetworkMessage(String intent, String target, MessageAbstract message) throws IllegalArgumentException {
106         _serializeLength = -1;
107         if ( !(intent != null && intent.length() > 0) ) 
108             throw(new IllegalArgumentException("intent cannot be null or empty"));
109         if (message == null)
110             throw(new IllegalArgumentException("message cannot be null"));
111         _intent = intent;
112         _serviceSender = null;
113         _serviceTarget = target;
114         _message = message;
115         // _class = _message.getClass();
116     }
117     
118     /**
119      * Set the number of bytes used by the serialized object.
120      * 
121      * @param length number of bytes
122      */
123     void setSerializeLength(int length) {
124         _serializeLength = length;
125     }
126
127     /**
128      * Set to the title of the sending service to allow replies to be routed correctly.
129      * 
130      * @param sender title of the sending service
131      */
132     public void setSender(String sender) {
133         _serviceSender = sender;
134     }
135     
136     /**
137      * Get the title of the service that sent this message.
138      * 
139      * @return title of the sending service
140      */
141     public String getSender() {
142         return _serviceSender;
143     }
144
145     /**
146      * Set to the title of the target service to allow correct routing.
147      * 
148      * @param target title of the destination service
149      */
150     public void setTarget(String target) {
151         _serviceTarget = target;
152     }
153
154     /**
155      * Get the name of the sending service.
156      * 
157      * @return the sender
158      */
159     public String getTarget() {
160         return _serviceTarget;
161     }
162
163     /**
164      * Get the intended destination topic.
165      * @return the intent
166      */
167     public String getIntent() {
168         return _intent;
169     }
170     /**
171      * Get the content of this NetworkMessage.
172      * 
173      * @return the encapsulated message
174      */
175     public MessageAbstract getMessage() {
176         return _message;
177     }
178
179     /**
180      * Get the size of the serialized object.
181      * 
182      * @return -1 if not yet serialized, otherwise number of bytes required
183      */
184     public int getSerializeLength() {
185         return _serializeLength;
186     }
187     
188     /**
189      * Set the key of the TCP stream in the messsage.
190      * 
191      * @param key 
192      */
193     public void setKey(long key) {
194         _key = key;
195     }
196     
197     /**
198      * Get the key set by the ServerTCP.
199      * 
200      * @return the key of the stream associated
201      */
202     public long getKey() {
203         return _key;
204     }
205     
206     /**
207      * Create a message for passing over the network.
208      * 
209      * @param intent The purpose of the attached object
210      * @param target The title of the target service
211      * @param message The encapsulated message
212      * contain native types that are serializable
213      * @return a freshly constructed NetworkMessage object
214      * @throws IllegalArgumentException 
215      */
216     public static NetworkMessage createNetworkMessage(String intent, String target, MessageAbstract message) throws IllegalArgumentException {
217         return new NetworkMessage(intent, target, message);            
218     }
219
220     /**
221      * Make a deep clone of this message and all its member objects.
222      * 
223      * It is not possible to safely use shallow or deep cloning of all this class's
224  members since its _message member reference could be to an object of any type,
225  and those types and their member attributes may not override the Object.clone()
226  method via the Cloneable interface.
227  
228  Cannot rely on using copy constructors since _message (or its member attributes)
229  may reference an object  that does not have a copy constructor.
230  
231  That leaves an expensive (in time) but reliable trick - serialize and deserialize 
232  the object to/from a byte stream. Since NetworkMessage already has methods supporting
233  serialization this is a simple solution to implement.
234      * 
235      * @see NetworkMessage#serialize
236      * @see NetworkMessage#deserialize
237      * @param message The message to clone
238      * @return Deep clone of the message object
239      * @throws java.lang.CloneNotSupportedException
240      */
241     public static NetworkMessage clone(NetworkMessage message) throws CloneNotSupportedException {
242         NetworkMessage result = null;
243         
244         if (! (message instanceof Serializable))
245             throw new CloneNotSupportedException();
246
247         try {
248             result = deserialize(serialize(message));
249         } catch (IOException | ClassNotFoundException e) {
250             throw new CloneNotSupportedException();
251         }
252
253         return result;
254     }
255     
256     /**
257      * Support the Cloneable interface but use the class's static serialization clone method.
258      * 
259      * @see NetworkMessage#clone(NetworkMessage) 
260      * @return Deep clone of this object
261      * @throws CloneNotSupportedException
262      */
263     @Override
264     @SuppressWarnings("CloneDoesntCallSuperClone")
265     protected Object clone() throws CloneNotSupportedException {
266         return NetworkMessage.clone(this);
267     }
268     
269     /**
270      * Serialize the message.
271      * 
272      * @param message
273      * @return the message as an array of bytes
274      * @throws IOException
275      */
276     public static byte[] serialize(NetworkMessage message) throws IOException {
277         ByteArrayOutputStream baos = new ByteArrayOutputStream();
278         ObjectOutputStream oos = new ObjectOutputStream(baos);
279         oos.writeObject(message);
280         byte[] bytes = baos.toByteArray();
281         message.setSerializeLength(bytes.length);
282         return bytes;
283     }
284     
285     /**
286      * Deserialize raw data into a NetworkMessage.
287      * 
288      * @param bytes raw bytes
289      * @return the NetworkMessage object
290      * @throws IOException
291      * @throws ClassNotFoundException 
292      */
293     public static NetworkMessage deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
294         ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
295         ObjectInputStream ois = new ObjectInputStream(bais);
296         NetworkMessage temp = (NetworkMessage) ois.readObject();
297         temp.setSerializeLength(bytes.length);
298         return temp;
299     }
300 }