JDK utilities

To wind up this initial section, here is a small collection of utilities that you can use to get information from the JDK. These Tcl procedures will be available to you if you have executed:
  package require javatute
in your tclsh or wish.

jdkProperties

This Tcl proc expands on the java::call proc to print all JDK system properties:

proc jdkProperties {} {
    set props [java::call System getProperties]
    set names [$props propertyNames]
    while { [$names hasMoreElements] } {
	set name [$names nextElement]
	puts "$name=[$props getProperty $name]"
    }
}

jdkVersion

This Tcl proc prints Tcl and Java version information:

proc jdkVersion {} {
    global tcl_version tcl_patchLevel
    puts "jdk version: [java::call System getProperty "java.version"] \
	    Tcl Blend patch level: $::java::patchLevel"
    puts "tcl version: $tcl_version \
	    tcl patch level: $tcl_patchLevel"
    puts "java package: [package versions java] \
	    info loaded: [info loaded]"   
}

jdkCapture

This Tcl proc executes Tcl code, but captures any output produced into Java's standard output and writes it into the variable given by the second argument. It is a good illustration of how to capture output from Java:

proc jdkCapture {script varName} {
    upvar $varName output
    set stream [java::new java.io.ByteArrayOutputStream]
    set printStream [java::new \
	    {java.io.PrintStream java.io.OutputStream} $stream]
    set stdout [java::field System out]
    java::call System setOut $printStream
    set result [uplevel $script]
    java::call System setOut $stdout
    $printStream flush
    set output [$stream toString]
    return $result
}
Most of this code is tedious but straight-forward. What we are doing is creating a new output stream and assigning it to the out field of System. We then execute the Tcl script argument, and get anything printed to the output stream and assign it to the Tcl variable given by varName. Finally, we restore the output stream to the value it originally had, and return the result of evaluating the Tcl script.

Here is an example that illustrates the use of this procedure. What we are doing is asking the System properties object to print itself to the System output, which is then returned by our call to jdkCapture:

jdkCapture {[java::call System getProperties] \
             {list java.io.PrintStream} [java::field System out]} props
puts $props
(This example is a little forced, since we had to find somewhere in the standard Java libraries that prints to standard output. This is more likely to be useful once you start writing your own Java code.)

jdkStackTrace

If you call Java and there is an error in the Java code, Tcl Blend doesn't directly give you a way to access the Java stack trace. Fortunately, the Java exception is referenced in the errorCode global variable, so we can get the stack trace from it. Here is a little Tcl proc that prints the Java stack trace from the most recent exception:

proc jdkStackTrace {} {
    global errorCode errorInfo
    if { [string match {JAVA*} $errorCode] } {
	set exception [lindex $errorCode 1]
	set stream [java::new java.io.ByteArrayOutputStream]
	set printWriter [java::new \
		{java.io.PrintWriter java.io.OutputStream} $stream]
	$exception {printStackTrace java.io.PrintWriter} $printWriter
	$printWriter flush

	puts "[$exception getMessage]"
	puts "    while executing"
	puts "[$stream toString]"
	puts "    while executing"
    }
    puts $errorInfo
}
Here is a small piece of Tcl Blend code to illustrate its use:
  set s [java::new {String java.lang.String} "123"]
  if { [catch {$s charAt 4}]} {
    jdkStackTrace
  }