Pages

Friday, December 14, 2012

Exposing EJB as Web Service

Web Services are now industry standard for application integration, especially if you have a wider audience to support. Web Services provide seamless interoperability across heterogeneous applications in a loosely coupled way. More and more organizations are now moving towards a Service Oriented Architecture (SOA) where the functionality is provided as a Service. Details of the Web Services Architecture are out of the scope of this tutorial, however, you can go to this W3C link for more details.

JCP identified this trend and focused on providing out of the box support for Web Services in the Java EE from ground up. As a result of their efforts, creating and consuming the SOAP services is a breeze in java. However, the inherent communication architecture of SOAP means that you will be sending a significant amount of XML data working with SOAP services. For that reason RMI may be a better option for you unless you are looking to expose your services to non-java clients.

In this tutorial I will be presenting how you can expose an EJB as a web service and invoke that service with a simple client. All you need to do to expose the EJB as a web service is to annotate the EJB with the @WebService annotation and annotate all the methods that you want to expose as a service with @WebMethod annotation. Refer to my older post for a tutorial on how to create and deploy an EJB project in Glassfish for the full project. Here is a sample EJB that is part of the project I created earlier that is exposed as a web service.



package com.test.webservice.soap;

import javax.ejb.Stateless;
import javax.jws.WebMethod;
import javax.jws.WebService;

@Stateless
@WebService(targetNamespace="http://www.mynamespace.co.uk")
public class TestSOAP {

 @WebMethod
 public String sayHello(String name) {
  String str = "SOAP Says Hello " + name;
  return str;
 }
}

All we have done here is annotate the class as a @WebService and annotate the method as @WebMethod and Glassfish will do the rest for us. Refer to my other tutorial that I created earlier explaining how to create and deploy an EJB. All you have to do is to add another EJB to that same application and annotate that EJB with these Web Service annotations and deploy that to the Glassfish or any other EJB Application Server and the server will do the rest for you.

Once the EJB is deployed you can go to the Glassfish admin console to double check if your service is listed under the Web Services section of Glassfish menu. You can also double check your service by going to the URL http://localhost:8080/TestSOAPService/TestSOAP?tester which is created as a test page for your service. Notice this URL closely, the First part is where your web application is listening for any HTTP requests and then "TestSOAPService" is actually the ClassName + Service and then ClassName with a ?tester . This is the standard pattern whenever you will create and deploy your services, Glassfish will create a test page for you in the same format. This test page will invoke the service and will show you the response.

If you click on the WSDL File link, it will show you the WSDL file for that service.


<?xml version="1.0" encoding="UTF-8"?>
 <!--
  Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is
  JAX-WS RI 2.1.3.3-hudson-757-SNAPSHOT.
 -->
 <!--
  Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is
  JAX-WS RI 2.1.3.3-hudson-757-SNAPSHOT.
 -->
<definitions
 xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.mynamespace.co.uk"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/"
 targetNamespace="http://www.mynamespace.co.uk" name="TestSOAPService">
 <ns1:Policy xmlns:ns1="http://www.w3.org/ns/ws-policy"
  wsu:Id="TestSOAPPortBinding_sayHello_WSAT_Policy">
  <ns1:ExactlyOne>
   <ns1:All>
    <ns2:ATAlwaysCapability xmlns:ns2="http://schemas.xmlsoap.org/ws/2004/10/wsat"></ns2:ATAlwaysCapability>
    <ns3:ATAssertion xmlns:ns4="http://schemas.xmlsoap.org/ws/2002/12/policy"
     xmlns:ns3="http://schemas.xmlsoap.org/ws/2004/10/wsat"
     ns1:Optional="true" ns4:Optional="true"></ns3:ATAssertion>
   </ns1:All>
  </ns1:ExactlyOne>
 </ns1:Policy>
 <types>
  <xsd:schema>
   <xsd:import namespace="http://www.mynamespace.co.uk"
    schemaLocation="http://localhost:8080/TestSOAPService/TestSOAP?xsd=1"></xsd:import>
  </xsd:schema>
 </types>
 <message name="sayHello">
  <part name="parameters" element="tns:sayHello"></part>
 </message>
 <message name="sayHelloResponse">
  <part name="parameters" element="tns:sayHelloResponse"></part>
 </message>
 <portType name="TestSOAP">
  <operation name="sayHello">
   <input message="tns:sayHello"></input>
   <output message="tns:sayHelloResponse"></output>
  </operation>
 </portType>
 <binding name="TestSOAPPortBinding" type="tns:TestSOAP">
  <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
   style="document"></soap:binding>
  <operation name="sayHello">
   <ns5:PolicyReference xmlns:ns5="http://www.w3.org/ns/ws-policy"
    URI="#TestSOAPPortBinding_sayHello_WSAT_Policy"></ns5:PolicyReference>
   <soap:operation soapAction=""></soap:operation>
   <input>
    <soap:body use="literal"></soap:body>
   </input>
   <output>
    <soap:body use="literal"></soap:body>
   </output>
  </operation>
 </binding>
 <service name="TestSOAPService">
  <port name="TestSOAPPort" binding="tns:TestSOAPPortBinding">
   <soap:address location="http://localhost:8080/TestSOAPService/TestSOAP"></soap:address>
  </port>
 </service>
