Use Microsoft's Internet Information Server as a Java servlet engine

Run Java servlets with Microsoft's IIS -- without sacrificing portability

Did you know that you can run Java Servlets with Microsoft's Internet Information Server (IIS) without any third-party products? All you need is plain old IIS and pure Java. Granted, you do need to use Microsoft's Java SDK for reasons that I will explain in this article, but rest assured that your code will be free of any proprietary extensions and remain completely portable to other servlet engines.

Microsoft's Internet Information Server

But why would you want to do something as silly as running a Java servlet in an environment that wasn't designed for that purpose? First, many of us die-hard Java fanatics are trapped in Microsoft-only shops due to circumstances beyond our control. We all have our Linux boxes tucked away under our desks, running IBM's latest JDK and Apache's latest servlet engine, but it will be a cold day in the underworld before our bosses let us deploy products on such a system. You can certainly find commercial servlet engines that run on Microsoft's platforms, but they can cost big bucks. Try explaining to your boss that you need a few thousand dollars for a new Web server because you're going to scrap the free one that came with the operating system (or use it as a simple pass-through proxy, which is how many offerings currently work). Then, once your boss stops swearing, you can ask yourself if you're just a little too anxious to abandon the Microsoft ship. Microsoft and Sun have had their problems, but that doesn't change the fact that IIS is a respectable piece of software. And now that you know it can run Java servlets, it has become a little more appealing.

The Adapter design pattern

The magic that glues those two technologies together is a simple application of the Adapter design pattern. Quoting from the infamous Gang of Four book, Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Resources), the intent of the Adapter pattern is to convert the interface of a class into another interface clients expect. But which classes must you adapt? The answer is the handful of core classes that a Java Servlet uses to interact with its environment -- specifically, the Request, Response, and Session objects. As luck would have it, you don't have to adapt the Cookie class -- the translation is handled in-line by the other adapters.

IIS, or more specifically its Active Server Page (ASP) environment, contains a core group of classes that virtually mirror those of the Java Servlet specification. Actually, I should say the servlets mirror the ASP framework, since IIS shipped long before the servlet specifications were written, but I won't add any more fuel to the Microsoft-versus-Sun fire.

The Request, Response, Session, and Cookie objects exist in both frameworks. The only problem is that the interfaces for those objects are incompatible between environments. That's where the Adapter design pattern comes into play. You have to adapt (or wrap) the IIS versions of the objects to make them look and act like servlet versions.

A quick and dirty overview of servlets

A servlet, at a bare minimum, simply has to implement a single method:

public void doGet( HttpServletRequest request, HttpServletResponse response );

Technically, the servlet must also implement a doPost method if it wishes to handle client requests that use the HTTP POST command instead of GET. For the purpose of keeping this article simple, however, you can assume that all client requests are of type GET.

The doGet method takes two objects: a request and a response. The request object encapsulates any data that the client sent to the server, along with some meta-information about the client itself. You use the response object to send data back to the client. That's a very abstract explanation, but this article isn't an introduction to servlets, so I won't go into greater detail. For a good primer to servlets, I recommend Java Servlet Programming (O'Reilly & Associates) by Jason Hunter, William Crawford, and Paula Ferguson.

Active Server Pages

When you call the servlet from the ASP, you're just going to call the doGet method and pass in the appropriate request and response objects. From that point on, the servlet has full control. The ASP script acts as a bootstrap to the servlet. But before you can pass in the request and response objects, you must wrap them with the respective adapter classes (which I will examine in detail later on).

I'll start from the top and work my way down. The URL that the client is going to request will look something like http://localhost/servlet.asp. The .asp extension means that the requested document is an Active Server Page script. Here's the servlet.asp script in its entirety:

dim requestAdapter
set requestAdapter = getObject( "java:com.nutrio.asp.RequestAdapter" )
dim responseAdapter
set responseAdapter = getObject( "java:com.nutrio.asp.ResponseAdapter" )
dim servlet
set servlet = getObject( "java:com.nutrio.servlet.HelloWorldServlet" )
servlet.doGet requestAdapter, responseAdapter

Breaking it down, you'll see that you start out by declaring a variable called requestAdapter. The dim command is the Visual Basic version of a variable declaration. There are no hard types in Visual Basic. Variables are actually wrapped by a Variant object, which exposes the variable in any flavor that the calling code desires (for example, number, string, and so forth). That is very convenient, but it can lead to confusing and dangerous code. That's why the Hungarian Notation was invented (see Resources). But that's a whole other debate.

After declaring the variable, you instantiate your first adapter class, using the ASP getObject method, and assign it appropriately. The getObject method is a new addition to IIS version 4. It's called a moniker (a COM object that is used to create instances of other objects, see Resources), but it lets you access Java objects without any of the Component Object Model's (COM, see Resources) registration headaches. In turn, you then declare, instantiate, and assign the response wrapper, and then do the same for the servlet. Finally, you call the servlet's doGet method and pass in the adapted request and response objects.

That particular script is fairly limited because it only launches one particular servlet. You'll probably want to expand it to launch an entire suite of servlets, so you'll need to make a couple of minor modifications. Assuming that all your servlets are in the same package, you can pass in the class name of the target servlet as an argument to the URL such as http://localhost/servlet.asp?class=HelloWorldServlet. Then you'll have to change the end of the script to load the specified class. Here's the new code:

