IBM Research


Developing a WBI plugin consists of three basic steps:
  1. Write a plugin class which instantiates the plugin's MEGs and registers them with WBI;
  2. Write any MEGs for which suitable MEG Beans do not already exist; and
  3. Register your plugin with WBI.
This document explains the basic process of developing a WBI plugin. It will refer to a demonstration plugin that is provided with the WBI Development Kit. The source code for this plugin is wbij45/com/ibm/wbi/examples/demo/ The registration file (see Registering Plugins) file is demo.reg, in the same directory.

If you are already using WBI DK version 4.1, see our notes on porting your plugins to the version 4.3. Little or no porting should be required in moving from 4.3 to 4.4, but see the Release Notes for details. Once again, no porting is required in moving from 4.4 to 4.5, but several new APIs and other changes might make it worth your while to update your code, see our latest Porting Notes.

Note: In this and other sections of the documentation, we use forward slashes (i.e., "/") as the path separator character. If you are using a DOS or Windows system, be sure to use back slashes (i.e., "\") instead.

Writing a Plugin Class

Getting Started

You can write WBI plugins with the Sun Microsystems Java Development Kit (JDK). With the JDK, you only need to decide what package you want your plugin to be in and where you will put the source files. We have found it convenient to put each plugin in its own package and to place the source file in the appropriate subdirectory of wbij45. For example, our demo plugin is in the package The source file is in the wbij45/com/ibm/wbi/examples/demo directory. You may find it convenient to place your source file in a similar package and place in this source tree. You can likewise put it in another convenient place, but you must ensure that Java can find your .class files by appropriately setting your CLASSPATH environment variable. Set your CLASSPATH to add the WBI executables, library, and demo classes, for instance,

Extending HttpPlugin

The way to write a plugin is to extend the HttpPlugin class, which is in the package.

When WBI starts up, it will instantiate an object of your plugin class using its default constructor. This instantiation of your plugin will persist throughout the entire life of WBI. So you can feel free to add member variables that keep track of information across many transactions. Your plugin can be a central information access point for all of the MEGs that form your application.

The demo plugin only has one static member variable that keeps track of a string HTML that will be delivered to the browser. The basic class definition looks like this:


  import java.text.DateFormat;
  import java.util.Date;


  public class demo extends HttpPlugin {
    final static String html = 
      "<h1>Hello, world!</h1>" +
      "<a href=/whoami>Who Am I</a>" +
      "<br><a href=/time>Time</a>" +
      "<br><a href=/template>Test Template</a>" +
      "<br><a href=/fetch>Fetch URL</a>";

    public void initialize() {




The import statements give the plugin access to some of the WBI classes, as well as some basic Java classes.

The only method we implement is the initialize method of HttpPlugin. This method will be called when our plugin is loaded into WBI, at power-up. Its main job is to instantiate and register the MEGs that constitute our plugin. It can also take care of any initial housekeeping associated with starting up the plugin (e.g. opening files, initializing data structures). The initialize() method takes no parameters.

Our First MEG

Now our initialize method gets to work initializing and registering the MEGs for the demo. The first one looks like this:
   // A generator to say "Hello", 
   // either as a server at the path: "/" or
   // as a proxy at the URL: "//_demo/"
   StaticHtmlGenerator shg = new StaticHtmlGenerator();
   shg.setName( "helloWorld" );
     "(host=%null% | host~_demo)" +
     " & path=/" );
   shg.setPriority( 10 );
   shg.setStaticHtml( html );
   addMeg( shg );

In this case, it is instantiating a pre-existing MEG Bean which can handle the requests appropriately. Before writing your own custom MEGs, it is worthwhile checking out the included MEG Bean library. If one of these beans does not suit your needs, you can often construct a simple custom MEG which does some of its own computation and then constructs an appropriate MEG bean to which the request can be forwarded. This technique will be explained in the section on writing your own MEGs.

Our plugin is instantiating a MEG that will simply produce an HTML page whenever its magic URL is requested. Since the MEG will receive a request (asking for the magic URL) and produce a document (the designated HTML page), it is a generator. The appropriate MEG Bean is StaticHtmlGenerator. This generator simply delivers a static chunk of HTML whenever it handles a request. In our case, this static HTML is a welcome page that presents links to the various demos.

After instantiating the StaticHtmlGenerator, the initialize method sets its properties to customize it appropriately. Every MEG has four properties that can be manipulated by the plugin: name, condition, priority, and enabled. The name will be output when WBI produces tracing information. The condition specifies when WBI should route the transaction through this MEG. The priority value is used to order MEGs when several wish to be involved in a transaction (higher numbers mean higher priority). The enabled property is used to turn MEGs on and off -- it defaults to enabled.

The most complex property is the condition, which determines when the MEG will be used. The complete grammar for specifying the condition can be found in the api documentation for setCondition.

In our case, we specify that this generator should be used whenever WBI receives a request for a particular host (to be discussed in a moment) and the path '/'. The path is the portion of the requested URL which follows the server name. The path always begins with a '/'. The path does not include any hash (e.g. "#mySection") or query (e.g. "?foo=bar") portion of the requested URL.

The part of the condition that checks the host allows two possibilities: that there is no host specified ("host=%null%") or that the host is "_demo". This part of the condition allows our generator to be accessed from a browser that is using WBI as a proxy (host is null) or as a web server (host is _demo). (For more information about the difference between browsers and servers, see Proxies & Servers). In brief, when your browser is configured to use WBI as a proxy, it sees every URL that your browser requests. So if you access the URL http://_demo/, then WBI will receive that request and parse it apart into a request for the path '/' from the server '_demo' using the 'http' protocol. This request matches this generator's condition and it handles the request. Likewise, if you do not have WBI configured to be your browser's proxy, but you request the URL or http://localhost:8088/, then WBI receives the request because you are accessing it through the URL. In this case, WBI receives a request for the path '/' through the http protocol. The server name will not be part of the request, because the server name simply tells the browser which server to contact and request the document '/' from. When WBI parses this apart, it sees a request for the path '/' from the server 'null' using the 'http' protocol. This also satisfies the condition for the generator, so it handles the request.

In most cases, your plugin will be designed to handle either server requests or proxy requests, not both, so you would only need to specify one of these conditions.

More MEGs

Now that we have looked at the simplest MEG, let's see what other things we can do.
  // now one for "/home"
  Generator pmg = 
     new PageMovedGenerator( "/" );
  // a simpler way to set name, condition and priority
  pmg.setup( "goHome", 
             "(host=%null% | host~_demo) & path=/home", 
             10 );
  addMeg( pmg );

  // now one for "/template" 
  // using the HtmlTemplateGenerator
  HtmlTemplateGenerator tg = 
    new HtmlTemplateGenerator();
  tg.setTemplate("<h1>Hello, ${name}!</h1>");
    "(host=%null% | host~_demo) & path=/template", 
    10 );
  addMeg( tg );


These two generators demonstrate two other MEG Beans. The first uses the PageMovedGenerator. This generator produces a document which instructs the browser to go to another URL to find the desired document. This happens all the time in normal web activity, but most users never notice. The "URL Location" field in the web browser will actually reflect the new URL rather than the one the user actually typed in. The PageMovedGenerator allows its DestinationUrl property to be set through its constructor, but it could likewise be set with the setDestinationUrl(...) method. We also see a simplified way to set the name, condition and priority properties with the setup(...) method. It allows you to set all three properties with one call. This generator redirects the browser to the demo main page whenever the URL http://_demo/home is accessed through WBI as a proxy or the URL is accessed through WBI as a web server.

The second generator uses the HtmlTemplateGenerator MEG Bean. This generator takes a template and a set of variables and produces an HTML document from them. The full description of the possibilities is in the documentation for the class, but this example simply takes a variable named 'name' and produces a greeting page for that person. The HtmlTemplateGenerator is useful for separating boilerplate and customization information. Thus the look-and-feel for web pages can be changed just by changing the templates -- effectively separating the customization logic from the look and feel. (You can just imaging the possibilities of merging this with XML). The condition for this one hooks it up to either http://_demo/template or, depending on whether you are using WBI as a proxy or as a server.

A Simple Editor

For variety, there is an HttpEditor in our plugin, too. It is based on the AddPreambleEditor MEG bean. This bean is used to add extra HTML or Javascript to the beginning of a web page. Doing this operation correctly is not trivial, as displayable HTML cannot appear before the <BODY> tag (if such a tag occurs at all!). Things get even more complicated when authors produce syntactically incorrect HTML. Though this editor doesn't always get it right in such cases, it does a pretty good job.
   // add a header to every HTML page if proxying
   AddPreambleEditor ape = new AddPreambleEditor();
      "<a href=''>" 
      + "WBI Home</a>" );
   ape.setup( "AddPreambleEditor", 
              "!host=%null% & content-type~text/html", 
              10 );
   addMeg( ape );

This MEG is setup with a condition that asks for it to run on every page it proxies (i.e., whose host is not null) of type text/html. In the code, you'll also find a corresponding editor for pages the demo plugin serves (i.e., whose host is null). This one adds a link to localhost rather than to _demo at the top of the page.

Clearly we do not wish to add text to the top of images and Java applets. Unfortunately, many servers lie about the kind of data they are delivering -- this often results from poor server configuration. This deception is not a problem for browsers, for browsers often know whether they expect to be receiving an image or Java classes or whatever. But WBI has no such luxury. Therefore, this editor will clobber such mis-labeled content. One solution is to register a high-priority editor which examines the reported content-type, the filename extension of the URL, and maybe the first few bytes of the data to try and determine the true content-type. This editor is known as the FixContentTypeEditor.

The AddPreambleEditor bean has a PreambleHtmlString property which determines what HTML should be added to the top of the page. We simply set this property with some HTML that provides a link to the WBI Home Page and then register the editor with WBI. Now most HTML pages will contain this extra link at the top. We say most instead of all because the editor does not edit:

  • HTML pages that are mis-labeled by the server as another content-type, and
  • pages in HTML frames.

As may be gathered from this discussion, editing arbitrary content that comes from the web has its pitfalls. However, the benefits in added web functionality can be worth the trouble. For example, see the Personal History plugin and the Edmark KidDesk Internet Safe application, which both use WBI for page editing.

