1 package org.sim0mq.message;
2
3 import java.io.Serializable;
4
5 import org.djutils.exceptions.Throw;
6 import org.djutils.serialization.SerializationException;
7 import org.sim0mq.Sim0MQException;
8
9 /**
10 * The abstract body of the message with the first fields of every Sim0MQ message.
11 * <p>
12 * Copyright (c) 2016-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
13 * BSD-style license. See <a href="http://sim0mq.org/docs/current/license.html">Sim0MQ License</a>.
14 * </p>
15 * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
16 * initial version Apr 22, 2017 <br>
17 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
18 */
19 public abstract class Sim0MQMessage implements Serializable
20 {
21 /** */
22 private static final long serialVersionUID = 20170422L;
23
24 /** version of the protocol, magic number. */
25 protected static final String VERSION = "SIM01";
26
27 /**
28 * the Simulation run ids can be provided in different types. Examples are two 64-bit longs indicating a UUID, or a String
29 * with a UUID number, a String with meaningful identification, or a short or an int with a simulation run number.
30 */
31 private final Object simulationRunId;
32
33 /** The sender id can be used to send back a message to the sender at some later time. */
34 private final Object senderId;
35
36 /**
37 * The receiver id can be used to check whether the message is meant for us, or should be discarded (or an error can be sent
38 * if we receive a message not meant for us).
39 */
40 private final Object receiverId;
41
42 /**
43 * Message type ids can be defined per type of simulation, and can be provided in different types. Examples are a String
44 * with a meaningful identification, or a short or an int with a message type number.
45 */
46 private final Object messageTypeId;
47
48 /**
49 * The unique message number is meant to confirm with a callback that the message has been received correctly. The number is
50 * unique for the sender, so not globally within the federation.
51 */
52 private final long messageId;
53
54 /**
55 * Three different status messages are defined: 1 for new, 2 for change, and 3 for delete. This field is coded as a byte.
56 */
57 private final MessageStatus messageStatus;
58
59 /**
60 * Encode the object array into a message.
61 * @param simulationRunId the Simulation run ids can be provided in different types. Examples are two 64-bit longs
62 * indicating a UUID, or a String with a UUID number, a String with meaningful identification, or a short or an
63 * int with a simulation run number.
64 * @param senderId The sender id can be used to send back a message to the sender at some later time.
65 * @param receiverId The receiver id can be used to check whether the message is meant for us, or should be discarded (or an
66 * error can be sent if we receive a message not meant for us).
67 * @param messageTypeId Message type ids can be defined per type of simulation, and can be provided in different types.
68 * Examples are a String with a meaningful identification, or a short or an int with a message type number.
69 * @param messageId The unique message number is meant to confirm with a callback that the message has been received
70 * correctly. The number is unique for the sender, so not globally within the federation.
71 * @param messageStatus Three different status messages are defined: 1 for new, 2 for change, and 3 for delete. This field
72 * is coded as a byte.
73 * @throws Sim0MQException on unknown data type
74 * @throws NullPointerException when one of the parameters is null
75 */
76 public Sim0MQMessage(final Object simulationRunId, final Object senderId, final Object receiverId,
77 final Object messageTypeId, final long messageId, final MessageStatus messageStatus)
78 throws Sim0MQException, NullPointerException
79 {
80 Throw.whenNull(simulationRunId, "simulationRunId cannot be null");
81 Throw.whenNull(senderId, "senderId cannot be null");
82 Throw.whenNull(receiverId, "receiverId cannot be null");
83 Throw.whenNull(messageTypeId, "messageTypeId cannot be null");
84 Throw.whenNull(messageId, "messageId cannot be null");
85 Throw.whenNull(messageStatus, "messageStatus cannot be null");
86
87 this.simulationRunId = simulationRunId;
88 this.senderId = senderId;
89 this.receiverId = receiverId;
90 this.messageTypeId = messageTypeId;
91 this.messageId = messageId;
92 this.messageStatus = messageStatus;
93 }
94
95 /**
96 * @return simulationRunId
97 */
98 public final Object getSimulationRunId()
99 {
100 return this.simulationRunId;
101 }
102
103 /**
104 * @return senderId
105 */
106 public final Object getSenderId()
107 {
108 return this.senderId;
109 }
110
111 /**
112 * @return receiverId
113 */
114 public final Object getReceiverId()
115 {
116 return this.receiverId;
117 }
118
119 /**
120 * @return messageTypeId
121 */
122 public final Object getMessageTypeId()
123 {
124 return this.messageTypeId;
125 }
126
127 /**
128 * @return messageId
129 */
130 public final long getMessageId()
131 {
132 return this.messageId;
133 }
134
135 /**
136 * @return messageStatus
137 */
138 public final MessageStatus getMessageStatus()
139 {
140 return this.messageStatus;
141 }
142
143 /**
144 * Create a Sim0MQ object array of the fields.
145 * @return Object[] a Sim0MQ object array of the fields
146 */
147 public abstract Object[] createObjectArray();
148
149 /**
150 * Create a byte array of the fields.
151 * @return byte[] a Sim0MQ byte array of the content
152 * @throws Sim0MQException on unknown data type as part of the content
153 * @throws SerializationException when the byte array cannot be created, e.g. because the number of bytes does not match
154 */
155 public abstract byte[] createByteArray() throws Sim0MQException, SerializationException;
156
157 /**
158 * Check the consistency of a message from an Object[] that was received.
159 * @param fields Object[]; the fields in the message
160 * @param expectedPayloadFields the expected number of payload fields
161 * @param expectedMessageType the expected message type
162 * @param intendedReceiverId id of the intended receiver
163 * @throws Sim0MQException when errors in the message have been detected
164 */
165 public static void check(final Object[] fields, final int expectedPayloadFields, final String expectedMessageType,
166 final Object intendedReceiverId) throws Sim0MQException
167 {
168 Throw.when(fields.length != expectedPayloadFields + 8, Sim0MQException.class,
169 "Message " + expectedMessageType + " does not contain the right number of fields");
170
171 for (int i = 0; i < fields.length; i++)
172 {
173 Object field = fields[i];
174 if (field == null)
175 {
176 throw new Sim0MQException("Message " + expectedMessageType + " field " + i + " equals null");
177 }
178 }
179
180 Throw.when(!expectedMessageType.equals(fields[4].toString()), Sim0MQException.class,
181 "Message type not right -- should have been " + expectedMessageType);
182
183 Throw.when(!fields[3].equals(intendedReceiverId), Sim0MQException.class,
184 "Receiver in message of type " + expectedMessageType + " not right. Should have been: " + intendedReceiverId);
185
186 Throw.when(!(fields[7] instanceof Short), Sim0MQException.class,
187 "Message " + expectedMessageType + " does not have a short field[7] for the number of fields");
188 Throw.when(((Short) fields[7]).intValue() != expectedPayloadFields, Sim0MQException.class,
189 "Message " + expectedMessageType + " does not contain the right number of payload fields in field[7]");
190 }
191
192 /**
193 * Builder for the Sim0MQMessage. Can string setters together, and call build() at the end to build the actual message.
194 * <p>
195 * Copyright (c) 2016-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
196 * <br>
197 * BSD-style license. See <a href="http://sim0mq.org/docs/current/license.html">Sim0MQ License</a>.
198 * </p>
199 * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
200 * initial version Apr 22, 2017 <br>
201 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
202 * @param <B> the actual inherited builder for the return types.
203 */
204 public abstract static class Builder<B extends Sim0MQMessage.Builder<B>>
205 {
206 /**
207 * the Simulation run ids can be provided in different types. Examples are two 64-bit longs indicating a UUID, or a
208 * String with a UUID number, a String with meaningful identification, or a short or an int with a simulation run
209 * number.
210 */
211 @SuppressWarnings("checkstyle:visibilitymodifier")
212 protected Object simulationRunId;
213
214 /** The sender id can be used to send back a message to the sender at some later time. */
215 @SuppressWarnings("checkstyle:visibilitymodifier")
216 protected Object senderId;
217
218 /**
219 * The receiver id can be used to check whether the message is meant for us, or should be discarded (or an error can be
220 * sent if we receive a message not meant for us).
221 */
222 @SuppressWarnings("checkstyle:visibilitymodifier")
223 protected Object receiverId;
224
225 /**
226 * Message type ids can be defined per type of simulation, and can be provided in different types. Examples are a String
227 * with a meaningful identification, or a short or an int with a message type number.
228 */
229 @SuppressWarnings("checkstyle:visibilitymodifier")
230 protected Object messageTypeId;
231
232 /**
233 * The unique message number is meant to confirm with a callback that the message has been received correctly. The
234 * number is unique for the sender, so not globally within the federation.
235 */
236 @SuppressWarnings("checkstyle:visibilitymodifier")
237 protected long messageId;
238
239 /**
240 * Three different status messages are defined: 1 for new, 2 for change, and 3 for delete. This field is coded as a
241 * byte.
242 */
243 @SuppressWarnings("checkstyle:visibilitymodifier")
244 protected MessageStatus messageStatus;
245
246 /**
247 * Empty constructor.
248 */
249 public Builder()
250 {
251 // nothing to do.
252 }
253
254 /**
255 * @param newSimulationRunId set simulationRunId
256 * @return the original object for chaining
257 */
258 @SuppressWarnings("unchecked")
259 public final B setSimulationRunId(final Object newSimulationRunId)
260 {
261 this.simulationRunId = newSimulationRunId;
262 return (B) this;
263 }
264
265 /**
266 * @param newSenderId set senderId
267 * @return the original object for chaining
268 */
269 @SuppressWarnings("unchecked")
270 public final B setSenderId(final Object newSenderId)
271 {
272 this.senderId = newSenderId;
273 return (B) this;
274 }
275
276 /**
277 * @param newReceiverId set receiverId
278 * @return the original object for chaining
279 */
280 @SuppressWarnings("unchecked")
281 public final B setReceiverId(final Object newReceiverId)
282 {
283 this.receiverId = newReceiverId;
284 return (B) this;
285 }
286
287 /**
288 * @param newMessageTypeId set messageTypeId
289 * @return the original object for chaining
290 */
291 @SuppressWarnings("unchecked")
292 protected final B setMessageTypeId(final Object newMessageTypeId)
293 {
294 this.messageTypeId = newMessageTypeId;
295 return (B) this;
296 }
297
298 /**
299 * @param newMessageId set messageId
300 * @return the original object for chaining
301 */
302 @SuppressWarnings("unchecked")
303 public final B setMessageId(final long newMessageId)
304 {
305 this.messageId = newMessageId;
306 return (B) this;
307 }
308
309 /**
310 * @param newMessageStatus set messageStatus
311 * @return the original object for chaining
312 */
313 @SuppressWarnings("unchecked")
314 protected final B setMessageStatus(final MessageStatus newMessageStatus)
315 {
316 this.messageStatus = newMessageStatus;
317 return (B) this;
318 }
319
320 /**
321 * Build the object.
322 * @return the message object from the builder.
323 * @throws Sim0MQException on unknown data type
324 * @throws NullPointerException when one of the parameters is null
325 */
326 public abstract Sim0MQMessage build() throws Sim0MQException, NullPointerException;
327
328 }
329 }