Tuesday, February 8, 2011

A simple tutorial about creating SOAP Web Service in netbean 6.9.1 with Metro and use user defined class as parameters for Web service call.

In Java world, as far as I know, there are at least five Web service stacks: Metro, Axis2, Glue, JBossWS and CXF. According to this WS stack comparison, it seems that Axis2 supports most features than others do. However, according to this performance comparison article, it seems that Metro has better performance than Axis2 when WS-Security is used in application. I used Axis before. And I think every thing keeps changing including the performance benchmark. I will just give Metro a try this time. There are many tutorials online. But, most of them just show a simple function (operator) to operate on primary or String type. Obviously, it is not enough for a real project as real project always requires to deliver user defined classes as parameters for SOAP Web Service call.

Below is the steps to create a simple Metro (JAX-WS) based Web service and the service consumer. It is a simple stub Web service. But, it could be a good start for a real project, where user defined classes as parameters are required.

1) Well, let's start it by creating a Web project in Netbeans. By clicking  File -> New Project... -> Java Web -> Web Application, we see screen like below. I name the project as JiaMetroWS and give it package name as jia.blog.ws. Click Finish to close this dialogue window.


2) Select the new created project jiaBlogWS in project list and right click the node. Click New -> Web Service... as shown below.


3) Right click the new created Web service jiaBlogWs and select add operator, we see below screen. We click the "add operation" button to add a operator (function) into the new created Web service jiaBlogWS.
4) After add the operator, we can see the main area of Netbean is like this. Please notice that there are two buttons there, source and Design. I suggest to click source to have a look at the source code with Web service annotation. These annotations are keys.
I added some dummy data in the class. And, I created two helper classes Invoice.java and Item.java as return parameters. Now, we have jiaMetroWS source code like below,

JiaBlogWS.java
package jia.blog.ws;

import java.util.ArrayList;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

/**
 *
 * @author Yiyu Jia
 */
@WebService()
public class jiaBlogWS {

    /**
     * Web service operation
     */
    @WebMethod(operationName = "callSP")
    public Invoice[] callSP(@WebParam(name = "customID")
    final String customID) {
        //creat a dummy invoice data.
        Invoice inv = new Invoice();
        inv.setInvoiceNum(2);
        inv.setName("yiyu jia");
        ArrayList list1 = new ArrayList();
        Item i1 = new Item();
        i1.setItemNum("00011");
        list1.add(i1);
        inv.setItems(list1);
        //crteate another dummy invoice data.
        Invoice inv2 = new Invoice();
        inv2.setInvoiceNum(3);
        inv2.setName("yan zhang");
        ArrayList list2 = new ArrayList();
        Item i2 = new Item();
        i2.setItemNum("00011");
        list2.add(i2);
        inv2.setItems(list2);

        //create a dummy invoice array as return data.
        Invoice[] invs = new Invoice[2];
        invs[0] = inv;
        invs[1] = inv2;
        return invs;
    }
}

Invoice.java
package jia.blog.ws;

import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;

/**
 *
 * @author Yiyu Jia
 */
@XmlAccessorType (XmlAccessType.FIELD)
public class Invoice {

    public int invoiceNum;

    public String name;

    @XmlElement(name = "items")
    List items;

    public int getInvoiceNum() {
        return invoiceNum;
    }

    public void setInvoiceNum(int invoiceNum) {
        this.invoiceNum = invoiceNum;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List getItems() {
        return items;
    }

    public void setItems(List items) {
        this.items = items;
    }

}

Item.java
import javax.xml.bind.annotation.XmlAccessorType;
/**
 *
 * @author Yiyu Jia
 */
@XmlAccessorType (XmlAccessType.FIELD)
public class Item {
    String itemNum;

    public String getItemNum() {
        return itemNum;
    }

