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