Monday, April 20, 2009

Developing web service client using WebSphere WSDL2Java ant task

Clients for web services can be easily generated/coded any eclipse (or eclipse based) or any other modern IDEs for java development. All we need is a WSDL or URL pointing a WSDL. However, very often we are required to automate the process of generating client side java stubs (artifacts) using the build tool like Apache ant. Most of the web service engines/tools come bundled with ant tasks for generating java artifacts. In the following section we will see how to generate client side stubs using WebSphere WSDL2Java ant task and code a sample java web service client.
The foremost thing we need is the structure of "wsdl2java" ant task to generate the java client stubs. Here is the call to WebSphere wsdl2java ant task to generate client classes:
<wsdl2java url="${local.wsdl}"
output="${src-gen.dir}"
role="client"
container="none"
introspect="false"
genjava="true"
verbose="true">
</wsdl2java>
This is pretty simple. local.wsdl ant property points the the WSDL file (in local file system) of the Web Service for which I need to code the client. I have placed the WSDL under a folder wsdl (wsdl\ServiceA.wsdl) under the current directory (basedir, in ant term) from where I am running the ant command. Another property "src-gen.dir" the directory where the generated java files are placed. I am keeping these file in a separated directory, than the java files I coded to make the actual web service call, just for the convenience of managing/deleting these generated files.
As you are aware, to make wsdl2java task available to ant, we have two options. Either we have to place the jar containing this class in the ant's lib folder so that ant loads the tasks in start up, or explicitly load the task in ant build script it self. I have opted for the second option. Here is the taskdef for loading the wsdl2java task:
<taskdef name="wsdl2java" classname="com.ibm.websphere.ant.tasks.WSDL2Java">
<classpath refid="taskdefs.classpath">
</taskdef>
This is self explanatory. The actual class containing the definition of wsdl2java is "com.ibm.websphere.ant.tasks.WSDL2Java". This class in bundled in the jar "ws_runtime.jar" which is available one of the folders under your WAS/RAD installation folder. In my case it is "${WAS_HOME}\runtimes\bi_v61\deploytool\itp\plugins\com.ibm.websphere.v61_6.1.200\ws_runtime.jar". I refer the top level installation folder using a property "WAS_HOME". I am using WID (WebSphere Integration Developer) tool locally, so my WAS_HOME is the top level installation folder of WID - "C:\Program Files\ibm\WID61". You will need to change this accordingly for the WebSphere development environment like RAD that you are using. So the class path definition for this task goes like:
<path id="taskdefs.classpath">
<pathelement location="${WAS_HOME}\runtimes\bi_v61\deploytool\itp\plugins\com.ibm.websphere.v61_6.1.200\ws_runtime.jar">
</path>
I have added the call to wsdl2java under target "gen". The target definition is:
<target name="gen" depends="init">
<wsdl2java url="${local.wsdl}"
output="${src-gen.dir}"
role="client"
container="none"
introspect="false"
genjava="true"
verbose="true">
</wsdl2java>
</target>
Running this "gen" taget from ant generates all the client java classes we need to make a call to the web service "ServiceA" to the directory "src-gen" in my case!
I have coded a simple client java class for making the call to the Web Service using the generated stubs and placed it under the folder src. So I have two source folders namely src and src-gen with java source files to compile. By the way, following is the simple java code calling the web service from my client java class "com.ibswings.test.Main":
package com.ibswings.test;
import com.ibswings.ws.test.servicea.ServiceAImpl;
import com.ibswings.ws.test.servicea.ServiceAPortServiceLocator;
import com.ibswings.ws.test.servicea.User;

public class Main {

public static void main(String ... args) {

try {
ServiceAImpl proxy = new ServiceAPortServiceLocator().getServiceAPort();
System.out.println("Created User: " + proxy.createUser(new User()).isSuccess());
} catch (Exception exp) {
exp.printStackTrace();
}
}
}
Here is the ant target for compiling the java source files:
<target name="build" depends="gen">
<javac srcdir="${src.dir}"
destdir="${build.dir}"
classpathref="master.classpath"
debug="on">
<src path="${src-gen.dir}">
</javac>
</target>
To compile the java classes we need some jar files, which are again under some of the WebSphere's installation folder. I have added these jars under classpath definition "master.classpath" like:
<path id="master.classpath">
<pathelement path="${build.dir}">
<path refid="taskdefs.classpath">
<pathelement location="${WAS_HOME}\runtimes\base_v61_stub\runtimes\com.ibm.ws.webservices.thinclient_6.1.0.jar">
<pathelement location="${WAS_HOME}\runtimes\base_v61_stub\java\jre\lib\xml.jar">
</path>
In this case, I have given the exact path names of the jar files. However, in your case these jars might be in different directories, depending on the IDE/WebSphere environment you are using. Please search for these jars in your filesystem under WAS_HOME and replace the paths accordingly. Optionally, you can use <fileset> ant type to load the jars like:
<path id="master.classpath">
<pathelement path="${build.dir}">
<path refid="taskdefs.classpath">

