[Updated on 2009-06-22] The JSE's native wsimport
tool has been added along with custom binding in a more recent post - JAXB Custom Data Binding.
From the previous post I showed how to create a simple web service using WCF and consume it with a windows console application written in C#.
To have the same web services consumed by a Java client, I needed to make the web service WS-I Basic Profile 1.1 compliant so that it becomes portable cross-platforms. To do this, it is simply a matter of following instructions on MSDN. In my case, I modified Web.Config file: in <services> section I changed the wsHttpBinding into basicHttpBinding and removed the identity tags. The resulting snippet:
IntelliJ IDEA
To generate the Java web service client, I created a Java project in IDEA 7.0.2. Then right-click on the src tree to select New -> Web Service Client, then paste in the wsdl url (http://localhost/PromoService.svc?wsdl). Bingo, IDEA generates all the interface and class files, together with the client test code. I guess it is using JAXB under the hood. Adding the bits into the client test code resulted:
import svdemo.PromoService;
import svdemo.PromoInfo;
public class SvdemoClient {
public static void main(String[] argv) {
svdemo.IPromoService service = (new PromoService()).getBasicHttpBindingIPromoService();
//invoke business method
String keywords = "action";
if(service.hasPromo(keywords)) {
PromoInfo promo = service.getFirstPromo(keywords);
System.out.println("promo:"+promo.getPromoName().getValue()
+","+promo.getPromoDateTime());
} else {
System.out.println("nothing");
}
}
}
Notice how the service is instantiated (code not as nice as in C# version). Also, the PromoInfo class's members data type are of JAXBElement and XMLGregorianCalender..., which are not as nice as native primitive types. But it works - here is the output:
promo:Batman The Dark Knight,2008-07-30T10:00:00
NetBeans 6.0
I created a Java application project in NetBeans. From the project's source branch, right-clicking will bring up the New -> Web Service Client option. Providing the WSDL URI and selecting JAX-WS style will generate the WS client classes. The results are similar to IDEA, the exact same client code (see above) can be used to test the client. However, it is extremely slow so that I thought NetBeans froze. This happens to almost every step onwards. I eventually gave up on NetBeans. Maybe it is time to upgrade.
CXF
Apache CXF is the 2nd generation XFire. By default, it generates JAXB style binding. It came with wsdl2java utility. To generate the client code from wsdl, I used the following commands:
mkdir d:\projects\svdemocxf
wsdl2java -d d:\projects\svdemocxf -client -ant http://localhost/PromoService.svc?wsdl
I then moved all the source files into a src directory. Then build the project.
D:\projects\svdemocxf>ant build
D:\projects\svdemocxf>ant IPromoServiceClient
Buildfile: build.xml
compile:
[javac] Compiling 15 source files to D:\projects\romen\build\classes
IPromoServiceClient:
[java] Invoking hasPromo...
[java] hasPromo.result=false
[java] Invoking getFirstPromo...
[java] getFirstPromo.result=null
[java] Invoking getKeywordsFromMySpace...
[java] getKeywordsFromMySpace.result=
BUILD SUCCESSFUL
Total time: 4 seconds
Note that the environment variable CXF_HOME had to be defined and point to the CXF installation directory for the ant build.xml file to work.
I added the client testing code similar to the one above:
package svdemo;
import org.datacontract.schemas._2004._07.svdemo.PromoInfo;
import org.tempuri.*;
public class Program {
/**
* @param args
*/
public static void main(String[] args) {
IPromoService service=(new PromoService()).getBasicHttpBindingIPromoService();
String keywords="action";
if(service.hasPromo(keywords)) {
PromoInfo promo=service.getFirstPromo(keywords);
System.out.println("promo:"+promo.getPromoName().getValue()
+","+promo.getPromoDateTime());
} else {
System.out.println("nothing");
}
}
}
To run the test, I added the following target to the build.xml file:
Running the RunTest target yields:
D:\projects\svdemocxf>ant RunTest
Buildfile: build.xml
compile:
[javac] Compiling 16 source files to D:\projects\svdemocxf\build\classes
RunTest:
[java] promo:Batman The Dark Knight,2008-07-30T10:00:00
BUILD SUCCESSFUL
Total time: 4 seconds
Axis2
Apache Axis2 (v1.4) comes with the wsdl2java utility which generates java source code from WSDL file. Using the following command:
wsdl2java.bat -o \projects\svdemoAxis -p svdemoClient -uri http://localhost/PromoService.svc?wsdl
This generates the client stub class PromoServiceStub and dozens of inner classes - one for each input/output parameter and data type! The client test code ends up like this:
package svdemoClient;
import java.rmi.RemoteException;
import org.apache.axis2.AxisFault;
import svdemoClient.PromoServiceStub.PromoInfo;
public class Program {
/**
* @param args
*/
public static void main(String[] args) {
try {
PromoServiceStub service=new PromoServiceStub();
String keywords = "action";
PromoServiceStub.HasPromo hasPromoPara=new PromoServiceStub.HasPromo();
hasPromoPara.setKeyword(keywords);
PromoServiceStub.HasPromoResponse hasPromoResp = service.HasPromo(hasPromoPara);
if(hasPromoResp.getHasPromoResult()) {
PromoServiceStub.GetFirstPromo firstPromoPara=new PromoServiceStub.GetFirstPromo();
firstPromoPara.setKeyword(keywords);
PromoServiceStub.GetFirstPromoResponse firstPromoResponse = service.GetFirstPromo(firstPromoPara);
PromoInfo promo = firstPromoResponse.getGetFirstPromoResult();
System.out.println("promo:"+promo.getPromoName()
+","+promo.getPromoDateTime().getTime());
} else {
System.out.println("nothing");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Compiling and running it in Eclipse 3.4 yields:
promo:Batman The Dark Knight,Wed Jul 30 10:00:00 EST 2008
As shown above, the code is very UGLY! For each input parameter and output response, a class has to be instantiated (even when the parameter is simply a string). This not only makes the code long, but also very hard to read. This is the ADB binding used by Axis2 by default. Changing the binding style into xmlbeans or jibx slightly improves the esthetic and readability of the code, but not much. The following test client code snippet is written against XMLBeans binding style from Axis:
String keywords = "action";
HasPromoDocument hasPromoDoc = HasPromoDocument.Factory.newInstance();
hasPromoDoc.getHasPromo().setKeyword(keywords);
HasPromoResponseDocument hasPromoResp;
hasPromoResp = service.HasPromo(hasPromoDoc);
if(hasPromoResp!=null && hasPromoResp.getHasPromoResponse().getHasPromoResult()) {
GetFirstPromoDocument firstPromoDoc=GetFirstPromoDocument.Factory.newInstance();
firstPromoDoc.getGetFirstPromo().setKeyword(keywords);
GetFirstPromoResponseDocument firstPromoResp=
service.GetFirstPromo(firstPromoDoc);
if(firstPromoResp!=null) {
PromoInfo promo=firstPromoResp.getGetFirstPromoResponse().getGetFirstPromoResult();
System.out.println("promo:"+promo.getPromoName()
+","+promo.getPromoDateTime().getTime());
}
}
This
article from DeveloperWorks gives more insights and examples on the different types of Axis2 bindings. As of Axis2 v1.4, it came with a JAXB experimental support. The above is just my feeble attempt on Axis2. However, Axis2's wsdl2java is quite capable of generating esthetic code - Eclipse 3.4 uses Axis under the hood (using JAXB I guess) to generate web service clients.
Eclipse Ganymede JEE Distribution
Eclipse Ganymede (v3.4) has an option to add new Web Service Client. However, it requires you to specify a Server - this is totally wrong, there is absolutely no need to have a server definition to generate WS client code. However, I just played along and defined a Tomcat 6.0 server. It allows me to continue with the WS Client wizard. Then, after I specified the WSDL URI, it gave me the error:
IWAB0503E Unable to update Java build path. Please check your system environment.
Java Model Exception: Java Model Status [Build path contains duplicate entry: 'D:tools/eclipse/plugins/org.apache.axis_1.4.0.v200806030120/lib/axis.jar' for project 'svdemoAxis']
...
Days later, I upgraded one of the Eclipse plugins (which was auto-detected and suggested by Eclipse), then things were better. Retrying generating the web service client resulted in a new WebServiceProject project being created. I then added the testing client:
package svdemoClient;
import java.rmi.RemoteException;
import org.datacontract.schemas._2004._07.svdemo.PromoInfo;
import org.tempuri.*;
public class Program {
/**
* @param args
*/
public static void main(String[] args) {
String keywords="action";
IPromoService service=(new IPromoServiceProxy()).getIPromoService();
try {
if(service.hasPromo(keywords)) {
PromoInfo promo=service.getFirstPromo(keywords);
System.out.println("promo:"+promo.getPromoName()
+","+promo.getPromoDateTime().getTime());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Note that the promotDateTime field generated is of java.util.Calendar type, not XMLCalendar.
Related Posts: