Monday, December 16, 2013

Java, expect and groovy

If you need the TCL expect functionality in your java software, you basically have 4 options

TCL-Java bridges such as Jacl/TclBlend ( are useful to run small pieces of TCL code, but since expect contains some native C libraries, jacl is not an option. In the best case, you can write a TCL wrapper with TclBlend that calls your java code instead of the opposite.

So, from these 4 options, only ExpectJ supports expect "interact" mode. In expect, the "interact" command gives back the session control to the user. So, if the idea is to connect to a remote site after multiple hops, and get the session back (instead of running fire-and-forget scripts), ExpectJ is the way to go.

Now, you can also expose ExpectJ directives in a nice way (DSL) using groove. This is specially useful if you want to let the user write his/her own "expect" scripts.

Allowing the user to run his/her own scripts from inside an application also has security issues. For example, would you allow the user to add a "system.exit()" command? Not a good idea. We have to limit this too.

Groovy has some syntactic sugar features that lets the developer to write DSLs in a much faster and simpler way that it would be if you decide to write a full language from the scratch, with tools like ANTLR and JavaCC. You can also use eclipse to edit and run your Groovy code in a nice IDE and the learning curve, for Java developers, is nice.

To demonstrate a simple proof-of-concept, let's assume these 4 steps

  • Wrap ExpectJ engine in a java class
  • Wrap java class into a Groovy class
  • Add Groovy syntatic sugar
  • Sandbox Groovy script interpreter

We're also using Jsch (, a pure java-based SSH client.

Step #1 - Wrap ExpectJ engine in a java class

    public class Expect {
        private ExpectJ expectinator;
        private Spawn spawn;
        private int lastPos;

        public Expect() throws IOException{
            expectinator = new ExpectJ(5);
            spawn = expectinator.spawn("localhost", 22, "leoks", "xyz");
        public void send(String s) throws IOException{
        public String expect(String s) throws IOException, TimeoutException{
            return getReply();
        private String getReply(){
            String all = spawn.getCurrentStandardOutContents();
            int newLastPos = all.length();
            String reply = all.substring(lastPos);
            lastPos = newLastPos;
            return reply;

Step #2 - Wrap java class into a Groovy class

    class ExpectGroovy {
        def Expect expectJava = new Expect()
        def expect(String expression){
        def send(String command){
            return expectJava.send(command)

Step #3 - Add Groovy syntatic sugar

    class Example {
        static main(args) {
            def binding = new Binding(exp: new ExpectGroovy())
            ExpectSandbox sandbox = new ExpectSandbox();
            def config = new CompilerConfiguration()
            config.scriptBaseClass =
            config.addCompilationCustomizers(new SandboxTransformer())
            def shell = new GroovyShell(this.class.classLoader, binding, config)
            String script = ...
            print "OK"
    abstract class ExpectBaseScriptClass extends Script{
        void expect(String expression){
            this.binding.exp.expect expression
        void send(String command){
            this.binding.exp.send command

Step #4 - Sandbox Groovy script interpreter

Now, the groovy script may look like this

    expect "\\$"

    //try to uncomment - the script does not kill the process, instead this instruction
    //is blocked by groovy security restrictions

    x = 0
    while (x < 5){
        result = send "hostname"
        expect "\\$"

Please notice that

  •     The “expect” object is implicit (binding)
  •     Groovy loops and variable assignment for free (mallet for example needed a special code just for loop evaluation)
  •     Groovy allows to restrict certain undesired commands, as well as java packages restrictions
  •     No explicit grammar defined
  •     Groovy can be coded with eclipse, with code completion, etc


1 comment:

  1. If you are considering using an Expect tool, give a try to yet another 'Expect for Java' implementation:

    It doesn't depend on third-party libraries, available on the Maven central and Apache licensed.

    Here is a code example of interacting with a public ssh service capturing the server output using regular expressions. You can compare the code below to Expect4J / expectj examples.

    JSch jSch = new JSch();
    Session session = jSch.getSession("new", "");
    Properties config = new Properties();
    config.put("StrictHostKeyChecking", "no");
    Channel channel = session.openChannel("shell");
    // jsch is ready
    Expect expect = new ExpectBuilder()
    .withInputs(channel.getInputStream(), channel.getExtInputStream())
    // trace all the I/O activity to the standard output stream
    // remove ANSI color escape sequences and non-printable chars
    .withInputFilters(removeColors(), removeNonPrintable())
    String ipAddress = expect.expect(regexp("Trying (.*)\\.\\.\\.")).group(1);
    System.out.println("Captured IP: " + ipAddress);