locomotive.org


Chapter III. Steps in Writing a Locomotive Module

Now that you understand some of the components that are involved in writing for the Locomotive, let's step back and summarize the steps that you yourself might take to write a typical module for the Locomotive. In writing for the Locomotive, there are five steps you need to take, not necessarily in any specific order:

  1. If you intend to generate dynamic HTML, then you need to create the HTML documents containing embedded Steam commands.
  2. You need to decide the different URLs to map to different actions in your module. Remember to take into account that some URLs are POST actions (HTML form submissions), while others are GET actions (no form submitted).
  3. If your module behaves differently for anonymous users vs. users who are logged in, you need to decide what the differences are before you can write code.
  4. You need to decide whether your module will be a Servlet or a Handler. We talk about the merit and disadvantages of each below.
  5. You'll need to add your module to the existing architecture. You can either do this through code, by having one module execute another directly, or you can add your module to either the "servlet.conf" or "hrt.conf" files and have the Locomotive execute it dynamically.
  6. Once you decide the service you're going to use, you can either write your module from scratch, or you can choose to subclass one of the classes provided with the Locomotive.
  7. You either need to create a new class of Handler, or you must modify an existing Handler, in order to tell the Locomotive how to route Web requests to your module.
  8. Often, when you create a new table in the database, you might create a Java class to represent the same entity within your program. That new class will handle searching, reading, and writing that entity to the database. Often, it is easy to take another Java class which does something similar, and modify it for the new entity. But beware! Be extra careful that all the appropriate sections of the old code have been updated.
  9. If your module reads or writes to the database, you need to create the database tables used by your module.
Now, even though we say that you can do these steps in any order, we find that the order above is most natural. When we create the HTML documents first, it's easier to visualize the features and functionality of the Web page we need to create, taking "the user's point of view". Once the basic Web pages are created, it is simple to decide how the different URLs map to each page -- just like in the DisplayPageHandler class did in the previous example. The Web pages naturally define different "regions" within our module.

Next, after deciding which URLs map to which actions, you can begin writing the basic structure of your module. In modules we write, the top-level method often serves as a "road map" for the rest of the module, by showing which actions to take for different URLs. We usually document this mapping between URLs and actions in the comments area of the top-level method call. In our code, each distinct action is usually handled by a separate, well-named method. This keeps the road map clear and easy to understand. The road map should also show what happens if users are anonymous, vs. logged in, and whether a particular action expects data from an HTML form submission. Of course, HttpServlets make this easy by providing doGet() and doPost() methods already.

 

Developing with the Locomotive

Now that you know the steps required in writing a module, let's say you've created your Java class, and want to add it to the Locomotive. Let's also say you've already added it to the servlet.conf or hrt.conf file. Now, all you need to do is compile it and you're off and running.

Automatic Class Reloading

Right now, the Locomotive uses a Classloader to allow developers to reload classes they're working on without restarting the server. The only thing you need to do to make use of this is to compile your module into the directory you've set the config variable SERVICE_CLASSPATH to. During development, we also recommend that you set the config variable AUTO_RELOAD_CLASSES to TRUE, which means that all you have to do to reload a class is recompile it into the SERVICE_CLASSPATH directory, and request the URL for that module. The Locomotive will automatically reload it for you.

Checking the Log files for Errors

Generally, the Locomotive will catch all exceptions and errors generated by modules. If you request your module and an error message is returned, be sure to look in the main server log, which will usually print out a Stack Trace of the exception. If you get an error message saying "A module to handle that request cannot be found", even though you know you've added your module to the routing table config file, then take a look in the log file for possible errors during startup. If your module could not be initialized properly, then an error message will appear in the logs.

 

Pros and Cons of Handlers and Servlets

At this point you may be wondering just what kind of service your module should be. We've talked a bit about both, and we've even showed an example of a Handler. So, just what are the differences between the two, and when should you use one as opposed to the other?

To start with, the Servlet interface is an increasingly popular external API created and backed by Javasoft. This means that if you write your module as a Servlet, you should be able to move your code from one Servlet Manager to another with very little trouble. This is a fairly compelling reason to use Servlets.

However, on the other hand, the Servlet interface, while powerful, is fairly complex. If you simply want to create a simple module for the Locomotive, you may want to go with a Handler- it's simplicity makes it very fast to develop. The Handler API was designed with the specific purpose of building web sites that required sophisticated Session and User management and interaction with the database, as well as easy editable templates to allow for sophisticated page layout. The purposes driving the Servlet API are much more broad, and consequently, the API is somewhat harder to learn and harder to use, if all you want to do what the Handlers were designed to do.

Perhaps a good compromise here is making your Servlet subclass LocoServlet. The LocoServlet class sets things up so that creating a module should be fairly easy. And, if you need to port you're code to another ServletManager, all you have to do is write a new superclass that emulates the functionality of the LocoServlet for the new ServletManager. This shouldn't be hard to do- just take a look at the LocoServlet source if you have any question about how to do it.

In terms of architecture, the major difference between Servlets and Handlers is that new Handlers get created in every request, while Servlets get created only once during Server startup. Both approaches have their pros and cons- Creating new Handler objects for each request is more expensive in terms of performance; making sure Servlet resources are thread safe can be troublesome. Both Servlets and Handlers allow you to initialize resources on Server startup, so properly written Handlers shouldn't be too expensive to create; likewise, properly written Servlets shouldn't have any multi-threading issues, and Servlets have the option of implementing SingleThreadModel to make threading issues disappear. So, the choice is yours.

Now, if the Servlet interface is more complex, does that mean that Handlers can't do everything Servlets can do? Well, yes and no. It's true that Servlets have some features Handlers don't; the most apparent one might be the Session objects table that lets Servlets store session associated objects persistently from request to request. While this makes things fairly easy when you're just starting up, as you begin to scale your site to multiple machines and multiple Locomotives, there can be an issue with maintaining consistency of session objects from Locomotive to Locomotive. While we can write the Objects table to the database, or even pass it from Locomotive to Locomotive using CORBA or RMI, it might well be more efficient for individual modules to figure out themselves how best to store data efficiently, and handle it themselves. So, while the Servlet interface may make things easier at the beginning, it may leave you with performance issues as your site becomes more popular.

As far as other features go, the two are nearly identical. With Servlets, you forward a request by using the getServletManager().getRequestDispatcher() method. With Handlers, you can do the same thing using the Loco.redirect() method. Both Handlers and Servlets support configuration files, both Servlets and Handlers allow you to initialize module specific resources on server startup and clean them up on server shutdown. Both provide easy access to the Request headers and form data, and both provide easy ways to write information back to the client. Generally, you should be able to mirror functionality between the two without any noticeable difference.

So, in the end, there really isn't much difference. Servlets are a popular external API, which means you're code will work with more that just the Locomotive. Handlers have a simple API that's streamlined for handling web-based requests by interacting with a database. Handlers get instantiated on every request, while Servlets get instantiated only once, at startup. When you make your choice, keep these in mind.

SUMMARY

In this chapter we talked about the basic steps you'll have to consider when you develop your module, and we talked a little about the differences between Servlets and Handlers.

In the next example, we'll run through how to create both a Handler and a Servlet for handling Login requests, so you should see more clearly how all these issues play out.