From: <mwl...@us...> - 2007-07-13 21:16:25
|
Revision: 421 http://svn.sourceforge.net/cishell/?rev=421&view=rev Author: mwlinnem Date: 2007-07-13 14:16:22 -0700 (Fri, 13 Jul 2007) Log Message: ----------- Initial import. JythonRunner allows CIShell to support jython algorithms. Added Paths: ----------- trunk/templates/jythonrunner/META-INF/ trunk/templates/jythonrunner/META-INF/MANIFEST.MF trunk/templates/jythonrunner/OSGI-INF/ trunk/templates/jythonrunner/OSGI-INF/algorithm.properties trunk/templates/jythonrunner/OSGI-INF/component.xml trunk/templates/jythonrunner/OSGI-INF/l10n/ trunk/templates/jythonrunner/OSGI-INF/l10n/bundle_en.properties trunk/templates/jythonrunner/OSGI-INF/metatype/ trunk/templates/jythonrunner/OSGI-INF/metatype/METADATA.XML trunk/templates/jythonrunner/build.properties trunk/templates/jythonrunner/src/ trunk/templates/jythonrunner/src/org/ trunk/templates/jythonrunner/src/org/cishell/ trunk/templates/jythonrunner/src/org/cishell/templates/ trunk/templates/jythonrunner/src/org/cishell/templates/jythonrunner/ trunk/templates/jythonrunner/src/org/cishell/templates/jythonrunner/JythonAlgorithmFactory.java trunk/templates/jythonrunner/src/org/cishell/templates/jythonrunner/JythonFileProperty.java trunk/templates/jythonrunner/src/org/cishell/templates/jythonrunner/JythonRunnerAlgorithm.java Added: trunk/templates/jythonrunner/META-INF/MANIFEST.MF =================================================================== --- trunk/templates/jythonrunner/META-INF/MANIFEST.MF (rev 0) +++ trunk/templates/jythonrunner/META-INF/MANIFEST.MF 2007-07-13 21:16:22 UTC (rev 421) @@ -0,0 +1,25 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: JythonRunner +Bundle-SymbolicName: org.cishell.templates.jythonrunner +Bundle-Version: 0.0.1 +Bundle-ClassPath: . +Bundle-Localization: plugin +Import-Package: org.cishell.framework, + org.cishell.framework.algorithm, + org.cishell.framework.data, + org.osgi.framework;version="1.3.0", + org.osgi.service.component;version="1.0.0", + org.osgi.service.log;version="1.3.0", + org.osgi.service.metatype;version="1.1.0", + org.osgi.service.prefs;version="1.1.0", + org.python.compiler, + org.python.core, + org.python.modules, + org.python.modules.sre, + org.python.parser, + org.python.rmi, + org.python.util +Export-Package: org.cishell.templates.jythonrunner +X-AutoStart: true +Service-Component: OSGI-INF/component.xml Added: trunk/templates/jythonrunner/OSGI-INF/algorithm.properties =================================================================== --- trunk/templates/jythonrunner/OSGI-INF/algorithm.properties (rev 0) +++ trunk/templates/jythonrunner/OSGI-INF/algorithm.properties 2007-07-13 21:16:22 UTC (rev 421) @@ -0,0 +1,6 @@ +label=Jython Script Runner +description=Runs Jython Algorithms +in_data=null +out_data=null +service.pid=edu.iu.nwb.converter.JythonRunnerAlgorithm +remoteable=true Added: trunk/templates/jythonrunner/OSGI-INF/component.xml =================================================================== --- trunk/templates/jythonrunner/OSGI-INF/component.xml (rev 0) +++ trunk/templates/jythonrunner/OSGI-INF/component.xml 2007-07-13 21:16:22 UTC (rev 421) @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="org.cishell.templates.jythonrunner.component" immediate="false"> + <implementation class="org.cishell.templates.jythonrunner.JythonAlgorithmFactory"/> + <properties entry="OSGI-INF/algorithm.properties"/> + <reference name="LOG" interface="org.osgi.service.log.LogService"/> + <reference name="MTS" interface="org.osgi.service.metatype.MetaTypeService"/> + + <service> + <provide interface= + "org.cishell.framework.algorithm.AlgorithmFactory"/> + </service> +</component> \ No newline at end of file Added: trunk/templates/jythonrunner/OSGI-INF/l10n/bundle_en.properties =================================================================== --- trunk/templates/jythonrunner/OSGI-INF/l10n/bundle_en.properties (rev 0) +++ trunk/templates/jythonrunner/OSGI-INF/l10n/bundle_en.properties 2007-07-13 21:16:22 UTC (rev 421) @@ -0,0 +1,7 @@ +#Localization variables for OSGI-INF/metatatype/METADATA.XML +# +#Samples: +#input=Input +#desc=Enter an integer (that will be converted to a string) +#name=Input->String +#name_desc=Converts inputted integer to string Added: trunk/templates/jythonrunner/OSGI-INF/metatype/METADATA.XML =================================================================== --- trunk/templates/jythonrunner/OSGI-INF/metatype/METADATA.XML (rev 0) +++ trunk/templates/jythonrunner/OSGI-INF/metatype/METADATA.XML 2007-07-13 21:16:22 UTC (rev 421) @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<metatype:MetaData xmlns:metatype="http://www.osgi.org/xmlns/metatype/v1.0.0"> + <OCD name="My Algorithm" id="org.my.algorithm.MyAlgorithm.OCD" + description=""> + </OCD> + <Designate pid="org.my.algorithm.MyAlgorithm"> + <Object ocdref="org.my.algorithm.MyAlgorithm.OCD" /> + </Designate> +</metatype:MetaData> Added: trunk/templates/jythonrunner/build.properties =================================================================== --- trunk/templates/jythonrunner/build.properties (rev 0) +++ trunk/templates/jythonrunner/build.properties 2007-07-13 21:16:22 UTC (rev 421) @@ -0,0 +1,6 @@ +source.. = src/ +output.. = build/ +bin.includes = META-INF/,\ + .,\ + OSGI-INF/ +jars.compile.order = . Added: trunk/templates/jythonrunner/src/org/cishell/templates/jythonrunner/JythonAlgorithmFactory.java =================================================================== --- trunk/templates/jythonrunner/src/org/cishell/templates/jythonrunner/JythonAlgorithmFactory.java (rev 0) +++ trunk/templates/jythonrunner/src/org/cishell/templates/jythonrunner/JythonAlgorithmFactory.java 2007-07-13 21:16:22 UTC (rev 421) @@ -0,0 +1,53 @@ +package org.cishell.templates.jythonrunner; + +import java.util.Dictionary; + +import org.cishell.framework.CIShellContext; +import org.cishell.framework.algorithm.Algorithm; +import org.cishell.framework.algorithm.AlgorithmFactory; +import org.cishell.framework.data.Data; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.metatype.MetaTypeProvider; +import org.osgi.service.metatype.MetaTypeService; +/** + * + * @author mwlinnem + * + */ + +//TODO: cleanup + +public class JythonAlgorithmFactory implements AlgorithmFactory { + private BundleContext myBundleContext; + private Bundle myBundle; + private MetaTypeProvider provider; + private Dictionary properties; + + protected void activate(ComponentContext ctxt) { + + MetaTypeService mts = (MetaTypeService)ctxt.locateService("MTS"); + this.myBundleContext = ctxt.getBundleContext(); + this.myBundle = myBundleContext.getBundle(); + this.provider = mts.getMetaTypeInformation(myBundle); + this.properties = ctxt.getProperties(); + } + + + protected void deactivate(ComponentContext ctxt) { + provider = null; + } + + public Algorithm createAlgorithm(Data[] data, Dictionary parameters, + CIShellContext context) { + return new JythonRunnerAlgorithm(data, parameters, context, + properties, myBundle); + + + } + + public MetaTypeProvider createParameters(Data[] data) { + return provider; + } +} \ No newline at end of file Added: trunk/templates/jythonrunner/src/org/cishell/templates/jythonrunner/JythonFileProperty.java =================================================================== --- trunk/templates/jythonrunner/src/org/cishell/templates/jythonrunner/JythonFileProperty.java (rev 0) +++ trunk/templates/jythonrunner/src/org/cishell/templates/jythonrunner/JythonFileProperty.java 2007-07-13 21:16:22 UTC (rev 421) @@ -0,0 +1,15 @@ +package org.cishell.templates.jythonrunner; + +public class JythonFileProperty { + public static final String SCRIPT_PATH_KEY = "script_path"; + + public static final String RESULT_PREFIX = + JythonRunnerAlgorithm.SCRIPT_RESULT_PREFIX; + public static final String ARGUMENT_PREFIX = + JythonRunnerAlgorithm.SCRIPT_ARGUMENT_PREFIX; + + public static final String LABEL_SUFFIX = ".label"; + public static final String TYPE_SUFFIX = ".type"; + public static final String PARENT_SUFFIX = ".parent"; + +} Added: trunk/templates/jythonrunner/src/org/cishell/templates/jythonrunner/JythonRunnerAlgorithm.java =================================================================== --- trunk/templates/jythonrunner/src/org/cishell/templates/jythonrunner/JythonRunnerAlgorithm.java (rev 0) +++ trunk/templates/jythonrunner/src/org/cishell/templates/jythonrunner/JythonRunnerAlgorithm.java 2007-07-13 21:16:22 UTC (rev 421) @@ -0,0 +1,368 @@ +package org.cishell.templates.jythonrunner; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.List; + +import org.cishell.framework.CIShellContext; +import org.cishell.framework.algorithm.Algorithm; +import org.cishell.framework.data.BasicData; +import org.cishell.framework.data.Data; +import org.cishell.framework.data.DataProperty; +import org.osgi.framework.Bundle; +import org.osgi.service.log.LogService; +import org.python.core.PyFile; +import org.python.core.PyJavaInstance; +import org.python.core.PyObject; +import org.python.util.PythonInterpreter; + +/** + * + * @author mwlinnem + * + */ + +//TODO:refactor me into multiple classes +//TODO:general cleanup, renaming, etc... + +public class JythonRunnerAlgorithm implements Algorithm { + private Data[] data; + private Dictionary parameters; + private Dictionary properties; + + private LogService logger; + + private URL script; + + public static final String SCRIPT_ARGUMENT_PREFIX = "arg"; + public static final String SCRIPT_RESULT_PREFIX = "result"; + + public JythonRunnerAlgorithm(Data[] data, Dictionary parameters, + CIShellContext context, Dictionary properties, Bundle myBundle) { + this.data = data; + this.parameters = parameters; + this.properties = properties; + + String scriptPath = (String) properties.get( + JythonFileProperty.SCRIPT_PATH_KEY); + script = myBundle.getResource(scriptPath); + + this.logger = (LogService) context.getService( + LogService.class.getName()); + } + + public Data[] execute() { + + PythonInterpreter interp = initializeInterpreter( + new PythonInterpreter(), data, parameters); + + List rawResults = runScript(interp, script); + + Data[] results = formatRawResults(rawResults, data, properties); + + return results; + } + + + private PythonInterpreter initializeInterpreter(PythonInterpreter interp, + Data[] data, Dictionary parameters) { + interp = passUserProvidedArguments(interp, parameters); + interp = passCIShellProvidedArguments(interp, data); + interp = initializeLogging(interp); + return interp; + } + + /** + * Executes the script and extracts the raw results. + * @param interp The initialized python interpreter + * @param script The jython script itself, which the interpreter will run + * @return A list of objects that the script returned. + */ + private List runScript(PythonInterpreter interp, URL script) { + interp = executeFile(interp, script); + List rawResults = getRawResults(interp); + return rawResults; + } + + private Data[] formatRawResults(List rawResults, Data[] inputData, + Dictionary properties) { + List dataResults = convertToData(rawResults); + List dataResultsWithMetaData = addMetaData(dataResults, inputData, + properties); + Data[] resultsArray = convertToArray(dataResultsWithMetaData); + return resultsArray; + } + + + + private PythonInterpreter executeFile(PythonInterpreter interp, + URL script) { + try { + interp.execfile(script.openStream()); + } catch (IOException e) { + logger.log(LogService.LOG_ERROR, "Unable to open jython script " + + script.toString() + ".", e); + e.printStackTrace(); + } + return interp; + } + + private PythonInterpreter passUserProvidedArguments( + PythonInterpreter interp, Dictionary parameters) { + + Enumeration enumer = parameters.keys(); + while (enumer.hasMoreElements()) { + String key = (String) enumer.nextElement(); + Object value = parameters.get(key); + String argName = key; + + interp = passArgument(value, argName, interp); + } + + return interp; + } + + private PythonInterpreter passCIShellProvidedArguments( + PythonInterpreter interp, Data[] data) { + for (int ii = 0; ii < this.data.length; ii++) { + Data argData = this.data[ii]; + Object arg = argData.getData(); + String argName = SCRIPT_ARGUMENT_PREFIX + String.valueOf(ii); + + interp = passArgument(arg, argName, interp); + } + + return interp; + } + + private PythonInterpreter initializeLogging(PythonInterpreter interp) { + interp.setErr(System.err); + interp.setOut(System.out); + return interp; + } + + /** + * Gets Java versions of all the results from the script. + * @param interp a python interpreter that holds results (presumably + * after having executed a script) + * @return A list of objects, where each object is a result from + * the interpreters environment. + */ + private List getRawResults(PythonInterpreter interp) { + List results = new ArrayList(); + + /* + * gets the values held in result variables, from + * "result0" counting upward, until we reach a result + * variable which is not defined. + */ + int ii = 0; + String resultName = SCRIPT_RESULT_PREFIX + ii; + + while (variableIsDefined(interp, resultName)) { + + results.add(interp.get(resultName, Object.class)); + + ii++; + resultName = SCRIPT_RESULT_PREFIX + ii; + } + + return results; + } + + /** + * adds metadata obtained from the algorithm's .properties files + * that specify information about what the script returns. + * @param data a list of data objects, in the order + * they were returned. + * @param inputData the data passed from CIShell to this algorithm + * @param properties information about the script, such as + * the labels, types, and parents of all the returned data. + * @return a list of data objects in the order they were provided, + * now containing the appropriate metadata obtained from the + * .properties file. + */ + private List addMetaData(List data, Data[] inputData, Dictionary properties) { + List results = new ArrayList(); + for (int ii = 0; ii < data.size(); ii++) { + Data result = ((Data) data.get(ii)); + Dictionary metadataHolder = result.getMetaData(); + + String dataLabel = getResultLabel(properties, ii); + metadataHolder.put(DataProperty.LABEL, dataLabel); + + String dataType = getResultType(properties, ii); + metadataHolder.put(DataProperty.TYPE, dataType); + + Data dataParent = getResultParent(properties, ii, inputData); + if (dataParent != null) { + metadataHolder.put(DataProperty.PARENT, dataParent); + } //it's okay to not have a parent, little Timmy. + + results.add(result); + } + return results; + } + + public String getResultLabel(Dictionary props, int numResult) { + String labelKey = JythonFileProperty.RESULT_PREFIX + numResult + + JythonFileProperty.LABEL_SUFFIX; + Object labelValue = props.get(labelKey); + + String labelValueString; + if (!(labelValue == null)) { + labelValueString = (String) labelValue; + } else { + labelValueString = "Data"; + logger.log(LogService.LOG_WARNING, "Label of data returned from " + + "jython script not specified in .properties file. " + + "Assigning label to '" + labelValueString + "'."); + } + return labelValueString; + } + + public String getResultType(Dictionary props, int numResult) { + String typeKey = JythonFileProperty.RESULT_PREFIX + + numResult + JythonFileProperty.TYPE_SUFFIX; + Object typeValue = props.get(typeKey); + + String typeValueString; + if (! (typeValue == null)) { + typeValueString = (String) typeValue; + checkType(typeValueString); + + } else { + typeValueString = DataProperty.OTHER_TYPE; + logger.log(LogService.LOG_WARNING, "Type of data returned from " + + "jython script not specified in .properties file. " + + "Assigning type to '" + typeValueString + "'."); + } + return typeValueString; + } + + /** + * Looks to see whether the result has a parent specified in the + * algorithm's .properties file. If it does, return the parent data. + * Otherwise return null. + * @param props information about the script, such as + * the labels, types, and parents of all the returned data. + * @param numResult specifies which result's information we need to look + * up + * @param inputData the data CIShell passed this algorithm. + * @return either the parent of the result data specified by numResults, + * or null, if there is no parent specified. + */ + public Data getResultParent(Dictionary props, int numResult, Data[] inputData) { + String childKey = JythonFileProperty.RESULT_PREFIX + numResult + + JythonFileProperty.PARENT_SUFFIX; + Object parentName = props.get(childKey); + + Data parent; + if (! (parentName == null)) { + //TODO: more validation on parentName + char parentDataIndexChar = getLastChar((String) parentName); + int parentDataIndex = Character.digit(parentDataIndexChar, 10); + if (parentDataIndex < inputData.length) { + parent = inputData[parentDataIndex]; + } else { + logger.log(LogService.LOG_WARNING, ".properties file " + + "tried to assign result" + numResult + "the " + + "parent arg"+ parentDataIndex + ", which has an " + + "index greater than any arg provided. Cannot " + + "assign result" + numResult + " a parent."); + parent = null; + } + } else { + //it's okay not to specify a parent. + parent = null; + } + return parent; + } + + private PythonInterpreter passArgument(Object arg, String argName, + PythonInterpreter interp) { + if (! (arg instanceof File)) { + interp.set(argName, + new PyJavaInstance(arg)); + } else { + try { + File fileArg = (File) arg; + InputStream fileStream = fileArg.toURL().openStream(); + interp.set(argName, + new PyFile(fileStream)); + } catch (IOException e) { + logger.log(LogService.LOG_ERROR, "Problem opening file" + + " provided as an argument to jython script.", e); + e.printStackTrace(); + } + } + + return interp; + } + + /** + * Takes java objects and wraps them in our CIShell data objects + * so that they can be returned from the algorithm. + * @param rawResults a list of java objects. + * @return a list of data objects. + */ + private List convertToData(List rawResults) { + List results = new ArrayList(); + for (int ii = 0; ii < rawResults.size(); ii++) { + Object rawResult = rawResults.get(ii); + String resultClassName = rawResult.getClass().getName(); + BasicData data = new BasicData(rawResult, resultClassName); + results.add(data); + } + return results; + } + private boolean variableIsDefined(PythonInterpreter interp, + String variableName) { + String predicate = "vars().has_key('" + variableName + "') or " + + "globals().has_key('" + variableName + "')"; + boolean result = evalPredicate(interp, predicate); + return result; + } + private boolean evalPredicate(PythonInterpreter interp, String predicate) { + PyObject pyResult = interp.eval(predicate); + Boolean resultObj = (Boolean) pyResult.__tojava__(Boolean.class); + boolean result = resultObj.booleanValue(); + return result; + } + + private Data[] convertToArray(List dataList) { + Data[] dataArray = new Data[dataList.size()]; + for (int ii = 0; ii < dataArray.length; ii++) { + dataArray[ii] = (Data) dataList.get(ii); + } + return dataArray; + } + + private void checkType(String ts) { + if (! (ts.equals(DataProperty.MATRIX_TYPE) || + ts.equals(DataProperty.NETWORK_TYPE) || + ts.equals(DataProperty.TEXT_TYPE) || + ts.equals(DataProperty.OTHER_TYPE) || + ts.equals(DataProperty.TEXT_TYPE))) { + logger.log(LogService.LOG_WARNING, "JythonRunnerAlgorithm: " + + "Assigning return data an unsupported data type " + + ts +". Either the type is invalid or " + + "JythonRunnerAlgorithm has not be updated to reflect " + + "types introduced in newer versions"); + } + } + + public char getLastChar(String s) { + if (s.length() > 0) { + return s.charAt(s.length() - 1); + } else { + throw new IndexOutOfBoundsException("Cannot get the last " + + "character of an empy string"); + } + } +} \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |