Skip to main content

skip to main content

developerWorks  >  Web development | Sample IT projects  >

Manage an HTTP server using RESTful interfaces, Project Zero, and WebSphere sMash

Create a Zero-based RESTful interface for httpd

developerWorks
Document options

Document options requiring JavaScript are not displayed

Sample code


Rate this page

Help us improve this content


Level: Intermediate

Dan Jemiolo (danjemiolo@us.ibm.com), Advisory Software Engineer, IBM 

18 Dec 2007

WS-* users and REST users have an ongoing debate over which technique is most appropriate for which problem sets, with WS-* users often claiming that more complex, enterprise-level problems cannot be solved RESTfully. This article puts that theory to the test by trying to create a RESTful solution for a problem area that is not often discussed by REST users: systems management. In a previous developerWorks tutorial, I showed how to create a Web services interface for managing HTTP server products; the tutorial used concepts from WSDL and the WS-* standards to define the management interface and software from Apache Muse and Apache Axis to create the management application. For this article, I use Project Zero and REST design principles to recreate the interface and function of the original application and determine if REST is a valid option for this enterprise project.

Editor's note: IBM® WebSphere® sMash and IBM WebSphere sMash Developer Edition are based on the highly acclaimed Project Zero incubator project. Project Zero is the development community for WebSphere sMash and will continue to offer developers a cost-free platform for developing applications with the latest builds, the latest features, and the support of the community.

Before you get started

This article assumes that you have downloaded Project Zero and either completed the introductory tutorial or written a simple application yourself. You should also be familiar with the developerWorks tutorial Create a WSDM interface for an HTTP server using Apache Muse, which shows how to create a management interface for an HTTP server using Apache Muse and the WS-* specifications. This article does not require that you be an expert user of Muse or WS-* technology, just that you understand the problem that the tutorial was trying to solve and what features the code provided.

Introduction

This article is not about enumerating the pros and cons of WS-* technology versus REST-oriented technology, and it is not out to select a "winner." The goal of this article is to demonstrate whether or not REST and Web 2.0 development techniques provide a productive alternative for systems management projects and hopefully give developers some additional choices. WS-* users and REST users have an ongoing debate over which technique is most appropriate for which problem sets, with WS-* users often claiming that more complex, enterprise-level problems cannot be solved RESTfully.

The Project Zero community
Take a stroll around the Project Zero Web site and see how Project Zero provides a powerful — but radically simple — development and execution platform for modern Web applications. The active community discusses project development, provides help to developers, and wants to hear your ideas!

Review: Our WSDM interface for HTTP servers

The original Web services interface that we created for managing HTTP servers used a WSDL 1.1 document to expose a set of properties and operations. The properties were name-value pairs that could be accessed using the operations defined by the WS-ResourceProperties (WS-RP) specification. Some of the actual properties were defined by WS-* specifications (such as WS-DistributedManagement, or WSDM) while others were custom to our HTTP server resources. The complete list of properties we used is as follows:

  • ResourceId (WSDM)
  • ManageabilityCapability (WSDM)
  • Description (WSDM)
  • Caption (WSDM)
  • Version (WSDM)
  • OperationalStatus (WSDM)
  • Name (custom)
  • Port (custom)
  • ThreadsPerChild (custom)

The operations included in our interface were from WS-RP and revolved around reading and writing one or more property values. We also had two custom operations named Start and Stop that were used for starting and stopping the HTTP servers. All of the WS-RP operations were provided by the Apache Muse framework, while the custom operations were implemented with Java™ code and included using Apache Muse extension points. The complete list of operations we used is as follows:

  • GetResourceProperty (WS-RP)
  • GetResourcePropertyDocument (WS-RP)
  • GetMultipleResourceProperties (WS-RP)
  • Start (custom)
  • Stop (custom)

You may recall that we did not include any of the "write" operations from WS-RP because we did not have any properties that we wanted to be changed directly. Some of the properties were mutable, but only as a side effect (for example, the OperationalStatus property could change when the server was started or stopped).

