Friday, July 22, 2016

Creating WSDL/SOAP web services in Spring Boot using CXF, JAX-WS and JAXB

Here's a quick way to use Spring Boot to expose web services via WSDL/SOAP using CXF, JAX-WS and JAXB.

Add the following artifacts into your Spring Boot pom.xml:


    org.apache.cxf
    cxf-rt-frontend-jaxws
    3.1.6



    org.apache.cxf
    cxf-rt-transports-http
    3.1.6




We start off by creating the request and response classes. These classes act as the value objects for the operation.

Request class:

package com.techtots.contracts;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class UserRegisterRequest {
    
    @XmlElement(required = true, nillable = false)
    private String username;

    @XmlElement(required = true, nillable = false)
    private String password;

    @XmlElement(required = true, nillable = false)
    private String email;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

}


Response class:

package com.techtots.contracts;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class UserRegisterResponse {
    @XmlElement(required = true, nillable = false)
    private String status;

    @XmlElement(required = true, nillable = false)
    private String message;

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

Now over to the service interface. We have an interface with one operation:

package com.techtots.services;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlElement;

import com.techtots.contracts.UserRegisterRequest;
import com.techtots.contracts.UserRegisterResponse;

@WebService
public interface UserService {
    
    @WebMethod
    @WebResult(name = "userRegisterResponse")
    public @XmlElement(required = true, nillable = false) UserRegisterResponse registerUser(
            @XmlElement(required = true, nillable = false) 
            @WebParam(name = "userRegisterRequest")
            UserRegisterRequest userRegisterRequest);
}

@XmlElement(required = true, nillable = false) is defined to ensure the request and response object is tagged as required. By default, these are defined as minOccurs=0.

Implementation of this interface below. Nothing fancy. Just ensures that it gets called and returns the proper object:

package com.techtots.services;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.techtots.contracts.UserRegisterRequest;
import com.techtots.contracts.UserRegisterResponse;

public class UserServiceImpl implements UserService {

    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
    
    @Override
    public UserRegisterResponse registerUser(UserRegisterRequest userRegisterRequest) {
            
        log.info(userRegisterRequest.getEmail());
        
        UserRegisterResponse userRegisterResponse = new UserRegisterResponse();
        userRegisterResponse.setMessage("CREATION SUCCESSFUL");
        userRegisterResponse.setStatus("SUCCESS");
        
        return userRegisterResponse;
    }
}

Since we have all the classes defined, we'll need to hook this up into Spring Boot app. We do this by creating a config class:

package com.techtots;

import javax.xml.ws.Endpoint;

import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.techtots.services.UserServiceImpl;

@Configuration
public class WebServiceConfig {
    @Bean
    public ServletRegistrationBean dispatcherSerlvet() {
        return new ServletRegistrationBean(new CXFServlet(), "/services/*");
    }
    
    @Bean(name = Bus.DEFAULT_BUS_ID)
    public SpringBus springBus() {
        return new SpringBus();
    }
    
    @Bean
    public Endpoint userServiceEndpoint() {
        Endpoint ep = new EndpointImpl(springBus(), new UserServiceImpl());
        ep.publish("/UserService");
        
        return ep;
            
    }
}

If you have more than one service end point, create a new function which returns the new endpoint and it will be picked up.

Start up the app and you should be able to view the WSDL via http://localhost:8080/services/UserService?wsdl.

If you would like to list down all services exposed, you can access http://localhost:8080/services. This feature is built into CXF.