Let's Write a Document Style Web Service
Join the DZone community and get the full member experience.
Join For FreeYou might be aware that there are mainly four different styles of web services we can make use of. They are as follows;
- Document/Literal
- Document/Literal Wrapped
- RPC/Encoded
- RPC/Literal
- Write a simple web service based on Document/Literal wrapped
- See how to host the simple web service on a tomcat web container
- Make a simple test client to test our service
- Write a simple web service based on Document/Literal wrapped
package com.wsbindings; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; import javax.jws.soap.SOAPBinding; import javax.jws.soap.SOAPBinding.ParameterStyle; import javax.jws.soap.SOAPBinding.Style; import javax.jws.soap.SOAPBinding.Use; @WebService @SOAPBinding(style = Style.DOCUMENT, use = Use.LITERAL, parameterStyle = ParameterStyle.WRAPPED) public interface AddService { @WebMethod public int addIntegers(@WebParam(name = "intOne") int paramOne, @WebParam(name = "intTwo") int paramTwo); }
So this is our basic web service. This is our base interface for our
service. As you can see, we first annotate it with the
@javax.jws.WebService to indicate that its a web service we are going to
write. Then comes the interesting part where we define our SOAPBinding.
Here we state that we want to write a DOCUMENT style web service which
is LITERAL and is a WRAPPED style. One thing to note here is that all
three attribute values specified within the Soap Binding annotation are
the default values so you can get away without declaring them here
explicitly. I have done so for the purpose of clarity of this article.
Moving on, let us see how the implementation will look like for this particular interface;
package com.wsbindings; import javax.jws.WebService; @WebService(endpointInterface="com.wsbindings.AddService") public class AddServiceImpl implements AddService{ public int addIntegers(int paramOne, int paramTwo) { return paramOne+paramTwo; } }
Again nothing spectacular here in terms of what this service does. Just
adds the two numbers passed in and send back the result of the addition.
Note that here again we have to annotate the implementation class with
the @WebService annotation.
Now that we have completed the initial part of writing our web service
contract and the implementation, let us see how we can host this on a
tomcat web container.
- How to host the simple web service on a tomcat web container
<dependency> <groupId>com.sun.xml.ws</groupId> <artifactId>jaxws-rt</artifactId> <version>2.1.3</version> <exclusions> <exclusion> <groupId>com.sun.xml.stream</groupId> <artifactId>sjsxp</artifactId> </exclusion> </exclusions> </dependency>
Note that i have added one exclusion here for the sjsxp artifact since i
needed a newer version than which was being drawn up from transitive
dependency. Because else you will get the following exception;
Could not initialize class javax.xml.stream.XMLStreamException:
Underlying stream encoding UTF-8 and input paramter for
writeStartDocument() method utf-8 do not match.
In order to overcome this issue i needed to add the following dependency to my pom;
<dependency> <groupId>com.sun.xml.stream</groupId> <artifactId>sjsxp</artifactId> <version>1.0.1</version> </dependency>
I was able to find this solution thanks to this thread.
Moving on, we need to define a specific xml file which should go under the WEB-INF directory called sun-jaxws.xml.
This XML specifies how we can access our web services and where the
implmentation class is found. Lets look at the content of this file;
<?xml version="1.0" encoding="UTF-8"?> <endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0"> <endpoint name="AddWS" implementation="com.wsbindings.AddServiceImpl" url-pattern="/addws"/> </endpoints>
Here we give the package in which our web service implementation class
resides in as well as the URL pattern on how to access the particular
web service. One last thing we should do is to add the following to our
web.xml in order to host our web service successfully;
<listener> <listener-class> com.sun.xml.ws.transport.http.servlet.WSServletContextListener </listener-class> </listener> <servlet> <servlet-name>AddWS</servlet-name> <servlet-class> com.sun.xml.ws.transport.http.servlet.WSServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>AddWS</servlet-name> <url-pattern>/addws</url-pattern> </servlet-mapping>
Note that we have to define a context listener and a Servlet class which
will handle our web service invocations. If you look at the source of
the WSServletContextListner you will see it reads the sun-jaxws.xml file
from the WEB-INF directory and creates class loaders accordingly for
the web service context.
One thing about Document style web services is that you need to generate
some code for the request and response. If you do not do this, you will
get the following error with the following message;
Have you run APT to generate them?
You can generate the required classes using the wsgen tool which comes bundled up with your JDK installation. You can also use Apache-CXF to
generate these classes for you. We will use the latter approach by
using the apache-cxf maven plugin which is available for us. Include the
following to your pom and your good to go;
<plugin> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <version>2.0.9</version> <dependencies> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>2.0.9</version> </dependency> </dependencies> <executions> <execution> <id>generate-wsdl</id> <phase>process-classes</phase> <configuration> <className>com.wsbindings.AddServiceImpl</className> <argline>-classdir ${project.build.directory}/classes</argline> </configuration> <goals> <goal>java2wsdl</goal> </goals> </execution> </executions> </plugin>
Here we are using the java2wsdl command to generate the required request
and response objects for our web service. As you can see i have used
the <argline> attribute to specify where i want my generated
classes to go to. Since the normal maven compile task which is run when
building the war file will look in the classes directory, i have
specified our classes to be included in the same path as well so that
they will be bundled along with our web service class when the war is
created. You can see all possible commands you can issue by going
through the parameters specified here.
My pom was indicating an error when i included my apache-cxf maven plugin as follows;
Plugin execution not covered by lifecycle configuration
and after some research on the problem i stumbled upon a solution as stated here.Hence to overcome this issue you have to include the following snippet under the <build> tag of your pom;
<pluginManagement> <plugins> <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself. --> <plugin> <groupId>org.eclipse.m2e</groupId> <artifactId>lifecycle-mapping</artifactId> <version>1.0.0</version> <configuration> <lifecycleMappingMetadata> <pluginExecutions> <pluginExecution> <pluginExecutionFilter> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <version>2.0.9</version> <goals> <goal>test-compile</goal> <goal>compile</goal> </goals> </pluginExecutionFilter> <action> <execute /> </action> </pluginExecution> </pluginExecutions> </lifecycleMappingMetadata> </configuration> </plugin> </plugins> </pluginManagement>
That should get rid of that error for you though i cannot give you an
exact reason to why that warning pops up. If any of you know the exact
reason, i would appreciate if you could leave a comment for the benefit
of us all.
After that you can simply generate the war file and copy it to the
webapps directory of tomcat. Then you will be able to access the web
service on the following path;
http://localhost:8080/ws-bindings/addws
Where 8080 is the port on which I have hosted tomcat on and ws-bindings is the name of my war file.
Lastly let us see how to generate the client stubs required for our
service and then write a small client to test our web service.
- A simple test client to test our service
<plugin> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <version>2.0.9</version> <executions> <execution> <id>generate-sources</id> <phase>generate-sources</phase> <configuration> <wsdlOptions> <wsdlOption> <wsdl>${project.basedir}/src/main/resources/AddService.wsdl</wsdl> </wsdlOption> </wsdlOptions> </configuration> <goals> <goal>wsdl2java</goal> </goals> </execution> </executions> </plugin>This will generate the required stubs for you to test your web service. Lastly let us write a client to use the generated stub to access our web service;
import java.net.MalformedURLException; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.ws.Service; import com.wsbindings.AddService; public class DocWrapperClient { public static void main(String[] args) throws MalformedURLException { URL wsdlLocation = new URL("http://localhost:8080/ws-bindings/addws?wsdl"); QName qName = new QName("http://wsbindings.com/", "AddServiceImplService"); Service service = null; service = Service.create(wsdlLocation, qName); AddService ser = service.getPort(AddService.class); System.out.println(ser.addIntegers(1, 1)); } }That is about it guys, and i hope you found the content useful. You can check out the example by downloading the server related maven project from here and the client stub generation maven project from here.
Thank you for reading and hope you have a lovely day ahead of your. Comments and criticisms are welcome as always.
Opinions expressed by DZone contributors are their own.
Trending
-
What ChatGPT Needs Is Context
-
Extending Java APIs: Add Missing Features Without the Hassle
-
Effective Java Collection Framework: Best Practices and Tips
-
Writing a Vector Database in a Week in Rust
Comments