1 package org.sim0mq.message.federationmanager; 2 3 import org.djutils.exceptions.Throw; 4 import org.djutils.serialization.SerializationException; 5 import org.sim0mq.Sim0MQException; 6 import org.sim0mq.message.MessageStatus; 7 import org.sim0mq.message.Sim0MQMessage; 8 import org.sim0mq.message.SimulationMessage; 9 10 /** 11 * StartFederateMessage, FM.1. When it receives a StartFederate message, the Federate starter creates a process to run the model 12 * with the specifications given in the message, such as the working directory, model file, output and error files etc. Creating 13 * a model instance in this way also requires a port number, to which the model instance should bind as a ROUTER. This port 14 * number is assigned by the Federate Starter. Federate Starter picks an available port from a range of ports on the machine it 15 * is running (which must be open to outside connection) and gives this to the model as an argument. If the binding is not 16 * successful, the Federate Starter creates generates a new port number. 17 * <p> 18 * Copyright (c) 2016-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br> 19 * BSD-style license. See <a href="http://sim0mq.org/docs/current/license.html">Sim0MQ License</a>. 20 * </p> 21 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a> 22 */ 23 public class FM1StartFederateMessage extends Sim0MQMessage 24 { 25 /** 26 * Id to identify the callback to know which model instance has been started, e.g. "IDVV.14". The model instance will use 27 * this as its sender id. 28 */ 29 private final String instanceId; 30 31 /** 32 * Code for the software to run, will be looked up in a table on the local computer to determine the path to start the 33 * software on that computer. Example: "java". If the softwarePath is defined, softwareCode can be an empty String (0 34 * characters). 35 */ 36 private final String softwareCode; 37 38 /** 39 * Arguments that the software needs, before the model file path and name; e.g. "–Xmx2G -jar" in case of a Java model. This 40 * String can be empty (0 characters). 41 */ 42 private final String argsBefore; 43 44 /** 45 * The actual path on the target computer where the model resides, including the model that needs to be run. This String 46 * cannot be empty. 47 */ 48 private final String modelPath; 49 50 /** 51 * Arguments that the software or the model needs, after the model file path and name; e.g. arguments for the model itself 52 * to run like a data file or a data location . This String can be empty (0 characters), but usually we would want to send 53 * the port number(s) or a location where the model can find it as well as the name under which the model was registered. 54 */ 55 private final String argsAfter; 56 57 /** 58 * Full path on the target computer that will be used as the working directory. Some files may be temporarily stored there. 59 * If the working directory does not exist yet, it will be created. 60 */ 61 private final String workingDirectory; 62 63 /** Place to get user input from in case a model asks for it (it shouldn't, by the way). */ 64 private final String redirectStdin; 65 66 /** 67 * Place to send the output to that the model normally displays on the console. If this is not redirected, the memory buffer 68 * for the stdout might get full, and the model might stop as a result. On Linux systems, this often redirected to 69 * /dev/null. On Windows systems, this can e.g., be redirected to a file "out.txt" in the current working directory. For 70 * now, it has to be a path name (including /dev/null as being acceptable). If no full path is given, the filename is 71 * relative to the working directory. 72 */ 73 private final String redirectStdout; 74 75 /** 76 * Place to send the error messages to that the model normally displays on the console. If this is not redirected, the 77 * memory buffer for the stderr might get full, and the model might stop as a result. On Linux systems, this often 78 * redirected to /dev/null. On Windows systems, this can e.g., be redirected to a file "err.txt" in the current working 79 * directory. For now, it has to be a path name (including /dev/null as being acceptable). If no full path is given, the 80 * filename is relative to the working directory. 81 */ 82 private final String redirectStderr; 83 84 /** Whether to delete the working directory after the run of the model or not. */ 85 private final boolean deleteWorkingDirectory; 86 87 /** 88 * Whether to delete the redirected stdout after running or not (in case it is stored in a different place than the working 89 * directory). 90 */ 91 private final boolean deleteStdout; 92 93 /** 94 * Whether to delete the redirected stderr after running or not (in case it is stored in a different place than the working 95 * directory). 96 */ 97 private final boolean deleteStderr; 98 99 /** the unique message id. */ 100 private static final String MESSAGETYPE = "FM.1"; 101 102 /** */ 103 private static final long serialVersionUID = 20170422L; 104 105 /** 106 * @param simulationRunId the Simulation run ids can be provided in different types. Examples are two 64-bit longs 107 * indicating a UUID, or a String with a UUID number, a String with meaningful identification, or a short or an 108 * int with a simulation run number. 109 * @param senderId The sender id can be used to send back a message to the sender at some later time. 110 * @param receiverId The receiver id can be used to check whether the message is meant for us, or should be discarded (or an 111 * error can be sent if we receive a message not meant for us). 112 * @param messageId The unique message number is meant to confirm with a callback that the message has been received 113 * correctly. The number is unique for the sender, so not globally within the federation. 114 * @param instanceId Id to identify the callback to know which model instance has been started, e.g. "IDVV.14". The model 115 * instance will use this as its sender id. 116 * @param softwareCode Code for the software to run, will be looked up in a table on the local computer to determine the 117 * path to start the software on that computer. Example: "java". If the softwarePath is defined, softwareCode can 118 * be an empty String (0 characters). 119 * @param argsBefore Arguments that the software needs, before the model file path and name; e.g. "–Xmx2G -jar" in case of a 120 * Java model. This String can be empty (0 characters). 121 * @param modelPath The actual path on the target computer where the model resides, including the model that needs to be 122 * run. This String cannot be empty. 123 * @param argsAfter Arguments that the software or the model needs, after the model file path and name; e.g. arguments for 124 * the model itself to run like a data file or a data location . This String can be empty (0 characters), but 125 * usually we would want to send the port number(s) or a location where the model can find it as well as the name 126 * under which the model was registered. 127 * @param workingDirectory Full path on the target computer that will be used as the working directory. Some files may be 128 * temporarily stored there. If the working directory does not exist yet, it will be created. 129 * @param redirectStdin Place to get user input from in case a model asks for it (it shouldn't, by the way). 130 * @param redirectStdout Place to send the output to that the model normally displays on the console. If this is not 131 * redirected, the memory buffer for the stdout might get full, and the model might stop as a result. On Linux 132 * systems, this often redirected to /dev/null. On Windows systems, this can e.g., be redirected to a file 133 * "out.txt" in the current working directory. For now, it has to be a path name (including /dev/null as being 134 * acceptable). If no full path is given, the filename is relative to the working directory. 135 * @param redirectStderr Place to send the error messages to that the model normally displays on the console. If this is not 136 * redirected, the memory buffer for the stderr might get full, and the model might stop as a result. On Linux 137 * systems, this often redirected to /dev/null. On Windows systems, this can e.g., be redirected to a file 138 * "err.txt" in the current working directory. For now, it has to be a path name (including /dev/null as being 139 * acceptable). If no full path is given, the filename is relative to the working directory. 140 * @param deleteWorkingDirectory Whether to delete the working directory after the run of the model or not. 141 * @param deleteStdout Whether to delete the redirected stdout after running or not (in case it is stored in a different 142 * place than the working directory) 143 * @param deleteStderr Whether to delete the redirected stderr after running or not (in case it is stored in a different 144 * place than the working directory) 145 * @throws Sim0MQException on unknown data type 146 * @throws NullPointerException when one of the parameters is null 147 */ 148 public FM1StartFederateMessage(final Object simulationRunId, final Object senderId, final Object receiverId, 149 final long messageId, final String instanceId, final String softwareCode, final String argsBefore, 150 final String modelPath, final String argsAfter, final String workingDirectory, final String redirectStdin, 151 final String redirectStdout, final String redirectStderr, final boolean deleteWorkingDirectory, 152 final boolean deleteStdout, final boolean deleteStderr) throws Sim0MQException, NullPointerException 153 { 154 super(simulationRunId, senderId, receiverId, MESSAGETYPE, messageId, MessageStatus.NEW); 155 Throw.whenNull(instanceId, "instanceId cannot be null"); 156 Throw.whenNull(softwareCode, "softwareCode cannot be null"); 157 Throw.whenNull(argsBefore, "argsBefore cannot be null"); 158 Throw.whenNull(modelPath, "modelPath cannot be null"); 159 Throw.whenNull(argsAfter, "argsAfter cannot be null"); 160 Throw.whenNull(workingDirectory, "workingDirectory cannot be null"); 161 Throw.whenNull(redirectStdin, "redirectStdin cannot be null"); 162 Throw.whenNull(redirectStdout, "redirectStdout cannot be null"); 163 Throw.whenNull(redirectStderr, "redirectStderr cannot be null"); 164 165 Throw.when(instanceId.isEmpty(), Sim0MQException.class, "instanceId cannot be empty"); 166 Throw.when(softwareCode.isEmpty(), Sim0MQException.class, "softwareCode cannot be empty"); 167 Throw.when(modelPath.isEmpty(), Sim0MQException.class, "modelPath cannot be empty"); 168 Throw.when(workingDirectory.isEmpty(), Sim0MQException.class, "workingDirectory cannot be empty"); 169 Throw.when(redirectStdout.isEmpty(), Sim0MQException.class, "redirectStdout cannot be empty"); 170 Throw.when(redirectStderr.isEmpty(), Sim0MQException.class, "redirectStderr cannot be empty"); 171 172 this.instanceId = instanceId; 173 this.softwareCode = softwareCode; 174 this.argsBefore = argsBefore; 175 this.modelPath = modelPath; 176 this.argsAfter = argsAfter; 177 this.workingDirectory = workingDirectory; 178 this.redirectStdin = redirectStdin; 179 this.redirectStdout = redirectStdout; 180 this.redirectStderr = redirectStderr; 181 this.deleteWorkingDirectory = deleteWorkingDirectory; 182 this.deleteStdout = deleteStdout; 183 this.deleteStderr = deleteStderr; 184 } 185 186 /** 187 * @return instanceId 188 */ 189 public final String getInstanceId() 190 { 191 return this.instanceId; 192 } 193 194 /** 195 * @return softwareCode 196 */ 197 public final String getSoftwareCode() 198 { 199 return this.softwareCode; 200 } 201 202 /** 203 * @return argsBefore 204 */ 205 public final String getArgsBefore() 206 { 207 return this.argsBefore; 208 } 209 210 /** 211 * @return modelPath 212 */ 213 public final String getModelPath() 214 { 215 return this.modelPath; 216 } 217 218 /** 219 * @return argsAfter 220 */ 221 public final String getArgsAfter() 222 { 223 return this.argsAfter; 224 } 225 226 /** 227 * @return workingDirectory 228 */ 229 public final String getWorkingDirectory() 230 { 231 return this.workingDirectory; 232 } 233 234 /** 235 * @return redirectStdin 236 */ 237 public final String getRedirectStdin() 238 { 239 return this.redirectStdin; 240 } 241 242 /** 243 * @return redirectStdout 244 */ 245 public final String getRedirectStdout() 246 { 247 return this.redirectStdout; 248 } 249 250 /** 251 * @return redirectStderr 252 */ 253 public final String getRedirectStderr() 254 { 255 return this.redirectStderr; 256 } 257 258 /** 259 * @return deleteWorkingDirectory 260 */ 261 public final boolean isDeleteWorkingDirectory() 262 { 263 return this.deleteWorkingDirectory; 264 } 265 266 /** 267 * @return deleteStdout 268 */ 269 public final boolean isDeleteStdout() 270 { 271 return this.deleteStdout; 272 } 273 274 /** 275 * @return deleteStderr 276 */ 277 public final boolean isDeleteStderr() 278 { 279 return this.deleteStderr; 280 } 281 282 /** 283 * @return messagetype 284 */ 285 public static final String getMessageType() 286 { 287 return MESSAGETYPE; 288 } 289 290 /** {@inheritDoc} */ 291 @Override 292 public short getNumberOfPayloadFields() 293 { 294 return 12; 295 } 296 297 /** {@inheritDoc} */ 298 @Override 299 public Object[] createObjectArray() 300 { 301 return new Object[] {getMagicNumber(), getSimulationRunId(), getSenderId(), getReceiverId(), getMessageTypeId(), 302 getMessageId(), getMessageStatus(), getNumberOfPayloadFields(), this.instanceId, this.softwareCode, 303 this.argsBefore, this.modelPath, this.argsAfter, this.workingDirectory, this.redirectStdin, this.redirectStdout, 304 this.redirectStderr, this.deleteWorkingDirectory, this.deleteStdout, this.deleteStderr}; 305 } 306 307 /** {@inheritDoc} */ 308 @Override 309 public byte[] createByteArray() throws Sim0MQException, SerializationException 310 { 311 return SimulationMessage.encodeUTF8(getSimulationRunId(), getSenderId(), getReceiverId(), getMessageTypeId(), 312 getMessageId(), getMessageStatus(), this.instanceId, this.softwareCode, this.argsBefore, this.modelPath, 313 this.argsAfter, this.workingDirectory, this.redirectStdin, this.redirectStdout, this.redirectStderr, 314 this.deleteWorkingDirectory, this.deleteStdout, this.deleteStderr); 315 } 316 317 /** 318 * Build a message from an Object[] that was received. 319 * @param fields Object[]; the fields in the message 320 * @param intendedReceiverId id of the intended receiver 321 * @return a Sim0MQ message 322 * @throws Sim0MQException when number of fields is not correct 323 */ 324 public static FM1StartFederateMessage createMessage(final Object[] fields, final Object intendedReceiverId) 325 throws Sim0MQException 326 { 327 check(fields, 12, MESSAGETYPE, intendedReceiverId); 328 return new FM1StartFederateMessage(fields[1], fields[2], fields[3], ((Long) fields[5]).longValue(), 329 fields[8].toString(), fields[9].toString(), fields[10].toString(), fields[11].toString(), fields[12].toString(), 330 fields[13].toString(), fields[14].toString(), fields[15].toString(), fields[16].toString(), 331 (Boolean) fields[17], (Boolean) fields[18], (Boolean) fields[19]); 332 } 333 334 /** 335 * Builder for the StartFederate Message. Can string setters together, and call build() at the end to build the actual 336 * message. 337 * <p> 338 * Copyright (c) 2016-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. 339 * <br> 340 * BSD-style license. See <a href="http://sim0mq.org/docs/current/license.html">Sim0MQ License</a>. 341 * </p> 342 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a> 343 */ 344 public static class Builder extends Sim0MQMessage.Builder<FM1StartFederateMessage.Builder> 345 { 346 /** 347 * Id to identify the callback to know which model instance has been started, e.g. "IDVV.14". The model instance will 348 * use this as its sender id. 349 */ 350 private String instanceId; 351 352 /** 353 * Code for the software to run, will be looked up in a table on the local computer to determine the path to start the 354 * software on that computer. Example: "java". If the softwarePath is defined, softwareCode can be an empty String (0 355 * characters). 356 */ 357 private String softwareCode; 358 359 /** 360 * Arguments that the software needs, before the model file path and name; e.g. "–Xmx2G -jar" in case of a Java model. 361 * This String can be empty (0 characters). 362 */ 363 private String argsBefore; 364 365 /** 366 * The actual path on the target computer where the model resides, including the model that needs to be run. This String 367 * cannot be empty. 368 */ 369 private String modelPath; 370 371 /** 372 * Arguments that the software or the model needs, after the model file path and name; e.g. arguments for the model 373 * itself to run like a data file or a data location . This String can be empty (0 characters), but usually we would 374 * want to send the port number(s) or a location where the model can find it as well as the name under which the model 375 * was registered. 376 */ 377 private String argsAfter; 378 379 /** 380 * Full path on the target computer that will be used as the working directory. Some files may be temporarily stored 381 * there. If the working directory does not exist yet, it will be created. 382 */ 383 private String workingDirectory; 384 385 /** Place to get user input from in case a model asks for it (it shouldn't, by the way). */ 386 private String redirectStdin; 387 388 /** 389 * Place to send the output to that the model normally displays on the console. If this is not redirected, the memory 390 * buffer for the stdout might get full, and the model might stop as a result. On Linux systems, this often redirected 391 * to /dev/null. On Windows systems, this can e.g., be redirected to a file "out.txt" in the current working directory. 392 * For now, it has to be a path name (including /dev/null as being acceptable). If no full path is given, the filename 393 * is relative to the working directory. 394 */ 395 private String redirectStdout; 396 397 /** 398 * Place to send the error messages to that the model normally displays on the console. If this is not redirected, the 399 * memory buffer for the stderr might get full, and the model might stop as a result. On Linux systems, this often 400 * redirected to /dev/null. On Windows systems, this can e.g., be redirected to a file "err.txt" in the current working 401 * directory. For now, it has to be a path name (including /dev/null as being acceptable). If no full path is given, the 402 * filename is relative to the working directory. 403 */ 404 private String redirectStderr; 405 406 /** Whether to delete the working directory after the run of the model or not. */ 407 private boolean deleteWorkingDirectory; 408 409 /** 410 * Whether to delete the redirected stdout after running or not (in case it is stored in a different place than the 411 * working directory). 412 */ 413 private boolean deleteStdout; 414 415 /** 416 * Whether to delete the redirected stderr after running or not (in case it is stored in a different place than the 417 * working directory). 418 */ 419 private boolean deleteStderr; 420 421 /** 422 * Empty constructor. 423 */ 424 public Builder() 425 { 426 // nothing to do. 427 } 428 429 /** 430 * @param newInstanceId set instanceId 431 * @return the original object for chaining 432 */ 433 public final Builder setInstanceId(final String newInstanceId) 434 { 435 this.instanceId = newInstanceId; 436 return this; 437 } 438 439 /** 440 * @param newSoftwareCode set softwareCode 441 * @return the original object for chaining 442 */ 443 public final Builder setSoftwareCode(final String newSoftwareCode) 444 { 445 this.softwareCode = newSoftwareCode; 446 return this; 447 } 448 449 /** 450 * @param newArgsBefore set argsBefore 451 * @return the original object for chaining 452 */ 453 public final Builder setArgsBefore(final String newArgsBefore) 454 { 455 this.argsBefore = newArgsBefore; 456 return this; 457 } 458 459 /** 460 * @param newModelPath set modelPath 461 * @return the original object for chaining 462 */ 463 public final Builder setModelPath(final String newModelPath) 464 { 465 this.modelPath = newModelPath; 466 return this; 467 } 468 469 /** 470 * @param newArgsAfter set argsAfter 471 * @return the original object for chaining 472 */ 473 public final Builder setArgsAfter(final String newArgsAfter) 474 { 475 this.argsAfter = newArgsAfter; 476 return this; 477 } 478 479 /** 480 * @param newWorkingDirectory set workingDirectory 481 * @return the original object for chaining 482 */ 483 public final Builder setWorkingDirectory(final String newWorkingDirectory) 484 { 485 this.workingDirectory = newWorkingDirectory; 486 return this; 487 } 488 489 /** 490 * @param newRedirectStdin set redirectStdin 491 * @return the original object for chaining 492 */ 493 public final Builder setRedirectStdin(final String newRedirectStdin) 494 { 495 this.redirectStdin = newRedirectStdin; 496 return this; 497 } 498 499 /** 500 * @param newRedirectStdout set redirectStdout 501 * @return the original object for chaining 502 */ 503 public final Builder setRedirectStdout(final String newRedirectStdout) 504 { 505 this.redirectStdout = newRedirectStdout; 506 return this; 507 } 508 509 /** 510 * @param rewRedirectStderr set redirectStderr 511 * @return the original object for chaining 512 */ 513 public final Builder setRedirectStderr(final String rewRedirectStderr) 514 { 515 this.redirectStderr = rewRedirectStderr; 516 return this; 517 } 518 519 /** 520 * @param newDeleteWorkingDirectory set deleteWorkingDirectory 521 * @return the original object for chaining 522 */ 523 public final Builder setDeleteWorkingDirectory(final boolean newDeleteWorkingDirectory) 524 { 525 this.deleteWorkingDirectory = newDeleteWorkingDirectory; 526 return this; 527 } 528 529 /** 530 * @param newDeleteStdout set deleteStdout 531 * @return the original object for chaining 532 */ 533 public final Builder setDeleteStdout(final boolean newDeleteStdout) 534 { 535 this.deleteStdout = newDeleteStdout; 536 return this; 537 } 538 539 /** 540 * @param newDeleteStderr set deleteStderr 541 * @return the original object for chaining 542 */ 543 public final Builder setDeleteStderr(final boolean newDeleteStderr) 544 { 545 this.deleteStderr = newDeleteStderr; 546 return this; 547 } 548 549 /** {@inheritDoc} */ 550 @Override 551 public FM1StartFederateMessage build() throws Sim0MQException, NullPointerException 552 { 553 return new FM1StartFederateMessage(this.simulationRunId, this.senderId, this.receiverId, this.messageId, 554 this.instanceId, this.softwareCode, this.argsBefore, this.modelPath, this.argsAfter, this.workingDirectory, 555 this.redirectStdin, this.redirectStdout, this.redirectStderr, this.deleteWorkingDirectory, 556 this.deleteStdout, this.deleteStderr); 557 } 558 559 } 560 }