View Javadoc
1   /*
2    * Copyright 2008 Ange Optimization ApS
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  /**
17   * @author Kim Hansen
18   */
19  package eu.simuline.octave;
20  
21  import java.io.File;
22  import java.io.OutputStreamWriter;
23  import java.io.Writer;
24  
25  import java.nio.charset.Charset;
26  import java.nio.charset.StandardCharsets;
27  
28  import java.util.Arrays;
29  import java.util.Objects;
30  
31  /**
32   * Factory that creates OctaveEngines. 
33   * First of all, create an OctaveEngineFactory 
34   * using the default constructor {@link #OctaveEngineFactory()} 
35   * then, optionally, change parameters 
36   * and finally create an Octave Engine using {@link #getScriptEngine()} 
37   * with the current parameters. 
38   * To set a parameter, use the various setter methods. 
39   * In the documentation of each setter method, 
40   * also the default value is documented 
41   * which is used to create an {@link OctaveEngine} 
42   * if the setter method is not invoked. 
43   * Note that setter methods return the factory after modification 
44   * and so setter methods may be queued also. 
45   * 
46   */
47  public final class OctaveEngineFactory {
48  
49      /**
50       * The custom system property 
51       * which determines where the executable is found if {@link #octaveProgramFile} is not set. 
52       * A custom property is set when invoking the runtime with the option <code>-D</code> 
53       * For example <code>java -Deu.simuline.octave.executable=myoctave Application</code> 
54       * runs class <code>Application</code> 
55       * setting the system property <code>eu.simuline.octave.executable</code> 
56       * to 
57       */
58      public static final String PROPERTY_EXECUTABLE = 
59  	"eu.simuline.octave.executable";
60  
61      /**
62       * If this is not <code>null</code>, the octave engine created 
63       * writes the output to that log writer also. 
64       * By default, this is <code>null</code>. 
65       * The according setter method is {@link #setOctaveInputLog(Writer)}. 
66       */
67      private Writer octaveInputLog = null;
68      
69      /**
70       * The character set used to transform input stream , 
71       * output stream and error stream for 'octave process'. 
72       * The default value is {@link StandardCharsets#UTF_8} 
73       * which is the only value for octave TBD: reference. 
74       */
75      public Charset charset = StandardCharsets.UTF_8;
76      
77      /**
78       * The error writer for the octave process. 
79       * By default, this is just {@link System#err}. <!-- TBD: clarify encoding -->
80       * The according setter method is {@link #setErrorWriter(Writer)}. 
81       */
82      private Writer errWriter = new OutputStreamWriter(System.err);
83  
84      /**
85       * The file containing the octave program or is <code>null</code>. 
86       * In the latter case, the name of the octave program command 
87       * is determined as described for {@link #octaveProgramCmd}. 
88       * By default, this is <code>null</code>. 
89       * The according setter method is {@link #setOctaveProgramFile(File)}. 
90       */
91      private File octaveProgramFile = null;
92  
93      /**
94       * The command which determines the octave executable 
95       * if {@link #octaveProgramFile} is <code>null</code> 
96       * and if the property {@link #PROPERTY_EXECUTABLE} is not set. 
97       * By default, this is "<code>octave</code>". 
98       * The according setter method is {@link #setOctaveProgramCmd(String)}. 
99       */
100     private String octaveProgramCmd = "octave";
101 
102     /**
103      * The array of arguments of the octave engines created. 
104      * For details, see octave user manual, version 5.2.0, Section 2.1.1. 
105      * <p>
106      * Default value of this field is default value for octave engines created.
107      * The default value consists of the following components: 
108      * <ul>
109      * <li>
110      * <code>--silent</code>: 
111      * prevents octave from printing the usual greeting and version message 
112      * at startup. 
113      * <li>
114      * <code>--no-init-file</code>, <code>--no-site-file</code> 
115      * prevents octave from reading the initialization files 
116      * <code>~/.octaverc</code>, <code>.octaverc</code> and 
117      * site-wide <code>octaverc</code>. 
118      * <li>
119      * </ul>
120      *
121      * The only mandatory argument is <code>--silent</code>: 
122      * If not set this, octave's greeting message causes an exception. 
123      * Option <code>--no-init-file</code> makes the result independent 
124      * of user input, 
125      * whereas <code>--no-init-file</code> and <code>--no-site-file</code> 
126      * makes it independent of initialization files. 
127      * Since this is used to create scripting engines, 
128      * line editing and history seem superfluous 
129      * and so <code>--no-line-editing</code> and <code>--no-history</code> 
130      * seem appropriate. 
131      * Note that <code>--no-init-file</code> and <code>--no-site-file</code> 
132      * may be appropriate or not. 
133      * Instead of combining <code>--no-init-file</code> and <code>--no-site-file</code> 
134      * equivalently one can specify <code>--norc</code>. 
135      * As this is the default,  <code>--no-gui?</code> is not needed. 
136      * TBD: discuss --no-window-system
137      * Note that this array may be empty but can never be <code>null</code>. 
138      */
139     private String[] argsArray = {
140     	"--silent",          // greeting message causes exception **** 
141     	"--no-init-file",    // makes result depend on init file 
142      	"--no-site-file",    // see --no-init-file
143    	"--no-line-editing", // make independent of user input 
144     	"--no-history"       // superfluous, because commands come from scripts 
145     };
146 
147 
148     /**
149      * An array of strings of the form <code>name=value</code> 
150      * representing the environment, i.e. the set of environment variables 
151      * or <code>null</code>. 
152      * In the latter case, 
153      * the environment is inherited from the current process. 
154      * This field is initialized with <code>null</code>. 
155      */
156      private String[] environment = null;
157 
158     /**
159      * The file representing the working directory or <code>null</code>. 
160      * In the latter case, 
161      * the working directory is inherited from the current process. 
162      * By default, this is <code>null</code>. 
163      */
164     private File workingDir = null;
165 
166     /**
167      * The number of threads to be reused or 
168      * <code>-1</code> if there is no limit. 
169      * By default, this is <code>2</code>. 
170      */
171     private int numThreadsReuse = 2;
172 
173     /**
174      * Default constructor creating a factory with default parameters. 
175      */
176     public OctaveEngineFactory() {
177         // Empty constructor
178     }
179 
180     /**
181      * Returns a script engine with the parameters set for this factory. 
182      *
183      * @return 
184      *    a new OctaveEngine with the current parameters. 
185      */
186     public OctaveEngine getScriptEngine() {
187 	// determine the command/path of the octave program 
188 	String octaveProgramPathCmd = (this.octaveProgramFile == null)
189 	    ? System.getProperty(PROPERTY_EXECUTABLE, this.octaveProgramCmd)
190 	    : this.octaveProgramFile.getPath();
191 
192 	// determine the command array 
193 	final String[] cmdArray = new String[this.argsArray.length + 1];
194 	cmdArray[0] = octaveProgramPathCmd;
195 	System.arraycopy(this.argsArray, 0, cmdArray, 1, this.argsArray.length);
196 
197         return new OctaveEngine(this, 
198 		                this.numThreadsReuse,
199 				this.octaveInputLog, 
200 				this.errWriter,
201 				this.charset,
202 				cmdArray,
203 				this.environment,
204 				this.workingDir);
205     }
206 
207     /**
208      * Setter method for {@link #octaveInputLog}. 
209      *
210      * @param octaveInputLog
211      *    the octave input log to set or <code>null</code> if no such log is wanted. 
212      * @return
213      *    this octave engine factory after modification. 
214      */
215     public OctaveEngineFactory setOctaveInputLog(final Writer octaveInputLog) {
216         this.octaveInputLog = octaveInputLog;
217         return this;
218     }
219 
220     /**
221      * Setter method for {@link #errWriter}. 
222      *
223      * @param errWriter
224      *    the errWriter to set
225      *    This may be null TBC 
226      * @return
227      *    this octave engine factory after modification. 
228      */
229     public OctaveEngineFactory setErrorWriter(final Writer errWriter) {
230         this.errWriter = errWriter;
231         return this;
232     }
233 
234     /**
235      * Setter method for {@link #octaveProgramFile}. 
236      *
237      * @param octaveProgramFile
238      *    the octaveProgramFile to set or <code>null</code>. 
239      * @return
240      *    this octave engine factory after modification. 
241      */
242     public OctaveEngineFactory setOctaveProgramFile(final File octaveProgramFile) {
243         this.octaveProgramFile = octaveProgramFile;
244         return this;
245     }
246 
247     /**
248      * Setter method for {@link #octaveProgramCmd}. 
249      * This takes effect only, 
250      * if {@link #octaveProgramFile} is <code>null</code> 
251      * and if the property {@link #PROPERTY_EXECUTABLE} is not set. 
252      *
253      * @param octaveProgramCmd
254      *    the octave program executable to set
255      * @return
256      *    this octave engine factory after modification. 
257      * @throws NullPointerException
258      *    if <code>octaveProgramCmd</code> is <code>null</code>. 
259      */
260     public OctaveEngineFactory setOctaveProgramCmd(final String octaveProgramCmd) {
261 	Objects.requireNonNull(octaveProgramCmd, 
262 		"Octave command shall be non-null. ");
263         this.octaveProgramCmd = octaveProgramCmd;
264         return this;
265     }
266 
267     // TBD: optional: check that each entry does not contain a blank
268     /**
269      * Sets an array of arguments <code>argsArray</code> 
270      * used when creating an {@link OctaveEngine}. 
271      * The validity of the argument string is not proved. 
272      * Note that subsequent changes on the array <code>argsArray</code> 
273      * do not have any influence on this factory. 
274      * The default options 
275      * and a discussion of necessary options are 
276      * documented with {@link #argsArray}. 
277      *
278      * @param argsArray
279      *    the arguments as an array to set
280      * @return
281      *   this octave engine factory after modification. 
282      * @throws NullPointerException
283      *    if <code>argsArray</code> is <code>null</code> 
284      *    or contains a <code>null</code> entry. 
285      */
286     public OctaveEngineFactory setArgsArray(final String[] argsArray) {
287         // TBD: better to see the index where the null occurs. 
288         if (Arrays.stream(argsArray).anyMatch(p -> p == null)) {
289             throw new NullPointerException();
290         }
291         this.argsArray = Arrays.copyOf(argsArray, argsArray.length);
292         return this;
293     }
294     
295     /**
296      * Sets the encoding of input, output and error stream 
297      * of the 'octave process'. 
298      * The appropriate value for octave seems to be {@link StandardCharsets#UTF_8} 
299      * so this method may be used rarely.  
300      * 
301      * @param charset
302      *    the new charset. 
303      * @return
304      *   this octave engine factory after modification. 
305      * @throws NullPointerException
306      *    if <code>charset</code> is <code>null</code>. 
307      */
308     public OctaveEngineFactory setCharset(Charset charset) {
309  	if (charset == null) {
310  	    throw new NullPointerException("Found null character set. ");
311  	}
312  	this.charset = charset;
313  	return this;
314      }
315 
316 
317     /**
318      * Setter method for {@link #environment}. 
319      * Note that subsequent changes on the array <code>environment</code> 
320      * do not have any influence on this factory. 
321      * The details are documented with {@link #environment}. 
322      *
323      * @param environment
324      *    the environment 
325      *    <ul>
326      *    <li>as an array of entries 
327      *    of the form <code>name=value</code> </li>
328      *    <li>or <code>null</code> signifying 
329      *    that the environment is inherited from the invoking process. </li>  
330      * @return
331      *   this octave engine factory after modification. 
332      */
333     public OctaveEngineFactory setEnvironment(final String[] environment) {
334 	if (environment == null) {
335 	    this.environment = null;
336 	    return this;
337 	}
338         // TBD: better to see the index where the null occurs. 
339 	// TBD: check the form 'name=value'
340         if (Arrays.stream(environment).anyMatch(p -> p == null)) {
341             throw new NullPointerException();
342         }
343         this.environment = Arrays.copyOf(environment, environment.length);
344         return this;
345     }
346 
347     /**
348      * Setter method for {@link #workingDir}. 
349      *
350      * @param workingDir
351      *    the working directory to set 
352      *    or <code>null</code> signifying the current directory. 
353      * @return
354      *   this octave engine factory after modification. 
355      */
356     public OctaveEngineFactory setWorkingDir(final File workingDir) {
357         this.workingDir = workingDir;
358         return this;
359     }
360 
361     /**
362      * Sets the number of threads to be reused or <code>-1</code> 
363      * which indicates no limit. 
364      * The default value is 2 but this can be speed optimized 
365      * depending on the hardware. 
366      * The number of threads to be created shall be positive 
367      * or <code>-1</code> otherwise throwing an exception. 
368      * 
369      * @param numThreadsReuse
370      *    the number of threads for reuse which is positive or <code>-1</code>
371      * @return
372      *   this octave engine factory after modification. 
373      */
374     // TBC**** with -1 seems not to work: cached pool 
375     public OctaveEngineFactory setNumThreadsReuse(int numThreadsReuse) {
376 	if (numThreadsReuse == 0 || numThreadsReuse < -1) {
377 	    throw new IllegalArgumentException
378 	    ("Expected positive number of threads or -1 but found " + 
379 	    numThreadsReuse + ". ");
380 	}
381 	this.numThreadsReuse = numThreadsReuse;
382         return this;
383     }
384 }