Wednesday, April 24, 2013

Software Engineering: SOAP Web Service with CXF, Spring and JBoss

This article is going to differ from previous posts related to Java like Mastering Java: Boxing vs MutableInteger or programming puzzles like random walks with a left bound. There won't be any mathematics or investigation behind the post but some technical aspects of everyday work. I'm going to describe a simple way of creating SOAP Services using Spring, Apache CXF and JBoss. So If it's interesting to you - go ahead. 


A couple of days ago I needed to expose a functionality of some core distributed service written in Java with a lightweight API. Since there are several type of clients written in different languages I decided to go for SOAP. The good thing that it's really easy to create a SOAP Web Service with CXF and Spring from the scratch. I'm going to share some steps to simplify the process for you. If you got stuck somewhere check the github for an example implementation it might be helpful. 

First of all we need to define endpoints and a number of exposed methods. (there might be several WebService endpoints but for now we are going to have only one). For this purpose create an interface, and annotate it with @WebService. Each method which is going to be exposed must be annotated as WebMethod. There are some optional annotations (like WebResult or WebParam) to tweak an input and output, but I strongly encourage you to read and play with them by yourself.

@WebService
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface ArithmeticService {
    @WebMethod
    @WebResult(name = "OperationResult")
    ArithmeticResponse add(@WebParam(name = "Add") AddRequest request) 
                                          throws ApiValidationException;
}


I'd like to mention that CXF is an implementation of JSR standards (such as JSR-339), so if you'r going to substitute Apache CXF with other compatible implementation - everything should be working just fine. This also means that all the annotations go from javax.jws.* and javax.xml.*

So, next let's define some classes to represent an input and output of our methods. Mark each of them as XmlType, and define their attributes and values.

An input is going to be:

@XmlType (propOrder = {"op1", "op2"})
public class AddRequest {
    private Integer op2;
    private Integer op1;

    @XmlAttribute(name = "operand2")
    public Integer getOp2() {
        return op2;
    }

    public void setOp2(Integer op2) {
        this.op2 = op2;
    }

    @XmlAttribute(name = "operand1")
    public Integer getOp1() {
        return op1;
    }

    public void setOp1(Integer op1) {
        this.op1 = op1;
    }
}

And an output:

@XmlType
public class ArithmeticResponse {
    private Integer result;
    
    public ArithmeticResponse() {
    }

    public ArithmeticResponse(Integer result) {
        this.result = result;
    }

    @XmlElement(name = "Result")
    //@XmlValue also can be used. If a class has @XmlElement property, it cannot have @XmlValue property.
    public Integer getResult() {
        return result;
    }

    public void setResult(Integer result) {
        this.result = result;
    }
}

I'd like to point out that some of attributes are defined with XmlAttribute and one with XmlElement. It's just for purpose of showing how to define Elements and Attributes.
The AddRequest class specifies an order of attributes in generated xsd file with propOrder property.

Next we want to implement our service. Let's define an implementer, it should be annotated with WebService and the name of exposing Soap service as a parameter of annotation.

@WebService(endpointInterface = "com.vvlasov.samples.jaxws.api.v1.ArithmeticService")
public class ArithmeticServiceImpl implements ArithmeticService {
    @Autowired
    private Validator validator;

    @Override
    public ArithmeticResponse add(@WebParam(name = "Add") AddRequest request) throws ApiValidationException {
        //consider using the JSR 303 to check requests for null
        validator.validate(request);
        return new ArithmeticResponse(request.getOp1() + request.getOp2());
    }
}

Validation of user input

What we gonna do is to autowire a bean to validate user input (it implements Validator interface) just to demonstrate the Spring IOC is working. In real life I really recommend to use JSR303 to validate users input (to be precise hibernate validators makes it really easy and straightforward). But for today's purpose the  implementation of SimpleValidator is just fine.

Throwing Exceptions from Soap with CXF.

Throwing Exceptions is pretty easy: First define an exception. Annotate it with WebFault
Second, add the exceptions signature to methods of Web Service interface.

@WebFault
public class ApiValidationException extends RuntimeException {
}


When an exception happens you get the following response.

    soap:Server
    Operator 2 is null.
    
        
    

Combining it all together and deploying to JBoss


Let's define a web.xml where we put bindings and a spring configs.We don't define SOAP Endpoint in the same bean config for the purpose of testing.

cxf-api-beans.xml

    

    
    

    
    

    


cxf-api-exposure-beans.xml


    

    
    


web.xml


    
        contextConfigLocation
        classpath:/cxf-api-exposure-beans.xml
    

    
        org.springframework.web.context.ContextLoaderListener
    

    
        CXFServlet
        org.apache.cxf.transport.servlet.CXFServlet
        1
    
    
        CXFServlet
        /*
    



When everything is configured the war file can be packaged and deployed to JBoss.

Accessing the API

When WAR is deployed go there http://localhost:8080/jaxws-sample/arithmetic?wsdl and if everything is fine you'r able to see WSDL definition of CXF Service.

Let's try to play with Web Service a little.
  • Download and install SoapUI, open the SoapUI and create a new project. 
  • Then copy right-click the project in a navigation tree.
  • Select Add WSDL and copy-paste a link to wsdl (the one we mentioned above)
  • Press Ok
  • You should get the WSDL imported and some requests templates created (refer the screenshot)
  • Doble-click on request, replace attributes with actual values and press 'Submit' button
Request:

   
   
      
   

Response:

   
      
         15
      
   

If you get to this point - well done :) You now able to create API using Apache CXF, link components with Spring IOC, deploy to JBoss and test it with SoapUI.

Since your SOAP Service can be rich and have plenty of methods I strongly recommend to test the functions using some unit testing framework. I personally prefer JUnit. Please, check the code of ArithmeticServiceTest, it uses Spring4JUnitClassRunner to initialize test context.


Thanks for reading. Hope you find the article helpful.

P.S. If you are interested in improving your skills in Enterprise development you  might want to know how to use Map/Reduce with Grails and MongoDB. Enjoy.

No comments:

Post a Comment