Skip to main content

RESTful Web Service Endpoints in JAX-WS

Posted by mhadley on March 21, 2006 at 11:05 AM PST

In previous entries I've covered use of the client side JAX-WS Dispatch<T> interface to consume RESTful Web services. In this entry I'd like to demonstrate use of the Provider<T> interface to develop a RESTful endpoint and then publish that endpoint using the Endpoint API. To try out the code in this entry you'l need to grab a recent nightly build of JAX-WS since the functionality was not implemented in JWSDP 2.0.

A JAX-WS Provider-based endpoint has to implement the javax.xml.ws.Provider interface:

public interface Provider<T> {
  public T invoke(T request);
}

You also have to pick the service mode in which the endpoint will operate and annotate the endpoint class to allow the JAX-WS runtime to identify the correct class to publish. Here's the start of our endpoint class (I'm going to interleave code and narrative but if you extract and stitch together the code segments below you'll end up with a complete class):

@WebServiceProvider 
@ServiceMode(value=Service.Mode.PAYLOAD)
public class SystemPropertiesProvider  implements Provider<Source> {

    @Resource
    protected WebServiceContext context;

Arun has an informative blog entry that discusses the difference between PAYLOAD and MESSAGE mode and describes which types of Provider work in which mode. The context member variable is initialized by the JAX-WS runtime using resource injection and provides access to the message context which contains various properties that an endpoint may find useful - more on this below.

The endpoint we'll develop here will allow a client to query the Java system properties of the endpoint implementation class, here is the implementation of the Provider.invoke method.

  public Source invoke(Source source) {
    Map<String, List<String>> query = getQueryParameters();
    String replyData = getSystemInfoAsXML(query.get("property"));
    StreamSource reply = new StreamSource(new StringReader(replyData));
    return reply;
  }

The getQueryParameters method obtains the HTTP query string using the appropriate message context property (recall the context member varaible introduced above) and then parses that string into a Map.

  protected Map<String, List<String>> getQueryParameters() {
    MessageContext msgCtx = context.getMessageContext();
    String queryString = (String)msgCtx.get(MessageContext.QUERY_STRING);
    HashMap<String, List<String>> params =
      new HashMap<String, List<String>>();
    for (String s: queryString.split("&")) {
      String[] keyVal = s.split("=");       
      try {
        String key = URLDecoder.decode(keyVal[0], "UTF-8");
        String val = URLDecoder.decode(keyVal[1], "UTF-8");
        if (params.get(key) == null) {
          ArrayList<String> list = new ArrayList<String>();
          list.add(val);
          params.put(key,list);
        }
        else {
          ArrayList<String> list =
            (ArrayList<String>)params.get(key);
          list.add(val);
        }
      } catch (UnsupportedEncodingException ex) {
        ex.printStackTrace();
      }
    }
    return params;
  }

Its entirely possible that the above functionality already exists in some other class but, if it does, I didn't find it in the brief search I made. The getSystemInfoAsXML method takes a list of Java system property names and creates an XML document containing the name and value of each parameter.

  protected String getSystemInfoAsXML(List properties) {
    StringBuffer buf = new StringBuffer();
    buf.append("<?xml version='1.0' encoding='UTF-8'?>\n");
    buf.append("<?xml-stylesheet href='http://127.0.0.1/info.xsl' type='text/xsl'?>\n");
    buf.append("<properties>");

    for(String property: properties) {
      buf.append("<property name='");
      buf.append(property);
      buf.append("'>");
      buf.append(System.getProperty(property));
      buf.append("</property>");
    }

    buf.append("</properties>");
    return buf.toString();
  }

The xml-stylesheet processing instruction in the generated XML document will allow a modern browser to display the generated document in a human readable form; the stylesheet is included at the end of this entry for completeness. You'll have to put the stylesheet on a Web server somewhere and modify this line to point to the location you chose.

If we wanted to deploy the endpoint in a Java EE container then the above is all the code needed. However, for this example, we'll use the new JAX-WS Endpoint API to publish the endpoint without a container (this is really intended for lightweight endpoints and testing, don't throw out your application server). To do this we add a main method to the class.

  public static void main(String[] args) {
    Endpoint e = Endpoint.create(HTTPBinding.HTTP_BINDING,
      new SystemPropertiesProvider());
    e.publish("http://127.0.0.1:8084/system/info");
    System.out.println("Endpoint running");
    try {
      Thread.sleep(30000);
    } catch (InterruptedException ex) {
    }
    e.stop();
    System.out.println("Endpoint stopped");
  }
}

In the above we publish the endpoint for 30 seconds and then stop it, you could also wait for a keypress in the main thread or use some other scheme to determine when to stop the endpoint.

Now, stitch together the code segments from above, compile and run the class. Fire up a browser, point it to http://127.0.0.1:8084/system/info?property=os.version&property=os.name and see what OS the Java runtime thinks the endpoint is hosted on.

Here's the XSL stylesheet that converts the XML document into a HTML table:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
    <html><body><table border="1">
      <tr>
        <th>Property</th>
        <th>Value</th>
      </tr>
      <xsl:for-each select="/properties/property">
        <tr>
          <td>
            <xsl:value-of select="@name"/>
          </td>
          <td>
            <xsl:value-of select="."/>
          </td>
        </tr>
      </xsl:for-each>
    </table></body></html>
  </xsl:template>
</xsl:stylesheet>
Related Topics >>