JavaRanch Home    
 
This page:         last edited 10 October 2013         What's Changed?         Edit

Web Services How To   

This page collects code snippets that demonstrate how to accomplish a variety of tasks useful for implementing web services.

  • How can I set HTTP headers like SOAPAction or Basic Authentication in my SOAP request? (link)
  • How can I use HTTP Basic Authentication in my JAX-RPC client? (link)
  • With Axis, how can I access authentication information if I use HTTP Authentication? (link)
  • With Axis, how can I find out which service and operation was invoked? (link)
  • What is the simplest possible Java client for a web service? (link)
  • How do I handle WS-Security UsernameTokens with WSS4J? (link)
  • An overview of authentication in Web Services can be found in this JavaRanch Journal article, and there's an update to the article that covers Axis 2 here
  • HowToGetHelpWithSoapWebServices

As any other page in this wiki, the page is editable by anyone, so don't hesitate to add useful stuff.


  How can I set HTTP headers like SOAPAction or Basic Authentication in my SOAP request?

This works for both JAXWS SAAJ and Axis.

If "message" is a SOAPMessage object, it can be done like this:

  MimeHeaders hd = message.getMimeHeaders();
  hd.addHeader("SOAPAction", "http://www.webserviceX.net/ConversionRate");

Other HTTP headers can be set in the same way, e.g. for Basic Authentication:

  String authorization = Base64Coder.encode(username + ":" + password);
  hd.addHeader("Authorization", "Basic " + authorization);

The Base64Coder class can be found here, but there are many packages that perform base64 encoding, including Jakarta Commons Codec.


  How can I use HTTP Basic Authentication in my JAX-RPC client?

If you're using JAX-RPC, then the client will generally have a service locator which returns a service, looking something like this:

  Service service = new ServiceLocator();
  Impl impl = service.getImpl();

Then you can use the following:

  javax.xml.rpc.Stub stub = (javax.xml.rpc.Stub) impl;
  stub._setProperty(Stub.USERNAME_PROPERTY, "user");
  stub._setProperty(Stub.PASSWORD_PROPERTY, "pwd");

javax.xml.rpc.Call also lets you set properties, if you're working directly with Call. Note that the JAX-RPC API is now deprecated.


  With Axis, how can I access authentication information if I use HTTP Authentication?

The following code provides access to the HttpServletRequest, which makes the getRemoteUser, getUserPrincipal, getAuthType and isUserInRole methods available.

import org.apache.axis.MessageContext;
import org.apache.axis.transport.http.HTTPConstants;

MessageContext context = MessageContext.getCurrentContext(); 
HttpServletRequest req = (HttpServletRequest) context.getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST);

How to get this information using Rampart is described here and here.

It's also possible to get the client DN out of the public key certificate after authentication:

X509Certificate[] certificate = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
Principal clientDN = certificate[0].getSubjectDN();        // certificate[0] is the end of the chain.

Note that this ties you to Axis as the SOAP engine. Going forward it is better to use WS-Security instead of HTTP Authentication. The Apache WSS4J package can be used with Axis to implement this.


  With Axis, how can I find out which service and operation was invoked?

The following code determines both the target service and the operation invoked by the current request.

  import org.apache.axis.MessageContext;
  MessageContext context = MessageContext.getCurrentContext(); 
  String service = context.getTargetService();
  String operation = context.getOperation().getName());

An OperationDesc object -which is returned by context.getOperation()- also holds lots of other useful information like parameters, SOAPAction, style, use, etc.


  What is the simplest possible Java client for a web service?

The following code is a SAAJ client for the Calculator.jws web service that comes with Axis. It constructs a SOAP message that calls a web service with two parameters and a single scalar result, shows how to print request and response to standard out, and also can set HTTP and SOAP headers. To deploy the service it accesses, copy the file samples/userguide/example2/Calculator.java to the axis web app directory and rename it to Calculator.jws.

To run the client, the Axis libraries need to be in the classpath. The command line invocation would be "java SAAJClient 5 7" (which results in the numbers 5 and 7 being added).


import java.util.Iterator;

import javax.xml.soap.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Node;

public class SAAJClient
{

    public static void main (String args[])
    {
        String arg1 = args[0];
        String arg2 = args[1];

        String operation = "add";    // could also be "subtract"
        String urn = "CalculatorService";
        String destination = "http://localhost:8080/axis/Calculator.jws";

        try
        {
            // First create the connection
            SOAPConnectionFactory soapConnFactory = SOAPConnectionFactory.newInstance();
            SOAPConnection connection = soapConnFactory.createConnection();

            // Next, create the actual message
            MessageFactory messageFactory = MessageFactory.newInstance();
            SOAPMessage message = messageFactory.createMessage();

            SOAPPart soapPart = message.getSOAPPart();
            SOAPEnvelope envelope = soapPart.getEnvelope();

            // This method demonstrates how to set HTTP and SOAP headers.
            // setOptionalHeaders(message, envelope);

            // Create and populate the body
            SOAPBody body = envelope.getBody();

            // Create the main element and namespace
            SOAPElement bodyElement = body.addChildElement(
                    envelope.createName(operation, "ns1""urn:"+urn));
            // Add parameters
            bodyElement.addChildElement("in0").addTextNode(arg1);
            bodyElement.addChildElement("in1").addTextNode(arg2);

            // Save the message
            message.saveChanges();

            // Check the input
            /*
            System.out.println("\nRequest:\n");
            message.writeTo(System.out);
            System.out.println();
            */

            // Send the message and get the reply
            SOAPMessage reply = connection.call(message, destination);

            // Retrieve the result - no error checking is done: BAD!
            soapPart = reply.getSOAPPart();
            envelope = soapPart.getEnvelope();
            body = envelope.getBody();

            Iterator iter = body.getChildElements();
            Node resultOuter = ((Node) iter.next()).getFirstChild();
            Node result = resultOuter.getFirstChild();

            System.out.println("add("+arg1+","+arg2+") = "+result.getNodeValue());

            // Check the output
            /*
            System.out.println("\nResponse:\n");
            // Create the transformer
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            // Extract the content of the reply
            Source sourceContent = reply.getSOAPPart().getContent();
            // Set the output for the transformation
            StreamResult result = new StreamResult(System.out);
            transformer.transform(sourceContent, result);
            System.out.println();
            */

            // Close the connection           
            connection.close();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    // This method demonstrates setting HTTP headers (for authentication)
    // and SOAP headers. Both of these are optional.

    // Note: There's a bug in some Axis2 versions (https://issues.apache.org/jira/browse/AXIS2-1014)
    // that prevents SOAP/MIME headers from being transmitted for SAAJ clients. 
    // It has been fixed in Axis2 versions 1.5.3 and 1.6.0.

    private void setOptionalHeaders (SOAPMessage message, SOAPEnvelope envelope) throws SOAPException
    {
        // start: setting HTTP headers - optional, comment out if not needed
            String username = "wsuser";
            String password = "wspwd";

            String authorization = new sun.misc.BASE64Encoder().encode((username+":"+password).getBytes());
            MimeHeaders hd = message.getMimeHeaders();
            hd.addHeader("Authorization""Basic " + authorization);
        // end: setting HTTP headers

        // start: setting SOAP headers - optional, comment out if not needed
            // Create and populate the header
            SOAPHeader header = message.getSOAPHeader();
            if (header == null)
                header = envelope.addHeader();

            // Will use the default actor "next" for this example,
            // otherwise use he.setActor(String actorURI) to define actor.
            SOAPHeaderElement he = header.addHeaderElement(
                    envelope.createName("foo""ns1""http://localhost/SOAPHeader"));
            he.setMustUnderstand(false);
            he.setValue("bar");
        // end: setting SOAP headers
    }
}


  How do I handle WS-Security UsernameTokens with WSS4J?

The following code is a complete CallbackHandler implementation for handling authentication using WSS4J. It handles basic and digest authentication, but not client certificates. Its drawback is that it needs to know the username and password, and doesn't handle roles. A better solution is described in the JavaRanch Journal article mentioned above.


import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import org.apache.ws.security.WSPasswordCallback;

public class PWCallbackServer implements CallbackHandler
{

    // the username and password we expect incoming WS calls to use
    private String user = "wsuser";
    private String pwd = "wspwd";

    public void handle (Callback[] callbacks) throws IOExceptionUnsupportedCallbackException
    {
        for (int i = 0; i < callbacks.length; i++)
        {
            if (callbacks[i] instanceof WSPasswordCallback)
            {
                WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
                System.out.println("identifier: "+pc.getIdentifer()+", usage: "+pc.getUsage());

                if (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN)
                {
                    // for passwords sent in digest mode we need to provide the password,
                    // because the original one can't be un-digested from the message

                    // we can throw either of the two Exception types if authentication fails
                    if (! user.equals(pc.getIdentifer()))
                        throw new IOException("unknown user: "+pc.getIdentifer());

                    // this will throw an exception if the passwords don't match
                    pc.setPassword(pwd);

                } else if (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN)
                {
                    // for passwords sent in cleartext mode we can compare passwords directly

                    if (! user.equals(pc.getIdentifer()))
                        throw new IOException("unknown user: "+pc.getIdentifer());

                    // we can throw either of the two Exception types if authentication fails
                    if (! pwd.equals(pc.getPassword()))
                        throw new IOException("password incorrect for user: "+pc.getIdentifer());
                }
            } else
            {
                throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
            }
        }
    }
}


WebServicesFaq CategoryFaq CategoryHowTo CategoryCodeSamples CodeBarn

JavaRanchContact us — Copyright © 1998-2014 Paul Wheaton