Serve clients' specific protocol requirements with Brazil, Part 6

Plug Jini, BeanShell, and JAXM into Brazil

Brazil is a third-generation HTTP server that provides an extensible, understandable framework for building small, application-specific HTTP servers. In Parts 1 through 5 of this series, I discussed how to use Brazil technology to process content from various nontraditional sources, how to add to that content, and how to deliver that content to users via different delivery mechanisms and networks, such as applets, Java Reliable Multicast Service (JRMS), wireless clients, and plain HTML. In this final article of the series, I illustrate how to use the following technologies with Brazil:

  • Jini
  • BeanShell
  • JAXM (Java API for XML Messaging)

I also walk you through the first steps involved in integrating Brazil with Xalan-Java, servlets, Velocity, and LDAP (lightweight directory access protocol).

In some ways, this article resembles a shopping trip to Home Depot, where you purchase tools and gadgets not because you need them now, but because you might need them later. Many of you will not need the technologies listed above all at once or ever; however, you should know about them. So consider this article a short stroll down a short aisle in the open source world.

Read the whole series on Brazil technology:

In this article's examples, we will employ the Brazil server as a robust back plane to plug in various technologies using a combination of filters, handlers, and templates. Handlers provide basic functionality for accepting HTTP requests and dispatching to methods handling those requests; filters capture handlers' output and optionally modify the input for other filters, templates, or handlers to continue processing; and templates send HTML content through an HTML/XML parser to a set of templates. Each HTML/XML tag can dynamically invoke a Java method present in the templates. The dynamically generated content from the HTML/XML tag evaluation returns to the caller.

Use Jini with Brazil

I frequently run 10 to 20 instances of Brazil on my servers and often lose track of them. The last thing I want to do is remember where they are and what they do. I looked for a solution that provided basic services for locating and determining the servers' status and capabilities, as well as services for administering many Brazil servers. I wanted a solution that didn't require special bash scripts and monitor daemons, as traditional Unix/Linux/Windows approaches often do. Those solutions don't scale well when you have thousands of servers and you forget what they do.

Jini's lookup and discovery features combined with leasing can provide more dynamic and accurate solutions. The basic idea is to use the Jini lookup services provided via reggie, so interested users can find and interact with various Web servers without knowing the servers' URLs and services. reggie is Sun Microsystems' implementation of the Jini lookup service and is an activatable service that rmid (Java RMI Activation System Daemon) controls. The example I provide in this article deals only with a simple case and does not describe the services in great detail. Interested readers might consider expanding the example to support UDDI (Universal Description, Discovery, and Integration) and WSDL (Web Services Description Language). SUBHEAD2: Develop the application components

To register with the Brazil lookup service, we need a Jini lookup service and some client code. I provided some code to look up the registered services. In that example, which appears later in this section, I developed some additional scripts that allow you to start and stop reggie and rmid. You could also create a facility in Brazil that uses a reggie as a handler (an area requiring further research), allowing you, via a script or an ExecHandler interface, to start and manage the service without going to the operating system. Because the ExecHandler allows the services to automatically start up, it ensures fewer errors in the deployment of many servers providing reggie services (see the Brazil documentation for more information).

I wrote the reggie and rmid services in BeanShell. Before we move on, let's take a look at this tool in more detail.

BeanShell

Written in Java, BeanShell is a small, free, embeddable Java source interpreter with object-scripting language features. It executes standard Java statements and expressions in addition to obvious scripting commands and syntax. BeanShell supports scripted objects as simple method closures like those in Perl and JavaScript (see Resources for more information).

In prior JavaWorld articles, I discussed how to use Python and Tcl with Brazil technology. I like to program in one language everywhere; BeanShell allows me to use Java in yet another application space—in this case, scripting content on a Webpage—formerly occupied by languages like Tcl, Python, Perl, PHP (hypertext preprocessor), and Velocity. (I will discuss Velocity later in this article.)

The following documentation from the Brazil toolkit explains how BeanShell interfaces with Brazil as well as some of the configuration options available:

The BeanShellServerTemplate looks for one of the starting tags <server language="beanshell">, <beanshell>, or <bsh> in an HTML page and treats the following data up to the corresponding ending tag (</server>, </beanshell>, or </bsh>) as a BeanShell script to evaluate.

The reason that BeanShell scripts are included in an HTML page is usually to generate dynamic, server-side content. After running this template, everything between and including the starting tag and the ending tag is replaced by all output written to the BeanShell output stream (if any).

All BeanShell fragments within a given page are evaluated in the same BeanShell interpreter. The BeanShell interpreter actually lives for the entire duration of this Template object, so the user can implement persistence across requests.

Later in this article, I will introduce two additional methods for transforming and/or scripting content on a Webpage: using XSLT (Extensible Stylesheet Language Transformations) and XML; and, separately, Velocity.

The two BeanShell scripts below demonstrate how to register and look up a service. The services we use are written for Brazil Web servers so they can register themselves with a Jini lookup and provide a service for facilitating management and control as well as distributed computing environments that offer Web server features and Jini abilities.

To register the Jini Brazil service in our example, we use BeanShell to script a lookup discovery and subsequent registration on a Webpage. In other words, the Brazil server interprets the Webpage as containing BeanShell code, first processing the BeanShell code and then returning the page to the browser. This offers tremendous value for advanced developers and provides a powerful scripting language, powerful enough to script Jini directly.

The BeanShell code that follows registers a service with a running Jini lookup service such as reggie. Let's go over the code in detail.

