1 package org.sim0mq.message.federationmanager;
2
3 import org.sim0mq.Sim0MQException;
4 import org.sim0mq.message.MessageStatus;
5 import org.sim0mq.message.Sim0MQMessage;
6 import org.sim0mq.message.SimulationMessage;
7
8 import nl.tudelft.simulation.language.Throw;
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
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 }