</definitions>

WSDL is the definition file for that service. When publish your service to others, they will need this file to communicate with your service. We will also need the WSDL when we will create the Web Service client later on. That’s pretty much it. We are now ready to accept SOAP requests from any clients and serve them with our brand new Hello Service.

Client:

Now let’s create a client. Java based clients use the JAX-RPC API to access the web service. All we need now is the WSDL file. The easiest way to do that is to use the wsimport command that will generate all the necessary files you need to communicate with the web service.

Either download the file or directly point to the WSDL URI in your command. You can find the WSDL at http://localhost:8080/TestSOAPService/TestSOAP?wsdl. Here is the output of wsimport command:


C:\>wsimport -keep -p com.test.soap.generated -d C:\software\test\TestServiceClient\src\ http://localhost:8080/TestSOAPService/TestSOAP?wsdl
parsing WSDL...
generating code...
compiling code...
C:\>

Note here that the directory structure (com.test.soap.generated) you specified in the wsimport command must already exist otherwise it will throw errors. Also the directory you specify where you want to put all of the generated files.

Here is a sample java class that is using these generated files to communicate with the Web Service.



package com.test.soap.client;

import com.test.soap.generated.TestSOAP;
import com.test.soap.generated.TestSOAPService;

public class WebServiceClient {

 public static void main(String[] args) {
  
  System.out.println("Creating Service Handler...");
  TestSOAPService ss = new TestSOAPService();
  
  System.out.println("Acquiring Service Port...");
  TestSOAP port = ss.getTestSOAPPort();
  
  String msg=null;
  try{
   System.out.println("Invoking Service...");
   msg = port.sayHello("Raza");
  }catch(Exception e){
   msg = "Failed to open SOAP Request"; 
   e.printStackTrace();
  }
  
  System.out.println(msg);

 }

}

And here is the output when you run this class



Creating Service Handler...
Acquiring Service Port...
Invoking Service...
SOAP Says Hello Raza

I guess this will give you a good head start with the Web Services architecture as implemented by Java. You can now take this as a skeleton to build your applications around and play with more sophisticated options available.

2 comments:

  1. Hi,
    I followed this example. It's works well. Now i'm trying to connect my web service with another EJB on a server JBoss7 and i got this error :

    Exception in thread "main" AxisFault
    faultCode: {http://schemas.xmlsoap.org/soap/envelope/}Server.userException
    faultSubcode:
    faultString: java.lang.reflect.InvocationTargetException
    faultActor:
    faultNode:
    faultDetail:
    {http://xml.apache.org/axis/}hostname:UPS-NB03

    java.lang.reflect.InvocationTargetException
    at org.apache.axis.message.SOAPFaultBuilder.createFault(SOAPFaultBuilder.java:222)
    at org.apache.axis.message.SOAPFaultBuilder.endElement(SOAPFaultBuilder.java:129)
    at org.apache.axis.encoding.DeserializationContext.endElement(DeserializationContext.java:1087)

    If you have an idea ?
    Thanks a lot in advance :-)

    ReplyDelete
    Replies
    1. Hi Anthony,

      Sorry for the late reply, I am sure you must have solved your problem by now..

      Anyways, your error looks like there is something wrong with the classes that were generated from the WSDL file. The best way to generate the classes again. Please refer to the WSIMPORT command documentation for details.

      Using another EJB as a client for the SOAP Web Service is no different to using the using a standalone client, it will be exactly as I am doing in this example. All you need to make sure is that the classes generated by the WSIMPORT command are available on your application's classpath.

      Delete