Implementing RESTful views of an EJB with local interfaces

If you have Enterprise JavaBeans (EJB) applications that are exposed using a local interface view, you can expose a RESTful interface to the enterprise bean using Java™ API for RESTful Web Services (JAX-RS). By implementing JAX-RS annotated enterprise beans, you keep the EJB functionality including transaction support, injection of Java EE components and resources, and other EJB session bean capabilities.

Before you begin

Before EJB 3.1, enterprise beans that required an EJB local client view also needed a separate Java interface, usually located in a separate file, that declared the local view methods. The enterprise bean specified that it implemented the EJB local view interface using deployment descriptors or EJB annotations.

Using the EJB 3.1 specification, you have the option of exposing a local view of an enterprise bean without an explicit EJB local interface. Instead, the enterprise bean has a no-interface client view that is based on the public methods of your bean class. No-interface view enterprise beans can be more simple to develop than a local view enterprise bean for the following reasons:
  • No-interface view enterprise beans do not require a separate Java interface declaration.
  • No-interface view enterprise beans do not require specifying additional metadata in the deployment descriptor or when using annotations.
See the EJB 3.1 specification for more details on no-interface views of an enterprise bean.

JAX-RS supports the use of enterprise beans that declare a local business interface and no-interface view enterprise beans.

This task describes implementing RESTful views of an enterprise bean with a local interface to enable the enterprise bean to expose JAX-RS resources.

About this task

You can create a simple enterprise bean with JAX-RS annotations. Even though this task specifically describes how to implement RESTful views of a local interface view enterprise bean, it is important that you consider the full scope of your application architecture and how you want to expose resources as you decide your resource model and determine which RESTful views are appropriate for your enterprise beans application. These considerations are beyond the scope of this task.

JAX-RS supports stateless and singleton session beans. You can add JAX-RS annotations to the local interface of a session bean. Also, with EJB 3.1, you can add JAX-RS annotations directly to an EJB class if the enterprise bean exposes a no-interface view.

With the EJB 3.1 packaging rules, you can add JAX-RS enterprise beans in the web application archive (WAR) file either directly in the WEB-INF/classes directory or using a Java archive (JAR) file in the WEB-INF/lib directory. You can declare an enterprise bean using annotations, or using an EJB deployment descriptor, or using both annotations and a deployment descriptor. See Deploying EJB in a WAR or a JAR file.
Note: For JAX-RS enterprise beans in JAR files, you must declare all Application, resource (@path), provider (@Provider) as EJB beans because in JAR files, only EJB classes are supported, whereas POJO classes are not supported. If you want to use POJO classes in the EJB jar files, you must use the getClasses() or getSingletons() method of the EJB based application class to register these classes.

JAX-RS annotated enterprise beans in an ejb-jar file that is included in an EAR is now supported. See Deploying EJB in an EAR file.

Best practice: Although you can declare enterprise beans in different ways, it is a best practice to directly implement the EJB business local interface and to always declare the @javax.ejb.Local annotation. By using this method, the EJB bean is required to implement the local business interface, which eliminates errors in typing method names and changes to argument types. By always using the @javax.ejb.Local annotation, if there are ever multiple business interfaces, you can simply add the business interface to the annotation value. You can also use this approach to modify the enterprise bean using a deployment descriptor.

Procedure

  1. Create enterprise bean local interfaces for your enterprise bean application.
    The following example demonstrates a simple local business interface, the Purchasable EJB local interface, for items to purchase:
    package com.example.jaxrs;
    @javax.ws.rs.Path("itemsForPurchase/{itemID}")
    public interface Purchasable {
    
        public int getItemsLeft(String itemID);
    
        @javax.ws.rs.POST
        public Order purchase(
            @javax.ws.rs.PathParam("itemID") String itemID,
            @javax.ws.rs.QueryParam("orderId") String orderID);
    }

    The getItemsLeft method is a regular EJB method that is not JAX-RS related. A javax.ws.rs.Path annotation denotes the HTTP request path to use. When an HTTP POST request is made to the itemsForPurchase/{itemID} object, the JAX-RS runtime environment finds an EJB bean that implements the Purchasable local interface and invokes the purchase method on the enterprise bean.

    You can still use the purchase method outside of a JAX-RS runtime environment request. You can use injection or a JNDI lookup for a purchasable enterprise bean and invoke the purchase method with the two String arguments, itemID and orderID.

    Best practice: If there are multiple enterprise beans that implement a local business interface, the JAX-RS runtime environment chooses a random EJB bean to use when a JAX-RS request is made. It is a best practice to only enable one bean class to implement a JAX-RS annotated EJB local interface. If necessary, create a separate EJB local interface, use the JAX-RS annotations on the new interface, and then modify the metadata for the bean class so that it implements the new EJB local interface.
  2. Create the enterprise bean that implements the local business interface.
    The following example illustrates the Purchasable EJB bean:
        public int getItemsLeft(String itemID) {
            // Return the number of items that remain.  
        }
    
        public Order purchase(String itemID, String orderId) {
            // Add the given item to the order id and return it.  
        }
    
    }
  3. Declare that the Book class is an enterprise bean and implements a local interface.
    Use one of the following methods to declare your class as an enterprise bean that implements the local interface. In the following example, the Book class is declared an enterprise bean that implements a local interface:
    • Use the EJB annotations @javax.ejb.Stateless or @javax.ejb.Singleton on the Book class to specify that you want the EJB to be stateless or singleton. Also, add the @javax.ejb.Local annotation with the local interfaces as the annotation value; for example:
      @javax.ejb.Stateless
      @javax.ejb.Local(Purchasable.class)
      public class Book {
      If you have implemented multiple local business interfaces, add the interface classes to the @javax.ejb.Local annotation value; for example:
      @javax.ejb.Local({Purchasable.class, Rentable.class})
    • You can use a deployment descriptor to declare that an EJB bean and the business interfaces it implements; for example:
      <ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" version="3.1"
          xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd">
      <!--
          This file must exist in the WEB-INF/
          directory of your WAR file. See EJB 3.1 spec 20.4 for more details.
      -->
          <enterprise-beans>
              <session>
                  <ejb-name>Book</ejb-name>
                  <business-local>com.example.jaxrs.Purchasable</business-local>
                  <ejb-class>com.example.jaxrs.Book</ejb-class>
                  <session-type>Stateless</session-type>
              </session>
          </enterprise-beans>
      </ejb-jar>

      If you have implemented multiple local business interfaces, you must add business-local elements to each local interface in your bean definition.

    • If you have only one local business interface, you can implement the interface directly; for example:
      @javax.ejb.Stateless
      public class Book implements Purchasable {
  4. (optional) Add @javax.annotation.Resource annotated Java EE resource fields and properties to your JAX-RS EJB classes to easily access resources in your application.
    The Java EE injections do not work in plain Java classes with JAX-RS annotations. Injecting @javax.annotation.Resource annotated Java EE resource fields and properties to your JAX-RS EJB classes only works if your JAX-RS annotated classes are either an enterprise bean or a Java Context and Dependency Injection (JCDI) (JSR-299) managed bean; for example:
    package com.example.jaxrs;
    
    @javax.ejb.Stateless
    @javax.ejb.Local(Purchasable.class)
    public class Book implements Purchasable {
        @javax.annotation.Resource(name="jdcb/TestDataSource")
        private javax.sql.DataSource datasource;
    
        public int getItemsLeft(String itemID) {
            // Reads from the datasource. 
            // Returns the number of items that remain.  
        }
    
        public Order purchase(String itemID, String orderId) {
            // Reads from the datasource. 
            // Adds the given item to the order id and returns it.  
        }
    }
    In this example, if a data source is properly configured with the correct JNDI name, a DataSource object is injected into the resource class.
  5. (optional) Use JAX-RS @javax.ws.rs.core.Context injection to obtain access to information about the request.
    You can add an @javax.ws.rs.core.Context UriInfo field to your JAX-RS EJB class to access information about the request URI; for example:
    package com.example.jaxrs;
    
    @javax.ejb.Stateless
    @javax.ejb.Local(Purchasable.class)
    public class Book implements Purchasable {
        @javax.ws.rs.core.Context
        private UriInfo uriInfo;
    
        public int getItemsLeft(String itemID) {
            // Return the number of items that remain.  
        }
    
        public Order purchase(String itemID, String orderId) {
            // Add the given item to the order id and return it.  
        }
    }
    To read parameters from the request such as @javax.ws.rs.HeaderParam, @javax.ws.rs.QueryParam, and @javax.ws.rs.PathParam, add a parameter to your resource method; for example:
    package com.example.jaxrs;
    
    @javax.ws.rs.Path("itemsForPurchase/{itemID}")
    public interface Purchasable {
        public int getItemsLeft(String itemID);
    
        @javax.ws.rs.POST
        public Order purchase(
            @javax.ws.rs.PathParam("itemID") String itemID,
            @javax.ws.rs.QueryParam("orderId") String orderID);
    }
    package com.example.jaxrs;
    
    @javax.ejb.Stateless
    @javax.ejb.Local(Purchasable.class)
    public class Book implements Purchasable {
        @javax.ws.rs.core.Context
        private UriInfo uriInfo;
    
        public int getItemsLeft(String itemID) {
            // Returns the number of items that remain.  
        }
    
        public Order purchase(String itemID, String orderId) {
            // The method parameters contain the request values. 
            // Add the given item to the order id and return it.  
        }
    
        /*  The following field will not be set. */
        @javax.ws.rs.QueryParam("q")
        private String willNotWork;
    
        @javax.ws.rs.QueryParam("q")
        public void setMyQueryParam(String q) {
            /* This property will not be set. */
        }
    }
    Supported configurations: The JAX-RS parameter annotations must be added to the resource methods in the EJB business interface when using business interfaces. They cannot be added to the implementation bean.
  6. Package the enterprise beans into the WEB-INF/classes directory of the WAR file, or inside a JAR file that is included in the WEB-INF/lib directory of your WAR file.

    When a client makes a request to a JAX-RS annotated enterprise bean, the JAX-RS runtime environment looks up and uses an EJB instance of the class to then invoke the JAX-RS resource method.

Results

You have enabled an existing enterprise bean with local interfaces so that JAX-RS resources are exposed for consumption.

Deploying EJB in an EAR file

You can deploy EJB in an EAR file through an ejb-jar file and expose an EJB in that jar as a JAX-RS endpoint.

Procedure

  1. Open command line tool and change your directory to <WAS HOME>/bin.
  2. Run the following commands:
    endptEnabler -endpointType jaxrs path of myearfile.ear
    A new ear file with the same name myearfile.ear is generated. It contains the router module myejbjaxrs-RSRouter.war.
    Note:
    • If you do not customize the context root, the default context root is the short file name of the EJB jar file, like myejbjaxrs for myejbjaxrs.jar.
    • If you do not customize the context root and the JaxWS router module exists, the default context root is the short file name of the EJB jar file plus .jaxrs, like myejbjaxrs.jaxrs for <myejbjaxrs.jar>.
  3. Deploy the new myearfile.ear file to WebSphere® Application Server, and then use the following URL pattern to access the JAX-RS:
    http://<host>:<port>/<context root>/<path of jaxrs resource>
    For example, you can use the following URL to access the EJB JAX-RS in myejbjaxrs.jar:
    http://<host>:<port>/myejbjaxrs/<path of jaxrs resource>
    Tip: If the application startup is blocked by behavior changes to previous releases that are found on JAX-RS engines or exceptions that are reported by a JAX-RS engine, use a property to disable the capability of exposing EJB classes with JAX-RS annotations in EJB JAR file in EAR as JAX-RS service.
    • For Version 8.5.5.15 or earlier, use the com.ibm.webshere.jaxrs.server.DisableIBMEJBJAXRSInEJBJarSupport property.
    • [8.5.5.16 or later]For Version 8.5.5.16 or later, use the com.ibm.websphere.jaxrs.server.DisableIBMEJBJAXRSInEJBJarSupport property.