/*
 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.