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() );
}

The last method of the request object that you’ll adapt for this article is getCookies. As its name implies, it returns a collection of cookies, which the client has provided. The ASP version of the cookie object has me baffled. It appears to act as a collection of itself, exposing many methods with enigmatic functionality. However, I was able to decipher enough to write the servlet adaption. The only tricky part is that the ASP version returns an Enumeration, while the servlet version expects an array, offering a good chance to use the not so well known and underutilized copyInto method off the Vector class. Also note that I had to predicate each reference to a Cookie object since the class name is identical in both the com.ms.iis.asp and javax.servlet.http packages. Here’s the code:

public javax.servlet.http.Cookie[] getCookies()
{
    Vector tmpList = new Vector();
    CookieDictionary aspCookies = this.request.getCookies();
    IEnumerator e = aspCookies.keys();
    while( e.hasMoreItems() )
    {
        String key = (String) e.nextItem();
        String val = aspCookies.getCookie( key ).getValue();
        tmpList.addElement( new javax.servlet.http.Cookie( key, val ) );
    }
    javax.servlet.http.Cookie[] cookies = new javax.servlet.http.Cookie[ tmpList.size() ];
    tmpList.copyInto( cookies );
    return( cookies );
}

The session adapter

Now that you’re done with the request adapter, you need to backtrack and cover the session adapter. The session, in both ASP and servlets, is mainly used as a veritable hashtable. You simply put and get objects into and out of the session. Those values are acted upon almost identically to the respective response parameter rules discussed above. The implementation of the session adapter is too trivial to warrant discussion. The full source code is available in Resources.

The response adapter

The next major piece of the puzzle is the response adapter. Just like the request adapter, the response adapter requires a few clever tricks. But before I get into the difficult stuff, let me get the easy stuff out of the way. Here’s the supersimple code for two of the more popular response methods:

public void sendRedirect( String str )
{
    this.response.redirect( str );
}
public void setContentType( String str )
{
    // ASP automatically set's content type!
}

What’s up with setContentType? It doesn’t do anything! That’s right, IIS doesn’t make the perfect servlet engine after all. By the time the servlet gets executed, the ASP engine has already defined the content type, along with the other standard HTTP headers. But speaking from experience, the majority of servlets do not need to set the content type to anything other than plain text or HTML.

As mentioned earlier, you don’t require an adapter class for handling cookies. The addCookie method of the response object simply has to create an instance of a Microsoft cookie based on the contents of the supplied Sun cookie. Both Microsoft and Sun agree that cookies are simple name and value pairings of data. However, they disagree on the way that cookie expiration should be represented in an API.

Sun’s version of cookie expiration uses an integer value that specifies the cookie’s maximum age in seconds. That value is passed into the setMaxAge method of the Cookie object. A value of zero signifies immediate expiration while a negative value (being a special case) dictates that the cookie should be discarded when the user’s browser exits.

Microsoft’s version of cookie expiration is a little different. Microsoft’s cookies, by default, are set to expire when the user’s browser exits. Therefore, if the Sun version of the cookie has a negative expiration value, you should not alter Microsoft’s version of the cookie. If the maximum age of the Sun version is equal to or greater than zero, you have to translate the age into a Microsoft Time object and pass it into the Microsoft version of the cookie. Note that the month value is zero-based in Java’s Calendar class but one-based in Microsoft’s Time class, so you must increment the value during the conversion.

public void addCookie( javax.servlet.http.Cookie cookie )
{
    com.ms.iis.asp.Cookie aspCookie = this.response.getCookies().getCookie( cookie.getName() );
    aspCookie.setValue( cookie.getValue() );
    int age = cookie.getMaxAge();
    if( age < 0 ) 
    {
        // expire on browser exit
    }
    else
    {
        GregorianCalendar date = new GregorianCalendar();
        Date time = new Date( System.currentTimeMillis() + ( 1000 * age ) );
        date.setTime( time );
        Time aspTime = new Time( 
            date.get( Calendar.YEAR ),
            1 + date.get( Calendar.MONTH ),
            date.get( Calendar.DAY_OF_MONTH ),
            date.get( Calendar.HOUR ),
            date.get( Calendar.MINUTE ),
            date.get( Calendar.SECOND )
        );
        aspCookie.setExpires( aspTime );
    }
}

The most popular response method happens to also be the trickiest to implement, which is why I saved it for last. The method in question is getWriter. That method returns a PrintWriter object that lets the servlet write information to the client’s display. In most cases, the servlet is just composing HTML, which is buffered until it is all sent to the client. Why is it buffered? Because the servlet, after already dumping a lot of information to the PrintWriter, might decide that something is amiss and abort by calling the sendRedirect method. The redirection code must be the first thing that the browser receives, and obviously there’s no need to send any buffered information to the client once a redirect has been issued.

With that in mind, you have to create one more adapter class. That new adapter will wrap the PrintWriter object. It will buffer all of its contents until the close method is called. Here’s the corresponding response method:

public PrintWriter getWriter()
{
    return( new PrintWriterAdapter() );
}

And here’s the code for the PrintWriter adapter in its entirety:

public class PrintWriterAdapter extends PrintWriter
{
    private static final String CR = "n";
    private StringBuffer sb = new StringBuffer();
    public PrintWriterAdapter()
    {
        super( System.err );
    }
    public void print  ( String str ){ sb.append( str ); }//response.write( str ); }
    public void println( String str ){ print  ( str + CR ); }
    public void print  ( Object obj ){ print  ( obj.toString() ); }
    public void println( Object obj ){ println( obj.toString() ); }
    public void print  ( char[] chr ){ print  ( new String( chr ) ); }
    public void println( char[] chr ){ println( new String( chr ) ); }
    public void close()
    {
        AspContext.getResponse().write( sb.toString() );
    }
}

Conclusion

Microsoft’s Internet Information Server doesn’t make the perfect servlet engine, but it comes pretty darn close. In all of my servlet experience, the combination of IIS and those adapter classes have proven adequate for developing and deploying commercial applications. And, if you happen to be locked into a strictly Microsoft shop, those tools offer you the chance to branch out and experiment with the wonder of Java servlets. As always, I am interested in hearing your comments, criticisms, and suggestions on improving the code.

The source code for all of the classes I’ve introduced in this article, including a little more functionality than I’ve covered, can be found in Resources. Note that many of the methods, specifically those that I haven’t yet needed, remain unimplemented. If you venture to finish the job, send me a copy (wink).

A formal plea to Microsoft, or to the helpful reader

The technology that I have described in this article has been successfully deployed on most of the systems in my lab. However, on a few machines, it simply doesn’t work. The ASP page reports the error “No object for moniker” for any and all references to the adapter objects. That is undoubtedly due to some enigmatic combination of Microsoft’s Java SDK 4.0, Microsoft’s Internet Information Server (Windows NT Option Pack 4), Visual J++, or some Service Packs. I’ve searched the Microsoft Developer’s Network (MSDN) in vain and come up dry. If you know what the problem is and have a solution, please share it with me. Thanks.

Thomas E. Davis is a Sun-certified Java
developer. In addition to being a Java advocate, he is a strong
proponent of the extreme programming, design patterns, and
refactoring philosophies, which he preaches to his colleagues and
supports within his own department. Davis, who is chief technology
officer of Nutrio.com, resides in sunny Boca Raton, Fla.

Source: www.infoworld.com