WSS4J Tutorial revisit

While reading the WSS4J - Axis Deployment Tutorial I found it somewhat lacking. Also in later versions of Axis it seems that the StockQuoteService has been changed.

So here is a reworked version.

Prerequisties

This tutorial was performed on a Linux machine with Java 1.6.0_01-b06.

Installing WSS4J

  1. Install Apache Tomcat
    1. Unpack the Apache Tomcat archive. Below this folder will be called $CATALINA_HOME
    2. Make sure the environment variable $JAVA_HOME is set correctly.
  2. Install Axis
    1. Unpack the Axis archive
    2. Copy the axis folder from webapps into $CATALINA_HOME/webapps
  3. Install WSS4J and XML Security
    1. Unpack the WSS4J archive and copy wss4j.jar into $CATALINA_HOME/axis/WEB-INF/lib
    2. Unpack the XML Security archive and copy xmlsec-1.4.3.jar into $CATALINA_HOME/axis/WEB-INF/lib renaming it to xmlsec.jar
  4. Start up Apache Tomcat by running the startup.sh command in $CATALINA_HOME/bin
  5. Verify the installation by accessing the Axis Happiness Page. It should now list the XML Security API under Optional Components.

Creating and deploying the initial service

This tutorial will reuse the StockQuoteService that ships in the Axis sample folder. However this service tries to look up a real stock quote from a external service, located at services.xmethods.net, that doesn't exists any more. This means that the only symbol we can look up a quote for is XXX.

Compiling and deploying the sample

Go to the unpacked Axis folder and in under samples/stock.
Compile all code using ant. The out put folder is under the Axis folder build/classes.

~axis-1_4/samples/stock$ ant
Buildfile: build.xml
 
axis-clover-setenv:
 
axis-clover-setup:
 
axis-xmlbeans-setenv:
 
axis-xmlbeans-setup:
 
setenv:
 
copy:
 
compile:
    [javac] Compiling 6 source files to ~/axis-1_4/build/classes
 
BUILD SUCCESSFUL
Total time: 1 second

To deploy the sample service first copy the class files to $CATALINA_HOME/webapps/axis/WEB-INF/classes/

~axis-1_4/samples/stock$ cp -r ../../build/classes/samples $CATALINA_HOME/webapps/axis/WEB-INF/classes

Then edit the template deploy.wsdd to match the one below.

deploy.wsdd

<!-- Use this file to deploy some handlers/chains and services      -->
<!-- Two ways to do this:                                           -->
<!--   java org.apache.axis.client.AdminClient deploy.wsdd          -->
<!--      after the axis server is running                          -->
<!-- or                                                             -->
<!--   java org.apache.axis.utils.Admin client|server deploy.wsdd   -->
<!--      from the same directory that the Axis engine runs         -->
 
<deployment name="test" xmlns="http://xml.apache.org/axis/wsdd/" 
    xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
 
  <service name="stock-wss-01" provider="java:RPC">
    <parameter name="className" value="samples.stock.StockQuoteService"/>
    <parameter name="allowedMethods" value="getQuote test"/>
    <!-- parameter name="allowedRoles" value="user1,user2"/ -->
    <parameter name="wsdlServicePort" value="GetQuote"/>
 
    <!-- requestFlow name="checks">
      <handler type="java:org.apache.axis.handlers.SimpleAuthenticationHandler"/>
      <handler type="java:org.apache.axis.handlers.SimpleAuthorizationHandler"/>
    </requestFlow -->
  </service>
 
  <service name="cominfo" provider="java:RPC">
    <parameter name="className" value="samples.stock.ComInfoService" />
    <parameter name="allowedMethods" value="getInfo" />
    <parameter name="allowedRoles" value="user3"/>
    <requestFlow type="checks"/>
  </service>
 
</deployment>

Now deploy using Axis Admin Client.

java -cp $AXISCLASSPATH org.apache.axis.client.AdminClient -lhttp://localhost:8080/axis/services/AdminService deploy.wsdd

undeploy.wsdd

<undeployment name="test" xmlns="http://xml.apache.org/axis/wsdd/">
  <service name="stock-wss-01"/>
  <service name="cominfo"/>
</undeployment>

Creating the client

Adding usertoken security

