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 }