Monday 18 January 2010

WebService client: automating the generation of all java classes

The problem:
Sometimes when we have to make a webservice client, we have the wsdl definition file and we have to choose some tecnique to generate the java client classes. But after all the code generated automatically by the framework we have an xmlStream to treat with... So finally, we have to implement the model classes of the part of the xml that we're interested in.

To automate this final step we can generate the java classes to unmarshall the xml into java classes that matches data.

In my last project I had the requirement to do this with axis (Axis2) and xmlbeans (XMLBeans). The webService was a Sharepoint service: lists.asmx.

The solution:
Create an xml ant to transform the wsdl to java: wsdl2java-SharepointList.xml. And create another xml ant to transform the xml returned by the webService: buildSharepointListItems.xml

1.-wsdl2java-SharepointList.xml:
<!DOCTYPE project>

<project name="wsdl2java-WebXXXX_Service" default="usage" basedir=".">

<property name="project-name" value="XXXX.ws.XXXXNet"/>
<property name="definicionDelWebService" value="input/webXXXX_Service.wsdl"/>
<property name="packageDestino" value="es.XXXX.ws.XXXXNet"/>
<property file="build.properties"/>

<property name="build" value="build"/>
<property name="src" value="src"/>
<property name="build.classes" value="build/classes" />

<path id="axis.classpath">
<pathelement location="build/classes" />
<fileset dir="${axis.home}/lib">
<include name="**/*.jar" />
</fileset>
<fileset dir="${xmlBeans.home}/lib">
<include name="**/*.jar" />
</fileset>
<pathelement location="${build.classes}" />
</path>

<path id="axis_client.classpath">
<pathelement location="build/classes" />
<fileset dir="${axis.home}">
<include name="**/*.jar" />
</fileset>
<fileset dir="lib">
<include name="*.jar" />
</fileset>
<pathelement location="${build.classes}" />
</path>

<target name="usage" description="Build file usage info (default task)">
<echo message=" " />
<echo message="${project-name} " />
<echo message="-------------------------------------------------------" />
<echo message=" " />
<echo message="Available Targets:" />
<echo message=" Cleaning up:" />
<echo message="  clean             - Delete class files" />
<echo message=" " />
<echo message=" WSDL:" />
<echo message="  wsdl2java         - Generate source from WSDL" />
<echo message=" " />
<echo message=" Compiling:" />
<echo message="  compile           - Compiles the WSDL2Java source code" />
<echo message=" " />
<echo message=" jar final:" />
<echo message="  jar_wsdl          - Generate the jar from WSDL2Java source code" />
</target>

<target name="prepare" >
<mkdir dir="${build.classes}" />
</target>

<target name="clean" >
<delete dir="${build}" />
<delete dir="output" />
</target>

<target name="wsdl2java" depends="clean,prepare">
<java classname="org.apache.axis2.wsdl.WSDL2Java" fork="true">
<classpath refid="axis.classpath"/>          
<arg value="-uri"/>
<arg file="${definicionDelWebService}"/>
<arg value="-o"/>
<arg file="output"/>
<arg value="-d"/>
<arg value="xmlbeans"/>
<arg value="-p"/>
<arg value="${packageDestino}"/>
<arg value="-s"/>
<arg value="-u"/>
</java>

<!-- Move the schema folder to classpath-->
<move todir="${build.classes}">
<fileset dir="output/resources">
<include name="**/*schema*/**/*.class"/>
<include name="**/*schema*/**/*.xsb"/>
</fileset>
</move>

</target>

<target name="compile">
<echo message="Compiling wsdl2 files"/>
<javac
       srcdir="output"
       destdir="${build.classes}"
       deprecation="true"
      source="1.5"
          target="1.5"
    failonerror="true" debug="false">
<classpath refid="axis.classpath"/> 
</javac>
</target>

<target name="jar_wsdl" depends="compile">
<jar jarfile="lib/${project-name}.jar" >
<fileset dir="${build.classes}" />
</jar>
</target>

<target name="all" depends="wsdl2java,jar_wsdl">
<echo message="Generando el jar desde el wsdl"/>
</target>
</project>


2.-buildSharepointListItems.xml:
<!DOCTYPE project>

