7.2. The HTTP Service

The HTTP service in the Java Embedded Server product is a lightweight HTTP server. By having it on the gateway, information can be retrieved from and controls can be directed to the gateway through standardized technology such as the Java™ Servlet API and HTML. We also benefit from one of the most universal front-end user interfaces: the Web browser. This saves us from a tremendous amount of effort in creating proprietary solutions that are slow to develop and unfamiliar to end users.

We first look at how to use the OSGi standard HttpService API, and then we look at the extended features provided by the Java Embedded Server implementation.

7.2.1. The Standard HttpService API

The HttpService API allows you to register servlets or resources from your bundle to be made accessible to remote clients through HTTP.

Servlets[1] run on the HTTP server side. They can be programmed to perform operations on the server and to generate dynamic content. The servlet provides the same functionality as the Common Gateway Interface (CGI) programs, only in a more robust and efficient way. Traditionally, it has become one of the most popular choices to construct middleware between a Web-based front end like a browser and some type of enterprise back end such as an application server. In our domain, the back end usually involves certain operations of the gateway instead. We assume you are familiar with how servlets work for the following discussion.

[1] Tutorials of Java Servlet can be found at http://java.sun.com/docs/books/tutorial/servlets/.

Resources are usually static data such as HTML pages, images, and so on. You generally package servlets and resources within a bundle and then register with HttpService to export them through HTTP. In the case of the resource, you may also make a portion of your local file system accessible through HttpService.

7.2.1.1. The Alias Namespace

The servlet or resource is registered with HttpService under an alias. HttpService maps the alias to its corresponding registered servlet or resource. The alias is used by HTTP clients to form URLs to invoke the servlet or to access the resource. For example, if a servlet is registered under the alias /foo and an HTML document is registered under the alias /bar, an HTTP client would use URLs such as http://mybox:8080/foo and http://mybox:8080/bar to access the servlet and the HTML page, respectively. In this example, mybox would be the host name and 8080 would be the port number where HttpService accepts requests. Generally, HttpService takes the URL from every incoming request, maps the alias portion of the URL to the corresponding servlet or resource, carries out operations defined by the servlet if one is called for, and returns static or generated contents. Figure 7.1 illustrates such a setup.

Figure 7.1. Servlet/resource registration and alias mapping by HttpService


The allocation of the alias namespace is on a first-come-first-serve basis. If a servlet or a piece of resource has registered under an alias, subsequent requests to register anything under the same alias will fail and result in NamespaceException.

Valid aliases are slash-separated names. Suppose servlet A is registered under the /utilities alias, then any request to aliases beneath the /utilities tree would be directed to servlet A. In other words, the following requests all invoke servlet A:

http://mybox:8080/utilities/gas/readings

http://mybox:8080/utilities/water/

http://mybox:8080/utilities

However, if servlet B is subsequently registered at a more “specific” alias under the /utilities tree, say, /utilities/gas, then the more specific alias prevails. In other words, the following requests invoke servlet B but not servlet A:

http://mybox:8080/utilities/gas

http://mybox:8080/utilities/gas/readings

whereas the following requests are still sent to servlet A:

http://mybox:8080/utilities/water

http://mybox:8080/utilities

Notice that servlet registration and resource registration share the same alias namespace. Thus, if a servlet has occupied the alias /welcome.html, a resource registration is not able to claim it anymore.

A common misconception is that these aliases refer to some directory paths in the file system. People sometimes are frustrated by not finding the welcome.html file, not realizing that it is simply a name mapped to some dynamic content to be generated programmatically.

7.2.1.2. Getting and Using HttpService

To use the standard HttpService, follow these steps in your bundle:

1.
Declare to import the Java Servlet and org.osgi.service.http packages in your manifest:

Import-Package: javax.servlet; specification-version=2.1,
 javax.servlet.http; specification-version=2.1,
 org.osgi.service.http

Import the Java Servlet packages because most likely you will be developing servlets in your bundle. The Java Servlet API is provided in a library bundle called servlet.jar in the Java Embedded Server distribution (you need to install and activate the servlet bundle first).

2.
Obtain HttpService in your bundle:

import org.osgi.service.http.HttpService;
...
ServiceReference httpRef = bundleContext.getServiceReference(
   "org.osgi.service.http.HttpService");
HttpService httpService = (HttpService)
      bundleContext.getService(httpRef);

3.
Register the servlet or resource using the registerServlet or registerResources methods of the HttpService interface respectively.

4.
Unregister your servlet or resource when your bundle is to be stopped.

7.2.1.3. Registering a Servlet

HttpService provides the following API for registering servlets:

public void registerServlet(
   String alias,
   javax.servlet.Servlet servlet,
   java.util.Dictionary initParams,
   HttpContext httpCtxt) throws NamespaceException

For example,

httpService.registerServlet("/hello", new HelloServlet(),
      null, servletContext);

registers HelloServlet under the alias /hello, without any initial servlet parameters (hence the third parameter is null), and HttpContext (explained later). The servlet's init method is called to carry out any necessary initialization.

An HTTP client can later invoke this servlet by connecting to it at the URL http://host:port/hello. The bold type shows the correspondence between the registration alias and the URL that accesses the servlet.

7.2.1.4. Registering a Resource

HttpService defines the following API for registering resources:

public void registerResources(
   String alias
   String name,
   HttpContext httpCtxt) throws NamespaceException

For example,

HttpContext imgContext = new MyResourceContext();
httpService.registerResources("/hello/image",
      "/com/acme/servlet/image", imgContext);

This call registers images located by the name /com/acme/servlet/image under the alias /hello/image. What is /com/acme/servlet/image? It is a resource name whose interpretation is further determined by the HttpContext object, imgContext, which we investigate shortly.

An HTTP client can then fetch the images through the URL http://host:port/hello/image/pict.jpg. The bold type shows the correspondence between the registration alias and the URL that accesses the resource.

7.2.1.5. Unregistering Servlets or Resources

You can unregister any of the registered servlets or resources by calling the unregister method of the HttpService interface, and passing in the same alias used at the time of registration. For example,

httpService.unregister("/hello/image"); // unregister the resource
httpService.unregister("/hello");      // unregister the servlet

For a servlet, unregister invokes the servlet's destroy method to perform any required cleanup. If you do not unregister your servlet or resource, the HTTP service does so automatically. However, your servlet's destroy method is not called.

7.2.1.6. Example: Servlet and Resource Registration

Let's look at a complete example by creating a bundle that registers a servlet and an image included as a resource in the bundle. When accessed from a browser, the servlet generates an HTML page with an in-line picture (Figure 7.2).

Figure 7.2. Accessing a servlet registered with HttpService


The majority of the work is done in our bundle's activator. When the bundle is started, it obtains the HTTP service and registers a servlet. It also defines HttpContext, which is used in the servlet and image registration. The introduction of the HttpContext object adds a twist to how the HTTP service fetches the image as a resource. That is why we indicate in bold type the portion of the code that sets up the image resource for retrieval to be analyzed afterward:

package com.acme.servlet;
import org.osgi.framework.*;
import org.osgi.service.http.*;
import java.net.*;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
public class Activator implements BundleActivator {
   final static String IMAGE_ALIAS = "/hello/image";
     final static String SERVLET_ALIAS = "/hello";
     private HttpService http;

     public void start(BundleContext context) throws Exception {
        ServiceReference ref = context.getServiceReference(
           "org.osgi.service.http.HttpService");
        http = (HttpService) context.getService(ref);
     HttpContext hc = new
							HttpContext() {
							public String
							getMimeType(String name) {
							return null;  // let HttpService decide
							}
							public boolean
							handleSecurity(HttpServletRequest req,
							HttpServletResponse resp) throws IOException
							{
							return true;  // no authentication needed
         }

     public URL
							getResource(String name) {
							// get the named resource from the bundle JAR
							URL u = this.getClass().getResource(name);
							return u;
							}
							};
							http.registerResources(IMAGE_ALIAS,
							"/com/acme/servlet/image", hc);
      http.registerServlet(SERVLET_ALIAS,
         new HelloServlet(), null, hc);
   }

