Are you proclaimed as millennial Java developer who play with the newest lambda expressions, working on micro services architecture, experienced in building REST web services and even you know about Hypermedia as the Engine of Application State (HATEOAS). Then, the nightmare is come. You need to integrate your system with 0ld third-party system, and of course they don’t have fancy things like REST. They offer you Simple Object Access Protocol (SOAP) to communicate with their system.

Yeah, sometimes your application is not only consumes by human but a system or to be precise a machine. Integrate between system is already common, for an old school programmer they call it Enterprise Application Integration (EAI), moving into 20th century you will be heard the term Service Oriented Architecture (SOA), and the funny things is, now as millennial developer we called it Microservices.

Okay, there is some method for each application to communicate. Communication method that still exist until now was REST and SOAP, but there is still many method such as Remote Method Invocation (RMI), Common Object Request Broker Architecture (CORBA), and XML-RPC. Ok, I admit even I don’t know the last three method. I, like you, mostly working with REST and just recently I need to working to integrate my system using SOAP.

The best part being Java developer is you could easily build SOAP Web Service. Standardized in Java Specifications Request (JSR) 224 or more known as JAX-WS. You will be able build client-server SOAP web service with only Java EE without any external dependencies and no, this won’t be complicated tutorial since you will use Java EE 7. Since Java EE 5 everything become easier you don’t need to write cumbersome xml configuration or write boilerplate code and in Java EE 7 everything is just convention over configuration. This will required JDK version 8, Apache Maven, Payara as application server and Javaee7 Essentials Archetype.

SOAP Server Side

You couldn’t play with SOAP web service if you don’t have the producer or to be precise the server side, right? So, the first steps is you need to build your own SOAP web service. Of course this is for the sake of learning, in real world you just need to communicate with existing service. But, hey who knows maybe you will experienced to build your own SOAP web service

The service is pretty simple, it will receive request with customer credentials such as username and password. To be able passed the authentication the username must be swhp (yeah because it was me and no the password is not important just the username.

@Stateless
public class AccountManagement {

    // hard-coded the correct username
    private static final String CORRECT_USERNAME = "swhp";

    public Message validateAccount(Account account) {
        Status status = null;
        if(account.getUsername().equalsIgnoreCase(CORRECT_USERNAME)) {
            status = new Status("99", "SUCCESS");
        } else {
            status = new Status("00", "FAILED");
        }
        // generate random request number with java.util.Random
        return new Message(new Random().nextLong(), status);
    }
}

Ok, now you already know what is the business process. Let’s move to write the entity class for Account. Since you want more readable and explanatory response you could write Status entity plus with the code and description about the response.

@XmlRootElement(name = "AccountWs", namespace = "http://account.swhp.id")
@XmlAccessorType(XmlAccessType.FIELD)
public class Account {

    private Long id;
    private String username;
    private String password;

    public Account() {
    }

    public Account(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    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;
    }

    @Override
    public String toString() {
        return "Account{"
                + "id=" + id
                + ", username=" + username
                + ", password=" + password + '}';
    }
}
@XmlRootElement(name = "messageWs", namespace = "http://account.swhp.id")
@XmlAccessorType(XmlAccessType.FIELD)
public class Message {

    @XmlElement(name = "request_number")
    private Long requestNumber;
    @XmlElement(name = "status")
    private Status status;

    public Message() {
    }

    public Message(Long requestNumber, Status status) {
        this.requestNumber = requestNumber;
        this.status = status;
    }
}
@XmlType(name = "statusWs", namespace = "http://account.swhp.id")
@XmlAccessorType(XmlAccessType.FIELD)
public class Status {

    @XmlElement(name = "status_code")
    private String statusCode;
    @XmlElement(name = "status_description")
    private String statusDescription;

    public Status() {
    }

    public Status(String statusCode, String statusDescription) {
        this.statusCode = statusCode;
        this.statusDescription = statusDescription;
    }
}

Ok, you almost done. Now, you need to build class to become the endpoint for your SOAP web service.

@WebService(name = "AccountService", serviceName = "AccountService",
        portName = "AccountServicePort",
        targetNamespace = "http://account.swhp.id")
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT, use = SOAPBinding.Use.LITERAL)
public class AccountService {

    @Inject
    AccountManagement accountManagement;

    @WebMethod(operationName = "inqAccount", action = "tns:inqAccount")
    @WebResult(name = "return")
    public Message checkAccount(@WebParam(name = "inqAccount") Account account) {
        return this.accountManagement.validateAccount(account);
    }
}

Done, yes congratulations you already build your own SOAP web service. execute on command line mvn clean install deploy your Web Archive (war) project into Payara then open this URL: http://[your_host]:[your_port]/[your_app_name]/AccountService?wsdl and you will see the Web Service Description Language (WSDL). Actually, the WSDL not shown on my Firefox but shown properly on Chrome.

