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.
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 identity applications 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 26-1 Web Service Test Page
You can also access the SOAP endpoint by going to the Administration within the identity applications. 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.
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 identity applications 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 26-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 26-3 Output of getGraph
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.
To develop a Java client you must install a supported Java Developer’s Kit. Also, a client program needs the following JAR files:
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()); 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.
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
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 26-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 milliseconds 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 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
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()); } System.out.println(" approval status = " + procs[i].getApprovalStatus()); System.out.println(" process status = " + procs[i].getProcessStatus()); if (i != procs.length - 1) System.out.println(); } } } }
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.
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.
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 identity applications 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(); 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.
Use the following command to run the client:
mono provclient.exe
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/netiq/idm3"/> <property name="tomcat.lib" value="${idm.home}/tomcat-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="${tomcat.lib}" includes="activation.jar, mail.jar, log4j.jar"/> </copy> </target> <!-- 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>
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