/** * Global Sensor Networks (GSN) Source Code * Copyright (c) 2006-2016, Ecole Polytechnique Federale de Lausanne (EPFL) * * This file is part of GSN. * * GSN is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GSN is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GSN. If not, see <http://www.gnu.org/licenses/>. * * File: src/ch/epfl/gsn/utils/MatlabEngine.java * * @author Jerome Rousselot * @author Ali Salehi * */ package ch.epfl.gsn.utils; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; /** * <b>Java MatlabEngine for Matlab via Runtime</b><br> * This class demonstrates how to call Matlab from a Java program, * thereby employing Matlab as the computation engine. * The Matlab engine operates by running in the background as a separate * process from your own program. * <p> * Date: 04.04.03 * <p> * Copyright (c) 2003 Andreas Klimke, Universitaet Stuttgart * <p> * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * <p> * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * <p> * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Original author: W. Andreas Klimke, University of Stuttgart * (klimke@ians.uni-stuttgart.de) * */ public class MatlabEngine { /** * The constructor initializes the buffer storing Matlab's output. */ public MatlabEngine() { // Initialize the ouput buffer (stores Matlab's output). outputBuffer = new char[DEFAULT_BUFFERSIZE]; } // indicates wether a Matlab session is currently open or closed private boolean isOpen = false; // a handle to the Matlab process private Process p; // the output stream of the Matlab process is piped to the stream 'in' private BufferedReader in; // the input stream of the Matlab process is piped to the stream 'out' private BufferedWriter out; // the error stream is piped to the stream 'err' private BufferedReader err; // Buffer size for receiving Matlab output. If buffer is full, remaining // data is retrieved and discarded. static final int DEFAULT_BUFFERSIZE = 65536; private char[] outputBuffer; // Once the output buffer is full, the remaining data is discarded into // a buffer, reading DEFAULT_SKIP characters at a time. private static final int DEFAULT_SKIP = 65536; // Stores the actual number of bytes read after last command. private int totalCharsRead; /** * Starts a Matlab session on the current host using the command 'matlab'. * @throws IOException Thrown if invoking Matlab was not successful. */ public void open() throws IOException { open("matlab"); } /** * Starts a process using the string passed as an argument. * The operating system executes the string passed in as argument * <it>litteraly</it>.<br> * Examples for starting Matlab (Unix-based systems):<br> * <ul> * <li> <code>startcmd = "matlab -nojvm -nosplash"</code> starts Matlab * on the current host without splash screen and whithout Java support. * <li> <code>startcmd = "rsh hostname \"/bin/csh -c * 'setenv DISPLAY hostname:0; matlab'\""</code> starts Matlab on the host * "hostname" (under Linux). * <li> <code>startcmd = "ssh hostname /bin/csh -c * 'setenv DISPLAY hostname:0; matlab'"</code> starts Matlab through a * secure shell (under Linux). * </ul> * @param startcmd The start command string. * @throws IOException Thrown if starting the process was not successful. */ public void open(String startcmd) throws IOException { if (isOpen) { System.err.println("Matlab session already open."); return; } try { // System.err.println("Opening Matlab..."); synchronized(this) { p = Runtime.getRuntime().exec(startcmd); // get handle on the input/output strings out = new BufferedWriter(new OutputStreamWriter (p.getOutputStream())); in = new BufferedReader(new InputStreamReader (p.getInputStream())); err = new BufferedReader(new InputStreamReader (p.getErrorStream())); isOpen = true; } // Wait for the Matlab process to respond. receive(); } catch(IOException e) { System.err.println("Matlab could not be opened."); throw(e); } } /** * Shuts down the Matlab session. After the "exit" command is written to * the output stream to close Matlab, the method waits for the Matlab * process to terminate. Finally, the input and the output streams are * closed. * @throws InterruptedException Thrown if interrupt occured while waiting * for the Matlab process to terminate. * @throws IOException Thrown if I/O streams could not be closed. */ public void close() throws InterruptedException, IOException { // System.err.println("Closing the Matlab session..."); send("exit"); try { synchronized(this) { p.waitFor(); out.close(); in.close(); isOpen = false; } } catch(InterruptedException e) { throw(e); } catch(IOException e) { System.err.println("Error while closing input/output streams."); throw(e); } } /** * Send data to the Matlab process. */ private void send(String str) throws IOException { try { // System.err.println("Evaluation string: "+str); // Adding the end of line character sequence will cause the Matlab // process to execute the command string. str+="\n"; synchronized(this) { // write string to Matlab's input stream out.write(str, 0, str.length()); out.flush(); } } catch(IOException e) { System.err.println("IOException occured while sending data to the" +" Matlab process."); throw(e); } } /** * Skip over data from the Matlab output. * Instead of directly using the skip method of the BufferedReader, * it was necessary to use read instead, since skip did work properly * with our Java version (J2RE SE, 1.4.1) and operating system (Linux). * The skip method of BufferedReader used to block instead of returning * the actual number of characters skipped. */ private void skip() throws IOException { char[] skipBuffer = new char[DEFAULT_SKIP]; while(in.ready()) { in.read(skipBuffer,0,DEFAULT_SKIP); } System.err.println("Warning: Some output information was "+ "dropped, since the available number of "+ "bytes exceeded the output buffer "+ "size."); } /** * Receive data from the Matlab process. */ private void receive() throws IOException { int charsRead = 0; int numberToRead; totalCharsRead = 0; // System.err.println("Receiving..."); try { synchronized(this) { while (totalCharsRead == 0) { while (in.ready()) { if ((numberToRead = DEFAULT_BUFFERSIZE-totalCharsRead) > 0) { charsRead = in.read(outputBuffer, totalCharsRead, numberToRead); if (charsRead > 0) { totalCharsRead += charsRead; } } else { skip(); return; } } } } } catch(IOException e) { System.err.println("IOException occured while receiving data from" +" the Matlab process."); throw(e); } } /** * Evaluates the expression contained in <code>str</code> using * the current Matlab session, previously started by <code>open</code>. * Note: The method blocks until the result stream is received from * Matlab. The result is written to a private buffer, and may be * retrieved with <code>getOutputString</code>. * @param str Expression to be evaluated. * @throws IOException Thrown if data could not be sent to Matlab. This * may happen if Matlab is no longer running. * @see #open * @see #getOutputString */ public void evalString(String str) throws IOException { send(str); receive(); } /** * Reads a maximum of <code>numberOfChars</code> characters from the * buffer containg Matlab's output. * @return string containing the Matlab output. * @see #open */ public String getOutputString (int numberOfChars) { if (totalCharsRead < numberOfChars) numberOfChars = totalCharsRead; char[] result = new char[numberOfChars]; System.arraycopy(outputBuffer, 0, result, 0, numberOfChars); return new String(result); } }