EJB 3.0 Based Web Services

On one of many ways to cut a web service on Java

Tutorial You cannot have failed to notice Web Services and the prominence that they have achieved within the current computing world. However, at least within Java, there have been a number of differing ways in which you could implement a Java-based Web Service. You could use the Java Web Services toolkit from Sun, or the Apache Axis tool kit, even an Application Servers’ own facilities (such as those provided by WebSphere) – and, of course, you can “roll your own”.

In my own case, faced with these options and the apparent complexity of the underlying operations in the past, I have chosen to use Axis, which greatly simplifies the creation of web services in Java. It shields the programmer from the lower level details of XML, SOAP, JAX-RPC, JAXP and WSDL. Indeed it makes creating Java based web services and Java based web service clients straightforward and little different to using any other Java framework.

However, Axis is not a standard part of the language (even if it is almost a de facto standard in its own right).

With Java EE 5.0, we now have a simple to use mechanism included in Java that allows the creation of Web Services by merely annotating Java classes. In this column, we will look at how Web Services can be created using standard Java annotations (introduced in Java SE 5.0), EJB 3.0 classes and an EJB 3.0 compliant application server. [Note: strictly speaking, JBoss 4.0.5 is not fully EJB 3.0 compliant - but it is close.]

EJB 3.0 Specification

The EJB 3.0 specification for Enterprise Java Beans incorporates a complete revision of the EJB specification. This revision aims to make the implementation and maintenance of EJBs a great deal simpler. Gone are the slightly convoluted remote interfaces, implementation classes, home interfaces, XML deployment descriptors etc.

Instead, Java annotations are used to mark a class with specific properties that can be used to appropriately compile and deploy Java classes. Thus a Java class that is marked with @Stateless annotation is known to be a stateless EJB. Other options include @Stateful, @MessageDriven, or @Entity. EJB 3.0 based Web Services build on this by allowing the incorporation of additional annotations that can be used to mark an EJB as providing an implementation for a web service as well as what the protocol available for that web service. Later in this column we will see examples of this for a simple Echo Web Service.

What are web services?

Before looking at how we will implement an EJB 3.0 based web service, let us first consider what we mean by a Web Service. Web Services are no more and no less than distributed applications that run across heterogeneous networks using (currently) HTTP protocols and XML data formats. Of course this is a big statement but it illustrates the core concept. Thus we can implement a web services client on one machine (in our language of choice) and then we can call a “service” on a host machine hosted somewhere across the Internet without needing to worry about the platform, implementation language or the vendor of that service. The invocation method used between our client and the hosted implementation of the service is XML transported by HTTP, thus the whole process is language and platform independent.

To support the “standardization” of Web Services, various technologies have emerged. These technologies are vendor and language independent, and include:

  • SOAP
  • SOAP with Attachments (or SwA)
  • WSDL
  • UDDI

The key technology here is SOAP, which once stood for Simple Object Access Protocol (this is now considered misleading by the W3C). SOAP includes SOAP RPC that is a representation for remote procedure calls and responses for SOAP messages.

The other key technology is WSDL. WSDL (or Web Services Description Language) is an XML vocabulary for describing a web service interface. That is, it defines:

  • the service
  • the set of operations on the server
  • the format of client invocations
  • acts a bit like a Java interface but is language independent

That is, the WSDL describes what the contents of the SOAP message sent from the client should be. It also describes what the SOAP message sent back to the client will look like.

Do not be too worried about either SOAP or WSDL as most of this is hidden under the covers when you work with EJB 3.0 based Web Services and an Application Server such as JBoss.

Implementing the EJB3 Web Service

An Application Service

To deploy and test an EJB3 based web service we will need to have an EJB3 compliant application server. In this column we will be using the JBoss Application Server; in particular, JBoss version 4.0.5 (on which the code presented below has been tested). We will use this version of JBoss as it comes out of the box; there is therefore no need to install anything other than the core release, which you can download from JBoss here. Sun’s EJB3 Download Site is here. You may also find an EJB 3.0 Tutorial (and a tutorial for Sun Java EE 5); and a Sun EJB 3.0 FAQ useful.

The Web Service Business Interface

The first thing we will do to create our EJB3 based web server is to define our remote interface (that is the service endpoint interface). This interface specifies what methods our web service will publish. In our case, we will keep it very simple and define an interface with a single method echo. This method takes a string, adds a welcome message to that string and returns the result. The interface, called Echo, is presented in listing below.

package uk.co.regdeveloper.webservice;

import java.rmi.Remote;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;

@WebService
@SOAPBinding(style = Style.RPC)
public interface Echo extends Remote {
        String echo(String e);
}

There are a number of things to note about this interface. The first is that we use annotations to specify that this interface defines a web service and that the method echo is a web method. This is done via the @WebService annotation on the interface. This annotation is part of the Java Web Services (javax.jws) package. The @WebService annotation identifies a class as implementing a Web Service, or an interface as defining a Web Service interface.

The second annotation in this interface is the SOAPBinding annotation. This is from the javax.jws.soap package. This annotation defines the mapping of the Web Service onto the SOAP message protocol. The style element of this annotation allows the specification of the encoding method for messages sent to and from the Web Service. The valid values are Document and RPC. In this case, we use RPC as this allows for a simpler service endpoint interface.

The Web Service implementation class

Having defined the remote interface for the Web Service, we can now define the implementation class. As we are going to do this as a stateless session bean we will call it EchoBean. The functionality of the EchoBean class will be to add the string “Web Service Echo +” to the front of any string passed to it. The EchoBean class is presented below.


package uk.co.regdeveloper.webservice;

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

@Stateless
@WebService(endpointInterface = "uk.co.regdeveloper.webservice.Echo")
@Remote(Echo.class)
public class EchoBean {
        public String echo(String e) {
                return "Web Service Echo + " + e;
        }
}

Once again, we use annotations to define the meaning of this class. Thus we use the @Stateless annotation to indicate that this is a stateless session bean. This annotation is a standard part of the javax.ejb package. We also in this case use the @Remote annotation to define the remote business interface for this class. Both of these annotations could be used to define a standard EJB3 stateless session bean. The third annotation, the @WebService annotation is used to declare that this bean also provides the implementation for a web service. In this case, the WebService annotation includes endpointInterface element. This element cannot be defined on an interface. It is used to specify where the application server should look in order to find the definition that will be used to create the WSDL elements of the web service. In this case, it is also the interface that the EchoBean class implements as its remote interface, although this is not necessarily the case. We have now done enough to create a web service for our stateless session EJB. We can now compile these two classes and package them up into a jar file. This jar file is a simple Jar file containing only these two classes – there is no need for any additional XML files. The Jar file I have created is called echo.jar.

Deploying to JBoss

To deploy this EJB based web service to JBoss you need merely to copy it to the deploy directory of your JBoss server. In my case, my JBoss server is called “default”; thus, I must copy it to:

C:\jboss-4.0.5.GA\server\default\deploy

If you start JBoss and then copy the jar file to the deploy directory it will be auto-deployed. If this is successful, you should see the following in the JBoss output console:

19:05:06,750 INFO [Server] JBoss (MX MicroKernel) [4.0.5.GA (build: CVSTag=Branch_4_0 date=200610162339)] Started in 50s:204ms

19:05:53,953 INFO [Ejb3Deployment] EJB3 deployment time took: 1219

19:06:00,125 INFO [TomcatDeployer] deploy, ctxPath=/echo, warUrl=.../tmp/deploy/echo.jar-ws2206.war/

19:06:01,593 INFO [JmxKernelAbstraction] installing MBean: jboss.j2ee:jar=echo.jar,name=EchoBean,service=EJB3 with dependencies:

19:06:02,359 INFO [EJBContainer] STARTED EJB: uk.co.regdeveloper.webservice.EchoBean ejbName: EchoBean

19:06:08,421 INFO [EJB3Deployer] Deployed: file:/C:/jboss-4.0.5.GA/server/default/deploy/echo.jar

19:06:08,468 INFO [WSDLFilePublisher] WSDL published to: file:/C:/jboss-4.0.5.GA/server/default/data/wsdl/echo.jar/EchoService2205.wsdl

19:06:08,500 INFO [ServiceEndpointManager] WebService started: http://HAL:8080/echo/EchoBean

What you can see from the above is that JBoss has noticed that there is a new EJB3 session bean to deploy and has deployed it. Having done this, it has then generated a WSDL file for the web service and started that web service which can be accessed via the URL http://HAL:8080/echo/EchoBean. You can view this auto-generated WSDL file by appending “?wsdl” to this URL. Thus, you can access the WSDL file using the following URL http://HAL:8080/echo/EchoBean?wsdl. The actual file generated is illustrated in Firefox in Figure 1.

Shows code generated from JBoss

We are now in a position to implement a client that will access our Echo web service.

A Web Service Client

We will implement a very simple client whose sole purpose is to send a string to the web service and then to print the string returned by the web service. The simple web service client class is presented in the following listing:

package uk.co.regdeveloper.client;

import java.net.URL;

import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;

import uk.co.regdeveloper.webservice.Echo;

public class Client {
    public static void main(String[] args) throws Exception {
        System.out.println("Starting Test Client");
        URL url = new URL("http://HAL:8080/echo/EchoBean?wsdl");
        QName qname = new QName(
                        "http://webservice.regdeveloper.co.uk/jaws",
                        "EchoService");

        System.out.println("Creating a service Using: \n\t" 
                                   + url + " \n\tand " + qname);
        ServiceFactory factory = ServiceFactory.newInstance();
        Service remote = factory.createService(url, qname);

        System.out.println("Obtaining reference to a proxy object");
        Echo proxy = (Echo) remote.getPort(Echo.class);
        System.out.println("Accessed local proxy: " + proxy);
       
        String string = "John";
        System.out.println("Sending: " + string);
        
        System.out.println("Receiving: " + proxy.echo("John"));
    }
}

As can be seen from the listing, the web service client class contains a single public static void main method that is used as the test harness. This method contains a number of debugging print outs; allowing its operation to be easily followed. The important features of this class are that it:

  1. Creates a URL object that references the USDL file.
  2. Creates a QName (Qualified Name) object to be used with the service factory.
  3. Uses the ServiceFactory object to obtain a remote service for the given QName. A service object’s purpose is to allow the client to obtain a reference to the remote web service.
  4. The “reference” to the remote web service is provided in the form of a local proxy which implements the remote business interface of the web service. This Proxy object provides all the facilities required to marshal up parameters sent to the web service, transport data to the web service via SOAP/HTTP, and un-marshal results returned from the Web Service (thus greatly simplifying accessing the web service).
  5. The echo method is then called on the proxy object and the resulting string printed out.

The most interesting aspects of the above steps are the use of the ServiceFactory and the use of the resulting Service object. The ServiceFactory instance takes two parameters; the URL of the auto-generated WSDL file and the Qualified Name of the actual service being accessed. The ServiceFactory accesses the WSDL file and then services it for information relating to the EchoService within the specified target namespace (in this case our WSDL file only contains information about the EchoBean but it could contain information about other web services as well). Once we have access to the Web Service all that is now required is to access the appropriate “port” and cast the result to the Echo interface. That is, we must specify the service endpoint interface (or what type of object we are expecting to be returned) and the service object will try to generate an appropriate dynamic proxy. Thus, in this case we say we want something that implements the Echo interface and that it what it returns to us.

Running the Client

To run the client we must ensure that the correct jars are available on the classpath. This is not as trivial as it may sound, as it is necessary to have access to not just the appropriate web service jars but to underlying SOAP, XML and XML RPC jars. Thus, the following script is used to run our simple client on a Windows machine:

rem @echo off
echo on

set JBOSS_HOME=C:\jboss-4.0.5.GA

set CLASSPATH=..\build\classes
set CLASSPATH=%CLASSPATH%;%JBOSS_HOME%\client\jboss-saaj.jar
set CLASSPATH=%CLASSPATH%;%JBOSS_HOME%\client\jboss-ejb3-client.jar
set CLASSPATH=%CLASSPATH%;%JBOSS_HOME%\client\jboss-xml-binding.jar
set CLASSPATH=%CLASSPATH%;%JBOSS_HOME%\client\jboss-jaxrpc.jar
set CLASSPATH=%CLASSPATH%;%JBOSS_HOME%\client\jbossws-client.jar
set CLASSPATH=%CLASSPATH%;%JBOSS_HOME%\client\activation.jar
set CLASSPATH=%CLASSPATH%;%JBOSS_HOME%\client\mail.jar
set CLASSPATH=%CLASSPATH%;%JBOSS_HOME%\client\wsdl4j.jar
set CLASSPATH=%CLASSPATH%;%JBOSS_HOME%\lib\endorsed\xercesImpl.jar
set CLASSPATH=%CLASSPATH%;%JBOSS_HOME%\client\jbossall-client.jar
set CLASSPATH=%CLASSPATH%;%JBOSS_HOME%\server\default\lib\jboss-remoting.jar

java -cp %CLASSPATH% uk.co.regdeveloper.client.Client

Note that, as we are using JBoss for this example, the jar files we are using are the JBoss versions. If you are using a different EJBs application server, then you will need to modify your classpath as appropriate.

The result of executing the client is presented below:

Starting Test Client
Creating a service Using:
        http://HAL:8080/echo/EchoBean?wsdl
        and {http://webservice.regdeveloper.co.uk/jaws}EchoService
Obtaining reference to a proxy object
Accessed local proxy: org.jboss.ws.jaxrpc.CallImpl@f39b3a
Sending: John
Receiving: Web Service Echo + John

Summary

As can be seen from this column, implementing and deploying EJB3 based Web Services is very straight-forward and certainly a great deal simpler than the older Java Web Services toolkit. I, for one, will certainly be considering them the next time I need to start a Web Services project as well as the Axis 2 toolkit.

Sponsored: How to determine if cloud backup is right for your servers