Channels ▼
RSS

C/C++

Implementing C++ Servlet Containers

Source Code Accompanies This Article. Download It Now.


Apr02: Implementing C++ Servlet Containers

John is a software architect for Rogue Wave Software. He can be contacted at hinke@roguewave.com.


Creating applications for the Internet with C++ can be difficult, mostly due to the lack of pure C++ technologies that can be used. Consequently, most C++ developers are required to use another language for interfacing their C++ code with the Internet. They might use a Java servlet combined with Java Native Interface (JNI) for integrating their C++ code, or they may write web server adapters such as an Apache module or an ISAPI filter.

What if the full power of the Java servlet API could be harnessed in C++ to create a pure C++ web application by using the same techniques used when creating Java web applications? C++ developers would then be able to create C++ web applications without needing to use multiple languages, or using a slower technology such as Java or CGI.

In this article, I examine alternatives to C++ and some of the necessary details for creating a 100 percent pure C++ servlet container. Working in C++ is much more viable than the other alternatives because it is faster, more efficient, native to the application, and easier for the developer to learn and implement.

C++ Alternatives

Today, C++ programmers have limited options for bringing their applications to the Web: Java servlets combined with JNI, CGI, or a proprietary web server's API.

A pure C++ servlet container provides a highly efficient, scalable, robust, and very portable servlet API. By creating a C++ servlet container, you have the necessary technology to quickly and easily create 100 percent C++ web applications.

JNI. One approach is to use Java servlets combined with JNI for access to legacy C/C++ code. The downside to this approach is that it requires using two languages and a good understanding of the complicated JNI interface. Additionally, there is an associated performance cost. While there are techniques for reducing the complexity of the JNI code, the performance and multilanguage issues are still prevalent. As you can see in Figure 1, the pure C++ approach reduces many of these performance problems.

Listing One is a HelloWorld Java servlet that calls to a C++ method that executes the Hello World request. While that is a simple example, the JNI code is quite complex. Listing Two is the generated JNI layer.

Providing an implementation for this simple method is rather difficult and error prone. Also, JNI provides access to C methods, not C++ classes and methods. Therefore, you must manage accessing an instance of the C++ object and call the appropriate method.

CGI. While coding a C++ application to be used with CGI can be similar to coding a Java servlet, the C++ servlet gives much greater performance. Every request for a CGI application spawns a new process. This is not only resource intensive but prevents any of the CGI programs from efficiently saving state. One single browser request can spawn several different CGI processes; multiply this by thousands of simultaneous requests and the performance of the web server is severely affected.

The C++ servlet container holds a servlet in memory and all requests go through that one particular servlet. The only process created is for the servlet container itself. By actively holding a servlet in memory, the C++ servlet container also allows the code to maintain state (variable values, paths of execution, and so on) while it executes. This allows for better usage of resources on a web server. With CGI, the state is reinitialized each time a program executes.

Web Server Adapters. Another option for C++ programmers is to use a proprietary API such as Netscape NSAPI or Microsoft ISAPI. While these allow a more managed approach to integrating an application to the Web than CGI, they lock developers into a single vendor and platform. In addition, these APIs often only allow access to C code and not C++, which can be very limiting.

C++ Servlets: A Better Option

A pure C++ servlet container provides a highly efficient, scalable, robust, and very portable servlet API. A C++ servlet container solves the C++ developer's dilemma, providing a better way of integrating C++ with the Web than CGI. In addition, because they are compiled, C++ servlets provide better performance than Java, integrating easily with current C++ implementations with minimal effort and training at a significant cost savings. By following the Java servlet specification, you can quickly and easily learn this new technology.

Creating a C++ API: The Design

First, some goals need to be established for creating a C++ servlet container to help guide the design and implementation.

  • Consistent API. The C++ API should be nearly identical to Java, without any surprises. If parts of the Java API do not make sense in C++, they should either be removed or replaced with an alternate API. Users should be able to read any Java servlet book and be able to apply that information to a C++ version.
  • Performance. The C++ version must be faster than Java. As this is an alternative to Java servlets, it should not introduce any unnecessary overhead.

  • Use C++ features where possible. Examples might include templates, the Standard Library, and even destructors for cleaning up memory. C++ features should only be used if they aid in the usability of the API.

Second, port some sample Java servlets to C++. This is an exercise designed to help understand what the C++ API should look like. While the C++ API should be as close to the Java API as possible, it also should take advantage of C++-specific features.

While most of the Java API is fairly straightforward, there are a few places in the API that need special attention. (Issues such as connecting to the servlet container are beyond the scope of this article.)

A Simple Example

The first servlet I'll port is the "Hello World" servlet in Listing Three. While this servlet is extremely simple, it does demonstrate some of the issues that need to be resolved.