   public void stop(BundleContext context) throws Exception {
      if (http != null) {
            http.unregister(IMAGE_ALIAS);
            http.unregister(SERVLET_ALIAS);
      }
   }
}

The following code shows the HelloServlet implementation:

package com.acme.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.Date;

/**
 * This servlet responds to HTTP GET requests for the URL under which
 * this servlet is registered with HttpService. It generates a
 * message in HTML and returns it to the client.
 */
class HelloServlet extends HttpServlet {

   public void doGet(HttpServletRequest req,
      HttpServletResponse resp)
      throws ServletException, IOException
   {
      String html =
            "<HTML>
" +
            "<title>Hello World</title>
" +
            "<body bgcolor="#ffffff">
" +
            "<h1>Hello World</h1>

" +
       "<img src="" + Activator.IMAGE_ALIAS +
							"/sunflower.jpg"><br>
" +
            "</body></HTML>
";
      ServletOutputStream out = resp.getOutputStream();
      resp.setContentType("text/html");
      out.println(html);
      out.close();
   }
}

The doGet and doPost methods are probably the most important ones to override in the HttpServlet class. They process two common types of requests defined in the HTTP protocol, GET and POST respectively. Normally when you enter a URL directly into a browser's Location field to access the servlet, you send a GET request; if you submit a form, however, you supply the data with a POST request. We have a chance to see an example of handling the POST request in a servlet in Chapter 9.

To continue with our example, the following are the manifest definitions:

Bundle-Activator: com.acme.servlet.Activator
Import-Package: javax.servlet; specification-version=2.1,
 javax.servlet.http; specification-version=2.1,
 org.osgi.service.http

Finally, here are the contents of the bundle JAR file:

META-INF/MANIFEST.MF
com/acme/servlet/Activator.class
com/acme/servlet/Activator$1.class
com/acme/servlet/HelloServlet.class
com/acme/servlet/image/sunflower.jpg[2]
						

[2] You can substitute any JPEG image here and modify the code to use its filename.

Let's walk through the request/response cycles. Suppose the HTTP service is listening at mybox port 8080, and this bundle is activated. When a browser sends the request http://mybox:8080/hello, the HTTP service accepts the incoming request, extracts /hello, and looks it up in its registrants. No doubt HelloServlet is matched, and its doGet method is invoked. The HTML page is generated and returned to the browser.

The browser then encounters the following line while parsing and rendering the HTML page:

<img src="/hello/image/sunflower.jpg">

As a result, the browser comes back to the HTTP service with a second request: http://mybox:8080/hello/image/sunflower.jpg. Now, to make things easier, let's trace the rest of the process in a flow chart (Figure 7.3).

Figure 7.3. How a resource is retrieved


The HTTP service calls not only the getResource method, but also the getMimeType and handleSecurity methods of the HttpContext interface. In this example, we chose to do minimal customization for these methods, as described by the comments in the source code.

7.2.1.7. The HttpContext Interface

Both the servlet and the resource registration take an HttpContext argument. This interface is designed to give a registering bundle flexibility to customize how a request is handled in three aspects: deciding the returned MIME type, checking authentication, and finding a resource. At the time when the servlet or resource is accessed, the methods in this interface get a callback from the HTTP service to assist in making decisions in these areas. This is an example of the design pattern “Delegation and Callback,” described in Chapter 6.

The HttpContext API provides functionality similar in general but different in details for servlet and resource registrations. In the servlet registration, HttpContext is an interface meant to represent javax.servlet.ServletContext for the servlet. In fact, HttpContext has a one-to-one mapping to javax.servlet.ServletContext. Any servlets sharing the same instance of HttpContext belong to the same ServletContext. Obviously, such semantics do not apply to resource registration.

public String getMimeType(String name)
For Servlet Registration

This method allows you to define MIME[3] types based on the name obtained from the request. For example, a name ending with .html usually maps to a MIME type of text/html, .jpg to image/jpeg, and so on. You should implement this method if the contents produced by your servlet are of some special content type. In this case, the call to the getMimeType method of javax.servlet.ServletContext is delegated to your implementation of the getMimeType method in the HttpContext interface. Otherwise, simply have this method return null and ask the HTTP service to select an appropriate MIME type.

[3] MIME defines formats for multipart, textual, and nontextual messages. See RFCs 2045 [18], 2046 [19], 2047 [20], 2048 [21], and 2049 [22] for more details.

For Resource Registration

This method serves the same purpose as in the servlet registration. The MIME type is set in the Content-Type header of the response by the HTTP service. Return null if you'd like the HTTP service to select the best MIME type.

public boolean handleSecurity(HttpServletRequest req, HttpServletResponse resp)

Usually sensitive operations or data are protected and can only be accessed by authorized users. To determine the user, you can use some authentication techniques. One of those most frequently used is HTTP basic authentication [23], whereby a simple username/password pair is expected from the client.

This method allows authentication for each request, and works the same for both types of registrations. It returns true if a request has been authenticated, and returns false otherwise.

If you do not need such protection, always return true from this method. Otherwise, the steps to implement this method are as follows:

1.
Get and parse the username and password carried in the request. If none is present, send the client a challenge and ask for credentials.

2.
Compare the username and password in the request against a database of legitimate users. The user database may be maintained by the bundle that has registered the servlet or by a centralized service in the framework.

3.
Return true if the credential checks out, and HttpService will proceed to invoke the servlet's service method. Otherwise, return false, and send the appropriate error code to the client.

With the Java Embedded Server product, you can perform the authentication using the BasicSchemeHandler service provided in the httpauth.jar bundle, and can maintain your own user database using the httpusers.jar bundle. A detailed example is presented in the next section.

Another much stronger security mechanism is the secure socket layer (SSL). SSL uses public key cryptography to authenticate the client and the server engaged in a communication session. It also encrypts the messages as they are exchanged over the network to protect data privacy. SSL is on top of the TCP/IP layer but beneath application protocols. HTTPS refers to HTTP over an SSL channel. If HTTPS is enabled for both client and server, you may not need to worry about implementing the handleSecurity method. However, if you do not have client authentication, you may use a combination of HTTP basic authentication with HTTPS.

The Java Embedded Server product provides an SSL bundle to enable secure HTTP communication. For more information, consult the product documentation.

public String getResource(String name)
For Servlet Registration

The call to ServletContext.getResource is forwarded to your implementation of the getResource method in the HttpContext interface.

For Resource Registration

This method has quite different semantics from those for servlets. In resource registration, this method is expected to do additional name translation, depending on from where the resource comes. When you register a resource, you map the resource name to an alias. In our previous example:

http.registerResources(IMAGE_ALIAS,
      "/com/acme/servlet/image", hc);

where the resource name is the string /com/acme/servlet/image. When the resource is accessed, how and where this name is mapped is further determined by the getResource implementation of hc.

If the intention is to retrieve a resource included inside the bundle JAR file, you should ask the bundle's class loader to return a URL that identifies the resource. For example,

HttpContext hc = new HttpContext() {
   ...
   public URL getResource(String name) {
      return this.getClass().getResource(name);
   }
};

The URL is used internally to retrieve resources from bundle JAR files, and is understood only by the framework, which has implemented and installed a URL stream handler for it. If you were to expose its external form to a remote browser, you would have confused the browser utterly. Therefore, do not embed the URL in the HTML generated by your servlet, because it is sent to the remote client and won't be recognized.

The resource name can also refer to other types of resources. If our intention is to use HttpService as a conventional Web server and to export a portion of our file system as the document root, we would implement the getResource method quite differently:

class FileContext implements HttpContext {
   public String getMimeType(String name) { return null; }

   public boolean handleSecurity(
      HttpServletRequest req,
      HttpServletResponse resp) throws IOException
   {
      return true;
   }

   public URL getResource(String name) {
      return new URL("file", name);
   }
}

We would then register this resource in the following way:

httpService.registerResource("/web", "/wwwroot",
      new FileContext());

