Die Programmiersprachen Java und Tcl sind längst feste Größen in der Welt der Compiler und Skripte. Doch auch eingefleischte Anhänger der einen Welt können kaum die Vorzüge der jeweils anderen verhehlen. Oft wäre es praktisch, in einem Tcl-Programm zusätzlich auf Java-Pakete und -Klassen zuzugreifen oder ein Java-Programm um Tcl-Scripting-Fähigkeiten zu ergänzen. Die Brücke zwischen beiden Welten schlagen Jacl und Tclblend, beide Softwarepakete des Tcljava-Projekts[1].
Jacl ist eine komplett neue Implementation des Tcl-Interpreters in reinem Java. Er führt Tcl-Skripte bis auf wenige Ausnahmen so aus, als ob sie in der normalen C-basierten Tclsh liefen. Ähnliches gibt es auch für andere Skriptsprachen wie Python (Jython,[3]) oder ECMA-Script/Javascript (Rhino,[4]). Je nach Aufgabe startet die Java-Anwendung den Interpreter mit einem Skript oder das Skript kontrolliert den Ablauf und greift auf Java-Methoden zu.
Jacl
Ioi Lam begann als Student in Cornell damit, den Tcl-Interpreter in Java neu zu schreiben. Inzwischen arbeitet vor allem Mo DeJong von Red Hat an Jacl. Bruce Johnson ergänzte mit seinem Swank[2] noch eine Tk-Variante, die er ebenfalls in reinem Java implementiert hat. Als Java-Software lassen sich Jacl und Swank über Java Webstart ausprobieren. Wer darin die Tkcon startet, hat eine komfortable Tcl-Shell frei Haus. Dazu muss eine JVM installiert sein, insbesondere für Webstart sollte es allerdings eine aktuelle Version (1.4.2) sein.
In Abbildung 1 ist die Java-Version der Tkcon zu sehen. Mit dem Tcl-Kommando »puts« hat der Benutzer zunächst einen Hallo-Welt-Text ausgegeben (blau dargestellt) und dann mit mehreren Tk-Kommandos ein weiteres Fenster mit einem Button erzeugt. Dank Swank ist das Fenster tatsächlich mit Swing- und nicht mit nativen Tk-Widgets gebaut. Sogar die Systemvariablen im »env«-Array entsprechen den Java-System-Properties, nicht den normalen Tcl-Werten.
Die Installation von Jacl und Swank gelingt am einfachsten mit der Tar-Datei von Swank[2], sie enthält bereits alle notwendigen Java-Bibliotheken sowie die Startskripte. Nach dem Auspacken startet der Aufruf »./wisk Skriptname« ein Skript im Interpreter, ganz wie bei »wish«. Die Swank-Distribution enthält unter anderem das Skript »swkon.tcl«, es handelt sich um die Java-Version der Tkcon (in Abbildung 1 benutzt).
Abbildung 1: Die Tkcon läuft hier als Java-Anwendung: Interpreter und Widget-Set sind in reinem Java implementiert. Der Benutzer kann dennoch normale Tcl- und Tk-Kommandos eintippen (links) und damit zum Beispiel neue GUIs gestalten (rechts).
Tcl und Tk auf Java-Basis statt mit C ist technisch interessant - damit sollte es sogar möglich sein, Tcl auf einem Java-fähigen Handy zum Laufen zu bringen. Weitaus wichtiger ist jedoch der Zugriff auf die jeweils andere Sprache.
Federführend
Für den Zugriff von Tcl auf Java ist ein Tcl-Paket namens »java« zuständig. Es enthält Funktionen, mit denen ein Tcl-Programm Java-Objekte und -Methoden aufrufen kann. Listing 1 zeigt ein Tcl-Skript, das die Java-Klasse in Listing 2 verwendet. Die Klasse enthält neben einfachen Getter- und Setter-Methoden (Beans-Konzept) auch mehrere Konstruktoren, statische Variablen und überladene Methoden.
01 # Benutzt die Java-Klasse "Demo" aus Listing 2
02
03 package require java
04 set env(TCL_CLASSPATH) "."
05
06 # statische Variable
07 puts "Demo.NAME '[java::field Demo NAME]'"
08
09 # statische Methode
10 set instance0 [java::call Demo getInstance "Statisch"]
11 puts "Aufruf getInstance '[java::prop $instance0 a]'"
12
13 # Objekt erzeugen
14 set instance [java::new Demo "Erstes Objekt"]
15
16 # Wert abfragen und ändern
17 puts "Bean-Style '[java::prop $instance a]'"
18 java::prop $instance a "Geänderter Name"
19 puts "Bean-Style '[java::prop $instance a]'"
20
21 # Methode aufrufen
22 puts "Getter-Aufruf '[$instance getA]'"
23 puts "Aufruf von methode, Ausgabe '[$instance methode]'"
24 puts "Aufruf von toString, Ausgabe '[$instance toString]'"
25
26 # überladene Methode
27 catch {$instance überladen 1} err
28 puts stderr $err
29
30 puts "Eingabe als Integer, Ausgabe '[$instance {überladen int} 42]'"
31 puts "Eingabe als Double, Ausgabe '[$instance {überladen double} 42.0]'"
32
33 # Fehler fangen
34 foreach wert {0 1 2 3} {
35 puts -nonewline "Eingabe für Methode fehler='$wert'"
36 java::try {
37 puts ", Ausgabe '[$instance fehler $wert]'"
38 } catch {IllegalArgumentException iaexp} {
39 puts stderr ", Eingabefehler '[$iaexp getMessage]'"
40 } catch {IllegalStateException isexp} {
41 puts stderr ", Interner Fehler '[$isexp getMessage]'"
42 } catch {Exception exp} {
43 puts stderr ", Fehler '[$exp getMessage]'"
44 }
45 }
46
47 # gegen null prüfen
48 set b [java::prop $instance b]
49 if [java::isnull $b] {
50 puts "B ist null"
51 }
52
53 # Objektreferenzen in Java verwenden
54 set instance2 [java::new Demo "Zweites Objekt" $instance]
55 puts "toString '[$instance2 toString]'"
|
01 // Demo.java
02 import java.awt.event.*;
03
04 /**
05 * Beispielklasse für Tcl/Java-Integration
06 */
07 public class Demo {
08 public static final String NAME = "Demo";
09 private String a;
10 private Demo b;
11 private int c;
12
13 public Demo (String a) {
14 this.a = a;
15 }
16 public Demo (String a, Demo b) {
17 this.a = a;
18 this.b = b;
19 }
20 public static Demo getInstance (String a0) {
21 return new Demo(a0);
22 }
23
24 public void setA (String a) {this.a = a;}
25 public String getA () {return a;}
26 public Demo getB () {return b;}
27 public void setC (int c) {this.c = c;}
28 public int getC () {return c;}
29
30 public String fehler (long a) {
31 if (a>0) {
32 if ( (a%3) == 0 ) {
33 throw new IllegalArgumentException ("teilbar durch 3");
34 } else if ( (a%2) == 0 ) {
35 throw new IllegalStateException ("teilbar durch 2");
36 }
37 }
38 return String.valueOf (1/a);
39 }
40
41 public long methode() {
42 return System.currentTimeMillis();
43 }
44 public String toString() {
45 return (b == null) ? a : (b.toString( ) + a);
46 }
47
48 public String überladen (int a) {return String.valueOf(a/2); }
49 public String überladen (double a) {return String.valueOf( a ); }
50 }
|
Nach dem üblichen »package require java« (Zeile 3 in Listing 1) steht in Tcl eine Reihe neuer Kommandos im Namensraum »java« zur Verfügung. Eine Zusammenfassung der Befehle zeigt Tabelle 1. Damit Jacl die zusätzlichen Java-Klassen findet, setzt das Tcl-Skript in Zeile 4 einen zusätzlichen Classpath. Alternativ könnte man vor dem Start der JVM auch die globale »CLASSPATH«-Variable ändern.
|
|
|
Kommando
|
Aufgabe
|
|
java::new Klassenname Argumente
|
Erzeugt neue Java-Objekte
|
|
java::call Klassenname Methodenname Argumente
|
Ruft statische Methoden auf
|
|
java::field Klasse/Objekt Feldname [Wert]
|
Fragt oder setzt Objektattribute
|
|
java::instanceof Java-Objekt Klassenname
|
Wie »instanceof« in Java
|
|
java::prop Java-Objekt Name [Wert]
|
Fragt oder setzt Bean-Style-Attribute
|
|
java::bind Java-Objekt Event-Name Skript
|
Bindet ein Tcl-Skript an einen Java-Event
|
|
java::null
|
Gibt das Java-Objekt »null« zurück
|
|
java::isnull Objekt
|
Testet Java-Objekt gegen »null«
|
|
java::throw Throwable
|
Wirft eine Java-Exception
|
|
java::try Skript catch { Klassenname Variable} Skript finally Skript
|
Das Äquivalent zu Javas Try/Catch/Finally-Konstrukt
|
|
java::cast Klassenname Java-Objekt
|
Äquivalent zu Java »cast«
|
|
java::import Klassen/Paketname
|
Äquivalent zu Java »import«
|
« Zurück
1
2
3
4
5
Weiter »