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