Coding the Client

The following code snippets illustrate what's needed to add to the basic client examples in Creating a Client and Invoking the Web Service to utilize the preceding security illustrations.

To utilize a client axis2.xml descriptor file you would need to make the following API call where C:/Axis2/client also contains the Axis2 modules directory as indicated in Defining the Axis2 Security Configuration:

Figure 1. Identifying Axis2 Client Rampart Configuration
final ConfigurationContext ctx =
      ConfigurationContextFactory.
    createConfigurationContextFromFileSystem(
      // Looks for modules, etc. here:
      "C:/Axis2/client",
      // Axis2 client descriptor:
      "C:/Axis2/client/conf/client-axis2.xml");

To utilize a Rampart policy file you would need to create a context as above, but the client Axis2 descriptor is not necessary in this example, just the Axis2 modules directory:

final ConfigurationContext ctx =
      ConfigurationContextFactory.
    createConfigurationContextFromFileSystem(
      // Looks for modules, etc. here:
      "C:/Axis2/client",
      null);

When not utilizing an Axis2 configuration that specifies the necessary modules (as shown in Defining the Axis2 Security Configuration) you will need to explicitly engage the necessary module(s) prior to invoking the service. The specific modules required will depend on the security features and configuration you are using; for example:

client.engageModule("rampart");

Failing to do this will result in a server-side error; e.g.:

org.apache.rampart.RampartException:
  Missing wsse:Security header in request

To utilize a Rampart policy you would need to create a policy object and set it in the service options properties:

final org.apache.axiom.om.impl.builder.StAXOMBuilder builder =
  new StAXOMBuilder("C:/Axis2/client/policy.xml");
final org.apache.neethi.Policy policy =
  org.apache.neethi.PolicyEngine.
    getPolicy(builder.getDocumentElement());
  options.setProperty(
    org.apache.rampart.RampartMessageData.KEY_RAMPART_POLICY,
    loadPolicy(policy);
Note: Any number of client coding errors, policy specification errors, configuration errors, etc. can manifest in the client and/or the server. Often an error in the client cannot be debugged without access to the log4j trace from the server. For instance, the error when the proper module(s) has not been engaged (discussed earlier) may appear in the client as:
OMException in getSOAPBuilder
  org.apache.axiom.om.OMException:
  com.ctc.wstx.exc.WstxUnexpectedCharException:
  Unexpected character 'E' (code 69) in prolog; expected '<'
  at [row,col {unknown-source}]: [1,1]

Here is an example that combines the fragments above, illustrating providing a IBM Cúram Social Program Management custom SOAP header and using Rampart to encrypt it:

Figure 2. Sample Client Code to Encrypt a Custom SOAP Header
import wsconnector.MyServiceStub;
import java.io.File;
import java.net.URL;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ConfigurationContextFactory;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.neethi.Policy;
import org.apache.neethi.PolicyEngine;
import org.apache.rampart.RampartMessageData;

...

/**
 * Invoke a web service with encrypted credentials.
 *
 */
public void webserviceClient() {

    final String serviceName = "myService";
    final String operationName = "myOperation";


    // Instantiate the stub.
    final MyServiceStub stub =
      new MyServiceStub();

    // Get the end point of the service and convert it to a URL
    final Options options = stub._getServiceClient().getOptions();
    final EndpointReference eprTo = options.getTo();
    final URL urlOriginal = new URL(eprTo.getAddress());

    // Use that URL,
    // plus our service name to construct a new end point.
    final URL urlNew = new URL(
      urlOriginal.getProtocol(),
      urlOriginal.getHost(),
      urlOriginal.getPort(),
      "/CuramWS2/services/" + serviceName);
    final EndpointReference endpoint =
      new EndpointReference(urlNew.toString());


    // Load configuration.
    final ConfigurationContext ctx = ConfigurationContextFactory.
    createConfigurationContextFromFileSystem(
      "C:/Axis2/client", // Looks for modules, etc. here.
      null); // Configuration provided via API engaging rampart.

    final ServiceClient client = new ServiceClient(ctx, null);

    // Set the credentials - illustrated as an example earlier
    setCuramCredentials(client, "tester", "password");

    // Set the operation in the endpoint.
    options.setAction("urn:" + operationName);
    options.setTo(endpoint);

    // Set client timeout to 30 seconds for slow machines.
    options.setProperty(
      HTTPConstants.SO_TIMEOUT, new Integer(30000));
    options.setProperty(
      HTTPConstants.CONNECTION_TIMEOUT, new Integer(30000));

    // Load the Rampart policy file.
    final StAXOMBuilder builder =
      new StAXOMBuilder("C:/Axis2/client" + File.separator
        + "policy.xml");
    final Policy policy =
      PolicyEngine.getPolicy(builder.getDocumentElement());
    options.setProperty(RampartMessageData.KEY_RAMPART_POLICY,
      policy);
    client.setOptions(options);


    // Because we are not using an axis2.xml client
    // configuration file we MUST explicitly load
    // Rampart.
    client.engageModule("rampart");


    // Setup the SOAP message.
    // For this example three integers are to be summed.
    final OMFactory factory = OMAbstractFactory.getOMFactory();
    final OMNamespace ns = factory.
      createOMNamespace("http://remote.custom.util.curam", "ns1");
    final OMElement element = factory.
      createOMElement("myOperation", ns);

    final OMElement childElem1 = factory.
      createOMElement("args0", null);
    childElem1.setText("One");
    element.addChild(childElem1);

    final OMElement childElem2 = factory.
      createOMElement("args1", null);
    childElem2.setText("Two");
    element.addChild(childElem2);

    final OMElement childElem3 = factory.
      createOMElement("args2", null);
    childElem3.setText("Three");
    element.addChild(childElem3);


    // Invoke the service.
    final OMElement response =
      client.sendReceive(element);

    // Process the return data.
    final String sData = response.getFirstElement().getText();

    System.out.println("Service returned: " + sData);
  }

The following shows an equivalent technique for setting the security parameters programmatically, although it is deprecated, it would replace the block of code commented "Load the Rampart policy file" in Coding the Client, above as well as the related policy file:

Figure 3. Sample Client Code (Deprecated) for Setting the Client Security Configuration
final OutflowConfiguration outConfig =
      new OutflowConfiguration();
    outConfig.setActionItems("Signature Encrypt");
    outConfig.setUser("tester");
    outConfig.
      setPasswordCallbackClass("my.test.ClientPWCallback");
    outConfig.
      setSignaturePropFile("client-crypto.properties");
    outConfig.setSignatureKeyIdentifier(
      WSSHandlerConstants.BST_DIRECT_REFERENCE);
    outConfig.setEncryptionKeyIdentifier(
      WSSHandlerConstants.ISSUER_SERIAL);
    outConfig.setEncryptionUser("admin");

    final InflowConfiguration inConfig =
      new InflowConfiguration();
    inConfig.setActionItems("Signature Encrypt");
    inConfig.
      setPasswordCallbackClass("my.test.ClientPWCallback");
    inConfig.setSignaturePropFile("client-crypto.properties");

    //Set the rampart parameters
    options.setProperty(WSSHandlerConstants.OUTFLOW_SECURITY,
      outConfig);
    options.setProperty(WSSHandlerConstants.INFLOW_SECURITY,
      inConfig);