20.2 Developing Clients for the Provisioning Web Service

This section includes the following topics:

20.2.1 Web Access to the Provisioning Web Service

A SOAP-based Web service is usually accessed by inserting a SOAP message in the body of an HTTP Post request.The Web service toolkit used to build the provisioning Web service also supports access using HTTP GET. In other words, you can open the URL of the Web service endpoint in a browser and interact with the Web service. In particular, the provisioning Web service lets you invoke each of its operations.

Accessing the Test Page

You can access the provisioning Web Service endpoint using a URL similar to the following:

http://server:port/warcontext/provisioning/service?test

For example, if your server is named “myserver”, your User Application is listening on port 8080, and your User Application war file is named “IDMPROV”, the URL would be:

http://myserver:8080/IDMPROV/provisioning/service?test

The following page is displayed:

Figure 20-1 Web Service Test Page

You can also access the SOAP endpoint by going to the Administration tab within the User Application. To do this, you need to select the Application Configuration tab, then select Web Services from the left-navigation menu. After selecting Web Services, pick the Web Service endpoint you want from the list.

WARNING:The test page is disabled by default. Since some of the methods allow data to be updated, the test page presents a potential security vulnerability and should not be allowed in a production environment. For details on enabling the test page, see the instructions provided for the Role Service in Enabling the Test Page.

Entering Arguments for Operations

To see an example of an operation that is particularly useful to invoke from the browser, scroll down to the Miscellaneous section and click getGraph.

NOTE:The Graphviz program must be installed on the computer where the application server and the IDM User Application is running. For more information about Graphviz, see Graphviz.

A page is displayed that allows you to enter the parameters for the getGraph method.

Figure 20-2 Parameters for getGraph Method

The method takes one argument, which is the distinguished name of a provisioning request. Enter the DN, and the underlying workflow is displayed as a JPG file..

Figure 20-3 Output of getGraph

20.2.2 A Java Client for the Provisioning Web Service

This section describes how to develop a simple Java client for the provisioning Web service, which lists all the processes in the workflow system. For complete source code for the client, see Sample Code for the Java Client.

Prerequisites

To develop a Java client you must install a supported Java Developer’s Kit. Also, a client program needs the following JAR files:

  • activation.jar
  • commons-httpclient.jar
  • IDMfw.jar
  • log4j.jar
  • saaj-api.jar
  • wssdk.jar
  • commons-codec-1.3.jar
  • commons-logging.jar
  • jaxrpc-api.jar
  • mail.jar
  • workflow.jar
  • xpp3.jar

Developing a Java Client

Developing a client that accesses a Web service consists of two steps:

  • Get the stub, which is the object that represents the remote service

  • Invoke one or more of the operations available in the remote service

The Java programming model for Web services is very similar to RMI. The first step is to lookup the stub using JNDI:

InitialContext ctx = new InitialContext();
ProvisioningService service = (ProvisioningService) ctx.lookup("xmlrpc:soap:com.novell.soa.af.impl.soap.ProvisioningService");
Provisioning prov = service.getProvisioningPort();

The first line of code creates the initial context for JNDI lookups. The second line looks up the service object, which is a kind of factory that can be used to retrieve the stub for the provisioning Web service. The last line gets the provisioning stub from the service.

Before invoking an operation on the provisioning stub, it is necessary to set some properties, including the credentials used for authentication on the service, as well as the endpoint URL.

Stub stub = (Stub) prov;
// set username and password
stub._setProperty(Stub.USERNAME_PROPERTY, USERNAME);
stub._setProperty(Stub.PASSWORD_PROPERTY, PASSWORD);
// set the endpoint URL
stub._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, url);

These and other stub properties are described in more detail in Frequently Used Stub Constants. Now that we have a fully configured stub, we can invoke the getAllProcesses operation and dump information about each of the processes returned on the console:

