View Javadoc
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 }