At run-time, the client request URL http://mybox:8080/web/index.html causes index.html to be passed to the getResource method via the name parameter and is eventually translated into file:/wwwroot/index.html, causing the file /wwwroot/index.html in the local file system to be returned.

To summarize, we compare the usage of HttpContext in both servlet and resource registration in Table 7.1.

Table 7.1. Comparison of HttpContext Usage in Servlet and Resource Registration
MethodHttpContext in Servlet RegistrationHttpContext in Resource Registration
getMimeTypeReturn the appropriate MIME type based on the request. Within the servlet, calls to javax.servlet.ServletContext.getMimeType is delegated to this method.Return the appropriate MIME type based on the request. The MIME type is set in the Content-Type HTTP header.
handleSecurity The same
getResourceWithin the servlet, calls to javax.servlet.ServletContext.getResource is delegated to this method.Used to translate further the requested resource name to a resource that is retrievable with a URL.

7.2.2. Performing Basic Authentication

Web sites frequently use a username/password pair to authenticate users. We have explained that a servlet or resource can achieve this by implementing the handleSecurity method on HttpContext. The Java Embedded Server product provides a BasicSchemeHandler service that makes your job much easier.

In the following example, suppose accessing our servlet requires a username of “admin” and a password of “secret.” Furthermore, the “admin” user belongs to the “acme” realm. You only need to follow these steps to enable basic authentication:

1.
Declare to import the basic scheme handler's service interface package in the manifest:

Bundle-Activator: com.acme.servlet.Activator
Import-Package: javax.servlet; specification-version=2.1,
 javax.servlet.http; specification-version=2.1,
 org.osgi.service.http, com.sun.jes.service.http.auth.basic
							

2.
Get an instance of BasicSchemeHandler to parse the username and password from the HTTP client and to send challenges back when needed:

package com.acme.servlet;
... // Other imports
import com.sun.jes.service.http.auth.basic.*;

public class Activator implements BundleActivator {
   private HttpService http;
   private final String SERVLET_ALIAS = "/hello";
   ...

   public void start(BundleContext context) throws Exception {
      ...
      // Obtain BasicSchemeHandler
      ServiceReference basicRef = context.getServiceReference(
         BasicSchemeHandler.class.getName())
      final BasicSchemeHandler basicAuth = basicRef != null ?
         (BasicSchemeHandler)context.getService(basicRef) :
         null;
      ...
      HttpContext hc = new HttpContext() {
        String realm = "acme";

         public String getMimeType(String name) { ... }

         public URL getResource(String name) { ... }

         public boolean handleSecurity(HttpServletRequest req,
                HttpServletResponse resp)
            throws IOException
         {
            if (basicAuth == null) {
               // always deny access if
               // we can't perform authentication
               resp.sendError(resp.SC_UNAUTHORIZED);
               return false;
            }
            BasicSchemeHandler.Response response =
               basicAuth.getResponse(req);
            if ( response == null ) {
               // no credential is present;
               // send a challenge back to the client
               basicAuth.sendChallenge(resp, realm);
               // deny access to the client:
               return false;
            }
            // Check username and password,
            // challenge the client if the check fails
            String user = response.getName();
            String password = response.getPassword();
            if (!"admin".equals(user) ||
               !"secret".equals(password) )
            {
               basicAuth.sendChallenge(resp, realm);
               return false;
            }
            // Set the request attributes for the servlet
            req.setAttribute(
               "org.osgi.service.http.remote.user", user);
            req.setAttribute("org.osgi.service.http.auth.type",
               "basic");
            // Grant access
            return true;
         }
      };
      http.registerServlet(SERVLET_ALIAS,
         new HelloServlet(), null, hc);
      ...
   }

   public void stop(BundleContext context) {
      ...
   }
}

This version of handleSecurity only allows one user from a single realm to access the servlet, because the valid username and realm are hard coded in the HttpContext implementation. If you have a large number of users from several realms, you may want to resort to a dedicated service for user management.

handleSecurity is invoked for every request and response. BasicSchemeHandler's sendChallenge method sends the realm to the HTTP client when expected credentials are not present. In the case of a browser, this generally causes a dialog to pop up, prompting for username and password. Its getResponse method then extracts the returned credentials for verification.

