Create JAX-WS Service in 5 Minutes (Tutorial)

April 19th, 2007

This is a brief tutorial describing how to create a Web service using WSDL and annotations. The approach presented here allows you to design and implement your WSDL/Schema and Java classes independently without having to generate anything. You can then use annotations to map Java classes to appropriate WSDL and Schema elements.
Since JAXB and JAX-WS have sensible defaults, the number of annotations can be kept to the minimum.

In my opinion, it is always best to develop WSDL and schemas by hand to ensure that the service contract is appropriately defined and also that the schema can be re-used (by other services) and extended if necessary. I do not recommend using annotations for automatically producing WSDL and schemas at runtime as this leads to simplistic schemas and WSDLs.

Generating classes from WSDL/schema sometimes makes sense, say in situations when you have to use a pre-defined schema. However, as with any code generation, it creates a maitainance problem especially if there is a need to put behavior into generated classes. So it is desirable to be able to evolve object model and service implementation classes independently from WSDL/schemas. You can do it quite easily by following the steps below.

Even though I use the word "create" when describing some of the steps, the same approach will also work for updates or refactoring caused by changes in service requirements.

The steps are the following:

  • Design your service interface. In our case the service has a single "add" operation that accepts a "person" document, adds it to a database and returns back the status:
    public String add( Person person )
  • Design your data/object model. In our case Person class has three fields (ssn, firstName, lastName) and the list of addresses. Real world examples will certainly be more complex, but this is good enough for our purposes.
  • Define XML schema based on your object model. Use strict datatypes where possible. For example, we use xsd:token instead of xsd:string since "token" does not allow carriage return, tabs, and leading spaces:
  •     <xsd:element name="person">
            <xsd:complexType >
                <xsd:sequence>
                    <xsd:element name="ssn" type="xsd:token" 
                            minOccurs="1" maxOccurs="1"/>
                    <xsd:element name="firstName" type="xsd:token" 
                            minOccurs="1" maxOccurs="1"/>
                    <xsd:element name="lastName" type="xsd:token" 
                            minOccurs="1" maxOccurs="1" />
                    <xsd:element name="address" type="Address" 
                            minOccurs="1" maxOccurs="unbounded"/> 
                </xsd:sequence>
            </xsd:complexType>
        </xsd:element>
      
    Complete schema file

  • Create an XML file for your schema so you can test the schema. You can generate an XML instance document automatically from Eclipse (assuming you're using Eclipse Web Tool Platform) by right-clicking on the schema in navigator and selecting "generate". Test the schema by changing data and making sure that you're getting correct validation errors.
  • Sample XML file

  • Create WSDL. Our WSDL imports the schema that we just created. Note that the WSDL target namespace is different from the schema namespace since the same schema can be reused by different WSDLs:
  •     <wsdl:definitions 
              targetNamespace="http://personservice" 
              xmlns="http://personservice" 
              xmlns:pers="http://person"
              xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
              xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
              xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" 
              xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      
          <wsdl:types>
                <xsd:import namespace="http://person" 
                        schemaLocation="person.xsd" />
                <!-- Return value -->
                <xsd:element name="status" type="xsd:string" />
          </wsdl:types>
          ...
      
    Complete WSDL file

  • Create your object model classes and annotate them with appropriate JAXB annotations:
  •     // By default JAXB uses getters/setters for marshaling/unmarshaling
        // Using fields access allows us not to define getters/setters for 
        // all mapped instance variables.
        @XmlAccessorType(XmlAccessType.FIELD)
        // We need to provide the namespace, otherwise it defaults to
        // the package name
        @XmlRootElement(namespace="http://person")
        public class Person {
        
            // we only need annotations for variables that don't match 
            // element names
            private String ssn;
            private String firstName;
            private String lastName;
            
            @XmlElement(name = "address")
            protected List<Address> addresses;
          ...
      
    Person class
    Address class

  • Create ObjectFactory class. This class is required by JAXB and it should contain factory methods for all JAXB-mapped classes. ObjectFactory must resides in the same package with object model classes.
  •     @XmlRegistry
        public class ObjectFactory {
        
            public Person createPerson() {
                return new Person();
            }
            
            public Address createAddress() {
                return new Address();
            }
        }
      
    ObjectFactory class

  • Create Web Service implementation class. Note that with JAX-WS Service Endpoint Interface (SEI) is optional, so all you need is an implementation class:
  •       @WebService(
                  // all values must match corresponding attributes 
                  // and elements of the WSDL file
              
                  // Name of the port type in WSDL
                  name="PersonService",
                  // Target namespace of WSDL, could be 
                  // different from the schema namespace
                  targetNamespace="http://personservice",
                  serviceName="PersonServicePorts",
                  portName="PersonService",
                  // the file must be available to Web container
                  wsdlLocation="WEB-INF/wsdl/PersonService.wsdl"
          )
          
          /*
           * We'are using "bare" style since "add" operation 
           * takes only one argument, person document. Therefore, there 
           * is no need to "wrap" it by nesting it inside operation element.
           * Note that "wrapped" style is the default.
           */
          @SOAPBinding(parameterStyle=SOAPBinding.ParameterStyle.BARE)
          
          public class PersonService {
              
              @WebResult(name = "status")
              public String add( Person person ) {
                  
                  System.out.println("Adding person :"+person );
                  return "Added "+person.getFullName();
              }
          }
      
    PersonService class

  • Deploy your Web service to your application server or Web services container. Refer to your application server documentation for details.
  • Test the service. The easiest way to test the service is to use open source soapUI tool. You can install it as Eclipse plugin (there are also plugins for NetBeans and IDEA) following these instructions. In Eclipse, open SoapUI view, right click and create a project from your WSDL file. SoapUI will automatically create a sample SOAP request which you can update with your data. You can run it right away. Later, you can create a repeatable test suite with parametirized data using SoapUI properties and assertions:


SoapUI Project (you can import it into SoapUI).

You can also download all files in a zip file.

12 comments to “Create JAX-WS Service in 5 Minutes (Tutorial)”:

  1. Thanks for this excellent tutorial. Link to the soapUI tool is broken. I don’t know whether it would be useful for others if you note that soapui eclipse plugin doesn’t work well on Linux (it at least doesn’t work on my desktop, 2.6.20-1.2952.fc6).

    Posted by Mahmut Uludag
  2. In the PersonService.java file values of the serviceName and portName attributes were wrong they need to be changed with each other’s value. Without this fix you may get the following error.

    WSSERVLET11: failed to parse runtime descriptor: java.lang.IllegalArgumentException: getNamespaceURI(String prefix) is called with a null prefix.

    Posted by Mahmut Uludag
  3. Mahmut:

    Thanks for pointing out the broken link. I certainly agree that SoapUI Eclipse plugin is rough on edges, I was not aware of the Linux problem.

    Not sure about your specific error, I tested the example with WAS 6.1 with WS feature pack which uses Axis2 under the covers. It looks like you’re using JAX-WS RI, I can’t really tell you what the issue might be.

    Best regards,
    Alexander

    Posted by Alexander Ananiev
  4. I was wrong when suggesting there is an error in the serviceName and portName attributes, as the naming was intentional as explained in the “WSDL Naming Conventions” entry in this blog.

    The null prefix problem I mentioned above disappears if I use an extended namespace such as http://personservice.myarch.com instead of the existing one which is http://personservice. It is strange that if I remove the namespace annotations in the java files jaxws selects the namespace as http://personservice and can use it without the null prefix problem.

    Posted by Mahmut Uludag
  5. Hi Alexander,

    I tried your tutorial and get the following error from my Tomcat 5.5.9.

    SCHWERWIEGEND: WSSERVLET11: failed to parse runtime descriptor: java.lang.NullPointerException
    java.lang.NullPointerException
    at com.sun.xml.stream.xerces.util.SymbolTable.hash(SymbolTable.java:222)
    at com.sun.xml.stream.xerces.util.SymbolTable.addSymbol(SymbolTable.java:143)
    at com.sun.xml.stream.XMLReaderImpl.getNamespaceURI(XMLReaderImpl.java:1238)
    at com.sun.xml.ws.streaming.TidyXMLStreamReader.getNamespaceURI(TidyXMLStreamReader.java:204)
    at com.sun.xml.ws.wsdl.parser.ParserUtil.getQName(ParserUtil.java:54)
    at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.parsePortTypeOperationInput(RuntimeWSDLParser.java:509)
    at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.parsePortTypeOperation(RuntimeWSDLParser.java:498)
    at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.parsePortType(RuntimeWSDLParser.java:474)
    at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.parseWSDL(RuntimeWSDLParser.java:213)
    at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:66)
    at com.sun.xml.ws.server.RuntimeEndpointInfo.createSEIModel(RuntimeEndpointInfo.java:171)
    at com.sun.xml.ws.server.RuntimeEndpointInfo.init(RuntimeEndpointInfo.java:315)
    at com.sun.xml.ws.transport.http.servlet.WSServletContextListener.createModelAndMetadata(WSServletContextListener.java:197)
    at com.sun.xml.ws.transport.http.servlet.WSServletContextListener.contextInitialized(WSServletContextListener.java:117)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3669)
    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4104)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:759)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:739)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:524)
    at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:589)
    at org.apache.catalina.startup.HostConfig.deployDescriptors(HostConfig.java:536)
    at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:471)
    at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1102)
    at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:311)
    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:119)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1020)
    at org.apache.catalina.core.StandardHost.start(StandardHost.java:718)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1012)
    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:442)
    at org.apache.catalina.core.StandardService.start(StandardService.java:450)
    at org.apache.catalina.core.StandardServer.start(StandardServer.java:683)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:537)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:271)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:409)

    Do you have any Idea, where this error resides ? Where should I start searching ?

    Thanks and regards,
    Sebastian

    Posted by Sebastian
  6. Hi Sebestian,

    You can find working version of this example for jaxws on the following address.

    http://www.ebi.ac.uk/~uludag/tutorial-myarch.com/

    Basically I commented out the namespace definition in the Person and PersonService classes and let jaxws select the namesapce. It is strange that this example works for me even without the ObjectFactory class. If you want to download my eclipse project, I enclosed it in the following archive.

    http://www.ebi.ac.uk/~uludag/tutorial-myarch.com.zip

    Posted by Mahmut Uludag
  7. Hi all..

    Ole from the soapUI team here.. please let us know if you still have problems with the soapui-eclipse-plugin on Linux so we can get this sorted out.. either mail me directly or (even better) post on our sourceforge forums..

    thanks in advance!

    regards,

    /Ole
    eviware.com

    Posted by Ole Matzura
  8. […] Create JAX-WS Service in 5 Minutes […]

    Posted by Brahmasta » Membangun Java Web Service
  9. Hi Alexander,
    Based on your example, I tried writing my client but I get the following error. Any idea why is this?

    Exception in thread “main” java.lang.NullPointerException
    at com.sun.xml.stream.xerces.util.SymbolTable.hash(SymbolTable.java:222)
    at com.sun.xml.stream.xerces.util.SymbolTable.addSymbol(SymbolTable.java:143)
    at com.sun.xml.stream.XMLReaderImpl.getNamespaceURI(XMLReaderImpl.java:1238)
    at javax.xml.stream.util.StreamReaderDelegate.getNamespaceURI(StreamReaderDelegate.java:94)
    at com.sun.xml.ws.wsdl.parser.ParserUtil.getQName(ParserUtil.java:66)
    at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.parseMessage(RuntimeWSDLParser.java:773)
    at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.parseWSDL(RuntimeWSDLParser.java:304)
    at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.parseWSDL(RuntimeWSDLParser.java:262)
    at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:133)
    at com.sun.xml.ws.client.WSServiceDelegate.parseWSDL(WSServiceDelegate.java:222)
    at com.sun.xml.ws.client.WSServiceDelegate.(WSServiceDelegate.java:184)
    at com.sun.xml.ws.client.WSServiceDelegate.(WSServiceDelegate.java:159)
    at com.sun.xml.ws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:82)
    at javax.xml.ws.Service.(Service.java:56)

    Posted by Sudheendra
  10. Hi Sudheendra,

    Please see the comment from Mahmut Uludag in this thread, it has something to do with how jaxws interprets namespaces.

    Regards,

    Posted by Alexander Ananiev
  11. I’m the beginner in studying JAX-WS, please show me more detail about how to create an example
    In this tutorial, you only give me code but you don’t tell me where to put that code in
    And tell me what software at least need to be used.
    I read some other material on Internet, each page give me different softwares to install
    As I understand that we need to install JAX-WS 2.0 to create WS on server and NetBean to create client side
    Please guide me step by step
    Thank you for your paying time
    ———–
    CongPhuoc-Phan

    Posted by CongPhuoc
  12. I’d be really helpful if you could take it one step further and show how to populate the objects with data from a database or using Hibernate. I know I’m pretty confused on how to make all these different technologies work together in a solid application architecture.

    Posted by Michael

Post a comment