Some of the questions that need to be answered are:

  • How to handle imports?
  • How to pass the request and response objects? By value? Reference?

  • How does the servlet write data to the response object? In Java, you can use the getWriter or getOutputStream methods. What should those methods return in C++?

There are also many issues that are related to the servlet container, but those are implementation details. However, how the servlet container loads C++ servlets from DLLs or shared libraries is discussed later.

The answers to these questions really depend on how the API is ported from Java to C++, but some possible answers might include:

  • Use include statements for the different imports, where each package is a separate directory. For example, <servlet/ Servlet.h> and <servlet/http/HttpServlet.h>.
  • The request and response objects are passed by reference because they may need to be changed.

  • Provide a class that extends std::ostream (actually basic_ostream) and also provides the print and println methods from the Java PrintWriter class.

With those answers in mind, a C++ servlet might look like the simple C++ servlet in Listing Four, which is close to the original Java servlet, making it possible to quickly use the C++ API, especially if the Java API is already understood. As a C++ developer, this API provides a powerful mechanism for creating pure C++ web applications. There is no complicated JNI and no web server proprietary API to learn. C++ developers simply create a servlet and deploy that servlet into a C++ servlet container.

Memory Management

Memory management must be carefully addressed when designing the C++ servlet API. A good C++ servlet container implementation hides as many of the memory-management issues as possible. This might involve returning smart pointers, or using the handle/body design pattern to encapsulate the memory-management issues. For example, in the Java servlet API there are several methods that return complex objects, such as the getServletConfig method in the servlet class that returns the internal ServletConfig object; see Listing Five. By following the original design goals, the C++ API should be as similar as possible to the Java API. That means the C++ API should look like Listing Six.

Each nontrivial class that is returned in the servlet API should use the handle/body pattern (or something similar) to deal with memory-management issues. However, not all classes in the servlet API need to use this pattern. The servlet class, for example, is never returned from a method and is only used internally.

There are a few methods that are rather difficult to implement without exposing pointers. For example, the ServletContext: getResourceAsStream method returns an InputStream object in Java. In C++, it would be nice to be able to return an istream; however, an istream cannot be returned by value. This means the API would look like:

std::istream* getResourceAsStream(...) const;

This would require users to delete the returned object or risk a memory leak. In this case, the method is removed and a similar method added. The new method returned the entire resource as a string:

std::string getResourceData(...) const;

The downside to this method is that the entire resource would be loaded into memory, but if users wanted to access a resource as a stream, they could open the file themselves and access the data.

Another Example

Using this information, you can quickly port another servlet to C++. The servlet in Listing Seven is a simple servlet that accesses some session information and displays the session attributes. This example demonstrates the use of sessions, access to URL parameters, and adding attributes to a session. I won't discuss here the Enumeration class, but assume it has an interface similar to Java yet takes advantage of C++ templates. The HttpSession object follows the memory-management issues previously discussed, so it is safe to pass around by value. While this is a simple example, it does demonstrate some powerful capabilities of the servlet API — sessions. By using sessions, web applications can maintain state across multiple client invocations.

In Java, session attributes are accessed with the API in Listing Eight. In C++ there is no easy-to-use concept of an Object, so the API was changed to store strings instead, as Listing Nine shows. While this has some limitations, it is still easy to use and similar to the Java API. Methods that did not change the object were declared as const. This mechanism for handling attributes can also be applied to the other classes that store attributes such as the ServletRequest and ServletContext classes.

Deploying C++ Servlets

Deploying C++ servlets should be as simple as possible. Listing Ten shows how a simple Java deployment descriptor can be ported to C++. This deployment descriptor is used to describe the deployment for the previous Hello World servlet. The only difficulty is knowing how to create the servlet. In Java, the servlet-class element specifies the fully qualified class name, and the servlet container instantiates an instance of the named servlet. With C++, this is not as easy because it is not possible to load C++ classes with just a class name. However, C++ can load DLLs or shared libraries and locate named methods within the library; that information can be used to instantiate the servlet. A method will need to be created to return an instance of our servlet. For this container, the method looks like Listing Eleven. The method name can be any name as long as it returns an instance of the servlet. We will change the servlet-class element to include the DLL or shared library name and the method name used to create the servlet instance. The DLL or shared library name should also be portable across multiple operating systems, so you remove the library extension (.dll on Windows, .so or .sl on UNIX) and any library prefix (such as lib on UNIX). The servlet-class element then looks like Listing Twelve.

The servlet container loads the DLL or shared library by adding any operating-system-specific information. It then attempts to invoke the named method to create an instance of the servlet. Once the servlet container has an instance of the servlet, it can process requests normally.

This technique can also be used for other cases in the deployment descriptor where Java class names are used, such as the event listener and filter classes. Processing the rest of the deployment descriptor is fairly straightforward and doesn't require any special changes.

Conclusion

