How handlers work in Web-accessible home automation

Using Sun Labs' Brazil server as your starting point

1 2 Page 2
Page 2 of 2
    if (!request.url.startsWith(urlPrefix)) {
       log("Not my prefix: " + request.url);
       return false;
    }

is present near the top of every request method to quickly eliminate the processing of URLs that are meant for other handlers. By returning false, the server knows to pass the request on to the next handler. Once the handler has identified this request as one of its own, it needs to examine the URL (contained in request.url) to figure out what to do with it. Two common methods are used for processing URLs. The first one:

StringTokenizer fields = new StringTokenizer( request.url.substring(1),"/")

breaks the URL into its directory components. (URLs always start with a /). The second method uses the HTTP query data (the information after the ? in the URL), either directly or by placing the name/value pairs from the query into a hashtable for easy access.

In this handler, the query data contains a simple string, so request.query is used directly.

Now that we have identified the URL as belonging to the handler, we check it to make sure the parameters are valid (returning an error message if they are not) and acquire the mutex for sending a power-line command. The sendCommand method, implemented in a subclass, is responsible for actually delivering the command to the power line.

The data is returned back to the client in three possible ways:

  • As a plain text file in Java properties format
  • As a formatted HTML page
  • As a side effect, where the data is stored in the request object, to be consumed by a subsequent handler

The type of response generated depends on the particular handler.

If the result of the handler is expected to be consumed by an applet, JavaScript, or possibly another Web server, then the output is generally in the form of name/value pairs, and returned as a plain text file in Java properties format. This is a particularly easy format for an applet to deal with because a single Properties.load() call on the URLInputStream() captures all the data in an easy-to-use manner.

An HTML page is generally returned when the result is expected to be viewed directly by a user and contains fixed information. Error messages are commonly returned as HTML files.

If the result is to be combined with other information, or if it seems like a lot of HTML code is being written in Java, then returning HTML is probably a poor choice. Instead, the handler should store the results in the request object so that another handler, such as the TemplateHandler, can combine the results with a template that will be delivered to the user as HTML. This way, the look and feel of the result display is stored in the template file, not in the Java source code, and can be changed without resorting to modifying the Java code.

Handlers that return values by adding information to the request object, which can be done by creating entries in the request.props field, should have a configuration setting that allows them to return the data as as a plain text Java properties response as well. Then the ProxyPropertiesHandler can be used as part of a server configuration to allow the handler to be run either upstream of the handler consuming the results, or on an entirely different host. (The ProxyPropertiesHandler proxies URL requests to a remote server, retrieves replies in the form of Java properties files, and inserts the results into the Request.props field.) Using this technique, handlers that would normally just run for their side effects, passing information to another handler, can instead be run on another computer. This can be particularly useful when a physical device, such as an X10 interface, is involved, and the computer connected to the hardware interface is different than the computer on which the server is being run.

Notification requests are handled similarly to command requests, but no argument checking is required. A mutex, notifyMutex, is acquired, and then the command is processed. If an event from the power line is already on the queue, it is delivered back to the client. Otherwise, the request is held:

   while (queue.size() == 0) {
       queue.wait();
   }

until an event arrives, and the background thread has been placed in the queue.

The log method provides a convenient way for the concrete subclasses to log information to the server console without requiring them to have any server-specific knowledge.

The rest of the file defines the methods that need to be implemented by the concrete classes. All the HTTP-, URL-, and server-related information is dealt with in GenericProxyHandler (which is the parent class of TwoWayHandler), so its subclasses only need to concentrate on the specifics of the particular X10 interface device.

TwoWay X10 handler

TwoWay contains the code required to handle a specific X10 power-line device. It only needs to implement the four abstract methods defined above (run, init, respond, and request). The key point to note is that the init method tries hard to communicate with the device before returning true. Returning true would indicate that a two-way device is attached, so it would make sense to register the handler. Recall that the ChainHandler expects successful unit operations to return true. The getCommand method blocks until there is something on the power line to return. Please refer to the TwoWay manual and information in the Resources section below.

You can see annotated source code for the TwoWay X10 handler here: http://cv360428-a.norwlk1.ct.home.com/jw/x10/TwoWay.html

Final notes

We have developed more than 30 handlers for the Brazil architecture. Each of these handlers implements a small set of functionality that can be easily reused with other handlers. The Brazil programming style of minimizing the number of APIs a developer needs to understand allows developers to concentrate on the application domain, not on how to use the API -- and they don't have to worry that some feature of an API is not being used. The ability of Brazil to leverage Web browsers as a test and deployment platform without requiring significant investment in a UI component can also be utilized to quickly develop fully working prototypes.

Brazil is totally compatible with other technologies, like the Open Services Gateway Initiative (OSGI), Jini technology, and the Java Dynamic Management Kit (JDMK). Handlers for the Java Message Service (JMS) API and Java Reliable Multicast (JRMS) have been also been developed. A commercially available product similar to Brazil can be found at http://www.sun.com/software/embeddedserver

Rinaldo DiGiorgio developed some of the initial code for the X10 protocol implementation. He is a regular columnist for JavaWorld. Colin Stevens designed, coded, and tested significant parts of Brazil, including the X10 interfaces. Stephen Uhler is the primary designer of Brazil.

Learn more about this topic

1 2 Page 2
Page 2 of 2