Check out the new USENIX Web site.


The following paper was originally published in the
Proceedings of the USENIX Fourth Annual Tcl/Tk Workshop
Monterey, California, July 1996.


For more information about USENIX Association contact:
1. Phone: (510) 528-8649
2. FAX: (510) 548-5738
3. Email: office@usenix.org
4. WWW URL: http://www.usenix.org


TclJava: Toward Portable Extensions

Scott Stanton Sun Microsystems Laboratories sstanton@eng.sun.com

Ken Corey Sun Microsystems Laboratories kcorey@eng.sun.com

96-0149

Abstract

This paper discusses an extension to Tcl that allows the programmer to define Tcl commands in Java code, evaluate Tcl scripts and use the Tk graphical user interface from Java. This might be used to recode performance critical parts of Tcl scripts, provide user configuration of an application for Java, or offer other capabilities. The source code is available, see the `Sources' section at the end.


Introduction

Tcl [Ousterhout94] was originally created to add scripting capabilities to C programs. The design of Tcl makes it easy to add scripting to C applications by defining new Tcl commands in C.

However, as more applications are sent over the Internet, the need for completely portable applications is increasing. Since the Tcl language has already been ported to Microsoft Windows, Macintosh, and many Unix variants, the Tcl parts of hybrid C/Tcl applications are already portable. Unfortunately, C does not directly support cross platform execution without recompilation.

Fortunately, Java, a portable, compiled language similar to C++ [Hamilton96], can easily fill the same role C code does for Tcl, but in a portable way.

What can Java provide?

Java complements Tcl in ways similar to C. Java is a compiled language, so it is possible to write compute intensive functions in Java, while keeping the user interface in Tcl/Tk. Further, because Java is compiled, it can provide code obfuscation for organizations wanting the benefits of Tcl but who are reluctant to distribute source code. Java is portable, so it is possible to pass the compiled object files (`.class' files) between different machines, and be sure that they work on all platforms. Java provides support for data structures within an object-oriented framework.

How does Tcl fit Java?

Tcl complements Java in several ways as well. It provides a friendly yet powerful user interface that is portable between different platforms. It eases maintenance of legacy code by allowing changes to an interface very quickly because of Tcl's rapid development cycle. It also allows for ease of configuration in Java programs. These two languages work quite well in different spheres. When combined, they work even better.

Approaches

