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

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.jstatcom.engine.CFunc;
import com.jstatcom.engine.CMalloc;
import com.jstatcom.engine.CPtr;
import com.jstatcom.engine.Engine;
import com.jstatcom.engine.EngineTypes;
import com.jstatcom.engine.LoadTypes;
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;
import com.jstatcom.util.UMatrix;

/**
 * A StubEngine manages calls to methods defined in arbitrary
 * system libraries. It works on Win32, Linux (i386), and Solaris. It uses the
 * "Shared Stubs" example by Sheng Liang to dispatch method calls without the
 * need to create a distinguished JNI wrapper for every library to be used.
 * 

* The methods defined in the system library can take certain types as arguments * and returns, which are *

* The argument and return types must be compatible with the * JSCData objects defined in the call method. * * @author Markus Kraetzig */ public final class StubEngine implements Engine { // Holds pointers to deallocate memory after call was made. private final List pointerHolder = new ArrayList(); private static StubEngine stub = null; private String systemLib = null; /** * Create a StubEngine. */ private StubEngine() { super(); } /** * Returns an instance of the StubEngine that is a Singleton. * * @return an engine instance */ public static StubEngine getInstance() { if (stub == null) { stub = new StubEngine(); } return stub; } /** * Makes a call to procName in the native library that was * loaded before. The method signature is created from the specified input * and return parameters. The CSignTypes can be used to set * certain properties (FLOAT, VECTOR, BYREF) on the corresponding data * objects. This can be used to conform to a wide range of different method * signatures. The following C-types correspond to JSCTypes: *
    *
  • double - JSCNumber *
  • float - JSCNumber with FLOAT == true *
  • int - JSCInt *
  • &double - JSCNumber with * BYREF == true *
  • &float - JSCNumber with * FLOAT == true && BYREF == true *
  • &int - JSCInt with BYREF == true *
  • char* - JSCString *
  • char** - JSCSArray with VECTOR == true *
  • char*** - JSCSArray with VECTOR == false *
  • double* - JSCNArray with VECTOR == true *
  • double** - JSCSNrray with * VECTOR == false *
  • float* - JSCNArray with * VECTOR == true && FLOAT == true *
  • float** - JSCSNrray with * VECTOR == false && FLOAT == true *
*

* Input specification
* If a type corresponds to a C-pointer type (instead of a value), then the * memory is allocated according to the rows and columns of the respective * JSCData object. All input arguments are filled with the * values of the corresponding pointers after the procedure call, which * means that it is possible to use an argument that holds the results. * Value arguments must not be empty. *

* Return specification
* If retData == null or has zero length, then the procedure * is assumed to return void. If the return type * (retData[0].tpye()) is a pointer type, then it is assumed, that the * returned pointer from the procedure call has the same dimensions as * retData[0], because is not possible to retrieve the size * from the returned pointer. *

* Vectors
* Often C-functions have input arguments that correspond to a one * dimensional array. To call functions with these types, the vector * property of JSCSArray and JSCNArray must be * set to true. The data object is then vectorized (similar * to UMatrix.vec). After the function call the pointer is * indirected to the data object again, the array elements are filled with * the values from the pointer columnwise, keeping the original array * dimension. * * @param procName * name of the procedure to call, must be defined in the * currently loaded library * @param args * arguments for the procedure, the method signature is derived * from it * @param retData * return type, can contain at most 1 element, void * if empty or null, if type is an array, it has * to be set to the rows and cols of the returned pointer * @throws IllegalArgumentException *

    *
  • if args or retData contain * null elements or unsupported types *
  • if retData contains more than 1 element *
  • * if (procName == null || procName.length() == 0) *
  • if an element of args represents * INT, NUMBER or STRING but is empty and * BYREF == false *
  • if an element of args represents * SARRAY and contains null * strings *
* @throws RuntimeException * if procName adress could not be found in the * loaded library * @throws IllegalStateException * if no system library was loaded before */ public void call(String procName, JSCData[] args, JSCData[] retData) { if (systemLib == null) throw new IllegalStateException("No system library was loaded."); if (procName == null || procName.length() == 0) throw new IllegalArgumentException("Procedure name was empty."); if (args == null) args = new JSCData[0]; if (retData == null) retData = new JSCData[0]; if (retData.length > 1) throw new IllegalArgumentException( "Only one return parameter allowed for StubEngine (current:" + retData.length + ")."); CFunc funcPtr = null; try { funcPtr = new CFunc(systemLib, procName); } catch (Throwable e) { throw new RuntimeException("The adress of procedure\n\"" + procName + "\"\ncould not be found in library\n" + systemLib); } JSCTypes rtype = null; // Check return type. if (retData.length > 0) { if (retData[0] == null) throw new IllegalArgumentException("Return argument is null."); rtype = retData[0].type(); if (!isValid(rtype)) throw new IllegalArgumentException("Return type " + retData[0].type() + " cannot be handled by StubEngine."); } Object[] argArray = prepareInputArgs(args); // Call depending on return parameter types. if (rtype == null) { funcPtr.callVoid(argArray); } else if (rtype == JSCTypes.NARRAY || rtype == JSCTypes.SARRAY) { CPtr rtnPtr = funcPtr.callCPtr(argArray); getDataFromPtr(new CPtr[] { rtnPtr }, retData); } else if (rtype == JSCTypes.INT) { int rtnInt = funcPtr.callInt(argArray); ((JSCInt) retData[0]).setVal(rtnInt); } else if (rtype == JSCTypes.NUMBER) { JSCNumber num = (JSCNumber) retData[0]; double rtnDouble = 0; if (Boolean.valueOf(num.getJSCProperty(CSignTypes.FLOAT) + "") .booleanValue()) rtnDouble = funcPtr.callDouble(argArray); else rtnDouble = funcPtr.callFloat(argArray); num.setVal(rtnDouble); } else throw new IllegalArgumentException("Return type " + rtype + " cannot be handled by StubEngine."); // Input arguments might have been changed as well. getDataFromPtr(argArray, args); // Free allocated memory. for (Iterator iter = pointerHolder.iterator(); iter.hasNext();) ((CMalloc) iter.next()).free(); pointerHolder.clear(); } /** * Creates CPtr objects for each argument that allocates memory in the C * heap. * * @param args * data objects * @return array of arguments to be used for method call */ private Object[] prepareInputArgs(JSCData[] args) { if (args == null) throw new IllegalArgumentException("Argument was null."); if (args.length == 0) return new Object[0]; Object[] oArray = new Object[args.length]; for (int i = 0; i < args.length; i++) { if (args[i] == null) throw new IllegalArgumentException( "Argument at array position [" + i + "] is null."); JSCTypes type = args[i].type(); if (!isValid(type)) throw new IllegalArgumentException("Argument " + args[i].type() + " at array position [" + i + "] cannot be handled by StubEngine."); if (type == JSCTypes.STRING) { if (args[i].isEmpty()) throw new IllegalArgumentException("Argument " + args[i].name() + " at array position [" + i + "] is empty."); oArray[i] = args[i].value(); } else if (type == JSCTypes.INT) { if (!Boolean.valueOf( args[i].getJSCProperty(CSignTypes.BYREF) + "") .booleanValue()) { if (args[i].isEmpty()) throw new IllegalArgumentException("Argument " + args[i].name() + " at array position [" + i + "] is empty."); oArray[i] = args[i].value(); } else { CMalloc refPtr = new CMalloc(4); pointerHolder.add(refPtr); if (!args[i].isEmpty()) refPtr.setInt(0, ((JSCInt) args[i]).intVal()); oArray[i] = refPtr; } } else if (type == JSCTypes.NUMBER) { JSCNumber num = (JSCNumber) args[i]; if (!Boolean.valueOf( args[i].getJSCProperty(CSignTypes.BYREF) + "") .booleanValue()) { if (args[i].isEmpty()) throw new IllegalArgumentException("Argument " + args[i].name() + " at array position [" + i + "] is empty."); Object toSet = args[i].value(); if (Boolean.valueOf( num.getJSCProperty(CSignTypes.FLOAT) + "") .booleanValue()) toSet = new Float(num.doubleVal()); oArray[i] = toSet; } else { CMalloc refPtr = null; new CMalloc(8); if (!args[i].isEmpty()) if (Boolean.valueOf( num.getJSCProperty(CSignTypes.FLOAT) + "") .booleanValue()) { refPtr = new CMalloc(4); refPtr.setFloat(0, num.getNumber().floatValue()); } else { refPtr = new CMalloc(8); refPtr.setDouble(0, num.doubleVal()); } pointerHolder.add(refPtr); oArray[i] = refPtr; } } else if (type == JSCTypes.NARRAY) { JSCNArray nArray = (JSCNArray) args[i]; if (Boolean.valueOf( nArray.getJSCProperty(CSignTypes.VECTOR) + "") .booleanValue()) { oArray[i] = getNArrayVecPtr(nArray); } else { oArray[i] = getNArrayPtr(nArray); } } else if (type == JSCTypes.SARRAY) { JSCSArray sArray = (JSCSArray) args[i]; if (Boolean.valueOf( sArray.getJSCProperty(CSignTypes.VECTOR) + "") .booleanValue()) oArray[i] = getStringVecPtr(sArray); else oArray[i] = getStringArrayPtr(sArray); } else throw new RuntimeException("Unsupported type " + type + ". Code should not be reached."); } return oArray; } /** * Gets a pointer from a JSCNArray. * * @param nArray * @return pointer */ private CPtr getNArrayPtr(JSCNArray nArray) { CMalloc argPtr = new CMalloc(CPtr.SIZE * nArray.rows()); CMalloc[] argsi = new CMalloc[nArray.rows()]; pointerHolder.add(argPtr); if (!Boolean.valueOf(nArray.getJSCProperty(CSignTypes.FLOAT) + "") .booleanValue()) { double[][] values = nArray.doubleArray(); pointerHolder.add(argPtr); for (int j = 0; j < nArray.rows(); j++) { // size of double is 8 argsi[j] = new CMalloc(8 * nArray.cols()); pointerHolder.add(argsi[j]); argsi[j].copyIn(0, values[j], 0, nArray.cols()); argPtr.setCPtr(j * CPtr.SIZE, argsi[j]); } } else { float[][] values = new float[nArray.rows()][nArray.cols()]; for (int i = 0; i < nArray.rows(); i++) for (int j = 0; j < nArray.cols(); j++) values[i][j] = (float) nArray.doubleAt(i, j); pointerHolder.add(argPtr); for (int j = 0; j < nArray.rows(); j++) { // size of float is 4 argsi[j] = new CMalloc(4 * nArray.cols()); pointerHolder.add(argsi[j]); argsi[j].copyIn(0, values[j], 0, nArray.cols()); argPtr.setCPtr(j * CPtr.SIZE, argsi[j]); } } return argPtr; } /** * Gets a pointer from a vector JSCNArray. * * @param nArray * @return pointer */ private CPtr getNArrayVecPtr(JSCNArray nArray) { CMalloc argPtr = null; if (!Boolean.valueOf(nArray.getJSCProperty(CSignTypes.FLOAT) + "") .booleanValue()) { double[] values = UMatrix.vec(nArray.doubleArray()); argPtr = new CMalloc(8 * values.length); pointerHolder.add(argPtr); argPtr.copyIn(0, values, 0, values.length); } else { float[] values = new float[nArray.rows() * nArray.cols()]; for (int i = 0, index = 0; i < nArray.rows(); i++) for (int j = 0; j < nArray.cols(); j++) values[index++] = (float) nArray.doubleAt(i, j); argPtr = new CMalloc(4 * values.length); pointerHolder.add(argPtr); argPtr.copyIn(0, values, 0, values.length); } return argPtr; } /** * Gets a pointer from a JSCSArray. * * @param sArray * @return pointer */ private CPtr getStringArrayPtr(JSCSArray sArray) { CMalloc argPtr = new CMalloc(CPtr.SIZE * sArray.rows()); pointerHolder.add(argPtr); for (int j = 0; j < sArray.rows(); j++) { CMalloc argPtri = new CMalloc(CPtr.SIZE * sArray.cols()); pointerHolder.add(argPtri); CMalloc[] argsi = new CMalloc[sArray.cols()]; for (int k = 0; k < sArray.cols(); k++) { String el = sArray.stringAt(j, k); if (el == null) throw new IllegalArgumentException("Argument " + sArray.name() + " contains null element at index [" + j + "," + k + "]."); argsi[k] = new CMalloc(el.length() + 1); argsi[k].setString(0, el); argPtri.setCPtr(k * CPtr.SIZE, argsi[k]); pointerHolder.add(argsi[k]); } argPtr.setCPtr(CPtr.SIZE * j, argPtri); } return argPtr; } /** * Gets a pointer from a vector JSCSArray. * * @param sArray * @return pointer */ private CPtr getStringVecPtr(JSCSArray sArray) { String[] values = new String[sArray.rows() * sArray.cols()]; for (int j = 0, index = 0; j < sArray.cols(); j++) for (int k = 0; k < sArray.rows(); k++) { String el = sArray.stringAt(k, j); if (el == null) throw new IllegalArgumentException("Argument " + sArray.name() + " contains null element at index [" + k + "," + j + "]."); values[index++] = el; } CMalloc argPtr = new CMalloc(CPtr.SIZE * values.length); pointerHolder.add(argPtr); CMalloc[] argsi = new CMalloc[values.length]; for (int j = 0; j < values.length; j++) { // size of char is 1 argsi[j] = new CMalloc(values[j].length() + 1); pointerHolder.add(argsi[j]); argsi[j].setString(0, values[j]); argPtr.setCPtr(CPtr.SIZE * j, argsi[j]); } return argPtr; } /** * Gets a data object from the given object array. Only those array elements * are handled, that are of type CPtr. The type of the * JSCData object defines the type of indirection used. The * dimension of the data objects determines the assumed dimension of the * pointers. * * @param args * the pointer objects from which to extract the data * @param rtns * the data objects to store the values to, the type and * dimension define the pointer lengths and the indirection used */ private void getDataFromPtr(Object[] args, JSCData[] rtns) { if (args == null || rtns == null) throw new IllegalArgumentException("Argument was null."); if (args.length != rtns.length) throw new IllegalArgumentException("Array lengths do not match."); for (int i = 0; i < args.length; i++) { if (args[i] instanceof CPtr) { if (rtns[i] == null) throw new RuntimeException("Argument at index " + i + " was null."); JSCTypes type = rtns[i].type(); CPtr ptr = (CPtr) args[i]; if (type == JSCTypes.NARRAY) { JSCNArray nArray = (JSCNArray) rtns[i]; setNArrayFromPtr(nArray, ptr); } else if (type == JSCTypes.SARRAY) { JSCSArray sArray = (JSCSArray) rtns[i]; String[][] vals = new String[sArray.rows()][sArray.cols()]; if (Boolean.valueOf( sArray.getJSCProperty(CSignTypes.VECTOR) + "") .booleanValue()) { for (int j = 0, index = 0; j < sArray.cols(); j++) for (int k = 0; k < sArray.rows(); k++) { CPtr ptri = ptr.getCPtr(CPtr.SIZE * index++); vals[k][j] = ptri.getString(0); } } else { for (int j = 0; j < sArray.rows(); j++) { CPtr ptrRow = ptr.getCPtr(CPtr.SIZE * j); for (int k = 0; k < sArray.cols(); k++) { CPtr ptri = ptrRow.getCPtr(CPtr.SIZE * k); vals[j][k] = ptri.getString(0); } } } sArray.setVal(vals); } else if (type == JSCTypes.STRING) { ((JSCString) rtns[i]).setVal(ptr.getString(0)); } else if (type == JSCTypes.INT && Boolean.valueOf( rtns[i].getJSCProperty(CSignTypes.BYREF) + "") .booleanValue()) { JSCInt num = (JSCInt) rtns[i]; num.setVal(ptr.getInt(0)); } else if (type == JSCTypes.NUMBER && Boolean.valueOf( rtns[i].getJSCProperty(CSignTypes.BYREF) + "") .booleanValue()) { JSCNumber num = (JSCNumber) rtns[i]; if (Boolean.valueOf( rtns[i].getJSCProperty(CSignTypes.FLOAT) + "") .booleanValue()) num.setVal(ptr.getFloat(0)); else num.setVal(ptr.getDouble(0)); } else throw new RuntimeException("Argument type " + type + " at index " + i + " does not correspond to pointer."); } } } private void setNArrayFromPtr(JSCNArray nArray, CPtr ptr) { double[][] vals = new double[nArray.rows()][nArray.cols()]; if (!Boolean.valueOf(nArray.getJSCProperty(CSignTypes.FLOAT) + "") .booleanValue()) { if (Boolean.valueOf(nArray.getJSCProperty(CSignTypes.VECTOR) + "") .booleanValue()) { double[] values = new double[nArray.rows() * nArray.cols()]; ptr.copyOut(0, values, 0, values.length); for (int j = 0, index = 0; j < nArray.rows(); j++) for (int k = 0; k < nArray.cols(); k++) vals[j][k] = values[index++]; } else for (int j = 0; j < nArray.rows(); j++) ptr.getCPtr(j * CPtr.SIZE).copyOut(0, vals[j], 0, nArray.cols()); } else { if (Boolean.valueOf(nArray.getJSCProperty(CSignTypes.VECTOR) + "") .booleanValue()) { float[] values = new float[nArray.rows() * nArray.cols()]; ptr.copyOut(0, values, 0, values.length); for (int j = 0, index = 0; j < nArray.rows(); j++) for (int k = 0; k < nArray.cols(); k++) vals[j][k] = values[index++]; } else { float[][] fvals = new float[nArray.rows()][nArray.cols()]; for (int j = 0; j < nArray.rows(); j++) ptr.getCPtr(j * CPtr.SIZE).copyOut(0, fvals[j], 0, nArray.cols()); for (int j = 0; j < nArray.rows(); j++) for (int k = 0; k < nArray.cols(); k++) vals[j][k] = fvals[j][k]; } } nArray.setVal(vals); } /** * @see com.jstatcom.engine.Engine#isValid(com.jstatcom.model.JSCTypes) */ 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; } /** * Sets the name of the native library to be used. The library is then used * in the next call to a function. *

* Usage Note:
* if (loadType == StubLoadTypes.USERLIB) then the library is * searched in the jstub subdirectory. No system specific * extension (.dll/.so) is required but will be appended automatically * according to the running OS. *

* if (loadType == StubLoadTypes.SYSLIB) then the library is * searched in the library search path. The library name must be fully * qualified with extension, because not all system libraries have a default * suffix (e.g. libc.so.6). * * @param module * the name of the system library to load with/without the system * specific extension (SYSLIB/USERLIB) * @param loadType * the load type * @param args * always ignored * @throws IllegalArgumentException * if (loadName == null || loadType == null) or * if (module.length() == 0) or if no user * library with that name exists in the jstub * subdir * @throws RuntimeException * if operating system is not supported or if the system library * with the given name cannot be found * @see com.jstatcom.engine.Engine#load(String, * com.jstatcom.engine.LoadTypes, com.jstatcom.model.JSCData[]) */ public void load(String module, LoadTypes loadType, JSCData... args) { if (module == null || loadType == null) throw new IllegalArgumentException("Argument was null."); if (module.length() == 0) throw new IllegalArgumentException("Library name was empty."); String osName = System.getProperty("os.name").toUpperCase(); String sysExt = null; if (osName.indexOf("WIN") > -1) sysExt = ".dll"; else if (osName.indexOf("SUNOS") > -1 || osName.indexOf("SOLARIS") > -1 || osName.indexOf("LINUX") > -1) sysExt = ".so"; else throw new RuntimeException("Operating system " + System.getProperty("os.name") + " is not supported by StubEngine."); if (loadType == StubLoadTypes.USERLIB) { File f = new File(EngineTypes.STUB.DIR_NAME + File.separator + module + sysExt); if (!f.isFile()) throw new RuntimeException("The system library " + f.getAbsolutePath() + " does not exist."); systemLib = f.getAbsolutePath(); } if (loadType == StubLoadTypes.SYSLIB) systemLib = module; } /** * Gets the system library that is currently used by this engine. It can be * changed via the load method. * * @return the absolute path of the current system library or null if * none has been loaded so far */ public String getCurrentSysLib() { return systemLib; } /** * 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."); } /** * Does nothing. * * @see com.jstatcom.engine.Engine#shutdown() */ public void shutdown() { } }

Ads help maintain this website.