In this section we will try to explore the way java objects are accessible from wadm, and how to get the framework to do our bidding.

Our tools

log into wadm and type

> java::  
ambiguous command name "java::": ::java::autolock ::java::autolock_create_instance ::java::autolock_destroy_instance ::java::bind ::java::call ::java::cast ::java::defineclass ::java::event ::java::field ::java::getinterp ::java::import ::java::info ::java::instanceof ::java::isnull ::java::load ::java::lock ::java::new ::java::null ::java::prop ::java::throw ::java::try ::java::unlock  

These commands are explained here(external)

Exploring wadm.

> java::call java.lang.Thread dumpStack  
java.lang.Exception: Stack trace  
        at java.lang.Thread.dumpStack(  
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)  
        at sun.reflect.NativeMethodAccessorImpl.invoke(  
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(  
        at java.lang.reflect.Method.invoke(  
        at tcl.lang.reflect.PkgInvoker.invokeMethod(  
        at tcl.lang.JavaInvoke.callStaticMethod(  
        at tcl.lang.JavaCallCmd.cmdProc(  
        at tcl.lang.AutoloadStub.cmdProc(  
        at tcl.lang.Parser.evalObjv(  
        at tcl.lang.Parser.eval2(  
        at tcl.lang.Interp.eval(  
        at tcl.lang.Interp.eval(  
        at tcl.lang.Interp.recordAndEval(  
        at tcl.lang.ConsoleThread$1.processEvent(  
        at tcl.lang.Notifier.serviceEvent(  
        at tcl.lang.Notifier.doOneEvent(  
        at tcl.lang.JaclShell.invoke(  
        at com.sun.web.admin.cli.shelladapter.WSadminShell.invokeJaclShell(  
        at com.sun.web.admin.cli.shelladapter.WSadminShell.main(  

The java::call is used to call a static method in the provided class. In this instance we call dumpStack static method in Thread class.

The last few lines are pretty interesting, It tells us that there is a class called WSadminShell that has invoked the class called JaclShell.
Let us explore these two classes.


namespace eval Reflect {  
    namespace export get_class for_each_el  
    proc get_class {cl} {  
        return [java::field $cl class]  
    proc get_name {i} {  
        return [[$i getClass] getName]  
    proc for_each_el {arr cmd} {  
        set size [$arr length]  
        set result {}  
        for {set i 0} {$i < $size} {incr i} {  
            set e [$arr get $i]  
            java::lock $e  
            lappend result [eval $cmd]  
        return $result  
> source reflect.tcl  
> namespace import Reflect::\*  

The above commands will import all the methods defined in the reflect namespace to the global.

> set shelladapter "com.sun.web.admin.cli.shelladapter"  
> set methods [[get_class $shelladapter.WSadminShell] getDeclaredMethods]  
> for_each_el $methods {  puts [$e getName] }

As you would have doubtless noticed, the for_each_el is a control structure that we have just defined. It is a powerful faciilty that can be combined
with upvar and upeval to emulate almost any kind of features in other languages.

> for_each_el $methods {puts "[$e getName] - [[$e getParameterTypes] length]"}  
main - 1  
invokeJaclShell - 1  
exitIfNotShell - 2  
handleError - 1  
handleError - 1  
handleError - 1  
invokeFramework - 1  
getArg - 3  
getBooleanArg - 3  
getRCFile - 1  
getScriptFileName - 1  
getScriptFilePosition - 1  
invokeConnectCommand - 1  
isConnectOpt - 1  
printVersionAndExit - 0  
printCommandLine - 1  

The number of arguments taken by each method.
Some of the interesting methods that seem to exist are (purely based on method names)

invokeJaclShell - 1  
invokeFramework - 1  
invokeConnectCommand - 1  

let us try the same with JaclShell, the other interesting class

> set methods [[get_class tcl.lang.JaclShell] getDeclaredMethods]       
> for_each_el $methods {puts "[$e getName] - [[$e getParameterTypes] length]"}  
invoke - 1  
evalRC - 0  
loadWadm - 0  
reader - 0  
fetchCommands - 0  
getTclshrcContent - 0  

This is slightly more interesting than the other one.
the interesting ones here seems to be 
reader , fetchCommands

Let us take the first one, reader and see what it can tell us

> java::info class [java::call tcl.lang.JaclShell reader]  

This is pretty interesting. More so because it does not belong to either com.sun.xxxx or which 
we are expecting. There is a high chance that this is from a different product. Let us ask google.
The source for this particular class seems to be accessible here.

Jline is a package that gives you history, keboard shortcuts and auto completion according to their website.
Since the wadm does have all these, it stands to reason that wadm is using jline to implement these.

Let us see if we can add a simple completion command to it.
looking at the fields of jline.ConsoleReader

> set f_console [[java::field jline.ConsoleReader class] getDeclaredFields]  
> java::lock $f_console  
> for_each_el $f_console {puts "$i - [$e getName]" }  
0 - prompt  
1 - useHistory  
2 - CR  
4 - keybindings  
5 - bellEnabled  
6 - mask  
8 - autoprintThreshhold  
9 - terminal  
10 - completionHandler  
11 - in  
12 - out  
13 - buf  
14 - debugger  
15 - history  
16 - completors  
17 - echoCharacter  
18 - class$jline$ConsoleReader  

Of these, according to the jline source, the ‘completors’ holds the current completion classes.
let us define another procedure for easy access of fields

proc get_fields {cl method} {  
    set arr [[java::field $cl class] $method]  
    set result {}  
    foreach {l} [for_each_el $arr {list [$e getName] $e}] {  
        set result [concat $result $l]  
    return $result  

You can use it as

> array set c_fields [get_fields jline.ConsoleReader getDeclaredFields]  
> puts [$c_fields(completors) getName]  

circumvent private access.

> $c_fields(completors) setAccessible true  
> $c_fields(completors) get [java::call tcl.lang.JaclShell reader]  
> get_name [$c_fields(completors) get [java::call tcl.lang.JaclShell reader]]  

so let us go and get one of items out.

> [$c_fields(completors) get [java::call tcl.lang.JaclShell reader]] get 0               
no accessible method "get" in class java.lang.Object  

So we have to cast it to get this things out

> set completor [[java::cast java.util.List [$c_fields(completors) get [java::call tcl.lang.JaclShell reader]]] get 0]  

save for later

> java::lock $completor  
> get_name $completor  

So, we got the ArgumentCompletor instance in wadm which is used for completion of arguments. looking at it,

> get_fields jline.ArgumentCompletor getDeclaredFields  
completors java0x191 delim java0x192 strict java0x193  

From here(external) it looks like completors are the ones we need.

> array set a_completor [get_fields jline.ArgumentCompletor getDeclaredFields]  
> $a_completor(completors) setAccessible true  

Using the earlier set variable completor

> $a_completor(completors) get $completor      
> get_name [$a_completor(completors) get $completor]  

It is an array (the L syntax in java is for array)
so we use reflection to get to the array elements.

> set clist [$a_completor(completors) get $completor]   

save for later

> java::lock $clist  
> java::call java.lang.reflect.Array get $clist 0  
> get_name [java::call java.lang.reflect.Array get $clist 0]  

save for later

> set s_completor [java::call java.lang.reflect.Array get $clist 0]  
> java::lock $s_completor  
> get_fields jline.SimpleCompletor getDeclaredFields  
candidates java0x1ba delimiter java0x1bb filter java0x1bc  

The candidats seem an interesting option.

> array set s_fields [get_fields jline.SimpleCompletor getDeclaredFields]  
> puts $s_fields(candidates)  
> $s_fields(candidates) setAccessible true  
> get_name [$s_fields(candidates) get $s_completor]  
> [java::cast java.util.TreeSet [$s_fields(candidates) get $s_completor ]] add "true-blue"  

There. we have added a new auto completion to the wadm.
You can test it by trying this in the wadm prompt.

> true-[TAB]   

and it should complete itself to

> true-blue   

the complete reflect.tcl

namespace eval Reflect {  
    namespace export get_class for_each_el get_fields get_name  
    proc get_class {cl} {  
        return [java::field $cl class]  
    proc get_name {i} {  
        return [[$i getClass] getName]  
    #get_fields jline.ConsoleReader getDeclaredFields  
    proc get_fields {cl method} {  
        set arr [[java::field $cl class] $method]  
        set result {}  
        foreach {l} [for_each_el $arr {list [$e getName] $e}] {  
            set result [concat $result $l]  
        return $result  
    proc for_each_el {arr cmd} {  
        set size [$arr length]  
        set result {}  
        for {set i 0} {$i < $size} {incr i} {  
            set e [$arr get $i]  
            java::lock $e  
            lappend result [eval $cmd]  
        return $result  

You can load this in your wadm to get the customized auto completions in your wadm shell.

Please be ware that we are modifying the private interfaces of the jline and wadm 
and these may change in further releases. Do not use this for serious work.

We will explore adding options to the command completion at a later time.