Writing Your Own MEGs

The provided library of MEG beans perform many of the common functions that WBI plugins require. However, any substantial plugin will need some custom MEGs to perform application-specific computations. The demo plugin has two programmed MEGs to illustrate some of the basic concepts.

The WhoAmI Generator

Before we built MEG beans, programmers needed to carefully control every aspect of reading from and writing to the data streams. While this can be tedious, it is sometimes the best way to tailor a MEG optimally. The WhoAmITheOldWay generator is an example of such a MEG.

This generator is instantiated and registered with WBI like all the others:

  public void initialize() {
    // now one for "/whoami"
    Generator whog = new WhoAmITheOldWay();
    whog.setName( "whoAmI" );
      "(host=%null% | host~_demo)" + 
      " & path=/whoami" );  
    whog.setPriority( 10 );
    addMeg( whog );

Let us look in some detail at the actual WhoAmITheOldWay class to see how it works. We have made it an inner class of demo, but it could just as well be a separate class in the same source file (with package or private scope) or even a class in a different source file or entirely different package.

We will first look at the structure of the class:

  class WhoAmITheOldWay extends HttpGenerator {
    public void handleRequest( RequestEvent e ) 
      throws RequestRejectedException, IOException{

It extends the abstract base class HttpGenerator, which is an extension of Meg. There are corresponding base classes for each of the MEG types: HttpRequestEditor, HttpGenerator, HttpEditor, and HttpMonitor.Each MEG base class is abstract because it needs to have an implementation of the method handleRequest(RequestEvent).

The handleRequest(RequestEvent) method of a MEG is invoked whenever the MEG is included in a web transaction. For example, if a Generator has a condition "host~* & path=/wbi*" and WBI receives a request for, then that generator's handleRequest(RequestEvent) method will be called by WBI. It is the responsibility of that method to produce its proper output. Each MEG has its own input/output types:

MEG typeHTTP InputHTTP Output

Don't be confused by the fact that this method is called "handleRequest". This only means that WBI is requesting some action by the MEG. It does not mean that the MEG will be operating upon an HTTP Request. For example, an HttpEditor handles an HTTP Response.

The RequestEvent that is passed to the MEG contains three elements:

Note that the handleRequest(...) method can throw a RequestRejectedException. This exception allows the MEG to easily tell WBI that it does not wish to be involved in this particular transaction. If it is difficult to write a proper condition for the MEG that determines whether the MEG should invoked or not, simply write a condition which is more general than needed. Then have your MEG check to see if it really wants to be invoked -- if not, then throw this exception. Note: if the MEG has written to its output stream, or if the MEG has read improperly from its input stream, it is too late to reject the request. The results will be undefined; most likely the document returned to the client will be missing some or all data. See the documentation for RequestRejectedException for more detail on when requests may be legally rejected.

HandleRequest(...) can also throw IOExceptions and other Throwables such as RuntimeExceptions which do not show up in the signature of the method. An IOException is handled precisely like a RequestRejectedException, except that the message associated with the exception is logged. A Throwable which is not an IOException or RequestRejectedException is logged and causes the meg to be disabled. Recall that NullPointerException is a subclass of RuntimeException, and throwing one (which is easy to do) will cause your meg to be disabled. You can detect disabled megs at the command line by typing megs. To re-enable a meg, you need to restart WBI.

We can now look at the details of the WhoAmITheOldWay generator. The goal of this generator is to produce an HTML document that displays information about the browser's request. Here is the code for the entire generator:

class WhoAmITheOldWay extends HttpGenerator {
    int count = 0;

    public void handleRequest( RequestEvent e ) 
      throws RequestRejectedException, IOException {

      if (++count % 3 == 0) 
        throw new RequestRejectedException();
      // get document info, 
      // which holds structured info for request
      DocumentInfo di     =
      String requestedUrl = di.getUrl();
      String client       = di.getClient();
      String userAgent    = di.getUserAgent();

      HttpResponseHeader h = new HttpResponseHeader();
      h.set("Server", "IBM WBI");
      h.set("Content-Type", "text/html");

      // the stream we write to
      MegWriter w = e.getMegWriter();
      // now write to it
      w.write("<html>\n" );
      w.write("WhoAmI the Old Way");
      w.write("</title></head>\n" );
      w.write("<body><h1>WhoAmI the Old Way</h1>\n" );
      w.write("</th><th>Value</th></tr>\n" );
      w.write("<tr><td>Requested URL</td><td>" 
               + requestedUrl + "</td></tr>\n" );
               + client + "</td></tr>\n" );
              + userAgent + "</td></tr>\n" );
      w.write("</table>\n" );
      w.write("</body>\n</html>\n" );

Let's look at it piece by piece.
The count variable is declared as an instance variable. This generator is instantiated once (by the plugin's initialize() method) and remains persistent for as long as WBI runs. So the count variable retains its value across many transactions. MEGs can store state information of any complexity this way. In this example, all the generator does is to increment the count for each transaction. Then it uses this count to reject the request every third time. The user will then see an error page on his browser on every third reload of the page. This error will result because WBI will not be able to find a generator which will handle the request. (Technical note: in reality, WBI has a default generator that always handles a request when no other generator will. It is this default generator which produces the error page that appears on the browser.)

This method accesses the DocumentInfo that contains the structured part of the HTTP request. (See WBI Architecture for more details).

di.getUrl(), di.getClient(), di.getUserAgent()
The DocumentInfo object contains a parsed version of the HTTP request header. These methods return some of the information from those header fields. The generator will use this information to form the HTML page that will be returned to the browser.

HttpResponseHeader h = new HttpResponseHeader()
The generator is now ready to construct the HTTP response that will be sent back to the browser. The first thing is to construct the HTTP response header that will be returned. The HttpResponseHeader class represents response headers. In this example, we simply specify the type of the content and the server name that will be returned.
WBI needs to know which header to use for the response. The DocumentInfo class contains the request and response headers. Since this example is a Generator, it is the generator's responsibility to create a response header and put it in the DocumentInfo. In the case of an Editor, the response header should already be there.

Note: Any changes to the DocumentInfo, such as setting or changing the request or response headers, must be made before the first write to the MegWriter or MegOutputStream. Otherwise, the changes will be ignored.

MegWriter w = e.getMegWriter()
While the request and response headers are stored in the DocumentInfo as mentioned above, the body of the request or response is a different story. The RequestEvent stores input and output streams to read in and write out the bodies of the requests or responses. There are two kinds of streams, just like in normal Java I/O: character streams and byte streams. Character streams come as MegReaders and MegWriters. Byte streams come as MegInputStreams and MegOutputStreams. Character streams should be used whenever the request or response body consists of character data (generally, when the content type is "text/*", where "*" is anything). Byte streams should be used at all other times.

Since our generator example is returning an HTML document as a response, we use the MegWriter to write out the document.

w.write( ... )
The generator now writes the HTML document to the MegWriter. When it is done writing, it closes the stream. One very useful method of MegOutputStream is write(MegInputStream). This method copies all of the remaining data from a MegInputStream to the MegOutputStream in one call. It is useful for writing the contents of an entire file (through a FileInputStream) to the MegOutputStream, or for copying the input stream unchanged (e.g. in an Editor that has decided not to make any further changes to the data).

Note: There is no need to wrap the w.write(...) calls in a try-catch block, as handleRequest can throw an IOException.

The TimeGenerator Generator

In most cases it is unnecessary to program a MEG with the detail of the WhoAmITheOldWay generator. Oftentimes we can construct a MEG by dynamically instantiating appropriate MEG beans as we decide that we need them. The TimeGenerator demonstrates this approach.

The goal of the TimeGenerator is to produce an HTML page that displays the current time. We can't do this with a StaticHtmlGenerator MEG bean because time is not static! We could do it the same way we built the WhoAmITheOldWay generator, but this approach is often error-prone. Instead, what we do is determine the HTML text that we wish to display and then instantiate a StaticHtmlGenerator to produce the HTML page. This way we produce a new StaticHtmlGenerator for each HTTP transaction with the customized content that we desire to display. Perhaps the word static is misleading, but it means that the HTML is static over the lifetime of the StaticHtmlGenerator object. And in this case, the object will only live for the duration of this one transaction.

  class TimeGenerator extends HttpGenerator {
    public void handleRequest( RequestEvent e ) 
      throws RequestRejectedException, IOException {
      DateFormat dateFormat = 
      String time = 
        dateFormat.format( new Date() );
      StaticHtmlGenerator g = 
        new StaticHtmlGenerator();
      g.setStaticHtml( "<h1>The time is: <br>" +
                       "<center><font color=red>" +
                       time +
                       "</font></center>" +
                       "</h1>\n" );
      forwardRequest( g, e );

Looking at the code, we see that the basic structure of extending HttpGenerator and implementing handleRequest(RequestEvent) is the same as before. But instead of handling the minutiae of completing the transaction, we simply determine the current time as a String, instantiate a StaticHtmlGenerator, and set its static HTML to contain our computed page. (Note that we should have defined a better HTML page, containing tags such as <HTML>, <HEAD> and <BODY>, but we have tried to keep the demo code as brief as possible.)

Now that the StaticHtmlGenerator is ready, we take the request that was sent to the TimeGenerator and forward it along to the StaticHtmlGenerator. That generator now takes care of all of the detailed work of preparing the HTTP headers and sending the HTML document. The forwardRequest(...) method is part of the Meg base class and is available to all Monitors, Editors, Generators, etc. The two parameters for the method are the target MEG for the request and the original RequestEvent. You should be careful to return immediately after calling forwardRequest(...) so that your MEG will not accidently write more information into the MegOutputStream for that RequestEvent.

This technique of forwarding requests to dynamically-instantiated MEG beans is a powerful one. You can easily produce complex results by:

  1. examining the request and other resources,
  2. deciding what action to take,
  3. instantiating an appropriate MEG bean,
  4. setting the properties for the MEG bean, and
  5. forwarding the request to the MEG bean.
Programming this way eliminates many programming errors, reuses well-tested MEG bean code, and results in smaller, simpler, easier-to-understand MEG classes.

MEG Bean Library

Please refer to the MEG bean package documentation for a list of available MEG beans.

Registering a Plugin

Once our plugin is compiled without errors, it is ready to be registered with WBI so that it can be tested. Registration involves three steps:

The .reg File

The demo.reg file is in the wbij44/com/ibm/wbi/examples/demo directory. This file is a good example of a .reg file. Every plugin needs its own .reg file. The contents of a plugin's .reg file are used when registering a plugin with WBI. The file is not needed when using or deregistering a plugin -- only for the registration step.
  Demonstrate how to program WBI
  WBI Java 4.5 demo

The form of the file is similar to XML. We explain each element:
<majorVersion> and <minorVersion>
These elements handle version information that WBI uses to prevent loading an older version of a plugin over a newer one. Version 4.4 of a plugin is represented with majorVersion=4 and minorVersion=4.

This is the full class name of the plugin class (i.e. the class that extends HttpPlugin). Note that it is case-sensitive and that it does not contain the .class extension.This field is required.

A unique identifier used internally or programatically to distinguish plugins. This name is also used to determine the location of the plugin's data files in the $WBIHOME/etc/ directory when getHomeSection() is used. The forward-slash "/" can be used to imply subdirectories: e.g., ibm/demo places the demo data in a subdirectory called ibm/. This field is required.

<descriptiveName> and <description>
These tags give a human-readable name and brief description of the plugin. The name is used for logging and tracing purposes. They are both displayed by the WBI setup page.

If you want to have multiple instances of a plugin class loaded simultaneously, you can create multiple reg files that vary only in the pluginName field.

If any of the required fields are missing, the plugin cannot be registered. If any of the other fields are missing, they will be given reasonable defaults.

Note: The obsolete tag <pluginOrClassName> is still accepted for compatibility with old .reg files. However, new .reg files should use <pluginClass> and <pluginName> as described above.


Plugin registration can be accomplished via the register command at the WBI console or through WBI's graphical user interface.

IMPORTANT: The Java class loader caches copies of the .class files that it has loaded. If you have recompiled your plugin, deregistering and registering it will not force Java to reload your classes. The console and GUI "restart" commands will not work either. To accomplish a reload of your classes, you must shutdown WBI completely and re-start the whole process (i.e., you must re-start the Java Virtual Machine to empty the class loader cache).

Using the WBI Console

To register a plugin using a .reg file, type the command:
register full_path_to_plugin.reg
For example, to register the demo plugin, run the command:
register com/ibm/wbi/examples/demo/demo.reg

To query the list of currently running plugins, type the command:


To deregister a plugin from WBI, type the command:

deregister name_of_plugin
For example, to deregister the demo plugin, type the command:
deregister ibm/demo

To deregister all plugins (including pre-registered plugins such as the administration plugin, the NoGZipPlugin, and the demo plugin), type the command:

deregister all

Note: To see all of the available console commands see the console documentation or type '?' into the WBI Console.

Using the WBI GUI

Launch WBI with the -g option. The GUI allows the user to perform all the commands that can be run on the WBI console. See the GUI documentation on handling plugins for information about plugin registration.

Viewing Loaded Plugins

After registering your plugins and verifying that WBI starts up again with no error messages, it is comforting to see WBI report that your plugin is loaded and operational.

Go to the URL (if WBI is running as a web server on port 8088) or http://_wbi/setup (if WBI is running as the browser's proxy). This magic URL invokes a WBI generator that produces a page with WBI's setup information. This page can be used to adjust WBI's firewall information (so that it can retrieve content outside the firewall by using a proxy). It also displays a list of loaded plugins. The checkboxes beside the plugin can be adjusted to enable/disable the plugins. Note that the default plugin is not displayed in this list. Use the WBI GUI or the WBI Console to verify that the default plugin is registered. When a plugin is disabled, its disable() method is called and all of its MEGs are marked as disabled so that they will not be invoked.

The plugin programmer can specify a setup URL for a plugin so that the name of the plugin will become a link to its own setup page. This is accomplished with the Plugin.setSetupUrl(...) method. The plugin usually hooks a generator up to the specified setup URL so that it can display a setup form to the user and then process the form submission to configure the plugin. When the configuration is complete, the plugin should return the user to the WBI Setup page.