SOAP Web services on WildFly made simple

JAX-WS simplifies the development model for a web service endpoint a great deal. In short, an endpoint implementation bean is annotated with JAX-WS annotations and deployed to the server. The server automatically generates and publishes the abstract contract (i.e. wsdl+schema) for client consumption. All marshalling/unmarshalling is delegated to JAXB.

You can follow two approaches for developing your SOAP web services:

  • Top-down: This approach, also known as contract first, starts with the contract (the WSDL) by defining operations, messages, and so forth. When both the consumer and provider agree on the contract, you can then implement the Java classes based on that contract.
  • Bottom-up: This approach, also known as code first, mandates that the WSDL file has to be generated by the programming interfaces. It is simpler to use for developers that do not have a deep knowledge of the WSDL syntax and it’s the easiest choice if you want to turn your Java classes or EJB into web services.

We will use the code-first approach, that is, we will start by creating a Java class and convert this into a Web service component. The Java class in turn can be either a simple POJO or an EJB component. We will start from a simple POJO class going through the following steps:

  • Create a Service Endpoint Interface (SEI) and define a business method to be used with the web service.
  • Create the implementation class and annotate it as a web service.

Coding the Service Interface

The Service Endpoint Interface is just an abstract interface which outlines the business methods which are available in our Web service.

@WebService
public interface AccountWSItf {
        public void newAccount( String name);

        @WebMethod
        public void withdraw(String name, long amount) throws RuntimeException;

        @WebMethod
        public void deposit(String name, long amount);

        @WebMethod
        public Account findAccountByName(String name);
}

It is noteworthy to observe the @WebService annotation. This annotation is really all it takes to leverage a Web service; when adding this annotation the XML Web Services runtime will generate all the necessary plumbing for transforming a Java method invocation into an XML over HTTP call.

The @WebService annotation also comes with attributes that let you completely define a Web service in terms of contract exposed via the WSDL file. For the moment we will ignore these attributes and proceed with our development.

The other annotation included is javax.jws.WebMethod which is optional and provides the operation name and the action elements that are used to customize the attribute names of the operation in the WSDL document. For the moment, we will rely on the default values of it.

The @WebMethod can be used also to restrict which methods of the Web service are visible to your clients. If it is not included, all Web service methods are available to Web service clients. On the other hand, if any method is annotated with @WebMethod, only those methods will be available.

Our Web service interface references the Account class that is used as persistence layer to store the account attributes:

@Entity
@XmlRootElement
public class Account implements Serializable{

        @Id
        @GeneratedValue
        private Long id;

        String name;
        long amount;

        // Getters/Setters omitted for brevity
}

As you can see, we have added a @XmlRootElement annotation to the Account class. The @XmlRootElement is part of the Java Architecture for XML Binding (JAXB) annotation library. JAXB provides a convenient way to map XML schema to a representation in Java code. In practice, JAXB shields the conversion of XML schema messages in SOAP messages to Java code without having the developers know about XML and SOAP parsing. Therefore, the @XmlRootElement annotation associated with the Account class map the Account class to the XML Root element.

Developing the Implementation class

Having defined our contract, let’s check out the implementation class which is a wrapper to the AccountManager EJB:

@WebService
@SOAPBinding(style= SOAPBinding.Style.RPC)
public class AccountWS implements AccountWSItf{
        @Inject
        AccountManager ejb;

        public void newAccount(@WebParam(name = "name") String name) {
                ejb.createAccount(name);
        }
        public void withdraw(@WebParam(name = "name") String name,
                        @WebParam(name = "amount") long amount) throws RuntimeException {
                ejb.withdraw(name, amount);
        }
        public void deposit(@WebParam(name = "name") String name,
                        @WebParam(name = "amount") long amount) {
                ejb.deposit(name, amount);
        }
       @WebResult(name = "BankAccount")
        public Account findAccountByName(String name) {
                return ejb.findAccount(name);
        }
}

