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-2019 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 Magic number = |9|0|0|0|5|S|I|M|#|#| where ## stands for the version number, e.g., 01. Internally, the magic
97 * number is always coded as a UTF-8 String, so it always starts with a byte equal to 9.
98 */
99 public final Object getMagicNumber()
100 {
101 return VERSION;
102 }
103
104 /**
105 * @return simulationRunId
106 */
107 public final Object getSimulationRunId()
108 {
109 return this.simulationRunId;
110 }
111
112 /**
113 * @return senderId
114 */
115 public final Object getSenderId()
116 {
117 return this.senderId;
118 }
119
120 /**
121 * @return receiverId
122 */
123 public final Object getReceiverId()
124 {
125 return this.receiverId;
126 }
127
128 /**
129 * @return messageTypeId
130 */
131 public final Object getMessageTypeId()
132 {
133 return this.messageTypeId;
134 }
135
136 /**
137 * @return messageId
138 */
139 public final long getMessageId()
140 {
141 return this.messageId;
142 }
143
144 /**
145 * @return messageStatus
146 */
147 public final MessageStatus getMessageStatus()
148 {
149 return this.messageStatus;
150 }
151
152 /**
153 * Create a Sim0MQ object array of the fields.
154 * @return Object[] a Sim0MQ object array of the fields
155 */
156 public abstract Object[] createObjectArray();
157
158 /**
159 * Create a byte array of the fields.
160 * @return byte[] a Sim0MQ byte array of the content
161 * @throws Sim0MQException on unknown data type as part of the content
162 * @throws SerializationException when the byte array cannot be created, e.g. because the number of bytes does not match
163 */
164 public abstract byte[] createByteArray() throws Sim0MQException, SerializationException;
165
166 /**
167 * Get the number of payload fields in the message.
168 * @return short; the number of payload fields in the message.
169 */
170 public abstract short getNumberOfPayloadFields();
171
172 /**
173 * Check the consistency of a message from an Object[] that was received.
174 * @param fields Object[]; the fields in the message
175 * @param expectedPayloadFields the expected number of payload fields
176 * @param expectedMessageType the expected message type
177 * @param intendedReceiverId id of the intended receiver
178 * @throws Sim0MQException when errors in the message have been detected
179 */
180 public static void check(final Object[] fields, final int expectedPayloadFields, final String expectedMessageType,
181 final Object intendedReceiverId) throws Sim0MQException
182 {
183 Throw.when(fields.length != expectedPayloadFields + 8, Sim0MQException.class,
184 "Message " + expectedMessageType + " does not contain the right number of fields. " + "Expected: "
185 + (expectedPayloadFields + 8) + ", Actual: " + fields.length);
186
187 for (int i = 0; i < fields.length; i++)
188 {
189 Object field = fields[i];
190 if (field == null)
191 {
192 throw new Sim0MQException("Message " + expectedMessageType + " field " + i + " equals null");
193 }
194 }
195
196 Throw.when(!expectedMessageType.equals(fields[4].toString()), Sim0MQException.class,
197 "Message type not right -- should have been " + expectedMessageType);
198
199 Throw.when(!fields[3].equals(intendedReceiverId), Sim0MQException.class,
200 "Receiver in message of type " + expectedMessageType + " not right. Should have been: " + intendedReceiverId);
201
202 Throw.when(!(fields[7] instanceof Short), Sim0MQException.class,
203 "Message " + expectedMessageType + " does not have a short field[7] for the number of fields");
204 Throw.when(((Short) fields[7]).intValue() != expectedPayloadFields, Sim0MQException.class,
205 "Message " + expectedMessageType + " does not contain the right number of payload fields in field[7]");
206 }
207
208 /**
209 * Builder for the Sim0MQMessage. Can string setters together, and call build() at the end to build the actual message.
210 * <p>
211 * Copyright (c) 2016-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
212 * <br>
213 * BSD-style license. See <a href="http://sim0mq.org/docs/current/license.html">Sim0MQ License</a>.
214 * </p>
215 * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
216 * initial version Apr 22, 2017 <br>
217 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
218 * @param <B> the actual inherited builder for the return types.
219 */
220 public abstract static class Builder<B extends Sim0MQMessage.Builder<B>>
221 {
222 /**
223 * the Simulation run ids can be provided in different types. Examples are two 64-bit longs indicating a UUID, or a
224 * String with a UUID number, a String with meaningful identification, or a short or an int with a simulation run
225 * number.
226 */
227 @SuppressWarnings("checkstyle:visibilitymodifier")
228 protected Object simulationRunId;
229
230 /** The sender id can be used to send back a message to the sender at some later time. */
231 @SuppressWarnings("checkstyle:visibilitymodifier")
232 protected Object senderId;
233
234 /**
235 * The receiver id can be used to check whether the message is meant for us, or should be discarded (or an error can be
236 * sent if we receive a message not meant for us).
237 */
238 @SuppressWarnings("checkstyle:visibilitymodifier")
239 protected Object receiverId;
240
241 /**
242 * Message type ids can be defined per type of simulation, and can be provided in different types. Examples are a String
243 * with a meaningful identification, or a short or an int with a message type number.
244 */
245 @SuppressWarnings("checkstyle:visibilitymodifier")
246 protected Object messageTypeId;
247
248 /**
249 * The unique message number is meant to confirm with a callback that the message has been received correctly. The
250 * number is unique for the sender, so not globally within the federation.
251 */
252 @SuppressWarnings("checkstyle:visibilitymodifier")
253 protected long messageId;
254
255 /**
256 * Three different status messages are defined: 1 for new, 2 for change, and 3 for delete. This field is coded as a
257 * byte.
258 */
259 @SuppressWarnings("checkstyle:visibilitymodifier")
260 protected MessageStatus messageStatus;
261
262 /**
263 * Empty constructor.
264 */
265 public Builder()
266 {
267 // nothing to do.
268 }
269
270 /**
271 * @param newSimulationRunId set simulationRunId
272 * @return the original object for chaining
273 */
274 @SuppressWarnings("unchecked")
275 public final B setSimulationRunId(final Object newSimulationRunId)
276 {
277 this.simulationRunId = newSimulationRunId;
278 return (B) this;
279 }
280
281 /**
282 * @param newSenderId set senderId
283 * @return the original object for chaining
284 */
285 @SuppressWarnings("unchecked")
286 public final B setSenderId(final Object newSenderId)
287 {
288 this.senderId = newSenderId;
289 return (B) this;
290 }
291
292 /**
293 * @param newReceiverId set receiverId
294 * @return the original object for chaining
295 */
296 @SuppressWarnings("unchecked")
297 public final B setReceiverId(final Object newReceiverId)
298 {
299 this.receiverId = newReceiverId;
300 return (B) this;
301 }
302
303 /**
304 * @param newMessageTypeId set messageTypeId
305 * @return the original object for chaining
306 */
307 @SuppressWarnings("unchecked")
308 protected final B setMessageTypeId(final Object newMessageTypeId)
309 {
310 this.messageTypeId = newMessageTypeId;
311 return (B) this;
312 }
313
314 /**
315 * @param newMessageId set messageId
316 * @return the original object for chaining
317 */
318 @SuppressWarnings("unchecked")
319 public final B setMessageId(final long newMessageId)
320 {
321 this.messageId = newMessageId;
322 return (B) this;
323 }
324
325 /**
326 * @param newMessageStatus set messageStatus
327 * @return the original object for chaining
328 */
329 @SuppressWarnings("unchecked")
330 protected final B setMessageStatus(final MessageStatus newMessageStatus)
331 {
332 this.messageStatus = newMessageStatus;
333 return (B) this;
334 }
335
336 /**
337 * Build the object.
338 * @return the message object from the builder.
339 * @throws Sim0MQException on unknown data type
340 * @throws NullPointerException when one of the parameters is null
341 */
342 public abstract Sim0MQMessage build() throws Sim0MQException, NullPointerException;
343
344 }
345 }