I am sure there are issues with this, but I thought it worth sharing, if only to get corrections from people who know better. ====== 1. Unpack a recent commons-bsf as a peer to this directory. i used https://github.com/apache/commons-bsf. 2. add this: "jtcl = org.apache.bsf.engines.jtcl.JtclEngine, jtcl | tcl" to language.properties in src/main/java/org/apache/bsf/ 3. build commons-bsf "by the numbers" (you only need go as far as 'jar') 4. in this dir : ./build.sh I exclude user libs tomake sure I don't pick up past junk. I hope I made the build.xml portable (enough) The jtcl-2.8.0.jar in lib is built from source, you can put your own in too. 5. copy ../commons-bsf/build/lib/bsf.jar to ~/.ant/lib/ now: ant test_ext and ant test_script should work (I hope) BSFCommand.java and JtclEngine.java Are pretty much what came from JACL, whith very minimal changes, there are more API points to BSF but this makes a baseline. This gets me started using ant to do interesting things with jtcl like: * remote admin with an RMI based ant system called (surprisingly) remoteant :) * producing itclproxy wrappers with ant (-contrib) tasks I have jdom2 and lwjgl in progress * manipulating ant tasks from jtcl * calling beans via the "bsf" command ... it fires the imagination ... ====== Directory structure for ant project : ====== ./test1.jtcl ./src/main/java/org/apache/bsf/engines/jtcl/BSFCommand.java ./src/main/java/org/apache/bsf/engines/jtcl/JtclEngine.java ./build.xml ./lib/jtcl-2.8.0.jar ./build.sh ====== Contents of BSFCommand.java ====== /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.bsf.engines.jtcl; import org.apache.bsf.BSFEngine; import org.apache.bsf.BSFException; import org.apache.bsf.BSFManager; import org.apache.bsf.util.EngineUtils; import tcl.lang.Command; import tcl.lang.Interp; // import tcl.lang.ReflectObject; import tcl.pkg.java.ReflectObject; import tcl.lang.TCL; import tcl.lang.TclException; import tcl.lang.TclObject; // class used to add "bsf" command to the Jacl runtime class BSFCommand implements Command { BSFManager mgr; BSFEngine jengine; BSFCommand (BSFManager mgr, BSFEngine jengine) { this.mgr = mgr; this.jengine = jengine; } public void cmdProc (Interp interp, TclObject argv[]) throws TclException { if (argv.length < 2) { interp.setResult ("invalid # of args; usage: bsf " + "lookupBean|registerBean|unregisterBean|addEventListener args"); throw new TclException (TCL.ERROR); } String op = argv[1].toString (); if (op.equals ("lookupBean")) { if (argv.length != 3) { interp.setResult ("invalid # of args; usage: bsf " + "lookupBean name-of-bean"); throw new TclException (TCL.ERROR); } String beanName = argv[2].toString (); Object bean = mgr.lookupBean (beanName); if (bean == null) { interp.setResult ("unknown object: " + beanName); throw new TclException (TCL.ERROR); } interp.setResult (ReflectObject.newInstance (interp, bean.getClass (), bean)); } else if (op.equals ("registerBean")) { if (argv.length != 4) { interp.setResult ("invalid # of args; usage: bsf " + "registerBean name-of-bean bean"); throw new TclException (TCL.ERROR); } mgr.registerBean (argv[2].toString (), ReflectObject.get (interp, argv[3])); interp.setResult (""); } else if (op.equals ("unregisterBean")) { if (argv.length != 3) { interp.setResult ("invalid # of args; usage: bsf " + "unregisterBean name-of-bean"); throw new TclException (TCL.ERROR); } mgr.unregisterBean (argv[2].toString ()); interp.setResult (""); } else if (op.equals ("addEventListener")) { if (argv.length != 6) { interp.setResult ("invalid # of args; usage: bsf " + "addEventListener object event-set-name filter " + "script-to-run"); throw new TclException (TCL.ERROR); } try { // usage: bsf addEventListener object event-set filter script String filter = argv[4].toString (); filter = filter.equals ("") ? null : filter; EngineUtils.addEventListener (ReflectObject.get (interp, argv[2]), argv[3].toString (), filter, jengine, mgr, "", 0, 0, argv[5].toString ()); } catch (BSFException e) { e.printStackTrace (); interp.setResult ("got BSF exception: " + e.getMessage ()); throw new TclException (TCL.ERROR); } } } } ====== Contents of JtclEngine.java ====== /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.bsf.engines.jtcl; import java.util.Vector; import org.apache.bsf.BSFDeclaredBean; import org.apache.bsf.BSFException; import org.apache.bsf.BSFManager; import org.apache.bsf.util.BSFEngineImpl; import tcl.lang.Interp; //import tcl.lang.ReflectObject; import tcl.pkg.java.ReflectObject; import tcl.lang.TclDouble; import tcl.lang.TclException; import tcl.lang.TclInteger; import tcl.lang.TclObject; import tcl.lang.TclString; /** * This is the interface to Scriptics's Jacl (Tcl) from the * Bean Scripting Framework. *