<project name="buildSharepointListItems" default="usage" basedir=".">

 <property name="project-name" value="torres.ws.shareponint.listItems"/>
 <property name="xmlDeDatosInput" value="input/listItemsRetorn.xml"/>
 <property name="xmlDeDatos" value="listItemsRetorn"/>
 <property file="build.properties"/>

 <property name="build" value="build"/>
 <property name="src" value="src"/>
 <property name="build.classes" value="build/classes" />

 <path id="xmlBean.classpath">
  <pathelement location="build/classes" />
  <fileset dir="${xmlBeans.home}/lib">
   <include name="**/*.jar" />
  </fileset>
  <pathelement location="${build.classes}" />
 </path>

 <target name="usage" description="Build file usage info (default task)">
  <echo message=" " />
  <echo message="${project-name} " />
  <echo message="-------------------------------------------------------" />
  <echo message=" " />
  <echo message="Available Targets:" />
  <echo message=" Cleaning up:" />
  <echo message="  clean             - Delete class files" />
  <echo message=" " />
  <echo message=" returned XML:" />
  <echo message="  xml2xsd               - Generate xsd from xml" />  
  <echo message=" " />
  <echo message=" generate java binding:" />
  <echo message="  xsd2java           - Generate java from xsd" />  
  <echo message=" " />
  <echo message=" Compiling:" />
  <echo message="  compile           - Compiles the xsd2java source code" />
  <echo message=" " />
  <echo message=" jar final:" />
  <echo message="  exportJar               - Generate the from WSDL2Java source code" />
 </target>

 <target name="prepare" >
  <mkdir dir="${build.classes}" />
  <mkdir dir="output" />
  <mkdir dir="src" />
 </target>

 <target name="clean" >
  <delete dir="${build}" />
  <delete dir="output" />
  <delete dir="src" />
 </target>

 <target name="xml2xsd" depends="clean,prepare">
  <java classname="org.apache.xmlbeans.impl.inst2xsd.Inst2Xsd" fork="true">
   <classpath refid="xmlBean.classpath"/>
   <arg value="-design"/>
   <arg value="rd"/>
   <arg value="-outDir"/>
   <arg file="src"/>
   <arg value="-outPrefix"/>
   <arg value="${xmlDeDatos}"/>
   <arg value="${xmlDeDatosInput}"/>
  </java>
 </target>

 <target name="xsd2java">
  <java classname="org.apache.xmlbeans.impl.tool.SchemaCompiler" fork="true">
   <classpath refid="xmlBean.classpath"/>
   <arg value="-d"/>
   <arg file="output"/>
   <arg value="-javasource"/>
   <arg value="1.5"/>
   <arg value="-srconly"/>
   <arg file="src"/>
   <arg value="input/${xmlDeDatos}.xsdconfig"/>
  </java>
 </target>

 <target name="compile" depends="xsd2java">
  <echo message="Compiling files"/>
  <javac
       srcdir="output"
       destdir="${build.classes}"
       deprecation="true"
      source="1.5"
          target="1.5"
    failonerror="true" debug="false">
   <classpath refid="xmlBean.classpath"/>
  </javac>
   <!-- Move the schema folder to classpath-->
       <move todir="${build.classes}">
           <fileset dir="output">
               <include name="**/*schema*/**/*.class"/>
               <include name="**/*schema*/**/*.xsb"/>
           </fileset>
       </move>
 </target>

 <target name="exportJar" depends="compile">
  <jar jarfile="lib/${project-name}.jar" >
   <fileset dir="${build.classes}" />
  </jar>
 </target>

 <target name="all" depends="xml2xsd,exportJar">
  <echo message="Generando el jar desde el wsdl"/>
 </target>
</project>

3. And finally, the java that call the sharepoint service:

HttpTransportProperties.Authenticator auth = new HttpTransportProperties.Authenticator();
auth.setUsername(username);
auth.setPassword(password);
auth.setDomain(domain);
auth.setHost(host);
auth.setRealm(realm);

// Ensure NTLM authentication used
List authPrefs = new ArrayList(1);
authPrefs.add(AuthPolicy.NTLM);
auth.setAuthSchemes(authPrefs);

// Get stub
ListsStub stub = new ListsStub(sharepoinService  + "_vti_bin/lists.asmx");

// Set authenticator
stub._getServiceClient().getOptions().setProperty(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, auth);
// Get request and execute

com.microsoft.schemas.sharepoint.soap.GetListItemsDocument getListItems112 =com.microsoft.schemas.sharepoint.soap.GetListItemsDocument.Factory.newInstance();
GetListItems listaInput = GetListItems.Factory.newInstance();
listaInput.setListName(laguajeList[0]);
getListItems112.setGetListItems(listaInput );

GetListItemsResponseDocument listaResultado = stub.getListItems(getListItems112);
GetListItemsResult result = listaResultado.getGetListItemsResponse().getGetListItemsResult();
String resultString = result.xmlText();
log.debug(resultString);

resultString = resultString.replaceFirst("http://schemas.microsoft.com/sharepoint/soap/", "http://XXXXX/sharepoint/listItems");      
ListitemsDocument root= ListitemsDocument.Factory.parse(resultString);
List lista = root.getListitems().getData().getRowList();