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 }