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 }