4 * Copyright 2015 Eddie Berrisford-Lynch <dev@fun2be.me>.
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
24 package uk.ac.ntu.n0521366.wsyd.libs.net;
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;
35 * Wraps an object and intent label together for passing over a network.
37 * @see NetworkMessageEvent
38 * @author Eddie Berrisford-Lynch <dev@fun2be.me>
40 public class NetworkMessage implements Serializable, Cloneable {
42 * number of bytes used by serialized object
47 * Key describing purpose of message; receiver uses this to dispatch to the correct
53 * Filled in by the network server service that sends the message
55 String _serviceSender;
58 * Filled in by the message originator with the title of the destination service
60 String _serviceTarget;
63 * Optional user key of a NetworkStream to enable TCP to send userID on connection
68 MessageAbstract _message;
71 * Default constructor.
74 _serializeLength = -1;
76 _serviceSender = null;
77 _serviceTarget = null;
83 * Make the NetworkMessage printable.
85 * @return human readable text
88 public String toString() {
89 return new String("_serializedLength:" + _serializeLength +
90 ",_intent:" + _intent +
91 ",_serviceSender:" + _serviceSender +
92 ",_serviceTarget:" + _serviceTarget +
93 ",_message:" + _message.toString()
97 * Create a message for passing over the network.
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
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"));
110 throw(new IllegalArgumentException("message cannot be null"));
112 _serviceSender = null;
113 _serviceTarget = target;
115 // _class = _message.getClass();
119 * Set the number of bytes used by the serialized object.
121 * @param length number of bytes
123 void setSerializeLength(int length) {
124 _serializeLength = length;
128 * Set to the title of the sending service to allow replies to be routed correctly.
130 * @param sender title of the sending service
132 public void setSender(String sender) {
133 _serviceSender = sender;
137 * Get the title of the service that sent this message.
139 * @return title of the sending service
141 public String getSender() {
142 return _serviceSender;
146 * Set to the title of the target service to allow correct routing.
148 * @param target title of the destination service
150 public void setTarget(String target) {
151 _serviceTarget = target;
155 * Get the name of the sending service.
159 public String getTarget() {
160 return _serviceTarget;
164 * Get the intended destination topic.
167 public String getIntent() {
171 * Get the content of this NetworkMessage.
173 * @return the encapsulated message
175 public MessageAbstract getMessage() {
180 * Get the size of the serialized object.
182 * @return -1 if not yet serialized, otherwise number of bytes required
184 public int getSerializeLength() {
185 return _serializeLength;
189 * Set the key of the TCP stream in the messsage.
193 public void setKey(long key) {
198 * Get the key set by the ServerTCP.
200 * @return the key of the stream associated
202 public long getKey() {
207 * Create a message for passing over the network.
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
216 public static NetworkMessage createNetworkMessage(String intent, String target, MessageAbstract message) throws IllegalArgumentException {
217 return new NetworkMessage(intent, target, message);
221 * Make a deep clone of this message and all its member objects.
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.
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.
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.
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
241 public static NetworkMessage clone(NetworkMessage message) throws CloneNotSupportedException {
242 NetworkMessage result = null;
244 if (! (message instanceof Serializable))
245 throw new CloneNotSupportedException();
248 result = deserialize(serialize(message));
249 } catch (IOException | ClassNotFoundException e) {
250 throw new CloneNotSupportedException();
257 * Support the Cloneable interface but use the class's static serialization clone method.
259 * @see NetworkMessage#clone(NetworkMessage)
260 * @return Deep clone of this object
261 * @throws CloneNotSupportedException
264 @SuppressWarnings("CloneDoesntCallSuperClone")
265 protected Object clone() throws CloneNotSupportedException {
266 return NetworkMessage.clone(this);
270 * Serialize the message.
273 * @return the message as an array of bytes
274 * @throws IOException
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);
286 * Deserialize raw data into a NetworkMessage.
288 * @param bytes raw bytes
289 * @return the NetworkMessage object
290 * @throws IOException
291 * @throws ClassNotFoundException
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);