We examined three approaches for the TclJava interface. The first approach, developed by Patrick Naughton and Arthur van Hoff [Naughton94], forced the programmer to subclass the interpreter class to add new commands. This inheritance-based approach, though familiar and obvious from a C++ programmer's viewpoint, had several problems:

  • The second approach we considered was a completely string based implementation, where the name of the object and the method to invoke were constructed dynamically by the TCL script. This approach had the advantage of being very dynamic and flexible. However, this approach had problems as well:

    Our third design used a composition based model for defining Tcl commands in Java. commands defined in java are discrete objects, each with its own callback routine, deletion method for deletion from a Tcl Interpreter, and finalization method for use by the Java code. These objects are used as callbacks from withing an Interpreter object for the new tcl function. The results from these commands are also discrete objects. This approach allows commands to be added and removed from an interpreter while avoiding unsafe dynamic dispatches. This provides several critical features. The composition design is described in more detail in the following sections.

    Picture of the class hierarchy for the tcljava extension

    Figure 1. The TclJava Class Hierarchy

    An Interpreter in Java

    The TclJava interface is comprised of several Java classes that encapsulate a subset of the Tcl C API. These classes are shown in Figure 1, and explained in detail below.

    In C code that accesses the Tcl API's, the programmer must keep track of a `Tcl_Interp' structure. This structure encapsulates the state of the given interpreter. When using the TclJava interface, a Java class called `Interp' contains a reference to the C `Tcl_Interp' structure, in addition to several methods used to interact with an instance of the `Interp' class. This instance is passed to many of the methods of the various objects. There can be many interpreters in one Java program, so it is necessary to indicate which one to many of the methods on the various objects shown above.

    Evaluating a Tcl script from Java is similar to doing it in C. To execute a Tcl command in C, the programmer calls the Tcl_Eval function which returns the completion code. In Java, the programmer calls the `Interp.eval' method, which returns a completion code. The completion code is the numeric result indicating the success or failure of the command.

    In addition, the Java `Result' object contains both the numeric result and the string result of the last command. In C, the command procedure puts the string component of the result in the `result' field of the `Tcl_Interp' structure, and returns the numeric status code. In Java, the `Result' object contains both of these items: a numeric component to indicate if the command executed properly, and a `Value' component to contain the string result of commands.

    A new Tcl command is created from Java by defining a subclass of `Command' that implements the `invoke' method. The `Command' object is equivalent to the command procedure and the client data argument used in the C interface.

    The body of a callback is shown below. The programer subclasses the abstract class `Command', placing the body of the command in the `Command.invoke' method as below. Note how the `Command.invoke' procedure sets the string result using the method `interp.setResult', and then returns the status:

    class Callback extends Command {
        public int invoke(Interp interp, Value args[]) {
        /*
         * body of the command here
         */
        interp.setResult(new Value(``The result string to return.''));
            return Interp.TCL_OK;
        }
        public void deleted(Interp interp) {
            /*
             * code to execute when the
             * command is deleted
             * from the interpreter.
             */
        }
        protected void finalize() { 
            /*
             * code to execute when this
             * java command object
             * is finalized (garbage 
             * collected)
             */
        }
    }

    Figure 2. Defining a callback object for a new command from within Java.

    There are three main steps to writing a TclJava program. First, the Java application creates an Interp object, and initializes any other Tcl extensions that are to be used. Tk is the only such extension provided in this interface at this time. The `TkApplication' class provides a method to initialize the Tk extension, and a simple event handling loop to handle user interaction. No other hooks into the Tk API's are provided, so the Java code must use the `Interp.eval' method to control or query the Tk environment.

    class myClass {
        /*
         * Other methods may be defined 
         * here.
         */
        public static void main(String args[]) {
            Interp interp = new Interp();
            TkApplication.TkInit(interp);
                .
                .
                .

    Figure 3. Beginning a Java program that uses Tcl/Tk.

    After creating the interpreter, the application uses the `Interp.createCommand' method to create a new Tcl command that is implemented by the specified Command object.

            interp.createCommand(``newcommand\ name'', new Callback());
                .
                .
                .

    Figure 4. Creating a Tcl command from Java.

    Finally, the programmer enters the event loop for the TkApplication. Note that in Java programs that use AWT, this event loop is implicit. When the TclJava application enters the `mainLoop' method, it won't return until all the Tk windows are destroyed.

            TkApplication.mainLoop();
        } 
    }

    Figure 5. Finishing the TclJava program.

    Limitations of this interface

    Although the TclJava interface exposes most of the functionality needed to implement interesting applications using Tcl/Tk and Java, it still has a number of limitations:

    Passing arguments between Tcl and Java requires a fresh allocation and de-allocation of a Java array, and a copying of the C strings to Java Strings each time the callback is invoked. This results in the garbage collection system being called quite often. Because of these efficiency concerns, Tcl extensions written in Java should minimize the amount of data passed across the TclJava interface. In the example program, a file name is passed to the Java-defined command, and a single string (consisting of all the data in a list format) is returned. The traffic over the TclJava interface is kept to a minimum. One could imagine another design, where each element of the zip file were returned one at time. This approach could perhaps provide for more detailed control, but would also lower the efficiency of the interface.

    Most of the C API's provided by Tcl/Tk aren't exposed to Java. The only way for TCL scripts to interact with the Java binaries is through command implementation callbacks. The only way for Java to access the state of the interpreter is through the `Interp.eval' method. To make TclJava more complete, many of the existing C API's should be added.

    No link exists between Java variables and Tcl/Tk variables. Tcl provides interesting tracing capabilities on variables that are not available from Java.

    Java's graphical user interface, AWT, and Tk are mutually exclusive. One cannot currently write applications that utilize both interfaces.

    Tcl/Tk is not thread safe, so it is very important that all calls that do anything with the Tcl interpreter come from a single thread. If calls are made from different threads Tcl/Tk will crash, usually quickly.

    Conclusion

    This interface is a convenient way to extend the capabilities of both languages in way that is portable between the Unix, Macintosh and Windows worlds. Using composition instead of inheritance to add new commands to a Tcl interpreter allows for a dynamic environment in Java while maintaining the type safety of the Java objects. Given the minimalist nature of the TclJava interface, this work should be viewed as a starting place for further Tcl/Java integration.

    Acknowledgments

    Thanks to John Ousterhout, Chris Perdue, Steve Uhler and Brian Lewis for making valuable suggestions on short notice.

    References

    [Ousterhout94] John K. Ousterhout, (ouster@eng.sun.com), Writing Tcl Applications in C, Tcl and the Tk Toolkit, July 1994.

    [Naughton94] Patrick Naughton, (naughton@starwave.com), Java/Tcl Interface. Sun Microsystems, Inc, World Wide Web page http://java.sun.com/applets/alpha/notapplets/tcl/, November 1994.

    [Hamilton96] Mary Hamilton, Java Documentation. Sun Microsystems, Inc., World Wide Web page http://java.sun.com/doc.html, December 1995.

    Sources

    All source code mentioned in this paper is available online as
    ftp://ftp.smli.com/pub/tcl/tcljava#.#.tar.gz, where `#.#' is the version number.


    Last Modified: 11:31am PDT, May 29, 1996