At this point we need to create a Stateless Session Bean (EJB) that will query exhibition data through the JPA entities and return such information. Before getting into it, let's take a quick look at the types of beans supported by Java EE 6 and their definitions:
BEAN
.Another new feature related to beans is the no-interface view, which improves the local client view concept of EJB 3.0. Now, we don't have to create a separate interface to expose the methods of a bean—the public methods of a Session Bean are automatically exposed as its local business interface, making development easier. To use it, you either explicitly decorate your bean with @LocalBean
or leave it without any interface-related decoration (@LocalBean
, @Local
, and @Remote
) to implicitly use the no-interface strategy.
Let's get back to our code and create the stateless bean that is going to be exposed as a web service:
ExhibitionBean.java
under the package com.packt.theater.services
.Stateless
annotation.PersistenceContext
annotation:@PersistenceContext(unitName = "TheaterBO") private EntityManager em;
The session bean is now capable of interacting with the database through the persistence context we have declared and we can simply write Java methods to retrieve JPA objects, using named queries, for example. In the next section we're going to expose this stateless session bean as a RESTful service and how to write a REST client that will call this service from the Store application.
We're going to expose three basic functionalities as part of the RESTful API for the Theater application: list all exhibitions, list one exhibition, and retrieve an exhibition by movie. To support such operations, let's look at what must be done to the ExhibitionBean
class:
java.ws.rs.Path
annotation. This annotation specifies the relative path for a resource or method that is going to be exposed through REST:@Stateless @Path("/exhibition") public class ExhibitionBean { ...
@Produces
defines. We also use an HTTP verb, @GET
, since this operation will not have any side-effect (it will not change any entity state on the server side):@GET @Produces({ MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON }) public List<Exhibition> getAllExhibitions() { @SuppressWarnings("unchecked") List<Exhibition> result = em.createNamedQuery("Exhibition.findAll") .getResultList(); if (result.size() > 0) return result; else throw new WebApplicationException(Response.Status.NO_CONTENT); }
PathParam
and since this parameter will be part of the URL we need to use the Path
annotation to differentiate from the default path of the service:@GET @Path("{id}") @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) public Exhibition getExhibition(@PathParam("id") int id) { try { Exhibition entity = (Exhibition) em .createNamedQuery("Exhibition.findById") .setParameter("id", id) .getSingleResult(); return entity; } catch (NoResultException nre) { throw new WebApplicationException(Response.Status.NOT_FOUND); } }
/q
, and pass the movie ID as a URL parameter that can be read through the annotation QueryParam
of JAX-RS. The complete method is shown as follows:@GET @Path("/q") @Produces({ MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON }) public List<Exhibition> getAllExhibitionsByMovie(@QueryParam("movie") int movieId) { if (movieId > 0) { Query query = em.createNamedQuery(Exhibition.findByMovie); query.setParameter("movieId", movieId); @SuppressWarnings("unchecked") List<Exhibition> result = query.getResultList(); if (result.size() > 0) return result; else throw new WebApplicationException(Response.Status.NOT_FOUND); } else throw new WebApplicationException(Response.Status.BAD_REQUEST);
Note that if a request is made to http://<server>:<port>/theater/api/exhibition/q
without a parameter or with a parameter but no value, a
BAD REQUEST error code (HTTP 400) will be returned. Remember that HTTP status codes are heavily used for RESTful web services since they are a standard and have meaningful descriptions.
After the successful deployment, test the application accessing the following URLs from a browser:
http://localhost:7001/theater/api/exhibition/
http://localhost:7001/theater/api/exhibition/4
http://localhost:7001/theater/api/exhibition/q?movie=5
To complete the testing, we can call the service API from a command-line utility such as
cURL, setting the accept HTTP header to application/json
, which will change the web service's response, as shown in the following screenshot:
To force a RESTful service support only one media type (only JSON or only XML, for example), you can modify the Produces
annotation and set the appropriate media type. Actually the supported media types are listed in the Java docs of Jersey API at https://jersey.java.net/apidocs/latest/jersey/index.html.
You can check the RESTful APIs exposed by an application accessing the autogenerated Web Application Description Language (WADL) file at the API's root URL to get information about it. For the module we just coded, that address would be http://localhost:7001/theater/api/application.wadl
:
The application.wadl
file is generated by Jersey and contains all the basic information about the service such as operations, supported media types for each operation, HTTP verbs, and input/output data.