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 * 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 * Create a message for passing over the network.
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
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"));
96 throw(new IllegalArgumentException("message cannot be null"));
98 _serviceSender = null;
99 _serviceTarget = target;
101 _class = _message.getClass();
105 * Set the number of bytes used by the serialized object.
107 * @param length number of bytes
109 void setSerializeLength(int length) {
110 _serializeLength = length;
114 * Set to the title of the sending service to allow replies to be routed correctly.
116 * @param sender title of the sending service
118 public void setSender(String sender) {
119 _serviceSender = sender;
123 * Get the title of the service that sent this message.
125 * @return title of the sending service
127 public String getSender() {
128 return _serviceSender;
132 * Set to the title of the target service to allow correct routing.
134 * @param target title of the destination service
136 public void setTarget(String target) {
137 _serviceTarget = target;
141 * Get the name of the sending service.
145 public String getTarget() {
146 return _serviceTarget;
150 * Get the intended destination topic.
153 public String getIntent() {
157 * Get the content of this NetworkMessage.
159 * @return the encapsulated message
161 public MessageAbstract getMessage() {
166 * Get the size of the serialized object.
168 * @return -1 if not yet serialized, otherwise number of bytes required
170 public int getSerializeLength() {
171 return _serializeLength;
175 * Set the key of the TCP stream in the messsage.
179 public void setKey(long key) {
184 * Get the key set by the ServerTCP.
186 * @return the key of the stream associated
188 public long getKey() {
193 * Create a message for passing over the network.
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
202 public static NetworkMessage createNetworkMessage(String intent, String target, MessageAbstract message) throws IllegalArgumentException {
203 return new NetworkMessage(intent, target, message);
207 * Make a deep clone of this message and all its member objects.
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.
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.
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.
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
227 public static NetworkMessage clone(NetworkMessage message) throws CloneNotSupportedException {
228 NetworkMessage result = null;
230 if (! (message instanceof Serializable))
231 throw new CloneNotSupportedException();
234 result = deserialize(serialize(message));
235 } catch (IOException | ClassNotFoundException e) {
236 throw new CloneNotSupportedException();
243 * Support the Cloneable interface but use the class's static serialization clone method.
245 * @see NetworkMessage#clone(NetworkMessage)
246 * @return Deep clone of this object
247 * @throws CloneNotSupportedException
250 @SuppressWarnings("CloneDoesntCallSuperClone")
251 protected Object clone() throws CloneNotSupportedException {
252 return NetworkMessage.clone(this);
256 * Serialize the message.
259 * @return the message as an array of bytes
260 * @throws IOException
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);
272 * Deserialize raw data into a NetworkMessage.
274 * @param bytes raw bytes
275 * @return the NetworkMessage object
276 * @throws IOException
277 * @throws ClassNotFoundException
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);