a3e9dd3641042a4e5b45f36e8f50b3d57a3a88c1
[WeStealzYourDataz.git] / src / uk / ac / ntu / n0521366 / wsyd / libs / net / NetworkMessage.java
1 /*
2  * The MIT License
3  *
4  * Copyright 2015 TJ <hacker@iam.tj>.
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 TJ <hacker@iam.tj>
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     Class<?> _class;
63     MessageAbstract _message;
64     
65     /**
66      * Default constructor.
67      */
68     NetworkMessage() {
69         _serializeLength = -1;
70         _intent = null;
71         _serviceSender = null;
72         _serviceTarget = null;
73         _class = null;
74         _message = null;
75     }
76     
77     /**
78      * Create a message for passing over the network.
79      * 
80      * @param intent The purpose of the attached object
81      * @param target The title of the target service
82      * @param message The Message being passed. Must implement java.io.Serialize or only
83      * contain native types that are serializable
84      * @throws IllegalArgumentException 
85      */
86     NetworkMessage(String intent, String target, MessageAbstract message) throws IllegalArgumentException {
87         _serializeLength = -1;
88         if ( !(intent != null && intent.length() > 0) ) 
89             throw(new IllegalArgumentException("intent cannot be null or empty"));
90         if (message == null)
91             throw(new IllegalArgumentException("message cannot be null"));
92         _intent = intent;
93         _serviceSender = null;
94         _serviceTarget = target;
95         _message = message;
96         _class = _message.getClass();
97     }
98     
99     /**
100      * Set the number of bytes used by the serialized object.
101      * 
102      * @param length number of bytes
103      */
104     void setSerializeLength(int length) {
105         _serializeLength = length;
106     }
107
108     /**
109      * Set to the title of the sending service to allow replies to be routed correctly.
110      * 
111      * @param sender title of the sending service
112      */
113     public void setSender(String sender) {
114         _serviceSender = sender;
115     }
116     
117     /**
118      * Get the title of the service that sent this message.
119      * 
120      * @return title of the sending service
121      */
122     public String getSender() {
123         return _serviceSender;
124     }
125
126     /**
127      * Set to the title of the target service to allow correct routing.
128      * 
129      * @param target title of the destination service
130      */
131     public void setTarget(String target) {
132         _serviceTarget = target;
133     }
134
135     /**
136      * Get the name of the sending service.
137      * 
138      * @return the sender
139      */
140     public String getTarget() {
141         return _serviceTarget;
142     }
143
144     /**
145      * Get the intended destination topic.
146      * @return the intent
147      */
148     public String getIntent() {
149         return _intent;
150     }
151     /**
152      * Get the content of this NetworkMessage.
153      * 
154      * @return the encapsulated message
155      */
156     public MessageAbstract getMessage() {
157         return _message;
158     }
159
160     /**
161      * Get the size of the serialized object.
162      * 
163      * @return -1 if not yet serialized, otherwise number of bytes required
164      */
165     public int getSerializeLength() {
166         return _serializeLength;
167     }
168     
169     /**
170      * Create a message for passing over the network.
171      * 
172      * @param intent The purpose of the attached object
173      * @param target The title of the target service
174      * @param message The encapsulated message
175      * contain native types that are serializable
176      * @return a freshly constructed NetworkMessage object
177      * @throws IllegalArgumentException 
178      */
179     public static NetworkMessage createNetworkMessage(String intent, String target, MessageAbstract message) throws IllegalArgumentException {
180         return new NetworkMessage(intent, target, message);            
181     }
182
183     /**
184      * Make a deep clone of this message and all its member objects.
185      * 
186      * It is not possible to safely use shallow or deep cloning of all this class's
187  members since its _message member reference could be to an object of any type,
188  and those types and their member attributes may not override the Object.clone()
189  method via the Cloneable interface.
190  
191  Cannot rely on using copy constructors since _message (or its member attributes)
192  may reference an object  that does not have a copy constructor.
193  
194  That leaves an expensive (in time) but reliable trick - serialize and deserialize 
195  the object to/from a byte stream. Since NetworkMessage already has methods supporting
196  serialization this is a simple solution to implement.
197      * 
198      * @see NetworkMessage#serialize
199      * @see NetworkMessage#deserialize
200      * @param message The message to clone
201      * @return Deep clone of the message object
202      * @throws java.lang.CloneNotSupportedException
203      */
204     public static NetworkMessage clone(NetworkMessage message) throws CloneNotSupportedException {
205         NetworkMessage result = null;
206         
207         if (! (message instanceof Serializable))
208             throw new CloneNotSupportedException();
209
210         try {
211             result = deserialize(serialize(message));
212         } catch (IOException | ClassNotFoundException e) {
213             throw new CloneNotSupportedException();
214         }
215
216         return result;
217     }
218     
219     /**
220      * Support the Cloneable interface but use the class's static serialization clone method.
221      * 
222      * @see NetworkMessage#clone(NetworkMessage) 
223      * @return Deep clone of this object
224      * @throws CloneNotSupportedException
225      */
226     @Override
227     @SuppressWarnings("CloneDoesntCallSuperClone")
228     protected Object clone() throws CloneNotSupportedException {
229         return NetworkMessage.clone(this);
230     }
231     
232     /**
233      * Serialize the message.
234      * 
235      * @param message
236      * @return the message as an array of bytes
237      * @throws IOException
238      */
239     public static byte[] serialize(NetworkMessage message) throws IOException {
240         ByteArrayOutputStream baos = new ByteArrayOutputStream();
241         ObjectOutputStream oos = new ObjectOutputStream(baos);
242         oos.writeObject(message);
243         byte[] bytes = baos.toByteArray();
244         message.setSerializeLength(bytes.length);
245         return bytes;
246     }
247     
248     /**
249      * Deserialize raw data into a NetworkMessage.
250      * 
251      * @param bytes raw bytes
252      * @return the NetworkMessage object
253      * @throws IOException
254      * @throws ClassNotFoundException 
255      */
256     public static NetworkMessage deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
257         ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
258         ObjectInputStream ois = new ObjectInputStream(bais);
259         NetworkMessage temp = (NetworkMessage) ois.readObject();
260         temp.setSerializeLength(bytes.length);
261         return temp;
262     }
263 }