dim className
set className = Request.QueryString( "class" )
dim servlet
set servlet = getObject( "java:com.nutrio.servlet." & className )
servlet.doGet requestAdapter, responseAdapter

That's it! You've just turned Microsoft's Internet Information Server into a Java Servlet engine. It's not a perfect engine, as you'll see later, but it's pretty close. All that remains to be discussed is the nitty-gritty of the adapter classes.

For brevity, I'm just going to cover the implementation of the more popular methods in each adapter. The measurement of popularity is based on my personal experience and opinion; it doesn't get much more scientific than that (sic).

Microsoft's Java SDK

Starting with the request wrapper, the first thing that the object must do is acquire a reference to its ASP counterpart. That is accomplished via the AspContext object from the com.ms.iis.asp package. The what package, you ask? Ah yes, here is where I explain why you need to install Microsoft's Java SDK.

You can download Microsoft's Java SDK for free (see Resources). Make sure that you get the latest version, which is 4.0 at the time of this writing. Follow the simple installation instructions and reboot (sigh) when prompted. After you install the SDK, adjust your PATH and CLASSPATH environment variables appropriately. Take a tip from the wise and search your system for all the instances of jview.exe, then ensure that the latest version resolves first in your PATH.

Unfortunately, the documentation and sample code that comes with Microsoft's Java SDK is sorely lacking in regard to the IIS/ASP integration. There certainly is plenty of verbiage -- you get an entire compiled HTML document on the subject, but it appears more contradictory and confusing than explanatory in most places. Thankfully, there is an aspcomp package in the SDK's Samples directory that virtually mirrors the com.ms.iis.asp package and comes with the source code. You did install the sample files with the SDK, didn't you? That aspcomp package helped me to reverse-engineer a lot of the API logic.

The request adapter

Now that you have Microsoft's SDK at your disposal, you can get back to implementing the adapter classes. Below is the bare bones version of the request adapter. I have omitted the package declaration and import statements so that you can focus on the meat of the code.

public class RequestAdapter implements HttpServletRequest
{
    private Request request;
    public RequestAdapter()
    {
        this.request = AspContext.getRequest();
    }

Note that the class exposes a single public constructor that takes no arguments. That is required for the ASP script to instantiate the class as a moniker (through the getObject method). The constructor simply asks the AspContext object for a reference to the ASP version of the request object and stores a pointer to it. The adapter implements the HttpServletRequest interface, which lets you pass it into your servlets under the guise of a real servlet environment.

The most popular method of the request object is getParameter. That method is used to retrieve a piece of data that the client is expected to provide. For example, if the client has just filled out a form and submitted it to the server, the servlet would call getParameter to retrieve the values of each form item.

In the ASP version of the request object, Microsoft differentiates parameters between those that arrive via GET and those that arrive via POST. You have to call getQueryString or getForm, respectively. In the servlet version, there is no such differentiation at the request level because the GET versus POST mode is dictated when doGet or doPost is called. Thus, when you adapt the getParameter method, you must look in both the query string and the form collections for the desired value.

There's one more quirk. If the parameter is missing, the Microsoft version will return an empty string, whereas the Sun version will return a null. To account for that, you must check for an empty string and return null in its place.

public String getParameter( String str )
{
    String result = request.getQueryString().getString( str );
    if( ( result != null ) && result.trim().equals( "" ) )
    {
        result = request.getForm().getString( str );
        if( ( result != null ) && result.trim().equals( "" ) )
        {
            return( null );
        }
    }
    return( result );
}

It's pretty simple, but don't get your hopes up because things are about to get more complicated. The servlet version of the request object also exposes a method called getParameterNames, which returns an Enumeration of the keys for each client-provided piece of data. As above, that is a single point of entry as far as servlets are concerned, but ASP differentiates between the GET- and POST-provided data. In order to return a single Enumeration to the servlet, you must combine the two Enumerations of the ASP request object's query string and form collections. Below is a handy little tool that I whipped up just for that problem. The tool is called EnumerationComposite (not to be confused with the Composite design pattern), and it takes an array of RequestDictionarys (the ASP version of a Hashtable) and concatenates them into one big Enumeration. Here's the code in its entirety:

public class EnumerationComposite implements Enumeration
{
    private RequestDictionary[] array;
    private int stackPointer = 0; 
    
    public EnumerationComposite( RequestDictionary[] array )
    {
        this.array = array;
    }
    
    public boolean hasMoreElements()
    {
        if( this.stackPointer >= this.array.length ) 
        {
            return( false );
        }
        else if( this.array[ this.stackPointer ].hasMoreItems() )
        {
            return( true );
        }
        else
        {
            this.stackPointer += 1;
            return( this.hasMoreElements() );
        }
    }
    
    public Object nextElement()
    {
        return( this.array[ this.stackPointer ].nextItem() );
    }
}

That tool greatly simplifies your job now. Here's how the getParameterNames method looks:

public Enumeration getParameterNames()
{
    return(
        new EnumerationComposite(
            new RequestDictionary[] {
                request.getQueryString(),
                request.getForm() } ) );
}

The next most popular method of the response object is getSession. The session object is another core object that is mirrored between ASP and servlets. Thus, you must provide the session with its own adapter, and I will cover that shortly. But before I do, here's the request method:

public HttpSession getSession( boolean flag )
{
    return( new SessionAdapter() );
}
1 2 Page
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more