Chapter 14. guacamole-common

The Java API provided by the Guacamole project is called guacamole-common. It provides a basic means of tunneling data between the JavaScript client provided by guacamole-common-js and the native proxy daemon, guacd. There are other classes provided as well which make dealing with the Guacamole protocol and reading from guacamole.properties easier, but in general, the purpose of this library is to facilitate the creation of custom tunnels between the JavaScript client and guacd.

HTTP tunnel

The Guacamole Java API implements the HTTP tunnel using a servlet called GuacamoleHTTPTunnelServlet. This servlet handles all requests coming to it over HTTP from the JavaScript client, and translated them into connect, read, or write requests, which each get dispatched to the doConnect(), doRead(), and doWrite() functions accordingly.

Normally, you wouldn't touch the doRead() and doWrite() functions, as these have already been written to properly handle the requests of the JavaScript tunnel, and if you feel the need to touch these functions, you are probably better off writing your own tunnel implementation, although such a thing is difficult to do in a performant way.

When developing an application based on the Guacamole API, you should use GuacamoleHTTPTunnelServlet by extending it, implementing your own version of doConnect(), which is the only abstract function it defines. The tutorial later in this book demonstrating how to write a Guacamole-based web application shows the basics of doing this, but generally, doConnect() is an excellent place for authentication or other validation, as it is the responsibility of doConnect() to create (or not create) the actual tunnel. If doConnect() does not create the tunnel, communication between the JavaScript client and guacd cannot take place, which is an ideal power to have as an authenticator.

The doConnect() function is expected to "attach" a GuacamoleTunnel to the web session, abstracted by GuacamoleSession. Attaching a tunnel to the session allows future tunnel requests to retrieve the same tunnel and use it, thus allowing one tunnel to be split across multiple requests. Assuming the doConnect() function successfully creates the tunnel, it must then return the created tunnel. The already-implemented parts of GuacamoleHTTPTunnelServlet then return the unique identifier of this tunnel to the JavaScript client, allowing its own tunnel implementation to continue to communicate with the tunnel existing on the Java side.

Instances of GuacamoleTunnel are created associated with a GuacamoleSocket, which is the abstract interface surrounding the low-level connection to guacd. Overall, there is a socket (GuacamoleSocket) which provides a TCP connection to guacd. This socket is exposed to GuacamoleTunnel, which provides abstract protocol access around what is actually (but secretly, through the abstraction of the API) a TCP socket. The GuacamoleSession allows instances of GuacamoleTunnel to be shared across requests, and GuacamoleHTTPTunnelServlet pulls these tunnels from the session as necessary to fulfill requests made by the JavaScript client.

The Guacamole web application extends this tunnel servlet in order to implement authentication at the lowest possible level, effectively prohibiting communication between the client and any remote desktops unless they have properly authenticated. Your own implementation can be considerably simpler, especially if you don't need authentication:

public class MyGuacamoleTunnelServlet
    extends GuacamoleHTTPTunnelServlet {

    @Override
    protected GuacamoleTunnel doConnect(HttpServletRequest request)
        throws GuacamoleException {

        // Connect to guacd here (this is a STUB)
        GuacamoleSocket socket;

        // Establish the tunnel using the connected socket
        GuacamoleTunnel tunnel = new GuacamoleTunnel(socket);

        // Attach tunnel to session
        GuacamoleSession session = new GuacamoleSession(httpSession);
        session.attachTunnel(tunnel);

        // Return pre-attached tunnel
        return tunnel;

    }

}

Using the Guacamole protocol

guacamole-common provides basic low-level support for the Guacamole protocol. This low-level support is leveraged by the HTTP tunnel implementation to satisfy the requirements of the JavaScript client implementation, as the JavaScript client expects the handshake procedure to have already taken place. This support exists through the GuacamoleReader and GuacamoleWriter classes, which are similar to Java's Reader and Writer classes, except that they deal with the Guacamole protocol specifically, and thus have slightly different contracts.

GuacamoleReader

GuacamoleReader provides a very basic read() function which is required to return one or more complete instructions in a char array. It also provides the typical available() function, which informs you whether read() is likely to block the next time it is called, and an even more abstract version of read() called readInstruction() which returns one instruction at a time, wrapped within a GuacamoleInstruction instance.

Normally, you would not need to use this class yourself. It is used by ConfiguredGuacamoleSocket to complete the Guacamole protocol handshake procedure, and it is used by GuacamoleHTTPTunnelServlet within doRead() to implement the reading half of the tunnel.

The only concrete implementation of GuacamoleReader is ReaderGuacamoleReader, which wraps a Java Reader, using that as the source for data to parse into Guacamole instructions. Again, you would not normally directly use this class, nor instantiate it yourself. A working, concrete instance of GuacamoleReader can be retrieved from any GuacamoleSocket or GuacamoleTunnel.

GuacamoleWriter

GuacamoleWriter provides a very basic write() function and a more abstract version called writeInstruction() which writes instances of GuacamoleInstruction. These functions are analogous to the read() and readInstruction() functions provided by GuacamoleReader, and have similar restrictions: the contract imposed by write() requires that written instructions be complete

The only concrete implementation of GuacamoleWriter is WriterGuacamoleWriter, which wraps a Java Writer, using that as the destination for Guacamole instruction data, but you would not normally directly use this class, nor instantiate it yourself. It is used by ConfiguredGuacamoleSocket to complete the Guacamole protocol handshake procedure, and it is used by GuacamoleHTTPTunnelServlet within doWrite() to implement the writing half of the tunnel.

If necessary, a GuacamoleWriter can be retrieved from any GuacamoleSocket or GuacamoleTunnel, but in most cases, the classes provided by the Guacamole Java API which already use GuacamoleWriter will be sufficient.

Reading properties

The Guacamole Java API provides simple access to guacamole.properties for convenience, although such support is not strictly required. This support is provided through the GuacamoleProperies utility class, which cannot be instantiated and provides two simple property retrieval functions: getProperty() and getRequiredProperty(), the difference being that the former can return null if a property is not defined, while the latter will throw an exception instead. These functions are generic and typesafe and will return the correct Java class or type when given an instance of a property.

In Guacamole, each property is declared as an implementation of GuacamoleProperty, and must provide an implementation of getName(), which returns the name of the property as it should exist within guacamole.properties, and parseValue(), which is given the String value of the property as read from guacamole.properties, and must return the declared type of the GuacamoleProperty implementation. A good example of how this works is the IntegerGuacamoleProperty implementation included within guacamole-common:

public abstract class IntegerGuacamoleProperty implements GuacamoleProperty<Integer> {

    @Override
    public Integer parseValue(String value) throws GuacamoleException {

        // If no property provided, return null.
        if (value == null)
            return null;

        try {
            Integer integer = new Integer(value);
            return integer;
        }
        catch (NumberFormatException e) {
            throw new GuacamoleServerException("Property \"" + getName() +
                "\" must be an integer.", e);
        }

    }

}

Notice that this implementation does not actually provide getName(). Instead, it only implements parseValue(), the intent being to make other developers' lives easier when they need to retrieve an integer property from guacamole.properties. Using this class, retrieving an integer property is simple:

public class MyClass {

    public static final IntegerGuacamoleProperty IMPORTANT_INT =
        new IntegerGuacamoleProperty() {

        @Override
        public String getName() { return "important-int"; }

    };

}

... later on within MyClass ...

int important = GuacamoleProperties.getRequiredProperty(IMPORTANT_INT);

guacamole-common provides a couple of similar classes for retrieving common types of properties, such as a String or File, and implementing your own to facilitate properties that parse into arrays or a List, etc. should be reasonably simple.