View Javadoc
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 }