The setAttribute calls are workarounds. They communicate the information of the authenticated user and authentication method used back to the servlet. Imagine that you want to display, in the Web page generated in the servlet's doGet method, the user who is currently logged in. You can do so with the following code:

String user = (String) req.getAttribute(
   "org.osgi.service.http.remote.user");
out.println("<P>The current user is <b>" + user + "</b>");

In the next release of the Java Embedded Server product, you can simply call HttpServletRequest's getRemoteUser and getAuthType methods to retrieve the values set for the org.osgi.service.http.remote.user and org.osgi .service.http.auth.type attributes, respectively. These two attribute names are expected to be standardized in the future.

7.2.3. The Extended HTTP Service

The Java Embedded Server product's implementation of the HTTP service extends the functionality of the HttpService defined by the OSGi in two ways:

  1. The extended alias syntax that allows a servlet or resource to be registered on any host (if multihome) or port of the gateway. A multihome machine has more than one network interface card (NIC) installed, each with its own IP address and host name. A service gateway can naturally double as a network gateway by using one NIC for the subnet inside the house while using another for connecting to the Internet at large.

  2. An additional service interface, com.sun.jes.service.http.HttpAdmin, which reports servlet and resource registration status of HttpService.

The semantics of the alias parameter have been enriched to allow for registration on different hosts and ports as well as using HTTPS. The extended syntax is a superset of that specified in the OSGi HttpService API. The full syntax is

http[s]://host:port/alias

The various scenarios made possible by this syntax are summarized in Table 7.2. In the example, we assume that a host has two host names: myhost and myhost1. The default host name is myhost and the default port number is 8080. We are to register a home portal servlet with the HTTP service running on the host.

Table 7.2. Extended Alias Syntax
Alias Used at Registration URL for Accessing the Servlet or Resource Notes
/HomePortal http://myhost:8080/HomePortal Use default host and port number.
http://*:7070/HomePortal http://myhost:7070/HomePortal http://myhost1:7070/HomePortal On a multihome host, the same servlet can be accessed through any of the hosts on port 7070. In the example, one host has two host names: myhost and myhost1.
http://myhost1:6060/HomePortal http://myhost1:6060/HomePortal Specify a specific host only on the multihome host.
https://myhost:443/HomePortal https://myhost:443/HomePortal HttpService is communicating with clients over HTTPS connections on port 443.

The same alias registered on one host/port does not conflict with that on another.

Another augmented functionality is that the HTTP bundle registers a second service, com.sun.jes.service.http.HttpAdmin, in addition to the standard HttpService. The new API allows you to find out how many servlets or resources have been registered so far, what aliases have been used, and so on. Management applications may need to access such information.

To take advantage of this service, add the following to a client bundle:

1.
Declare to import the com.sun.jes.service.http package:

Import-Package: javax.servlet; specification-version=2.1,
 javax.servlet.http; specification-version=2.1,
 org.osgi.service.http, com.sun.jes.service.http
							

2.
Obtain a reference to the HttpAdmin service:

import com.sun.jes.service.http.HttpAdmin;
import com.sun.jes.service.http.HttpRegistration;
...
ServiceReference adminRef = bundleContext.getServiceReference(
   "com.sun.jes.service.http.HttpAdmin");
HttpAdmin httpAdmin = (HttpAdmin)
      bundleContext.getService(adminRef);

This service allows you to get an array of either servlet registrations or resource registrations. From each of the returned registration objects you can find out more details. The following code demonstrates this:

HttpRegistration[] regs = httpAdmin.getServletRegistrations();
for (int i=0; i<regs.length; i++) {
   System.out.println("Registered servlet #" + i + ":");
   System.out.println("Alias: " + regs[i].getAlias());
   System.out.println("URL: " + regs[i].getURL("*"));
   System.out.println("Initial servlet parameters:");
   Servlet servlet = regs[i].getServlet();
   ...
   System.out.println();
}

Because these APIs reveal a great deal of sensitive information, the caller must have administrative permission to use them. We cover security in Chapter 9.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.179.85