Site Search:

How to create a soap web service in 5 minutes

In previous post, we have setup a dynamic web project with eclipse IDE. It is a http server where client and server communicate with http protocol.

In this post, we will set up a soap web service. It is also http based web service, however http message have to follow SOAP protocol. SOAP defines a standard communication protocol (set of rules defined in .wsdl file) specification for XML-based message exchange. Since the XML have to conform to the specifications defined in the .wsdl file, the information you want to send need to be wrapped in a soap envelope, and the information returned need to be extracted from the soap envelope.

We go one step further, we will create a plain Pojo java bean, then expose it with soap web service endpoint. We will create a java client to test the soap endpoint. We will also use a third-party tool to test the soap endpoint so that we know the soap java client has nothing special.

Tools needed:

JDK 1.8 +

Latest eclipse IDE for J2EE

Apache tomcat 7+

Apache CXF 3+

SOAP UI

Step 1, Setup IDE.

If you have done these setup in previous post, just skip these steps.

Once you have all the above tools downloaded and unzipped, you need to setup eclipse to use them.

open eclipse -> Preferences  -> Java -> Installed JREs
If there's no JRE setup, you need to add one.
click Add, select standard JVM, click Next, browse to where your JRE is located.
The finish setup looks like:




open eclipse -> Preferences -> Server -> Runtime Environment
click Add, select Apache Tomcat v7.0 (which matches the version you downloaded), click Next, then
browse to where your apache-tomcat is located.
The finished setup looks like:





open eclipse -> Preferences -> Web Services -> CXF 2.x Preferences
click Add, browse to where your apache-CXF is located.
The finished setup looks like:


Now let's start the 5 minutes count down.

Step 2, create an empty dynamic web service.


select File -> New -> select others -> click Web drop down -> select Dynamic Web Project.
Now follow the wizard to finish the configuration.


Step 3. Create a plain POJO java bean.

Navigate to Java Resources/src folder, create a plain java class.

package helloworld3;

public class Holder {
public String getField() {
return field;
}

public void setField(String field) {
this.field = field;
}

String field;


}

Now we will turn this POJO into a soap web service.

Step 4. create a SOAP server and Client pair.
select File -> New -> select others -> click "Web Services" drop down -> select Web Service.


Click Next, If the default configurations is not tomcat v7.0 server and CXF 2.x, click the link to change it. then click next






click Next

 Click Next


Now we need to check the two additional checkboxes, "Generate Client" and "Generate Server". 
Click Next
 It asks for starting tomcat server, click Start.


Once the Finish button is available, click it, the end result is the generation of the wsdl file, the beans corresponding to the wsdl and a stand alone soap server and client.



Step 5. Test with Soup UI

right click Holder_PortTypeServer.java

Select Run -> Run As -> Java Application.

open SOAP UI, 
Click File -> Open SOAP Project
put the wsdl url http://localhost:9090/HolderPort?wsdl

now call the setField and press the triangle run button to see the result.







Step 6. Test with java client Holder_PortTypeClient.

Modify the code as the following. Notice we added two lines:
System.out.println(client.getField());
        client.setField("new Value");

================

/**
 * Please modify this class to meet your needs
 * This class is not complete
 */

package helloworld3;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;

/**
 * This class was generated by Apache CXF 3.2.6
 * 2018-09-09T11:44:57.351-04:00
 * Generated source version: 3.2.6
 *
 */
public class Holder_PortTypeClient {

    public static void main(String[] args) throws Exception {
        QName serviceName = new QName("http://helloworld3/", "HolderService");
        QName portName = new QName("http://helloworld3/", "HolderPort");

        Service service = Service.create(serviceName);
        service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING,
                        "http://localhost:9090/HolderPort");
        helloworld3.Holder_PortType client = service.getPort(portName,  helloworld3.Holder_PortType.class);

        // Insert code to invoke methods on the client here
        System.out.println(client.getField());
        client.setField("new Value");
    }

}


================
highlight it, Select Run -> Run As -> Java Application.
run it 2 times, the console output will be different. First time it output null, second time it output "new Value".

This client is basically did the same of the third party tool SOAP UI we saw in the last step -- convert request and response objects into xml according to wsdl and send the xml back-forth via http protocol.

What just happened?

Setup the soap web service might only take 5 minutes of mouse click and keyboard typing, but understand what's happened may take much longer.