The <bsh> tag informs the server that BeanShell code follows. You code your import statements as you would with Java. I have not used any wild cards, so you can see the classes that we're using:

 <bsh>
    import java.io.IOException;
    import java.rmi.RemoteException;
    import net.jini.core.lookup .ServiceRegistration;
    import net.jini.core.entry.Entry;
    import net.jini.core.lookup.ServiceID;
    import net.jini.core.lookup.ServiceItem;
    import net.jini.core.lookup.ServiceTemplate;
    import net.jini.discovery.DiscoveryManagement;
    import net.jini.lease.LeaseRenewalManager;
    import net.jini.lookup.ServiceItemFilter;
    import net.jini.lookup.ServiceDiscoveryManager;
    import net.jini.core.lookup.ServiceRegistrar;

Set some parameters for the lease duration and other attributes:

     long LEASE_DUR = 60*1000;
    String PRODUCT = "Brazil Registration Service";
    String MANUFACTURER = "Rinaldo DiGiorgio";
    String VENDOR = MANUFACTURER;
    String VERSION = "Jini 1.2";

BrazilAttribute.java contains the code for the Brazil attribute, which the lookup service uses. The example is simple. BrazilAttribute.java allows applications, other services, and users to look up services by requesting specific attributes.

The code below tells the lookup service that one of the attributes of the entity currently being registered is a location attribute:

     Entry[] serviceAttrs = new Entry[] {
        new BrazilAttribute("location"),
    };

This silly little print method prints some diagnostics:

     msg(String s) {
        System.err.println(s);
    }
    msg("Creating ServiceDiscoveryManager ...");
    DiscoveryManagement discoveryMgr = null;
    LeaseRenewalManager leaseMgr = null;
    ServiceDiscoveryManager sdm = null;

As of Jini 1.2, the preferred mechanism for discovering services is the ServiceDiscoveryManager:

     try {
        sdm = new ServiceDiscoveryManager(discoveryMgr, leaseMgr);
    } catch (IOException ioe) {
        msg("Trouble creating ServiceDiscoveryManager: " + ioe);
        return;
    }
    discoveryMgr = sdm.getDiscoveryManager();
    msg ( discoveryMgr.toString() );

Once we have a valid DiscoveryManager instance, register a Brazil service with the attributes supplied above in serviceAttrs:

     try {
        Thread.sleep(1000*5);
    } catch ( Exception e ) {
    if ( discoveryMgr != null ) {
        ServiceRegistrar[] regs = discoveryMgr.getRegistrars();
        for ( int k = 0; k < regs.length; k++) {
            msg("ServiceRegistrar found:" + regs[k]);
        }
        //
        // Register the service with the lookup service
        //
        BrazilService bs = new BrazilService("hostname:port");

The Jini lookup service returns a reference to a service. Our goal is to use Jini to look up Brazil instances; we do that by registering services with specific attributes and then looking for these services by providing attributes to the reggie lookup service. The BrazilService provides an example of some rudimentary interactions with a single instance of a Brazil server (there could be thousands of servers running). Register the Brazil service with the lookup:

        ServiceItem srvcItem = new ServiceItem(null,bs,serviceAttrs);
        for ( int h=0; h < regs.length; h++) {
            try {
                ServiceRegistration srvcRegistration = regs[h].register(srvcItem, LEASE_DUR);
                msg("Registered ServiceID:  "+(srvcRegistration.getServiceID()).toString());
            } catch (RemoteException e) {
                msg.println("RemoteException while registering the "+"Service: +service+"\n"+e.toString());
                e.printStackTrace();
            }
        }
    }
    sdm.terminate();
</bsh>

At the end of the above code, we register the BrazilService and terminate the ServiceDiscoveryManager. In our example, the BrazilService is associated with a running instance of a Brazil server. When the Brazil server starts, it registers itself with a Jini lookup service so that the other servers can find it using Jini lookup services. You can work with the leases to ensure that the registered servers are still up and operational, and enhance the BrazilService to include additional functionality.

Now that we have registered a service, how do we find it? To find a service matching the attributes we specified above, we can run our next example anywhere we wish. In this example, we look up and store the properties into the Brazil server's namespace, thereby making those properties available on a request, session, or server scope. When the user selects the URL for the code below, that code is processed along with the BSL (Brazil Scripting Language). Afterwards, Brazil's filter generates output.

Use BeanShell to look up the BrazilService:

 <bsh>
import java.io.IOException;
import BrazilService;
import net.jini.core.entry.Entry;
import net.jini.core.lookup.ServiceID;
import net.jini.core.lookup.ServiceItem;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.discovery.DiscoveryManagement;
import net.jini.lease.LeaseRenewalManager;
import net.jini.lookup.ServiceItemFilter;
import net.jini.lookup.ServiceDiscoveryManager;
msg(String s) {
   System.out.println(s);
}

Create ServiceDiscoveryManager with null arguments for default behavior:

   msg("Creating ServiceDiscoveryManager ...");
   DiscoveryManagement discoveryMgr = null;
   LeaseRenewalManager leaseMgr = null;
   ServiceDiscoveryManager sdm = null;
   try {
      sdm = new ServiceDiscoveryManager(discoveryMgr, leaseMgr);
   } catch (IOException ioe) {
      err("Trouble creating ServiceDiscoveryManager: " + ioe);
   }
   discoveryMgr = sdm.getDiscoveryManager();
   Create ServiceTemplate for an anything based lookup
   ServiceID serviceID = null;
   Class[] serviceTypes = { null };
   Entry[] attrSetTemplates = null;
   ServiceTemplate tmpl = new ServiceTemplate(null, null, null);

Perform nonblocking lookup with no local filtering; sleep just before lookup to give the discovery process time to find any available lookup services:

1 2 3 Page 1
Page 1 of 3