<!--
 Published by JAX-WS RI (http://jax-ws.java.net). RI's version is Metro/2.3.2-b608 (trunk-7979; 2015-01-21T12:50:19+0000) JAXWS-RI/2.2.11-b150120.1832 JAXWS-API/2.2.12 JAXB-RI/2.2.12-b141219.1637 JAXB-API/2.2.13-b141020.1521 svn-revision#unknown.
-->
<!--
 Generated by JAX-WS RI (http://jax-ws.java.net). RI's version is Metro/2.3.2-b608 (trunk-7979; 2015-01-21T12:50:19+0000) JAXWS-RI/2.2.11-b150120.1832 JAXWS-API/2.2.12 JAXB-RI/2.2.12-b141219.1637 JAXB-API/2.2.13-b141020.1521 svn-revision#unknown.
-->
<definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://account.swhp.id" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://account.swhp.id" name="AccountService">
    <types>
        <xsd:schema>
            <xsd:import namespace="http://account.swhp.id" schemaLocation="http://localhost:8080/server/AccountService?xsd=1"/>
        </xsd:schema>
    </types>
    <message name="inqAccount">
        <part name="parameters" element="tns:inqAccount"/>
    </message>
    <message name="inqAccountResponse">
        <part name="parameters" element="tns:inqAccountResponse"/>
    </message>
    <portType name="AccountService">
        <operation name="inqAccount">
            <input wsam:Action="tns:inqAccount" message="tns:inqAccount"/>
            <output wsam:Action="http://account.swhp.id/AccountService/inqAccountResponse" message="tns:inqAccountResponse"/>
        </operation>
    </portType>
    <binding name="AccountServicePortBinding" type="tns:AccountService">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
        <operation name="inqAccount">
            <soap:operation soapAction="tns:inqAccount"/>
            <input>
                <soap:body use="literal"/>
            </input>
            <output>
                <soap:body use="literal"/>
            </output>
        </operation>
    </binding>
    <service name="AccountService">
        <port name="AccountServicePort" binding="tns:AccountServicePortBinding">
            <soap:address location="http://localhost:8080/server/AccountService"/>
        </port>
    </service>
</definitions>

SOAP Client Side

Mostly you’re working on client side, building system that need to consume the SOAP web service. Nowadays is very rare to build server side with SOAP since almost millennial programmer prefer REST, right? SOAP is just for old system, but this old system is already live long enough to reach maturity level and widely used by many client and you’re one of this client. So, you need to build your system to be able communicate trough SOAP.

Before you could build a client side, you must know or have the WSDL. WSDL is more like documentation from the API. Just imagine you’re working with third-party system and they have SOAP to communicate, the first thing you need is to asking or looking their WSDL. Why? because you need that to generate the java files for your project. Sounds weird? maybe, but this is how SOAP working as far as I know and I’m open if there is another way to create a SOAP client.

You can easily generate java files from WSDL using wsimport. Wsimport is already provided in JDK so you just need to execute this command line on your client directory.

wsimport -keep -verbose http://[your_host]:[your_port]/[your_app_name]/AccountService\?wsdl -d src/main/java

Make sure you already deploy the SOAP server side project so the wsimport can generate the java files on src/main/java and you will see that the files was on package id.swhp.account just like the namespace in SOAP server side, so if you want the files stored in your desired package automatically just change the namespace on SOAP server side. Don’t forget to delete .class files since you just need the java files.

Knowing your objection is to validate the user credentials trough SOAP web service, first you need to write the validation class that use the generated artifact from WSDL.

public class AccountValidation {

    private static final AccountService_Service SERVICE;
    private static final AccountService ISERVICE;

    static {
        SERVICE = new AccountService_Service();
        ISERVICE = SERVICE.getAccountServicePort();
    }

    public boolean checkAccount(User user) {
        final Account account = new Account();

        account.setUsername(user.getUsername());
        account.setPassword(user.getPassword());

        Message response = validateAccount(account);

        if (response.getStatus().getStatusCode().equals("99")) {
            return true;
        } else {
            return false;
        }
    }

    private Message validateAccount(Account account) {
        return ISERVICE.inqAccount(account);
    }
}

Both AccountService and AccountService_Service is artifact from generated WSDL. The purpose is simple, call the SOAP web service and execute method inqAccount(). Now, you need to create your own entity for User on your client project.

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class User {

    private String name;
    private String username;
    private String password;

    public User() {
    }

    public User(String name, String username, String password) {
        this.name = name;
        this.username = username;
        this.password = password;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    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;
    }

    @Override
    public String toString() {
        return "User{"
                + "name=" + name
                + ", username=" + username
                + ", password=" + password + '}';
    }
}

The last, you need to create an EJB class as an interface either you want to build a website with JSF or build a REST with JAX-RS.

@Stateless
public class UserManagement {

    @Inject
    AccountValidation accountValidation;

    public boolean validateUser(User user) {
        return this.accountValidation.checkAccount(user);
    }
}

The Conclusion

You know, I just need to spend 2 hours to build this simple client-server SOAP web service with Java EE. Java already easier the process in making SOAP, you as developer just need to focus on business process. As always you could see the full code on my github, and as millennial developer I’m open to any advice from experienced SOAP developer about how to build a good SOAP web service.

References: