/*
This file is part of the software framework JStatCom,
a library for building applications for data based analysis.
Copyright (C) 2000-2005 Markus Kraetzig
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
*/
package com.jstatcom.engine.grte;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.util.Date;
import org.apache.log4j.Logger;
import com.aptech.gaussApi5_0.Gauss;
import com.aptech.gaussApi5_0.GaussException;
import com.aptech.gaussApi5_0.GaussLibrary;
import com.aptech.gaussApi5_0.GaussMatrix;
import com.aptech.gaussApi5_0.GaussReader;
import com.aptech.gaussApi5_0.GaussStringArray;
import com.aptech.gaussApi5_0.GaussWorkspace;
import com.jstatcom.engine.ConfigHolder;
import com.jstatcom.engine.Engine;
import com.jstatcom.engine.EngineTypes;
import com.jstatcom.engine.LoadTypes;
import com.jstatcom.model.JSCConstants;
import com.jstatcom.model.JSCData;
import com.jstatcom.model.JSCInt;
import com.jstatcom.model.JSCNArray;
import com.jstatcom.model.JSCNumber;
import com.jstatcom.model.JSCSArray;
import com.jstatcom.model.JSCString;
import com.jstatcom.model.JSCTypes;
/**
* Provides communication to the Gauss Runtime Engine and conforms to the
* Engine interface. This class serves as an adapter to the
* GaussWorkspace class of the GaussApi. Currently only realy
* numbers and arrays are recognized, the complex part of returns is ignored.
*
* Usage Note:
* The engine can be configured via the respective XML config file. On
* initialization the given GCG file is loaded and the procedure
* initJGRTE defined in jgrte.src is called. On
* shutdown the method shutdownGRTE is called. It
* is assumed that these method are compiled into the loaded GCG file before.
* Change the respective GAUSS code according to your needs. The default
* functionality depends on the system library jgrte.dll
* (Windows) to manage the display of graphics, which would otherwise not work
* properly.
*
* By default this communication class assumes that the GRT type engine version
* 5.0 is loaded. This can be changed via setting the system library in the XML
* config file.
*
* All resources must be placed in the subdirectory jgrte.
*
* @author Markus Kraetzig
*/
public final class GRTEngine implements Engine {
private static final Logger log = Logger.getLogger(GRTEngine.class);
private String workSpaceName = null;
private GaussWorkspace wk = null;
private static ConfigHolder cf = null;
private static GRTEngine grte = null;
private File graphicsDir = null;
/**
* Creates an instance of GRTEngine. This should only be
* invoked if more than one workspace is needed. Otherwise the static
* initializer should be preferred. This attaches System.out
* to the output and error writer of the created GaussWorkspace.
*
* @throws RuntimeException
* if something went wrong during the init process
* @throws GaussException
* if the workspace initialization failed
* @throws UnsatisfiedLinkError
* if system libraries cannot be found
*/
public GRTEngine() {
super();
init();
}
/**
* Does initialization tasks. Necessary on initialization and after
* shutdown.
*/
private void init() {
// Check whether workspace is still initialized.
if (wk != null && !wk.hasBeenDisposed())
return;
if (cf == null)
cf = ConfigHolder.valueOf(EngineTypes.GRTE);
if (!GaussLibrary.isLoaded()) {
GaussLibrary.setName(cf
.getConfig(GRTEConfigKeys.GAUSSAPI_SYSLIBNAME));
Gauss.setHome(EngineTypes.GRTE.DIR_NAME);
}
if (workSpaceName == null)
workSpaceName = "JGRTE_" + new Date().toString();
wk = new GaussWorkspace(workSpaceName);
GaussWorkspace.setGaussReader(new GaussReader(new StringReader("a")));
GaussWorkspace.addOutputWriter(new PrintWriter(System.out));
GaussWorkspace.addErrorWriter(new PrintWriter(System.out));
// Set the GRT library.
String gcgFile = EngineTypes.GRTE.DIR_NAME + File.separator
+ cf.getConfig(GRTEConfigKeys.GCG_FILENAME);
wk.load(new File(gcgFile));
if (!Boolean.valueOf(cf.getConfig(GRTEConfigKeys.DO_JGRTE_INIT))
.booleanValue())
return;
String graphicsPath = cf.getConfig(GRTEConfigKeys.GRAPHICS_DIR);
if (graphicsPath == null || graphicsPath.length() == 0)
graphicsPath = GRTEConfigKeys.GRAPHICS_DIR.defaultVal();
String error = null;
if ((error = GRTEConfigKeys.GRAPHICS_DIR.isValid(graphicsPath)) != null)
throw new RuntimeException(error);
String tmpDir;
try {
graphicsDir = File
.createTempFile("jst", "", new File(graphicsPath));
tmpDir = graphicsDir.getAbsolutePath();
// Create subdirectory for graphics. Will be deleted on
// shutdown.
graphicsDir = new File(tmpDir);
graphicsDir.delete();
graphicsDir.mkdir();
} catch (IOException e) {
throw new RuntimeException("Graphics directory " + graphicsDir
+ " could not be created.");
}
if (wk.getSymbolType("initJGRTE") == Gauss.PROCEDURE)
wk.call("initJGRTE", new Object[] { graphicsDir + File.separator,
"jgrte.tkf", EngineTypes.GRTE.DIR_NAME });
}
/**
* Calls a procedure with input and return parameters specified. If this
* method is called after shutdown then the initialization is
* done again. If JSCConstants.DEBUG == true, then
* initialization is done for every call. This allows to take changes in the
* compiled GCG file into account without restarting.
*
* IMPORTANT:
* Any GaussMatrix that is returned from a GRTE call that
* contains only a single NaN element is treated as an empty
* matrix and is transformed to an empty JSCData object of
* the specified type. This convention is choosen, because otherwise not
* initialized objects could not be returned.
*
* Please note:
* If a data object of type JSCTypes.INT is specified as
* return, then any double scalar will silently be cast to an int. It may
* happen that information is lost during that operation.
*
* @param procName
* procedure to be called
* @param args
* the data to be used as input, can be null or
* emtpy
* @param retData
* the data objects to store the return parameters, can be
* null or emtpy
* @throws IllegalArgumentException
* if args or retData contain
* elements that are null or
* if (procName == null || procName.length() == 0)
* or if procName is not a loaded procedure
* @throws RuntimeException
* if return types are not compatible with returns
* @throws GaussException
* if there were any GAUSS errors during the execution of the
* procedure
*/
public void call(String procName, JSCData[] args, JSCData[] retData) {
init();
// Do input checking.
if (procName == null || procName.length() == 0)
throw new IllegalArgumentException("Procedure name was empty.");
if (wk.getSymbolType(procName) != Gauss.PROCEDURE)
throw new IllegalArgumentException("Procedure " + procName
+ " is unknown.");
if (args == null)
args = new JSCData[0];
if (retData == null)
retData = new JSCData[0];
// Prepare input arguments.
Object[] gaussArgs = new Object[args.length];
for (int i = 0; i < gaussArgs.length; i++) {
if (args[i] == null)
throw new IllegalArgumentException(
"Argument at array position [" + i + "] is null.");
if (!isValid(args[i].type()))
throw new IllegalArgumentException("Argument " + args[i].type()
+ " at array position [" + i
+ "] cannot be handled by GRTE.");
gaussArgs[i] = getGaussEngineObject(args[i]);
}
// Check returns before executing for fast fail.
for (int i = 0; i < retData.length; i++) {
if (retData[i] == null)
throw new IllegalArgumentException("Return at array position ["
+ i + "] is null.");
if (!isValid(retData[i].type()))
throw new IllegalArgumentException("Return type "
+ retData[i].type() + " at array position [" + i
+ "] cannot be handled by GRTE.");
}
// Execute call.
Object[] rtnArgs = null;
try {
rtnArgs = wk.call(procName, gaussArgs);
} catch (Throwable ex) {
StringBuffer msg = new StringBuffer();
msg.append("Gauss procedure: \"" + procName + "\"\n");
/*
* StringBuffer arguments = new StringBuffer();
*
* arguments.append("arguments:\n"); if (gaussArgs != null &&
* args.length > 0) for (int i = 0; i < args.length; i++)
* arguments.append("arg_" + (i + 1) + ": " + args[i] + "\n"); else
* arguments.append("none\n");
*
* msg.append(arguments);
*/
msg.append(ex.getMessage());
throw new RuntimeException(msg.toString());
}
if (log.isDebugEnabled()) {
StringBuffer msg = new StringBuffer();
msg.append("procedure: \"" + procName + "\"\n");
msg.append("arguments:\n");
if (gaussArgs != null && args.length > 0)
for (int i = 0; i < args.length; i++)
msg.append("arg_" + (i + 1) + ": " + args[i] + "\n");
else
msg.append("none\n");
log.debug(msg);
}
// Check return arguments.
if (rtnArgs != null)
if (rtnArgs.length != retData.length)
throw new IllegalArgumentException(
"Error calling procedure: \"" + procName + "\":\n"
+ "Specified number of returns ("
+ retData.length
+ ") is different from actual number ("
+ rtnArgs.length + ").");
// Set values from GRTE returns.
setJSCDataFromGaussData(rtnArgs, retData);
}
/**
* Gets the GRTE type from arg with the same values.
*
* @param arg
* the data object to be transformed in a type understandable by
* the GRTE
* @return the data object to be used as argument for the GRTE
* @throws IllegalArgumentException
* if type is not recognized
*/
private Object getGaussEngineObject(JSCData arg) {
// NOT INITIALIZED.
if (arg.isEmpty())
return new GaussMatrix(new double[0][0]);
JSCTypes type = arg.type();
if (type == JSCTypes.NARRAY)
return new GaussMatrix(((JSCNArray) arg).doubleArray());
if (type == JSCTypes.NUMBER)
return new GaussMatrix(((JSCNumber) arg).doubleVal());
if (type == JSCTypes.INT)
return new GaussMatrix(((JSCInt) arg).doubleVal());
if (type == JSCTypes.SARRAY) {
GaussStringArray gsa = new GaussStringArray(((JSCSArray) arg)
.stringArray());
return gsa;
}
if (type == JSCTypes.STRING)
return ((JSCString) arg).string();
throw new IllegalArgumentException(type + " is not recognized.");
}
/**
* Sets the values from the elements of gaussData to the
* corresponding elements of jscData.
*
* @param gaussData
* the array with the data containing the values
* @param jscData
* the array with the data objects to set the values to
* @throws IllegalArgumentException
* if types are not compatible, if an array element is
* null or
* if (gaussData == null || jscData == null) or
* if the arrays have different lengths
*/
private void setJSCDataFromGaussData(Object[] gaussData, JSCData[] jscData) {
if (gaussData == null || jscData == null)
throw new IllegalArgumentException("Array argument was null.");
if (gaussData.length != jscData.length)
throw new IllegalArgumentException("Array lengths do not match.");
for (int i = 0; i < gaussData.length; i++) {
if (gaussData[i] == null || jscData[i] == null)
throw new IllegalArgumentException("Return at array index ["
+ i + "] was null.");
JSCTypes type = jscData[i].type();
if (gaussData[i] instanceof String) {
if (type != JSCTypes.STRING && type != JSCTypes.SARRAY)
throw new IllegalArgumentException("Specified return type "
+ type + " at array position [" + i
+ "] does not match with actual return type "
+ JSCTypes.STRING + "/" + JSCTypes.SARRAY + ".");
if (type == JSCTypes.STRING)
((JSCString) jscData[i]).setVal(gaussData[i].toString());
else
((JSCSArray) jscData[i])
.setVal(new String[][] { { gaussData[i].toString() } });
} else if (gaussData[i] instanceof GaussMatrix) {
GaussMatrix gMat = (GaussMatrix) gaussData[i];
// NOT INITIALIZED
if (gMat.getCols() == 0
|| gMat.getRows() == 0
|| (Double.isNaN(gMat.getReal(0, 0))
&& gMat.getCols() == 1 && gMat.getRows() == 1))
jscData[i].clear();
else if (gMat.getCols() > 1 || gMat.getRows() > 1) {
if (type != JSCTypes.NARRAY)
throw new IllegalArgumentException(
"Specified return type "
+ type
+ " at array position ["
+ i
+ "] does not match with actual return type "
+ JSCTypes.NARRAY + ".");
((JSCNArray) jscData[i]).setVal(gMat.getReals());
} else {
if (type != JSCTypes.NUMBER && type != JSCTypes.NARRAY
&& type != JSCTypes.INT)
throw new IllegalArgumentException(
"Specified return type "
+ type
+ " at array position ["
+ i
+ "] does not match with actual return type "
+ JSCTypes.NARRAY + "/"
+ JSCTypes.NUMBER + "/" + JSCTypes.INT
+ ".");
if (type == JSCTypes.NARRAY)
((JSCNArray) jscData[i]).setVal(gMat.getReals());
else if (type == JSCTypes.NUMBER)
((JSCNumber) jscData[i]).setVal(gMat.getReal(0, 0));
else
((JSCInt) jscData[i]).setVal(new Double(gMat.getReal(0,
0)).intValue());
}
} else if (gaussData[i] instanceof GaussStringArray) {
GaussStringArray gSArray = (GaussStringArray) gaussData[i];
// NOT INITIALIZED
if (gSArray.getCols() == 0 || gSArray.getRows() == 0)
jscData[i].clear();
else if (gSArray.getCols() > 1 || gSArray.getRows() > 1) {
if (type != JSCTypes.SARRAY)
throw new IllegalArgumentException(
"Specified return type "
+ type
+ " at array position ["
+ i
+ "] does not match with actual return type "
+ JSCTypes.SARRAY + ".");
((JSCSArray) jscData[i]).setVal(gSArray.getStrings());
} else {
if (type != JSCTypes.SARRAY && type != JSCTypes.STRING)
throw new IllegalArgumentException(
"Specified return type "
+ type
+ " at array position ["
+ i
+ "] does not match with actual return type "
+ JSCTypes.SARRAY + "/"
+ JSCTypes.STRING + ".");
if (type == JSCTypes.SARRAY)
((JSCSArray) jscData[i]).setVal(gSArray.getStrings());
else {
((JSCString) jscData[i])
.setVal(gSArray.getString(0, 0));
}
}
} else
throw new RuntimeException(
"Unknown GRTE return argument at position [" + i
+ "]: " + gaussData[i]);
}
}
/**
* Returns an initialized instance of the GRTEngine that is a
* Singleton.
*
* @return initialized GRTE instance
* @throws RuntimeException
* if something went wrong during the init process
* @throws GaussException
* if the workspace initialization failed
* @throws UnsatisfiedLinkError
* if system libraries cannot be found
*/
public static GRTEngine getInstance() {
if (grte == null)
grte = new GRTEngine();
else
grte.init();
return grte;
}
/**
* @see com.jstatcom.engine.Engine
*/
public boolean isValid(JSCTypes type) {
if (type == JSCTypes.NARRAY || type == JSCTypes.NUMBER
|| type == JSCTypes.INT || type == JSCTypes.SARRAY
|| type == JSCTypes.STRING)
return true;
return false;
}
/**
* Loads a module according to the load type. All filenames must be
* specified relative to the resource directory for the GRT engine. The
* args argument is ignored. If this command is called after
* shutdown then the initialization is done again.
*
* @param loadName
* the name of the module to load
* @param loadType
* the type of the load operation to invoke
* @param args
* always ignored
* @throws IllegalArgumentException
* if (loadName == null || loadType == null) or
* if (loadName.length() == 0) or if
* loadName is not valid
* @see com.jstatcom.engine.Engine
*/
public void load(String loadName, LoadTypes loadType, JSCData... args) {
if (loadName == null || loadType == null)
throw new IllegalArgumentException("Argument was null.");
if (loadName.length() == 0)
throw new IllegalArgumentException("Module name was empty.");
init();
// loads GCG file
if (loadType == GRTELoadTypes.GCG) {
log.debug("\nload " + loadType + ": " + loadName + "\n");
loadGCG(loadName);
}
// sets a global symbol
if (loadType == GRTELoadTypes.GLOBAL) {
if (args == null)
throw new IllegalArgumentException(
"Global symbol agument missing.");
if (args.length != 1)
throw new IllegalArgumentException(
"Global symbol agument length was " + args.length
+ " instead of 1.");
JSCConstants.checkNameThrowEx(loadName);
// to check whether exists, otherwise exception thrown
wk.getGlobal(loadName);
wk.setGlobal(loadName, getGaussEngineObject(args[0]));
log.debug("\nload " + loadType + ": " + loadName);
log.debug("argument: " + args[0]);
}
}
/**
* Loads GCG.
*
* @param toLoad
* String
*/
private void loadGCG(String toLoad) {
String loadName = EngineTypes.GRTE.DIR_NAME + "/" + toLoad.trim();
File gcgFile = new File(loadName);
if (!gcgFile.isFile())
throw new RuntimeException("File " + gcgFile.getAbsolutePath()
+ " does not exist.");
if (wk == null)
throw new IllegalStateException("GaussWorkspace is null.");
wk.load(gcgFile);
}
/**
* Throws an Exception, because the task cannot be stopped.
*
* @throws UnsupportedOperationException
* @see com.jstatcom.engine.Engine#stop()
*/
public void stop() {
throw new UnsupportedOperationException(toString()
+ " tasks cannot be stopped.");
}
/**
* Deletes graphics directory and shuts down all workspaces by calling
* Gauss.shutdown().
*/
public void shutdown() {
if (graphicsDir != null && graphicsDir.isDirectory()) {
File[] tmpFiles = graphicsDir.listFiles();
for (int i = 0; i < tmpFiles.length; i++)
tmpFiles[i].delete();
graphicsDir.delete();
}
if (wk.getSymbolType("shutdownJGRTE") == Gauss.PROCEDURE)
wk.execute("shutdownJGRTE");
wk.clear();
Gauss.shutdown();
workSpaceName = null;
}
/**
* Gets the workspace that is encapsulated by this engine.
*
* @return the current workspace used for this engine
*/
public GaussWorkspace getGaussWorkSpace() {
return wk;
}
/**
* Gets the gauss workspace that this engine uses.
*
* @return gauss workspace
*/
public GaussWorkspace getWk() {
return wk;
}
}
Ads help maintain this website.