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