New deploy.wsdd

<!-- Use this file to deploy some handlers/chains and services      -->
<!-- Two ways to do this:                                           -->
<!--   java org.apache.axis.client.AdminClient deploy.wsdd          -->
<!--      after the axis server is running                          -->
<!-- or                                                             -->
<!--   java org.apache.axis.utils.Admin client|server deploy.wsdd   -->
<!--      from the same directory that the Axis engine runs         -->
 
<deployment name="test" xmlns="http://xml.apache.org/axis/wsdd/" 
    xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
 
  <service name="stock-wss-01" provider="java:RPC">
    <parameter name="className" value="samples.stock.StockQuoteService"/>
    <parameter name="allowedMethods" value="getQuote test"/>
    <!-- parameter name="allowedRoles" value="user1,user2"/ -->
    <parameter name="wsdlServicePort" value="GetQuote"/>
 
    <!-- requestFlow name="checks">
      <handler type="java:org.apache.axis.handlers.SimpleAuthenticationHandler"/>
      <handler type="java:org.apache.axis.handlers.SimpleAuthorizationHandler"/>
    </requestFlow -->
    <requestFlow>
   <handler type="java:org.apache.ws.axis.security.WSDoAllReceiver">
    <parameter name="passwordCallbackClass" value="PWCallback"/>
    <parameter name="action" value="UsernameToken"/>
   </handler>
  </requestFlow>
  </service>
 
  <service name="cominfo" provider="java:RPC">
    <parameter name="className" value="samples.stock.ComInfoService" />
    <parameter name="allowedMethods" value="getInfo" />
    <parameter name="allowedRoles" value="user3"/>
    <requestFlow type="checks"/>
  </service>
 
</deployment>

Programmatically setting PWCallback

package samples.stock.client;
 
import java.rmi.RemoteException;
import java.rmi.Remote;
import javax.xml.rpc.ServiceException;
import org.apache.axis.client.Stub;
import org.apache.ws.security.message.token.*;
import org.apache.ws.security.handler.*;
import org.apache.ws.security.message.token.*;
import org.apache.ws.security.*;
import org.apache.axis.EngineConfiguration;
import org.apache.axis.Handler;
import org.apache.axis.SimpleChain;
import org.apache.axis.SimpleTargetedChain;
import org.apache.axis.client.Stub;
import org.apache.axis.configuration.SimpleProvider;
import org.apache.axis.transport.http.HTTPSender;
import org.apache.axis.transport.http.HTTPTransport;
import org.apache.ws.axis.security.WSDoAllSender;
import org.apache.ws.security.handler.WSHandlerConstants;
 
 
public class StockQuoteServiceClient {
    public StockQuoteServiceClient() {
    }
    public static void main(String[] args) throws ServiceException, RemoteException {
        if (args.length == 0) {
            System.out.println("Usage:\njava StockQuoteServiceClient [symbol]");
            return;
        }
 
        SimpleProvider clientConfig = new SimpleProvider();
        Handler outHandler = new WSDoAllSender();
        outHandler.setOption(WSHandlerConstants.ACTION,
                         WSHandlerConstants.USERNAME_TOKEN);
        outHandler.setOption(WSHandlerConstants.USER, "wss4j");
        outHandler.setOption("passwordType", "PasswordText");
        SimpleChain reqHandler = new SimpleChain();
        reqHandler.addHandler(outHandler);
        Handler pivot=(Handler)new HTTPSender();
        Handler transport=new SimpleTargetedChain(reqHandler, pivot, null);
        clientConfig.deployTransport(HTTPTransport.DEFAULT_TRANSPORT_NAME,
                                 transport);
 
        StockQuoteServiceService locator = new StockQuoteServiceServiceLocator(clientConfig);
        Remote remote = locator.getPort(StockQuoteService.class);
        Stub axisPort = (Stub)remote;
        axisPort._setProperty(WSHandlerConstants.PW_CALLBACK_CLASS, "samples.stock.client.PWCallback");
 
        StockQuoteService service = (GetQuoteSoapBindingStub)axisPort; /* locator.getGetQuote(); */
        
        float quote = service.getQuote(args[0]);
        System.out.println("stock quote service returned " + args[0] + ": " + quote);
    }
}