    public void setItemNum(String itemNum) {
        this.itemNum = itemNum;
    }
}

Also, in order to illustrate a situation closing to real world app, I created two helper classes, which are used as parameters returned by jiaMetroWS.

Invoice.java
package jia.blog.ws;

import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;

/**
 *
 * @author Yiyu Jia
 */
@XmlAccessorType (XmlAccessType.FIELD)
public class Invoice {

    public int invoiceNum;

    public String name;

    //declaring list of items as List type.
    @XmlElement(name = "items")
    List items;

    public int getInvoiceNum() {
        return invoiceNum;
    }

    public void setInvoiceNum(int invoiceNum) {
        this.invoiceNum = invoiceNum;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List getItems() {
        return items;
    }

    public void setItems(List items) {
        this.items = items;
    }

}

Item.java
package jia.blog.ws;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
/**
 *
 * @author Yiyu Jia
 */
@XmlAccessorType (XmlAccessType.FIELD)
public class Item {
    String itemNum;

    public String getItemNum() {
        return itemNum;
    }

    public void setItemNum(String itemNum) {
        this.itemNum = itemNum;
    }
}

Now, we can test our Web service. Since I use Glassfish 3.0 as my Web container, I can see the test page by right click Web Service node in project explorer and select "Test Web Service". After the testing page has been shown in your Web browser, you can play around by input parameter and get return. Also, we can see the request SOAP message in XML format and the return SOAP message. And, the most useful info is that you can find WSDL in the test page. Then, you can find XSD file in WSDL document.


Once we successfully see the test page, we can move to next step to create an Web service client to test our Web service. If you can not successfully see the test page. I do not know why. But, I can suggest you to uninstall the netbeans and reinstall the latest netbeans. I am using Netbeans 6.9.1. You can try to reinstall netbeans even you think your netbeans has same version as mime.

Creating a simple Web Service client with Metro toolkit is simple too.

1) In netbeans, we create a new standalone java project.

2) right click the new created project and select New --> Web Service Client... . Then, we will see a screen as below,

3) After we input the correct WSDL URL and click Finish, the netbeans will generate lots of stuff for us, including the stub classes for calling jiaMetroWS, which is still running on our local machine now.

4) writing our own simple code to call jiaMetroWS through generated Web service stub classes. The code is as below,

MetroClientTest.java
package jia.blog.ws.client;

import java.util.Iterator;
import java.util.List;

/**
 *
 * @author Yiyu Jia
 */
public class MetroClientTest {

    public static void main(String[] args){
    jia.blog.ws.client.JiaBlogWSService service = new jia.blog.ws.client.JiaBlogWSService();
    jia.blog.ws.client.JiaBlogWS port = service.getJiaBlogWSPort();

    String customId = "100";
    //below invoice should be Invoice 
    List invs = port.callSP(customId);
    //below invoice should be Invoice 
    Iterator it = invs.iterator();

    while( it.hasNext()){
        Invoice inv = it.next();
        System.out.print("Invoice number: "+inv.invoiceNum + " Name: "+inv.getName());
        for(int i=0; i < inv.getItems().size(); i++){
               System.out.println(" item" + i +": "+inv.getItems().get(i).itemNum);
         }
    }
  }
}

The netbeans projects can be downloaded here: jiaMetroWS.zip and jiaMetroWSClient.zip .

