Friday, June 20, 2008

Developing Web Services Using EJB 3.0

The specification Web Services for Java EE, JSR 109 defines two ways of implementing a web service. One way is based on the Java class programming model -- the web service is implemented by a Java class that runs in a web container. The other way is based on the Enterprise JavaBeans (EJB) programming model -- the web service is implemented as a stateless session bean that runs in an EJB container. A previous Tech Tip, Developing Web Services Using JAX-WS, described how to develop a web service using the Java class programming model and Java API for XML Web Services (JAX-WS) 2.0, JSR 224. In the following tip, you'll learn how to develop a web service using JAX-WS and the EJB programming model. In particular, you'll learn how to use an EJB 3.0 stateless session bean to implement a Calculator service -- the same Calculator service that was covered in the previous Tech Tip.

A sample package accompanies this tip. It demonstrates a standalone Java client accessing the EJB 3.0-based Calculator service. The example uses an open source reference implementation of Java EE 5 called GlassFish. You can download GlassFish from the GlassFish Community Downloads page.

Writing the EJB 3.0 Stateless Session Bean Class

Let's start by creating the stateless session bean for the service. One of the significant improvements in the Java EE 5 platform is a much simpler EJB programming model as defined in the Enterprise JavaBeans 3.0 Specification, JSR-220. One of the simplifications is that a bean implementation class is no longer required to implement the javax.ejb.SessionBean or javax.ejb.EntityBean interface. You can declare a class a session bean or entity bean simply by annotating it. For example, you can declare a class a stateless session bean by annotating it with the @Stateless annotation.

EJB 3.0 does specify additional rules for the bean implementation class:

  • It must be declared public and must have a default constructor that doesn't take any arguments.
  • It must not be final or abstract and must be a top-level class.
  • It must not define a finalize() method.

Another simplification in EJB 3.0 is that a component interface or home interface is no longer required for a session bean. The one interface a session bean needs is a business interface that defines the business methods for the bean. Unlike business methods in a component interface, the business methods in a business interface are not required to throw java.remote.RemoteException. However the business methods can define throws clauses for arbitrary application exceptions. The EJB 3.0 business methods should not be static and final.

Given these simplifications and rules, here is a stateless session bean for the Calculator class that conforms to the EJB 3.0 programming model (you can find the source code for the Calculator class in the endpoint directory of the installed sample package):

package endpoint;

import javax.ejb.Stateless;

@Stateless
public class Calculator {

public Calculator() {}
public int add(int i, int j) {
int k = i +j ;
System.out.println(i + "+" + j +" = " + k);
return k;
}
}

Because the EJB 3.0 bean doesn't need to implement the javax.ejb.SessionBean interface, it no longer needs to include unimplemented lifecycle methods such as ejbActivate and ejbPassivate. This results in a much simpler and cleaner class. Various annotations defined in EJB 3.0 reduce the burden on developers and deployers by reducing or eliminating the need to write a deployment descriptor for the component.

Marking the EJB 3.0 Bean as a Web Service

To mark a bean as a web service simply annotate the class with the @WebService annotation. This is an annotation type defined in the javax.jws.WebService package, and is specified in Web Services Metadata for the Java Platform, JSR 181. Here is the code for Calculator class marked as a web service:

package endpoint;

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

@Stateless
@WebService
public class Calculator {

public Calculator() {}
public int add(int i, int j) {
int k = i +j ;
System.out.println(i + "+" + j +" = " + k);
return k;
}
}

Marking a Java class with a @WebService annotation makes it a service implementation class. Note that you don't have to implement a service endpoint interface. According to the JSR 109, you only need to provide a javax.jws.WebService-annotated service implementation bean. Deployment tools can then be used to generate a service endpoint interface, as well as a WSDL document, using JAX-WS rules for Java WSDL mapping.

Packaging the Web Service

A web service based on the EJB programming model needs to be packaged as a JAR file. Using the @WebService annotation, you only need to package the service implementation bean class (with its dependent classes, if any) and the service endpoint interface class (if explicitly provided). Additionally, the @Stateless annotation frees you from packaging ejb-jar.xml. By comparison, in the JAX-RPC style of packaging web services based on EJB 2.0 or earlier, you were responsible for providing the service endpoint interface class, the service implementation bean class (and dependent classes), generated portable artifacts, the JAX-RPC mapping file, and a web services deployment descriptor (webservices.xml and ejb-jar.xml).

With JSR 224, JSR 109, JSR 181 and JSR 220, an application server deployment tool can generate all the necessary artifacts such as a deployment descriptor (if not explicitly provided by the user) for deploying the web service. These artifacts, bundled in the EJB JAR file, are deployed in an EJB container. A deployer can choose to overwrite values specified by the @WebService and @Stateless annotations by explicitly providing any of the previously-mentioned artifacts and packaging them in the EJB module at the time of deployment. For this tip, the following files are packaged in the EJB module to be deployed:

  endpoint/Calculator.class