Mapping WS-ResourceProperties to REST

RESTful programming relies on the rules and conventions of the network transport level (usually HTTP) and some well-known data formats (usually XML or JSON). To expose the information that was in our resource properties document in a RESTful way, we will use the HTTP GET method to retrieve a JSON object that has fields (name-value pairs) that are similar to our resource properties. In other words, we are replacing the resource properties document with a JSON object and using HTTP methods in lieu of WS-RP methods. Listing 1 shows what a JSON representation of our HTTP server resource might look like:


Listing 1. JSON Representation of an HTTP server
                
{
  "name": "server1.ibm.com", 
  "port": 8080, 
  "threads": 250, 
  "admin": "admin@us.ibm.com"
}
      

You may notice that the WSDM properties are not present in our JSON representation. Some of the WS-* concepts do not translate in a RESTful design or are present in other areas of the application. For example, the WSDM ResourceId property and the implied resource pattern are not needed; instead of defining resources using endpoint references and ResourceId values, we will use URI patterns and force all resources to have a unique URI. By reusing HTTP for part of our resource interface, we are eliminating the need for some of our properties and operations (WS-* users may point out that we are also limiting ourselves to HTTP, but the debate about whether that is good or bad is beyond the scope of this article). We are also going to use descriptive URIs and provide human-readable documentation of our services in contrast to some of the descriptive properties that were embedded in our WSDL document. The bottom line is that our JSON representation is as complete as we need it to be given that we now have an HTTP-oriented solution.

To make our HTTP server resources easy to discover and manipulate, we will use a URI pattern of /httpd/{serverName} to distinguish them. The /httpd URI will refer to the collection of all HTTP servers managed by the application, and the /httpd/{serverName} URI will refer to an individual HTTP server. Requests sent to the collection URI will be evaluated against the entire collection (if possible) while requests sent to an individual server URI will be limited to the server it represents. Table 1 shows the REST API that will describe our HTTP server resources:


Table 1. REST API for HTTP server resources
MethodURIFormatResult
GET/httpdtext/jsonA list of names representing the servers managed by this application.
GET/httpd/{serverName}text/jsonA JSON object containing the configuration data for a specific server.

If we ever wanted to augment our interface to allow users to modify these properties directly, we would use HTTP's PUT instead of WS-RP's SetResourceProperties. The URI patterns for PUT requests would be the same as those for GET, with the major difference being that PUT requests send JSON data while GET requests receive them.

Mapping custom operations to REST

The custom operations Start and Stop present a greater challenge in the design department. We only have four HTTP methods to work with (GET, PUT, POST, and DELETE), and embedding operation names in PUT or POST requests is considered bad form in REST Land. To get around this problem, we need to change how we think about the act of starting and stopping the server; instead of thinking about it as an action against an HTTP server installation, we will think about it as the creation or destruction of an HTTP server process. This is not much of a stretch because this is exactly how your operating system handles the start and stop requests: it creates a process that can be monitored and (eventually) terminated. We will create an HTTP server process resource with its own set of URIs and its own HTTP semantics. Table 2 shows the REST API for this new resource type:


Table 2. REST API for HTTP process resources
MethodURIFormatResult
GET/httpd/{serverName}/processNone200 OK if the server is running, 404 Not Found if it's not.
POST/httpd/{serverName}/processNoneStarts the server and returns 201 Created.
DELETE/httpd/{serverName}/processNoneStops the server and returns 204 No Content.

As you can see, the HTTP process resource has no JSON representation — it is just a URI. Sending an HTTP POST to the URI will create a process ("start the server"), while HTTP DELETE will destroy the process ("stop the server") and HTTP GET will provide status (similar to WSDM's OperationalStatus property, which we no longer need). Breaking up our interface into two separate resource types has allowed us to achieve the same function without resorting to RPC or WS-* style techniques. In the next section, we will see how to translate this design decision into real, working code.

Implementing HTTP Server Management with Zero

Writing the code to implement the RESTful interfaces we created in the last section will be pretty straightforward, especially if you understood the Muse-based implementation from the previous tutorial. For this project, we will use Groovy scripts instead of Java classes, RESTdoc comments instead of WSDL files, and JSON instead of XML.

Accessing the Apache HTTP Server with Groovy

Before you begin coding, you should make sure that you have the Apache HTTP server (httpd) installed and working properly. If you did not complete the previous tutorial and don't have httpd installed, the Resources section has a link to the Apache Web site where you can download the server and its installation instructions. Be sure that you have installed the server as a service (or daemon); if you're not sure whether you have a service or not, just type httpd -k install on the command line and the service will be created if it does not already exist. Having httpd installed as a service will allow us to start and stop the server asynchronously from our code.

With httpd installed and ready, we need to create a Zero project to hold our code. Use the command line in Listing 2 to create a project named zero.httpd; by default, the project will contain dependencies on Zero Core and the RESTdoc tool, and that's all we need for the code we're writing in this article. If you want to jump ahead and see the completed project, you can download it from the Download section.


Listing 2. Creating the sample project
                
$ zero create zero.httpd
      

Before we can write any code, we need to decide how the code will find the httpd installations so it can read their files and execute commands against them. In the previous tutorial, we used the muse.xml file to create initialization parameters that contained the httpd installation paths. We could then create paths to httpd files without hard-coding installation data in our Java source files. We will take a similar approach with Zero, this time using its zero.config file to create a map of server names to installation directories; a server name can be any alphanumeric ID and will be used to create concrete URIs for HTTP server resources. Listing 3 shows a sample zero.config file that you can use as a starting point:


Listing 3. Configuring the HTTP server installation(s)
                
[/config/httpd/servers[]]
server1=/httpd/server1
buildServer=/dev/builds/httpd
intranetSite=/public/prod/apache
    
[/config/http]
port=9080
  

In Listing 3, our server names are highlighted in bold. We will be able to access this set of name-value pairs in our Groovy code using Zero's global context. For the sake of simplicity, we will use server1 for all of our testing. You should modify the value of server1's installation path to be the one used by your local httpd installation. You do not need the other two server entries — they are just there to illustrate our configuration format.

Before you save and close your zero.config file, you should add the same entry for /config/http/port that is shown in Listing 3. This entry will override Zero's default port of 8080 with the value 9080; httpd also has a default port of 8080, and we don't want any conflicts between our Zero application and httpd processes.

You should now create three files: httpd.groovy, process.groovy, and process.bnd and add them to the project's /app/resources directory. The first two files are Groovy scripts that will represent the two RESTful resources described in the previous section. The third file will be used to configure the RESTful URI patterns allowed by the resources.

Let's start with the easiest task: the process.bnd file. You only need to add one line to this file, and it's shown in Listing 4. This line tells the Zero runtime that it should allow for the /httpd/{id}/process URI pattern that we documented in our REST tables. Without this line, Zero would allow requests for /httpd/{id} and /process/{id}, but never /httpd/{id}/process.


Listing 4. Creating the BND file
                
httpd/process
      

The httpd.groovy file represents our httpd installations and requires two methods: onList() and onRetrieve(). The former will return a list of the httpd installations managed by this Zero application and the latter will return the JSON representation of an individual httpd installation. Our interface does not currently support remote modification of the httpd installations, so we do not need any other RESTful methods (onCreate(), and so on).

Listing 5 shows the code for onList() and onRetrieve(). The implementation of onRetrieve() uses a method named readConfig() to parse the httpd.conf files; the parsing code from this method is copied from the previous tutorial and modified slightly to use a more Groovy-like syntax. You can see the implementation of readConfig() by downloading the completed sample project and looking in /app/resources/httpd.groovy.


Listing 5. Creating httpd.groovy
                
def onList()
{
    request.view = "JSON";
    request.json.output = config.httpd.servers[0].keySet();
    render();
}

def onRetrieve()
{
    def serverConfig = readConfig();
        
    if (serverConfig.isEmpty())
    {
        request.status = 404;
        return;
    }
        
    request.view = "JSON";
    request.json.output = [
        name: serverConfig.ServerName, 
        port: serverConfig.Listen, 
        threads: serverConfig.ThreadsPerChild, 
        admin: serverConfig.ServerAdmin
    ];
    render();
}
      

Listing 5 shows that we pick out the httpd configuration properties that we want to expose as we create the JSON response data. Just as in our WS-* version, we have more generic names for the properties (so that the interface is not Apache-specific) and we ignore those things that are sensitive from a security standpoint. Also notice that we use a simple HTTP status code (404) to indicate when a server does not exist — there are no fault types or additional messages to send. The onRetrieve() method is similar in concept to WS-RP's GetResourcePropertyDocument, but there is no schema to validate the results against and the "faults" are defined by the transport layer (HTTP), not the application layer.

With our httpd installation resources defined, we can move on to the httpd process resources. Listing 6 shows an implementation of process.groovy; it's very simple because this resource type is only used to start the server, stop the server, and check for the existence of an httpd process. In a slight twist on common REST design principles, we are using HTTP POST to create an httpd process (start the server), HTTP DELETE to destroy an httpd process (stop the server), and HTTP GET to determine if the server is running. These three operations correspond to the onCreate(), onDeleteCollection(), and onList() methods, respectively.


Listing 6. Creating process.groovy
                
def onCreate()
{
    Runtime.getRuntime().exec("${getServerHome()}/bin/httpd -k start");    
    request.status = 201;
}

def onDeleteCollection()
{
    Runtime.getRuntime().exec("${getServerHome()}/bin/httpd -k stop");
    request.status = 204;
}

def onList()
{
    def pidFile = new File(getServerHome(), "logs/httpd.pid");
        
    if (!pidFile.exists())
        request.status = 404;
}
      

The onCreate() and onDeleteCollection() methods use the same code I used in my previous tutorial to start and stop the server; specifically, they use the JDK's Runtime.exec() API to load the httpd executable and execute the proper command. This is exactly like the custom operations Start and Stop in our WS-* interface. The onList() method takes advantage of the fact that httpd creates a process ID (or PID) file each time it starts and tests for its existence to determine whether the server is running or not; if the PID file does not exist, the method returns 404 Not Found instead of the normal 200 OK. Once again, you can get the complete process.groovy file by downloading the sample project.

At this point, we have a complete implementation of a RESTful API for httpd, but we don't have an easy way to test it. In the next section, we will create some RESTful documentation for our Groovy methods and use the RESTdoc tool to test our API without writing any code. In fact, you'll be able to verify the operations your code is having on httpd without leaving your browser!

Exposing REST APIs with RESTdoc

In the last section, we wrote a lot of code, but we didn't document it. When it comes to Zero's RESTful resource scripts, good documentation not only helps you remember how your code works, it can help client-side programmers create and test their client code. You can write JavaDoc-like comments for your Groovy and PHP methods and have Zero's RESTdoc tool create REST tables and test pages for them (for more background on RESTdoc, see Resources). We are going to add RESTdoc comments to our Groovy scripts so we can test out our httpd interface.

Listing 7 shows the RESTdoc comments for the httpd.groovy file; the method implementations have been removed for brevity. You can see the RESTdoc comments for both scripts by downloading the sample project. The httpd.groovy comments are most illustrative because they show all of the possible tags used in RESTdoc comments.


Listing 7. Documenting httpd.groovy
                
/**
 *
 * @success 200 Returns a list of server names that can be used to build server URIs.
 * @format text/json
 * @example
 * [
 *   "server1", 
 *   "server2",
 *   ...
 * ]
 *
 */
def onList()
{
    ...
}

/**
 *
 * @success 200 Returns the configuration data for the given server name.
 * @error 404 If there is no server associated with the given name.
 * @format text/json
 * @example
 * {
 *    "name": "server1.ibm.com", 
 *    "port": 8080, 
 *    "threads": 250, 
 *    "admin": "admin@us.ibm.com"
 * }
 *
 */
def onRetrieve()
{
    ...
}
      

With the RESTdoc comments in place, you can start the Zero application (type zero run) and navigate to http://localhost:9080/resources/docs in your browser. You should see an index page that lists all of the RESTful resources types in your application, with httpd and process among them. Click on the httpd link and you should see a page like the one in Figure 1. This page shows all of the URI patterns that can be used to manipulate the resource type and the information necessary to code that handles requests and responses.


Figure 1. Screenshot of RESTdoc Page
Figure 1. Screenshot of RESTdoc page

Testing REST APIs with RESTdoc

Testing our RESTful APIs can be accomplished using the same pages that describe them. From the httpd RESTdoc page, click /httpd, which is the first URI pattern. You should see the dialog shown in Figure 2. This URI pattern has no variables, so all you have to do is click the Send button and you should see a response that includes a list of server names — the same server names you put in your zero.config file. Figure 3 shows the response:


Figure 2. Screenshot of RESTdoc request page
Figure 2. Screenshot of RESTdoc request page

Figure 3. Screenshot of RESTdoc Response Page
Figure 2. Screenshot of RESTdoc response page

Click on the second URI pattern /httpd/{httpdId} and enter server1 for the value of the httpdId variable. Notice that the tool does not allow you to click Send until you've entered a value and completed the URI pattern. Click Send, and you should see a response that includes a JSON representation of your httpd installation.

Our final test with the RESTdoc interface will be for the "start" and "stop" features that we copied from the previous tutorial. Our Zero-based interface requires that we send an HTTP POST or HTTP DELETE to /httpd/{httpdId}/process to start or stop a server. You should test the start and stop features as follows:

  1. Select the process resource from the RESTdoc index.
  2. Click on the HTTP POST URI pattern and enter server1 for the value of the httpdId variable. Click Send.
  3. In another browser window (or tab), visit http://localhost:8080. You should see the default httpd welcome message: It works!
  4. From the RESTdoc page, click on the HTTP DELETE URI pattern and enter server1 for the value of the httpdId variable. Click Send.
  5. Refresh the page at http://localhost:8080. You should get a connection timeout because the server is no longer running.
  6. From the RESTdoc page, click on the HTTP GET URI pattern and enter server1 for the value of the httpdId variable. Click Send.
  7. You should see a status code of 404, which confirms that the server has stopped and its artifacts (PID file) have been deleted.

In this section, I have shown how to test a RESTful API without writing any additional code or documentation. The RESTdoc interface does not provide a means for automated testing, but it is great for initial application development or for client programmers who are trying to learn about a service.

Conclusion

In this article, you were able to make a Zero-based RESTful interface for httpd that is as functionally complete as our Apache Muse-based WS-* version. The combination of Groovy scripts and RESTdoc comments provides the same features and behavior as we had with Java classes and WSDL and demonstrates that REST can handle the tasks that are thought to be "too complicated" for HTTP alone. The REST and WS-* solutions each have their pros and cons, and which one you favor may change from project to project; in the end, we have not pointed you towards one or the other, we have simply shown that both are valid choices.




Back to top


Download

DescriptionNameSizeDownload method
Sample applicationwa-pz-httprest.zip8KBHTTP
Information about download methods


Resources

Learn

Get products and technologies
  • Download the Apache HTTP Server so that you can run the application built with this tutorial.

  • Download Project Zero and start applying the best practices covered in this article.


Discuss


About the author

Dan Jemiolo is an Advisory Software Engineer on IBM's Project Zero team in Research Triangle Park, NC. He is currently working on reusable components for the Zero platform and its service catalog. His previous work includes the design and development of Apache Muse 2.0 and participation in OASIS Web services standards bodies. Dan came to IBM three years ago after earning his Master of Science degree in Computer Science from Rensselaer Polytechnic Institute.




Rate this page


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top