19 comments:

  1. Hi, I am interested in yout tutorial, but I could not download those two .zip file.

    ReplyDelete
  2. It seems that the problem is from google doc. You need to login in your google account to download the code. I set it as no requirement for login. It worked before. But it does not work now. Anyway, you can just follow the tutorial to create the netbean project.

    ReplyDelete
  3. Hia Yiyu Jia, thanks for this post, It's cool.

    WS tested ok but in WSClient I'm getting 6 error like this:

    Compiling 17 source files to D:\Sun\PROYECTOS\jiaMetroWsClient\build\classes
    D:\Sun\PROYECTOS\jiaMetroWsClient\build\generated-sources\jax-ws\jia\blog\ws\client\CallSP.java:32: duplicate class: jia.blog.ws.client.CallSP
    public class CallSP {

    Any suggestion is appreciated.

    OriolM

    ReplyDelete
  4. I feel this is because of difference between Netbeans 6.9.1 and Netbeans 7.0? Let me check this weekend and let you know.

    ReplyDelete
  5. Hi OriolM,

    I checked code in Netbeans 7.0 just now. I think the duplicate class error because I copied those automatically generated code from generated code fold to src folder. Then, javac has duplicate java source file to be compiled in one time. It supposed that I should not to do that. I recall I did that because Netbeans 6.9.1 can not found those generated classes. So, I just simply copy the file instead of checking its ANT script. But, my memory could be wrong

    I have update the project file tested under netbean 7.0. You can feel free to download and try it.

    Thanks for comments!

    ReplyDelete
  6. Hi Yiyu Jia, thank you very much by this post and by your reply.

    You are right, I've checked my code and now is running smoothly.

    Regards.

    OriolM.

    ReplyDelete
  7. this is an awesome tutorial!!! thanks a lot! just one question, how could I see the SOAP request and response??? I will like to be able to retrieve this data...thanks in advance!

    PD: by the way, it worked great on NetBeans 7 with Apache Tomcat 7.0.11! I didn't download the example code but I did follow the steps in the tutorial and it worked perfectly!

    ReplyDelete
  8. Thanks for comments!

    Good to know this code works fine in Tomcat 7. I am planning to try those new features introdced in Tomcat 7 too.

    In order to see/monitor SOAP message, you can use TCP monitor, which works like a proxy between client and server. You can search "Apache tcpmon" for it or you maybe can find other similar products.

    ReplyDelete
  9. okay, and again, thanks a lot!!! It worked like a charm! haha

    ReplyDelete
  10. Very good tutorial, magnificient...
    Straight to the point !

    ReplyDelete
  11. Hi Yiyu, I am new to web service developement, just recently I was working on a web service client utilizing jax-ws and java xml digital signature APIs. While going through your sample projects, I don't see any reference made to METRO, maybe a dump question, so how to tell if a web service or web service client is built on METRO ?

    ReplyDelete
  12. Hi Raymond, we use Metro when we use netbeans and click the menu "New -> Web Service" as shown in step 2. Netbeans automaticaly import Metro WS stack into your Web application. If you want to use other WS stack like Axis 2, you need to configure netbeans to use them in other way. I guess you will have better understand if you try Axis too and compare it with the steps shown here.

    ReplyDelete
  13. Thanks Yiyu, one more question, does Metro get installed along with Netbeans by default ?

    ReplyDelete
  14. you are welcome! I downloaded and installed Netbeans All edition on my machine. I do not need to do extra steps to install Metro stack. So, Metro stack get installed along with netbeans by default. I think Metro stack will be installed by default with netbeans Java EE edition too. You can try it.

    ReplyDelete
  15. Thanks Yiju, but I need created a Web Client Service in MobileApplication Midlet and I can´t insert java.util.list Can you help me? Thanks a lot

    ReplyDelete
    Replies
    1. I dont think you can use the stub generated for Java SE for java ME. You may use Vector to replace List in j2me. Since you have WSDL already, you can specify the WSDL in Java ME Web Service Client wizard window for stub for your Java ME app. Hope this will be helpful.

      Delete
  16. Thanks Yiyu,but I don't understand....I have WSDL already, and my operation in web service return a List with 2 or more elements and each element with 3 field , How do I receive this List in Java ME? and How do I use java.util.vector? .... Thanks again Yiyu!!!

    ReplyDelete
  17. Hello Yiyu,

    It's working perfectly but when I am using SOAP with holder class for input and output params ,then it gives an error as wsimport error.Can you please suggest a solution for this problem?Thanks in advance ......

    neetu...

    ReplyDelete
  18. Hello Yiyu,
    thank you for sample. I try using @XmlRootElement annotation for class Invoice, but it's not working. Can you explain how can I use this annotation,

    Thank you

    ReplyDelete