Module:Base Apache CXF Infrastructure/Developers Guide/How To Create And Consume SOAP Web Services
Contents |
Introduction
In this document is described the required steps to implement and consume a SOAP web service using the infrastructure provided by the Base Apache CXF Infrastructure module.
This infrastructre is based on Apache CXF, an open source services framework that helps to build different kind of web services.
![]() | When using the Base Apache CXF Infrastructure in environments before the 3.0PR19Q3 release, it is necessary to remove the wsdl4j-1.5.1.jar from the classpath and include the wsdl4j-1.6.3.jar instead. Starting from 3.0PR19Q3 this is not needed. |
Example Module
The code shown in this guide is extracted from the Base Apache CXF Infrastructure Examples module which is available in this git repository.
Creating a SOAP Web Service
When developing a SOAP service it is possible to follow one of two approaches:
- WSDL First Development: start with a WSDL contract and generate Java objects to implement the service.
- Java First Development: start with a Java object and service enable it using annotations.
The example shown in this guide is based on the Java First Development approach.
SOAP Web Service Components
The first step is to create the components that define the SOAP web service.
Service Endpoint Interface
The definition of the service is done through a Java class called Service Endpoint Interface (SEI). This is the piece of Java code that is shared between a service and the consumers that make requests on it. The methods defined in this class are intended to be mapped to the operations exposed by the service:
@WebService public interface Calculator { @WebMethod public int sum(@WebParam(name = "sum1") @XmlElement(required = true) int sum1, @WebParam(name = "sum2") @XmlElement(required = true) int sum2); @WebMethod public int multiply(@WebParam(name = "mul1") @XmlElement(required = true) int mul1, @WebParam(name = "mul2") @XmlElement(required = true) int mul2); }
Note that the SEI makes use of standard JAX-WS annotations to specify the metadata used to map the SEI to a fully specified service definition:
- @WebService: this is the only mandatory annotation for the SEI.
- @WebMethod: it is placed on the methods in the SEI.
- @WebParam: this is needed just in case we want to name the method parameters. Without this annotation the parameters will be named arg0, arg1,...etc.
Here it is possible to see detailed information about these annotations and the different properties they accept.
And we are also using an annotation of the JAXB-API to complete the definition of the different parameters of the methods:
- @XmlElement: can be used for example to define if a parameter is mandatory (required = true) or not.
Service Implementation Class
The service implementation is a class that will implement two interfaces:
- The SEI which will allow to implement the logic of the service.
- The OBSingleton interface, because a single instance of the service implementation will always be used.
@WebService(endpointInterface = "org.openbravo.base.cxf.examples.Calculator", serviceName = "calculatorService", portName = "calculatorServicePort") public class CalculatorImpl implements Calculator, OBSingleton { @Override public int sum(int sum1, int sum2) { return sum1 + sum2; } @Override public int multiply(int mul1, int mul2) { return mul1 * mul2; } }
In addition to annotating the SEI with the @WebService annotation, the service implementation class is also annotated with the @WebService annotation. When adding the annotation to the service implementation class, the endpointInterface property needs to be specified and it should contain the full class name of the SEI. The rest of the properties are not mandatory.
Publishing the SOAP Web Service
The infrastructure of the Base Apache CXF module provides a mechanism that allows to register and publish automatically the SOAP web services on server startup.
To publish the services, one just need to create a class that implements the SOAPWebServiceRegister interface:
public class SOAPExampleWebServiceRegister implements SOAPWebServiceRegister { @Override public List<SOAPWebService<? extends OBSingleton>> getSOAPWebServices() { return Arrays .asList(SOAPWebService.createSOAPWebService("calculatorService", CalculatorImpl.class)); } }
Implement this interface is as simple as implement the getSOAPWebServices() method returning a list of SOAPWebService instances that represents the services to be published.
The SOAPWebService.createSOAPWebService method can be used to create the different SOAPWebService instances. This method receives a String with the name that will be used to publish the service (it will be part of the web service URL) and the Service Implementation Class.
By default, all the services registered as shown above will be automatically secured using basic HTTP Authentication. This security is implemented based on the interceptor mechanism. To know more about interceptors, please refer here.
In case it is desired to disable the basic authentication, implement a custom authentication or just include new incoming interceptors for the service, then it is required to override the default implementation of the getInterceptors() method:
public class SOAPExampleWebServiceRegister implements SOAPWebServiceRegister { @Override public List<SOAPWebService<? extends OBSingleton>> getSOAPWebServices() { return Arrays .asList(SOAPWebService.createSOAPWebService("calculatorService", CalculatorImpl.class)); } @Override public List<Interceptor<? extends Message>> getInterceptors() { // providing a custom interceptor list return Arrays.asList(new MyCustomInterceptor()); } }
Once we have deployed our SOAPWebServiceRegister, the list of available services can be accessed by default under the following URL:
http://<openbravo_url>/org.openbravo.base.cxf
And following our example, we can access to the WSDL definition generated for our service at:
http://<openbravo_url>/org.openbravo.base.cxf/calculatorService?wsdl
Creating a SOAP Web Service Consumer
In this section we are going to develop a web service consumer (client) for the SOAP web service created in the first part of this guide.
Generating the Java Stub Code
To develop a consumer the first step is to generate the Java stub code from an WSDL contract. The stub code provides the supporting code that is required to invoke operations on the remote service.
The first step is to retrieve the file containing the WSDL definition and store it in a file of our local computer, for example in /tmp/calculator.wsdl. A best practice for ensuring that we produce a portable client is to package this WSDL document with the sources of our client.
Second, to generate the code of the consumer we can make use of the wsdl2java utility, included in the Apache CXF source distribution that can be downloaded from here.
The command to generate the Java stub code would be as follows:
./wsdl2java -keep -wsdlLocation /calculator.wsdl -d /tmp/gen /tmp/calculator.wsdl
Note that we are using the options:
- keep: used to keep the generated .java files (if not used, then just the .class files are kept).
- d: defines the output directory where the generated code will be placed. If not used, the code will be generated in the same folder where the wsdl2java utility is executed.
- wsdlLocation: this is used to specify the location of the WSDL file that we are going to package in our client. This ensures that the generated artifacts always contain the correct location information.
Creating the Consumer
As a result of the code generation done in the previous section we should have an structure for our consumer similar to the following one:
We now need to create the implementation of the consumer that performs the calls to the operations exposed by the service:
public class SOAPClient { public static void main(String[] args) { // Create the service instance CalculatorService service = new CalculatorService(); Calculator calculator = service.getCalculatorServicePort(); // Set the credentials for the basic HTTP authentication BindingProvider bindingProvider = (BindingProvider) calculator; Map<String, Object> requestContext = bindingProvider.getRequestContext(); requestContext.put(BindingProvider.USERNAME_PROPERTY, "user"); requestContext.put(BindingProvider.PASSWORD_PROPERTY, "pass"); // Print the results System.out.println("Sum = " + calculator.sum(1, 2)); System.out.println("Multiply = " + calculator.multiply(1, 2)); } }
The final structure of our client will look like:
SAAJ Implementation
In many cases, the Apache CXF infrastructure can run without a SAAJ implementation. However, some features such as JAX-WS handlers and WS-Security do require a SAAJ implementation so that an implementation must be provided. One of the recommended ways to provide an implementation is to install this module which contains the SAAJ reference implementation.