* * @author Sanjiva Weerawarana */ public class JtclEngine extends BSFEngineImpl { /* the Jacl interpretor object */ private Interp interp; /** * * @param method The name of the method to call. * @param args an array of arguments to be * passed to the extension, which may be either * Vectors of Nodes, or Strings. */ public Object call (Object obj, String method, Object[] args) throws BSFException { StringBuffer tclScript = new StringBuffer (method); if (args != null) { for( int i = 0 ; i < args.length ; i++ ) { tclScript.append (" "); tclScript.append (args[i].toString ()); } } return eval ("", 0, 0, tclScript.toString ()); } /** * Declare a bean */ public void declareBean (BSFDeclaredBean bean) throws BSFException { String expr = "set " + bean.name + " [bsf lookupBean \"" + bean.name + "\"]"; eval ("", 0, 0, expr); } /** * This is used by an application to evaluate a string containing * some expression. */ public Object eval (String source, int lineNo, int columnNo, Object oscript) throws BSFException { String script = oscript.toString (); try { interp.eval (script); TclObject result = interp.getResult(); Object internalRep = result.getInternalRep(); // if the object has a corresponding Java type, unwrap it if (internalRep instanceof ReflectObject) return ReflectObject.get(interp,result); if (internalRep instanceof TclString) return result.toString(); if (internalRep instanceof TclDouble) return new Double(TclDouble.get(interp,result)); if (internalRep instanceof TclInteger) return new Integer(TclInteger.get(interp,result)); return result; } catch (TclException e) { throw new BSFException (BSFException.REASON_EXECUTION_ERROR, "error while eval'ing Jtcl expression: " + interp.getResult (), e); } } /** * Initialize the engine. */ public void initialize (BSFManager mgr, String lang, Vector declaredBeans) throws BSFException { super.initialize (mgr, lang, declaredBeans); // create interpreter interp = new Interp(); // register the extension that user's can use to get at objects // registered by the app interp.createCommand ("bsf", new BSFCommand (mgr, this)); // Make java functions be available to Jacl try { interp.eval("jaclloadjava"); } catch (TclException e) { throw new BSFException (BSFException.REASON_OTHER_ERROR, "error while loading java package: " + interp.getResult (), e); } int size = declaredBeans.size (); for (int i = 0; i < size; i++) { declareBean ((BSFDeclaredBean) declaredBeans.elementAt (i)); } } /** * Undeclare a previously declared bean. */ public void undeclareBean (BSFDeclaredBean bean) throws BSFException { eval ("", 0, 0, "set " + bean.name + " \"\""); } } ====== Contents of build.xml : ====== ====== Contents of build.sh : ====== #!/bin/sh ant \ -nouserlib \ "$@" ====== I made a BSF utility script to play with loading jtcl (and janino, and rhino). Here it is : ====== #!/bin/sh # ****************************************************************************** D=$(dirname `readlink -f $0`) LOC=$(readlink -f $D) export LOC LOC_LIB=${LOC}/lib/ export LOC_LIB # ------------------------------------- # JAVA set up unset JAVA_TOOL_OPTIONS JAVA_HOME=/usr/local/openjdk8/ export JAVA_HOME JAVA=${JAVA_HOME}/bin/java export JAVA # ------------------------------------- # BSF jars CLASSPATH=${CLASSPATH}:${LOC}/lib/jdom-b9.jar CLASSPATH=${CLASSPATH}:${LOC}/lib/commons-logging-1.0.4.jar CLASSPATH=${CLASSPATH}:${LOC}/lib/velocity-1.4.jar CLASSPATH=${CLASSPATH}:${LOC}/lib/xalan-2.7.0.jar CLASSPATH=${CLASSPATH}:${LOC}/lib/commons-collections-3.1.jar CLASSPATH=${CLASSPATH}:${LOC}/lib/junit-3.8.2.jar CLASSPATH=${CLASSPATH}:${LOC}/lib/xerces-2.4.0.jar CLASSPATH=${CLASSPATH}:${LOC}/lib/log4j-1.2.12.jar CLASSPATH=${CLASSPATH}:${LOC}/build/lib/bsf.jar export CLASSPATH # ****************************************************************************** # Parse the pre command options # args=`getopt j: $*` set -- $args while true; do case "$1" in -j) CLASSPATH=${CLASSPATH}:$2 export CLASSPATH shift; shift ;; --) shift; break ;; esac done # ------------------------------------- # Run the command case $1 in rhino) shift CLASSPATH=${CLASSPATH}:${LOC}/lib/engines/rhino/rhino,jar export CLASSPATH ${JAVA} \ -classpath ${CLASSPATH} \ org.apache.bsf.Main \ "$@" ;; janino) shift CLASSPATH=${CLASSPATH}:${LOC}/lib/engines/jtcl/jtcl-2.8.0.jar CLASSPATH=${CLASSPATH}:${LOC}/lib/engines/jtcl/jtcl-engine.jar export CLASSPATH # ------------------------------------ # Mains : # org/codehaus/janino/tools/Disassembler # org/codehaus/janino/tools/HprofScrubber # org/codehaus/janino/CachingJavaSourceClassLoader # org/codehaus/janino/samples/DeclarationCounter # org/codehaus/janino/samples/ExpressionDemo # org/codehaus/janino/samples/ShippingCost # org/codehaus/janino/samples/ScriptDemo # org/codehaus/janino/samples/ClassBodyDemo # org/codehaus/janino/UnicodeUnescapeReader # org/codehaus/janino/UnparseVisitor # case $1 in jload) shift ${JAVA} \ -classpath ${CLASSPATH} \ org.codehaus.janino.JavaSourceClassLoader \ "$@" ;; jgrep) shift ${JAVA} \ -classpath ${CLASSPATH} \ org.codehaus.janino.tools.JGrep \ "$@" ;; cc) shift ${JAVA} \ -classpath ${CLASSPATH} \ org.codehaus.janino.Compiler "$@" ;; simple) shift ${JAVA} \ -classpath ${CLASSPATH} \ org.codehaus.janino.SimpleCompiler "$@" ;; dis) shift ${JAVA} \ -classpath ${CLASSPATH} \ org.codehaus.janino.tools.Disassembler "$@" ;; script_demo) shift ${JAVA} \ -classpath ${CLASSPATH} \ org.codehaus.janino.samples.ScriptDemo "$@" ;; *) echo echo "Janino tools help:" echo echo "jload : org.codehaus.janino.JavaSourceClassLoader (-help)" echo "jgrep : org.codehaus.janino.tools.JGrep (-help)" echo "cc : org.codehaus.janino.Compiler (-help)" echo "simple : org.codehaus.janino.SimpleCompiler (-help)" echo "dis : org.codehaus.janino.tools.Disassembler (-help)" echo "script_demo : org.codehaus.janino.samples.ScriptDemo (-help)" echo esac ;; # ------------------------------------ jtcl) shift CLASSPATH=${CLASSPATH}:${LOC}/lib/engines/jtcl/jtcl-2.8.0.jar CLASSPATH=${CLASSPATH}:${LOC}/lib/engines/jtcl/jtcl-engine.jar export CLASSPATH ${JAVA} \ -classpath ${CLASSPATH} \ org.apache.bsf.Main \ "$@" ;; help) echo echo "Help :" echo echo "$0 jtcl ..." echo " set up jtcl and run org.apache.bsf.Main" echo "$0 janino ..." echo " set up janino utils" echo "$0 rhino ..." echo " set up rhino and run org.apache.bsf.Main" echo ;; *) ${JAVA} \ -classpath ${CLASSPATH} \ org.apache.bsf.Main \ "$@" ;; esac ====== This is how the directory structure for the script lays out : ====== ./lib ./lib/jdom-b9.jar ./lib/commons-logging-1.0.4.jar ./lib/velocity-1.4.jar ./lib/xalan-2.7.0.jar ./lib/commons-collections-3.1.jar ./lib/junit-3.8.2.jar ./lib/xerces-2.4.0.jar ./lib/log4j-1.2.12.jar ./lib/engines ./lib/engines/rhino ./lib/engines/rhino/rhino.jar ./lib/engines/jtcl ./lib/engines/jtcl/javahelp-2.0.05.jar ./lib/engines/jtcl/jtcl-2.8.0.jar ./lib/engines/jtcl/swank-3.0.0.jar ./lib/engines/jtcl/jtcl-engine.jar ./lib/engines/jtcl/jfreechart-1.0.13.jar ./lib/engines/jtcl/jcommon-1.0.16.jar ./build ./build/lib ./build/lib/bsf.jar ./bsf.sh ====== <>JTCL