// invoke the getAllProcesses method
ProcessArray array = prov.getAllProcesses();
Process[] procs = array.getProcess();
// print process array
System.out.println("list of all processes:");
if (procs != null) {
for (int i = 0; i < procs.length; i++) {
System.out.println(" process with request identifier " +
procs[i].getRequestId());
System.out.println(" initiator = " + procs[i].getInitiator());
System.out.println(" recipient = " + procs[i].getRecipient());
System.out.println(" processId = " + procs[i].getProcessId());
System.out.println(" created = " +
8
9
procs[i].getCreationTime().getTime());
if (null != procs[i].getCompletionTime()) {
System.out.println(" completed = " +
procs[i].getCompletionTime().getTime());
}
System.out.println(" approval status = " +
procs[i].getApprovalStatus());
System.out.println(" process status = " +
procs[i].getProcessStatus());
if (i != procs.length - 1)
System.out.println();
}
}

A method invocation on the stub results in a SOAP message being sent using the HTTP transport to the provisioning Web service. For operations that have arguments, the stub takes care of marshaling those Java objects into XML. The Web service returns a SOAP message, and the stub unmarshals the XML, in this case converting it into a ProcessArray Java object.

Running the Client

The sample ANT build file has a target for running the client (see Sample Ant File). The client needs the JAR files described in Prerequisites to be in the CLASSPATH. You can change the code to have a different default address for the provisioning Web service SOAP endpoint, or simply specify it as a command line argument. For example:

ant -Durl=http://www.company.com:80/IDMProv/provisioning/service run

Frequently Used Stub Constants

The com.novell.soa.ws.portable.Stub class (which is part of WSSDK) supports several properties that can be used to configure a stub instance (for example, to fine-tune aspects of the HTTP communication). The following table lists a small subset of these properties, which are frequently used:

Table 20-2 Provisioning Web Service Stub Constants

Property

Type

Description

ENDPOINT_ADDRESS_PROPERTY

java.lang.String

The URL of the Web service. The URL protocol scheme can be HTTP or HTTPS depending on the requirements of the server. The path portion should be:

/IDMProv/provisioning/service

HTTP_HEADERS

java.util.Map

Additional HTTP headers as String name/value pairs.

HTTP_TIME_OUT

java.lang.Integer

The number of seconds to wait to establish a connection to the host before timing out.

HTTP_MAX_TOTAL_CONNECTIONS

java.lang.Integer

The number of concurrent connections that this client program can establish to all server hosts it accesses. The default limit is 20.

HTTP_MAX_HOST_CONNECTIONS

java.lang.Integer

The number of concurrent connections this client program can establish to an individual server host. The default limit is 2. This value may not exceed that of HTTP_MAX_TOTAL_CONNECTIONS, so if a client requires more than 20 connections to the server, it must also set HTTP_MAX_TOTAL_CONNECTIONS to the desired value.

USERNAME

java.lang.String

The user ID for HTTP authentication.

PASSWORD

java.lang.String

The password for HTTP authentication.

HTTP_PROXY_HOST

java.lang.String

The host DNS name of a proxy. Setting this property requires setting HTTP_PROXY_PORT as well.

HTTP_PROXY_PORT

java.lang.Integer

The port to use on a proxy. Setting this property requires setting HTTP_PROXY_HOST as well.

HTTP_PROXY_AUTH_SCHEME

java.lang.Integer

The authentication scheme (Basic or Digest) to use for a proxy.

HTTP_PROXY_USERNAME

java.lang.String

The user ID for HTTP authentication using a proxy.

HTTP_PROXY_PASSWORD

java.lang.String

The password for HTTP authentication via proxy.

The TCP Tunnel

The TCP Tunnel is a useful tool for looking at the SOAP messages that are exchanged between a client and a server. The ANT build file (see Sample Ant File) has a target for starting the tunnel. Once the tunnel starts you need to enter the port on which the tunnel will listen, and the host/port of the remote Web service. The default settings cause the tunnel to listen on port 9999 and connect to a service running on localhost port 8080. The client program (see Developing a Java Client) uses the first command line parameter to set the ENDPOINT_ADDRESS_PROPERTY. Using the default values, you can run the client using the following command, after starting the tunnel:

ant -Durl=http://localhost:9999/IDMProv/provisioning/service run

Figure 20-4 shows the TCP tunnel with a request SOAP message in the left panel and the message in the right panel.

Figure 20-4 TCP Tunnel