behind the scene, it is the CXF tool converted the POJO into a web service. If you check the console,
there are 4 consoles' output involved: tomcat, CXF, server, client

The Apache CXF console output reveals the magic: the java2ws (jave to web service) command did all the work. Notice the flags actually specified. The input class is helloworld3.Holder, the binding technology is jaxb, the frontend is jaxws.

Sep 09, 2018 12:27:29 AM org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
INFO: Creating Service {http://savethis.com/}storeService from class com.savethis.store
java2ws -cp /Users/homenetwork/eclipse-workspace/helloworld3/build/classes -s /Users/homenetwork/eclipse-workspace/helloworld3/.cxftmp/src -d /Users/homenetwork/eclipse-workspace/helloworld3/.cxftmp/wsdl -classdir /Users/homenetwork/eclipse-workspace/helloworld3/build/classes -o holder.wsdl -createxsdimports -verbose -frontend jaxws -databinding jaxb -wsdl -client -server -wrapperbean helloworld3.Holder

java2ws - Apache CXF 3.2.6

The tomcat is needed during the setup, however, it is not needed to run the service. It is embedded jetty web container that runs the soap service, which can be seen from the server's console output. Notice the server's publish address can be arbitrary, in this following example I changed the default one to

String address = "http://localhost:9090/MyHolderPort";
in the server code.

Starting Server
Sep 09, 2018 5:21:31 PM org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
INFO: Creating Service {http://helloworld3/}HolderService from class helloworld3.Holder
Sep 09, 2018 5:21:32 PM org.apache.cxf.endpoint.ServerImpl initDestination
INFO: Setting the server's publish address to be http://localhost:9090/MyHolderPort
Sep 09, 2018 5:21:32 PM org.eclipse.jetty.util.log.Log initialized
INFO: Logging initialized @4567ms to org.eclipse.jetty.util.log.Slf4jLog
Sep 09, 2018 5:21:33 PM org.eclipse.jetty.server.Server doStart
INFO: jetty-9.4.11.v20180605; built: 2018-06-05T18:24:03.829Z; git: d5fc0523cfa96bfebfbda19606cad384d772f04c; jvm 1.8.0_31-b13
Sep 09, 2018 5:21:33 PM org.eclipse.jetty.server.AbstractConnector doStart
INFO: Started ServerConnector@1ab06251{HTTP/1.1,[http/1.1]}{localhost:9090}
Sep 09, 2018 5:21:33 PM org.eclipse.jetty.server.Server doStart
INFO: Started @4939ms
Sep 09, 2018 5:21:33 PM org.eclipse.jetty.server.handler.ContextHandler setContextPath
WARNING: Empty contextPath
Sep 09, 2018 5:21:33 PM org.eclipse.jetty.server.handler.ContextHandler doStart
INFO: Started o.e.j.s.h.ContextHandler@1e44b638{/,null,AVAILABLE}
Sep 09, 2018 5:21:33 PM org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean buildServiceFromWSDL
INFO: Creating Service {http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01}Discovery from WSDL: classpath:/org/apache/cxf/ws/discovery/wsdl/wsdd-discovery-1.1-wsdl-os.wsdl
Sep 09, 2018 5:21:34 PM org.apache.cxf.endpoint.ServerImpl initDestination
INFO: Setting the server's publish address to be soap.udp://239.255.255.250:3702
Sep 09, 2018 5:21:34 PM org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
INFO: Creating Service {http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01}DiscoveryProxy from class org.apache.cxf.jaxws.support.DummyImpl

Server ready...

Needless to say, the client side has to be changed to match the server address:

service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING, "http://localhost:9090/MyHolderPort");
Finally, the client console looks like this:

Sep 09, 2018 5:26:51 PM org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
INFO: Creating Service {http://helloworld3/}HolderService from class helloworld3.Holder_PortType

null

Actually, creating a dynamic web project is not necessary, you can convert a class in a normal java project into soap server with the same process above.





As long as the class is annotated as @webService and it is binding to the address you specified
Endpoint.publish(address, implementor);
the CXF will run it as soap service.
The classes inside the package helloworld4.jaxws is used by jaxb to marshal and un-marshal XML and create java objects for soap request and response.

Compare with servlet

If we compare soap web service with previous servlet based web service. We will notice the servlet is annotated with


@WebServlet("/hello")

As long as this annotation is there, web container such as tomcat will load the class as a servlet for url /hello, no web.xml is actually needed.