endpoint/jaxws/Add.class
endpoint/jaxws/AddResponse.class

The rest of the deployment artifacts are generated by the application server (in this case, GlassFish).

Writing the Client

After you deploy the web service, you can access it from a client program. A client uses a @WebServiceRef annotation to declare a reference to an EJB 3.0-based web service. The @WebServiceRef annotation is in the javax.xml.ws package, and is specified in JAX-WS 2.0 Web Services Metadata for the Java Platform, JSR 181. If you examine the source code JAXWSClient, the client program used in this tip (you can find the source code for JAXWSClient in the client directory of the installed sample package), you'll notice the following:

   @WebServiceRef(wsdlLocation=
"http://localhost:8080/CalculatorService/Calculator?WSDL")
static endpoint.CalculatorService service;

The value of the wsdlLocation parameter in @WebServiceRef is a URL that points to the location of the WSDL file for the referenced service. (The @WebServiceRef annotation supports additional properties that are optional. These optional properties are specified in section 7.9 of the JAX-WS 2.0 specification.) The static variable named service will be injected by the application client container.

Looking further at the source code for JAXWSClient, you'll notice the following line:

endpoint.Calculator port = service.getCalculatorPort();

The service object provides the method, getCalculatorPort, to access the Calculator port of the web service. Note that both endpoint.CalculatorService and endpoint.Calculator are portable artifacts that are generated by using the wsimport utility. The wsimport utility is used to generate JAX-WS artifacts (it is invoked as part of the build-client step when you run the example program.)

After you get the port, you can invoke a business method on it just as though you invoke a Java method on an object. For example, the following line in JAXWSClient invokes the add method in Calculator:

int ret = port.add(i, 10);

Running the Sample Code

A sample package accompanies this tip. It demonstrates the techniques covered in the tip. To install and run the sample:

  1. If you haven't already done so, download GlassFish from the GlassFish Community Downloads page.
  2. Set the following environment variables:
  3. GLASSFISH_HOME: This should point to where you installed GlassFish (for example C:\Sun\AppServer)

    ANT_HOME: This should point to where ant is installed. Ant is included in the GlassFish bundle that you downloaded. (In Windows, it's in the lib\ant subdirectory.)

    JAVA_HOME: This should point to the location of JDK 5.0 on your system.

    Also, add the ant location to your PATH environment variable.

  4. Download the sample package for the tip and extract its contents. You should now see the newly extracted directory as /ttmar2006ejb-ws, where is the directory in which you installed the sample package. For example, if you extracted the contents to C:\ on a Windows machine, then your newly created directory should be at C:\ttmar2006ejb-ws. The ejb-techtip directory below ttmar2006ejb-ws contains the source files and other support file for the sample.
  5. Change to the ejb-techtip directory and edit the build.properties files as appropriate. For example, if the admin host is remote, change the value of admin.host from the default (localhost) to the appropriate remote host.
  6. Start GlassFish by entering the following command:
  7.       /bin/asadmin start-domain domain1

    where is the directory in which you installed GlassFish.

  8. In the ejb-techtip directory, execute the following commands:
  9.       ant build

    This creates a build directory, compiles the classes, and puts the compiled classes in the build directory. It also creates an archive directory, creates a JAR file, and puts the JAR file in the archive directory.

          ant deploy

    This deploys the JAR file on GlassFish.

          ant build-client

    This generates portable artifacts and compiles the client source code.

          ant run

    This runs the application client and invokes the add operation in the Calculator service 10 times, adding 10 to the numbers 0-to-9. You should see the following in response:

          run:

    [echo] Executing appclient with client class as
    client.JAXWSClient
    [exec] Retrieving port from the service
    endpoint.CalculatorService@159780d
    [exec] Invoking add operation on the calculator port
    [exec] Adding : 0 + 10 = 10
    [exec] Adding : 1 + 10 = 11
    [exec] Adding : 2 + 10 = 12
    [exec] Adding : 3 + 10 = 13
    [exec] Adding : 4 + 10 = 14
    [exec] Adding : 5 + 10 = 15
    [exec] Adding : 6 + 10 = 16
    [exec] Adding : 7 + 10 = 17
    [exec] Adding : 8 + 10 = 18
    [exec] Adding : 9 + 10 = 19

  10. To undeploy the EJB Module from GlassFish, execute the following command:
  11.       ant undeploy

About the Author

Manisha Umbarje is a member of the product engineering group for the Sun Java System Application Server.

Copyright (c) 2004-2005 Sun Microsystems, Inc.
All Rights Reserved.