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 }