<fileset dir="${WAS_HOME}\runtimes">
<include name="**/com.ibm.ws.webservices.thinclient_6.1.0.jar">
<include name="**/xml.jar">
</fileset>

</path>
However, ant might take a while to search these jars and load these, slowing your build process. I would suggest you to find the exact path for jars.
Finally, I have added a target for running my client. Here is the ant script:
<target name="run">
<java classname="com.ibswings.test.Main" classpathref="master.classpath">
</target>
Here is the my complete build script (build.xml):
<project name="webserviceclient" default="build">

<property name="WAS_HOME" value="C:\Program Files\ibm\WID61">
<property name="local.wsdl" value="wsdl\ServiceA.wsdl">
<property name="src-gen.dir" value="src-gen">
<property name="src.dir" value="src">
<property name="build.dir" value="build">

<path id="taskdefs.classpath">
<pathelement location="${WAS_HOME}\runtimes\bi_v61\deploytool\itp\plugins\com.ibm.websphere.v61_6.1.200\ws_runtime.jar">
</path>

<taskdef name="wsdl2java" classname="com.ibm.websphere.ant.tasks.WSDL2Java">
<classpath refid="taskdefs.classpath">
</taskdef>

<path id="master.classpath">
<pathelement path="${build.dir}">
<path refid="taskdefs.classpath">
<pathelement location="${WAS_HOME}\runtimes\base_v61_stub\runtimes\com.ibm.ws.webservices.thinclient_6.1.0.jar">
<pathelement location="${WAS_HOME}\runtimes\base_v61_stub\java\jre\lib\xml.jar">
</path>

<target name="init">
<mkdir dir="${build.dir}">
<mkdir dir="${src-gen.dir}">

<delete includeemptydirs="true">
<fileset dir="${build.dir}" includes="**/*.*">
</delete>

<delete includeemptydirs="true">
<fileset dir="${src-gen.dir}" includes="**/*.*">
</delete>
</target>

<target name="gen" depends="init">
<wsdl2java url="${local.wsdl}"
output="${src-gen.dir}"
role="client"
container="none"
introspect="false"
genjava="true"
verbose="true">
</wsdl2java>
</target>

<target name="build" depends="gen">
<javac srcdir="${src.dir}"
destdir="${build.dir}"
classpathref="master.classpath"
debug="on">
<src path="${src-gen.dir}">
</javac>
</target>

<target name="run">
<java classname="com.ibswings.test.Main" classpathref="master.classpath">
</target>

</project>
One final and most important thing - you cannot run ant targets defined above with the ant executable that you have downloaded and installed from apache website! You need to run these using the "ws_ant" (ws_ant.bat in Windows and ws_ant.sh in UNIX) command line utility that comes bundled with webbphere. This is typically available under "<ws_home>\runtimes\bi_vxx\bin\" folder (C:\Program Files\ibm\WID61\runtimes\bi_v61\bin\ws_ant.bat in my case).
The command run "gen" target defined above, I run following command:
"C:\Program Files\ibm\WID61\runtimes\bi_v61\bin\ws_ant.bat" gen
To build target above generates the (Depends on 'gen') java artifacts and them compiles all java files (including the client Main.java I coded). 'build' target is also the default target of my ant project. The ant command to build is:
"C:\Program Files\ibm\WID61\runtimes\bi_v61\bin\ws_ant.bat" build
or simply
"C:\Program Files\ibm\WID61\runtimes\bi_v61\bin\ws_ant.bat"
That's it.. It's that easy to use WebSphere wsdl2java.
Please note that, to run the client, I need to have the actual webservice "ServiceA" running somwhere (locally, per my Main.java) ... Which I have not mentioned here, since that is sort of out of scope!
I have used wsdl1java in simplest way possible in this example. WSDL2Java has many more options. Please read more about WebSphere ant tasks at WebSphere info center. For example, http://publib.boulder.ibm.com/infocenter/wasinfo/v6r1/index.jsp.