Sample Code for the Java Client

The following is the code for the Java client for listing all processes in the workflow system

package com.novell.examples;
import javax.naming.InitialContext;
import com.novell.soa.af.impl.soap.AdminException;
import com.novell.soa.af.impl.soap.Process;
import com.novell.soa.af.impl.soap.ProcessArray;
import com.novell.soa.af.impl.soap.Provisioning;
import com.novell.soa.af.impl.soap.ProvisioningService;
import com.novell.soa.ws.portable.Stub;
public class Client
{
private static final String USERNAME = "admin";
private static final String PASSWORD = "test";
public static void main(String[] args)
{
try {
String url = args.length > 0 ? args[0] :
"http://localhost:8080/IDMProv/provisioning/service";
listProcesses(url);
} catch (AdminException ex) {
System.out.println("command failed: " + ex.getReason());
} catch (Exception ex) {
ex.printStackTrace();
}
}
private static void listProcesses(String url)
throws Exception
{
// get the stub
InitialContext ctx = new InitialContext();
ProvisioningService service = (ProvisioningService)
ctx.lookup("xmlrpc:soap:com.novell.soa.af.impl.soap.ProvisioningService");
Provisioning prov = service.getProvisioningPort();
Stub stub = (Stub) prov;
// set username and password
stub._setProperty(Stub.USERNAME_PROPERTY, USERNAME);
stub._setProperty(Stub.PASSWORD_PROPERTY, PASSWORD);
// set the endpoint URL
stub._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, url);
// invoke the getAllProcesses method
ProcessArray array = prov.getAllProcesses();
Process[] procs = array.getProcess();
// print process array
System.out.println("list of all processes:");
if (procs != null) {
for (int i = 0; i < procs.length; i++) {
System.out.println(" process with request identifier " +
procs[i].getRequestId());
System.out.println(" initiator = " + procs[i].getInitiator());
System.out.println(" recipient = " + procs[i].getRecipient());
System.out.println(" processId = " + procs[i].getProcessId());
System.out.println(" created = " +
procs[i].getCreationTime().getTime());
if (null != procs[i].getCompletionTime()) {
System.out.println(" completed = " +
procs[i].getCompletionTime().getTime());
}
17
18
System.out.println(" approval status = " +
procs[i].getApprovalStatus());
System.out.println(" process status = " +
procs[i].getProcessStatus());
if (i != procs.length - 1)
System.out.println();
}
}
}
}

20.2.3 Developing a Mono Client

The previous section described how to create a Java client using the Web service toolkit and the pre-compiled stub code included with Identity Manager. This section describes how to develop a client using just the WSDL for the provisioning Web service. This example uses Mono and creates a C# client that changes the default retention time of 120 days for completed workflows to 30.

Prerequisites

To get started, you need to download Mono and install it on your system (see the Mono Project Website). The version of Mono available at the time this document was written did not support complex schema types in which an element has the nillable attribute set to true. Because this construct is used in the provisioning WSDL, you must manually edit the Provisioning.WSDL file and remove the three places where nillable="true" is used.

Generating the Stub

Compared to the Java client developed in Developing a Java Client, there is one additional step required when building the C# client. Since the stub for accessing the Web service SOAP endpoint is not provided, you must generate the stub from the WSDL document. Mono includes a compiler called wsdl that processes the WSDL file and creates the stub. You can download the WSDL file from your User Application server by accessing the following URL:

http://myserver:8080/IDMProv/provisioning/service?wsdl

Replace “myserver” with the name of your server, and “IDMProv” with the name of your User Application war file.

Compile the WSDL file using the following command:

wsdl Provisioning.wsdl

This will generate a C# file called ProvisioningService.cs, which you need to compile into a DLL using the following Mono C# compiler command:

mcs /target:library /r:System.Web.Services.dll ProvisioningService.cs

Compared to the Java client, the resulting ProvisioningService.dll file is the equivalent of workflow.jar, which contains the stub code and supporting classes for accessing the provisioning Web service. The following is the source code for the simple C# client that sets the flow retention time and displays the new value on the console:

using System;
using System.Net;
class provclient {
public static void Main(string [] args) {
// create the provisioning service proxy
ProvisioningService service = new ProvisioningService();
// set the credentials for basic authentication
service.Credentials = new NetworkCredential("admin", "test");
service.PreAuthenticate = true;
// set the value for completed request retention to 30 days
setCompletedProcessTimeoutRequest req = new
setCompletedProcessTimeoutRequest();
11
12
req.arg0 = 30;
service.setCompletedProcessTimeout(req);
// display the new value on the console
getCompletedProcessTimeoutResponse res = service.getCompletedProcessTimeout(new getCompletedProcessTimeoutRequest());
Console.WriteLine(res.result);
}
}

You need to edit the file using the administrator credentials on your deployed Identity Manager system. Compile the client using the following command:

mcs /r:ProvisioningService.dll /r:System.Web provclient.cs

This generates the provclient.exe file.

Running the Client

Use the following command to run the client:

mono provclient.exe

20.2.4 Sample Ant File

The sample Ant file includes useful targets for extracting the necessary JAR files from the Identity Manager installation, compiling and running the Java client, and for launching the TCP Tunnel.

<?xml version="1.0"?>
<project name="client" default="all" basedir=".">
<target name="all" depends="clean, extract, compile"></target>
<!-- main clean target -->
<target name="clean">
<delete quiet="true" dir="classes"/>
<delete quiet="true" dir="lib"/>
</target>
<!-- init sets up the build environment -->
<target name="init">
<mkdir dir="classes"/>
<copy todir="${basedir}/lib">
<fileset dir="${basedir}" includes="log4j.properties"/>
</copy>
<!-- classpath -->
<path id="CLASSPATH">
<pathelement location="${basedir}/classes"/>
<fileset dir="${basedir}/lib" includes="*.jar"/>
</path>
</target>
<!-- extract -->
<target name="extract">
<property name="idm.home" value="/opt/novell/idm3"/>
<property name="jboss.lib" value="${idm.home}/jboss-4.0.3/server/IDMProv/lib"/>
<mkdir dir="lib"/>
<unzip src="${idm.home}/IDMProv.war" dest="${basedir}/lib">
<patternset>
<include name="WEB-INF/lib/commons-codec-1.3.jar"/>
<include name="WEB-INF/lib/commons-httpclient.jar"/>
<include name="WEB-INF/lib/commons-logging.jar"/>
<include name="WEB-INF/lib/jaxrpc-api.jar"/>
<include name="WEB-INF/lib/saaj-api.jar"/>
<include name="WEB-INF/lib/xpp3.jar"/>
<include name="WEB-INF/lib/workflow.jar"/>
<include name="WEB-INF/lib/wssdk.jar"/>
<include name="WEB-INF/lib/IDMfw.jar"/>
</patternset>
</unzip>
<move todir="${basedir}/lib">
<fileset dir="${basedir}/lib/WEB-INF/lib" includes="*.jar"/>
</move>
<delete quiet="true" dir="${basedir}/lib/WEB-INF"/>
<copy todir="${basedir}/lib">
<fileset dir="${jboss.lib}" includes="activation.jar, mail.jar, log4j.jar"/>
</copy>
</target>
18
19
<!-- tunnel -->
<target name="tunnel" depends="init">
<java classname="com.novell.soa.ws.impl.tools.tcptunnel.Tunnel" fork="true"
spawn="true">
<classpath refid="CLASSPATH"/>
</java>
</target>
<!-- compile -->
<target name="compile" depends="init">
<javac srcdir="${basedir}" destdir="classes"
includes="Client.java">
<classpath refid="CLASSPATH"/>
</javac>
</target>
<!-- run -->
<target name="run" depends="init">
<property name="url" value="http://localhost:8080/IDMProv/provisioning/service"/>
<java classname="com.novell.examples.Client" fork="true">
<arg line="${url}"/>
<classpath refid="CLASSPATH"/>
</java>
</target>
</project>

20.2.5 Sample Log4J File

The following log4j file sets the default log level to “error”:

log4j.rootCategory=ERROR, R
log4j.appender.R=org.apache.log4j.ConsoleAppender
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%-5p: %m%n