4 * Copyright 2015 TJ <hacker@iam.tj>.
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 TJ <hacker@iam.tj>
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 MessageAbstract _message;
66 * Default constructor.
69 _serializeLength = -1;
71 _serviceSender = null;
72 _serviceTarget = null;
78 * Create a message for passing over the network.
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
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"));
91 throw(new IllegalArgumentException("message cannot be null"));
93 _serviceSender = null;
94 _serviceTarget = target;
96 _class = _message.getClass();
100 * Set the number of bytes used by the serialized object.
102 * @param length number of bytes
104 void setSerializeLength(int length) {
105 _serializeLength = length;
109 * Set to the title of the sending service to allow replies to be routed correctly.
111 * @param sender title of the sending service
113 public void setSender(String sender) {
114 _serviceSender = sender;
118 * Get the title of the service that sent this message.
120 * @return title of the sending service
122 public String getSender() {
123 return _serviceSender;
127 * Set to the title of the target service to allow correct routing.
129 * @param target title of the destination service
131 public void setTarget(String target) {
132 _serviceTarget = target;
136 * Get the name of the sending service.
140 public String getTarget() {
141 return _serviceTarget;
145 * Get the intended destination topic.
148 public String getIntent() {
152 * Get the content of this NetworkMessage.
154 * @return the encapsulated message
156 public MessageAbstract getMessage() {
161 * Get the size of the serialized object.
163 * @return -1 if not yet serialized, otherwise number of bytes required
165 public int getSerializeLength() {
166 return _serializeLength;
170 * Create a message for passing over the network.
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
179 public static NetworkMessage createNetworkMessage(String intent, String target, MessageAbstract message) throws IllegalArgumentException {
180 return new NetworkMessage(intent, target, message);
184 * Make a deep clone of this message and all its member objects.
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.
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.
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.
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
204 public static NetworkMessage clone(NetworkMessage message) throws CloneNotSupportedException {
205 NetworkMessage result = null;
207 if (! (message instanceof Serializable))
208 throw new CloneNotSupportedException();
211 result = deserialize(serialize(message));
212 } catch (IOException | ClassNotFoundException e) {
213 throw new CloneNotSupportedException();
220 * Support the Cloneable interface but use the class's static serialization clone method.
222 * @see NetworkMessage#clone(NetworkMessage)
223 * @return Deep clone of this object
224 * @throws CloneNotSupportedException
227 @SuppressWarnings("CloneDoesntCallSuperClone")
228 protected Object clone() throws CloneNotSupportedException {
229 return NetworkMessage.clone(this);
233 * Serialize the message.
236 * @return the message as an array of bytes
237 * @throws IOException
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);
249 * Deserialize raw data into a NetworkMessage.
251 * @param bytes raw bytes
252 * @return the NetworkMessage object
253 * @throws IOException
254 * @throws ClassNotFoundException
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);