By porting the Java servlet API to C++, you finally have the power and flexibility of the servlet specification that Java developers have enjoyed for so long. Creating pure C++ web applications then becomes a matter of writing a C++ servlet and deploying that servlet into a pure C++ servlet container. You no longer need to use a multilanguage approach for their web applications. By combining the performance benefits of C++ and the power of the servlet API, C++ developers can easily create powerful, robust web applications.

For more information on C++ servlet containers and to download an evaluation version, see http://www.roguewave.com/developer/tac/bobcat/.

DDJ

Listing One

class HelloWorld extends HttpServlet {
  public void doGet(HttpServletRequest req, HttpServletResponse res) {
    response.setContentType("text/plain");
    try {
      PrintWriter out = response.getWriter();
      out.println(helloWorld("Hello C++"));
    } catch(IOException e) { /* ignore for now. */ }
  }
  private native String helloWorld(String in);
}

Back to Article

Listing Two

extern "C" {
  JNIEXPORT jstring JNICALL Java_HelloWorld_helloWorld
    (JNIEnv *, jobject, jstring);
}

Back to Article

Listing Three

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWorld extends HttpServlet {
  public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
  {
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.println("<html><head>");
    out.println("<title>Hello World</title>");
    out.println("</head>");
    out.println("<body bgcolor=\"white\">");
    out.println("<body>");
    out.println("<h1>Hello World!</h1>");
    out.println("</body></html>");
  }
}

Back to Article

Listing Four

// include the necessary classes
#include <servlet/ServletOutputStream.h>
#include <servlet/ServletException.h>
#include <servlet/http/HttpServlet.h>
#include <servlet/http/HttpServletRequest.h>
#include <servlet/http/HttpServletResponse.h>
class HelloWorld : public HttpServlet {
  public:
    virtual void doGet(HttpServletRequest& request, 
                                    HttpServletResponse& response)
    {
      response.setContentType("text/html");
      ServletOutputStream& out = response.getWriter();
      out.println("<html><head>");
      out.println("<title>Hello World</title>");
        // we can mix both println and << method calls!
      out << "</head>" << endl;
      out << "<body bgcolor=\"white\">" << endl;
      out << "<body>" << endl;
      out << "<h1>Hello World!</h1>" << endl;
      out << "</body></html>" << endl;
    }
};

Back to Article

Listing Five

interface Servlet {
  public ServletConfig getServletConfig();
}

Back to Article

Listing Six

class Servlet {
  public:
    ServletConfig getServletConfig() const;
};

Back to Article

Listing Seven

#include <servlet/ServletOutputStream.h>
#include <servlet/http/HttpServlet.h>
#include <servlet/http/HttpServletRequest.h>
#include <servlet/http/HttpServletResponse.h>
#include <servlet/http/HttpSession.h>
class SessionExample : public HttpServlet {
  public:
    virtual void doGet(HttpServletRequest& request,
                       HttpServletResponse& response)
    {
      response.setContentType("text/html");
      ServletOutputStream& out = response.getOutputStream();
      out.println("<html><body bgcolor=\"white\"><head>");
      out.println("<title>SessionExample</title>");
      out.println("</head><body>");
        // get the session from the request
      HttpSession session = request.getSession();
        // should this session be invalidated?
      std::string invalid = request.getParameter("INVALIDATE");
      if(invalid != ""){
        session.invalidate();
      } else {
          // set any attributes that are specified
        std::string name = request.getParameter("dataname");
        std::string value = request.getParameter("datavalue");
        if(name != "") {
          session.setAttribute(name, value);
        }
          // print out all attributes in this session.
        out.println("<P>");
        out.println("Session Data<br>");
        Enumeration<std::string> names =
          session.getAttributeNames();
        while(names.hasMoreElements()) {
          std::string name = names.nextElement(); 
          std::string value = session.getAttribute(name);
          out.println(name + " = " + value + "<br>");
        }
      }
      out.println("</body>");
      out.println("</html>");
    }
};

Back to Article

Listing Eight

interface HttpSession {
  public Object getAttribute(String name);
  public Enumeration getAttributeNames();
  public void setAttribute(String name, Object value);
}

Back to Article

Listing Nine

class HttpSession {
  public:
    std::string getAttribute(const std::string& name) const;
    Enumeration<std::string> getAttributeNames() const;
    void setAttribute(const std::string& name, const std::string& value);
};

Back to Article

Listing Ten

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app>
  <servlet>
    <servlet-name>HelloWorld</servlet-name>
    <servlet-class>com.mycompany.HelloWorld</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>HelloWorld</servlet-name>
    <url-pattern>/HelloWorld/*</url-pattern>
  </servlet-mapping>
</web-app>

Back to Article

Listing Eleven

extern "C" {
  Servlet *createHelloWorld() {
    return new HelloWorld;
  }
}

Back to Article

Listing Twelve

<servlet-class>mydll.createHelloWorld</servlet-class>

Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video