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