Our service implementation class, named AccountWS, contains a few annotations, which are specific of XML Web Services. The @SOAPBinding annotation included in the class determines how the Web service is bound to the SOAP messaging protocol. There are two programming styles for SOAP binding:

  • RPC: The SOAP message contains the parameters and the return values. The <soap:Body> carries an element with the name of the method or remote procedure being invoked. This element in turn contains an element for each parameter of that procedure.
  • Document-style web service: The SOAP Message has no restrictions on how the SOAP body must be constructed. It allows you to include whatever XML data you want and to include as well a schema for this XML.

Therefore, the Document style is probably more flexible, but the effort for implementing the Web service and clients may be slightly larger.

Besides this, the implementation class contains two annotations: The @javax.jws.WebParam annotation is used to specify the parameter’s name that needs to be exhibited in the WSDL.

You can even define the param type which can be IN, OUT, or INOUT (both) and determines how the parameter is flowing (default is IN).

The other annotation, @javax.jws.WebResult, controls the generated name of the message returned value in the WSDL. By default, the name of the returned value in the WSDL is set to return. By using the @WebResult annotation you can be more specific and have a more expressive contract.

Deploying the Web service

In order to compile and test the example, as usual, you can include the umbrella artifact jakarta.jakartaee-api, otherwise the Web Service dependencies to resolve are the following ones:

<dependency>
    <groupId>org.jboss.spec.javax.xml.ws</groupId>
    <artifactId>jboss-jaxws-api_2.3_spec</artifactId>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>javax.jws</groupId>
    <artifactId>jsr181-api</artifactId>
    <scope>provided</scope>
</dependency>

Once deployed, your server logs will produce the following output:

Adding service endpoint metadata: id=com.itbuzzpress.jaxws.ws.AccountWS
address=http://localhost:8080/ee-ws-basic/AccountWS
 implementor=com.itbuzzpress.jaxws.ws.AccountWS
 serviceName={http://ws.jaxws.itbuzzpress.com/}AccountWSService
 portName={http://ws.jaxws.itbuzzpress.com/}AccountWSPort
 annotationWsdlLocation=null
 wsdlLocationOverride=null
 mtomEnabled=false
Creating Service {http://ws.jaxws.itbuzzpress.com/}AccountWSService from class com.itbuzzpress.jaxws.ws.AccountWSItf
Setting the server's publish address to be http://localhost:8080/ee-ws-basic/AccountWS

Creating a WS Test client

Once that the Web service is published, you can access the generated WSDL at URL http://localhost:8080/ee-ws-basic/AccountWS?wsdl

From the WSDL file, we can run a simple JUnit test case that executes the three basic use cases (newAccount, deposit, withdraw). So add to your src/test/java folder a class named TestWS, which contains the following:

public class TestWS {
   @Test
   public void testWS() {
   String name ="John Smith";
        try {
       URL url = new URL("http://localhost:8080/ee-ws-basic/AccountWS?wsdl");
                QName qname = new QName("http://ws.jaxws.itbuzzpress.com/",
                                        "AccountWSService");
                Service service = Service.create(url, qname);
                AccountWSItf ws = service.getPort(AccountWSItf.class);
                ws.newAccount(name);
                System.out.println("Created account name " +name);
                ws.deposit(name, 1000);
                System.out.println("Deposit $ 1000 ");
                ws.withdraw(name, 500);
                System.out.println("Withdraw $ 500 ");
                Account account = ws.findAccountByName(name);
                assertNotNull(account);
                long money = account.getAmount();
                assertEquals(500l, money);
                System.out.println("Account balance is " +account.getAmount());                }
       catch (Exception e) {         System.out.println("Exception: "+ e);                 }
   }
}

Within the testWS method, the service.getPort call returns the client proxy to the SOAP

Web service where you can then invoke the methods for creating, depositing and withdrawing money from the account.

The source code for this tutorial is available here:

https://github.com/fmarchioni/practical-enterprise-development/tree/master/code/jaxws/ee-ws-basic

Fetch and Parse the Web service in one line

To finish this tutorial, we will add a quick hack. Here is how to fetch and parse an XML web service in just one line of code using javax.xml.parsers.DocumentBuilderFactory:

Document parse = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new URL("http://localhost:8080/ee-ws-basic/AccountWS").openStream());