Chapter 4.  Create a RESTful web service implemented by an EJB component

4.1.  Create a web service using JAX-RS from EJB classes.

A root resource class is anchored in URI space using the @Path annotation. The value of the annotation is a relative URI path template whose base URI is provided by the deployment context.

The following code example is a very simple example of a root resource class using JAX-RS annotations:

import javax.ws.rs.GET;
import javax.ws.rs.Produces;
import javax.ws.rs.Path;

// The Java class will be hosted at the URI path "/helloworld"
@Path("/helloworld")
public class HelloWorldResource {

    // The Java method will process HTTP GET requests
    @GET
    // The Java method will produce content identified by the MIME Media
    // type "text/plain"
    @Produces("text/plain")
    public String getClichedMessage() {
        // Return some cliched textual content
        return "Hello World";
    }
}
					

4.1.1.  Annotate an enterprise bean class with a @Path annotation to respond to URL patterns.

@Target(value={TYPE,METHOD})
@Retention(value=RUNTIME)
public @interface Path

Identifies the URI path that a resource class or class method will serve requests for.

Paths are relative. For an annotated class the base URI is the application path. For an annotated method the base URI is the effective URI of the containing class. For the purposes of absolutizing a path against the base URI , a leading '/' in a path is ignored and base URIs are treated as if they ended in '/'. E.g.:

@Path("widgets")
public class WidgetsResource {

    @GET
    String getList() {...}

    @GET @Path("{id}")
    String getWidget(@PathParam("id") String id) {...}

}
						

In the above, if the application path is catalogue and the application is deployed at http://example.com/, then GET requests for http://example.com/catalogue/widgets will be handled by the getList() method while requests for http://example.com/catalogue/widgets/nnn (where nnn is some value) will be handled by the getWidget() method. The same would apply if the value of either @Path annotation started with '/'.

Session beans (Stateless or Singleton) can be implemented as JAX-RS resource or provider classes. EJB 3.1 and JAX-RS work very well together thanks to JAX-RS flexibility in handling many kinds of resource classes, the EJB 3.1 no-interface view, and the ability to package enterprise beans directly in a .war:

import javax.ejb.*;
import javax.ws.rs.*;

@Path("name")
@Stateless
public class NameService {

    @EJB
    private NameBean nameBean;

    @GET
    @Produces("text/plain")
    public String getHtml() {
        return "Hello " + nameBean.getName();
    }

    @PUT
    @Consumes("text/plain")
    public void put(String content) {
        nameBean.setName(content);
    }
}
						

The @Path annotation's value is a relative URI path. In the HelloWorldResource example above, the Java class will be hosted at the URI path /helloworld. This is an extremely simple use of the @Path annotation. What makes JAX-RS so useful is that you can embed variables in the URIs.

URI path templates are URIs with variables embedded within the URI syntax. These variables are substituted at runtime in order for a resource to respond to a request based on the substituted URI. Variables are denoted by curly braces. For example, look at the following @Path annotation:

@Path("/users/{username}")
						

In this type of example, a user will be prompted to enter their name, and then a web service configured to respond to requests to this URI path template will respond. For example, if the user entered their username as "Mikalai", the web service will respond to the following URL:

http://example.com/users/Mikalai
						

To obtain the value of the username variable the @PathParam may be used on method parameter of a request method, for example:

@Path("/users/{username}")
public class UserResource {

    @GET
    @Produces("text/xml")
    public String getUser(@PathParam("username") String userName) {
        ...
    }
}
						

If it is required that a user name must only consist of lower and upper case alpha-numeric characters then it is possible to declare a particular regular expression, which overrides the default regular expression, "[^/]+?", for example:

@Path("users/{username: [a-zA-Z][a-zA-Z_0-9]*}")
						

In this type of example the username variable will only match user names that begin with one upper or lower case letter and zero or more alpha numeric characters and the underscore character. If a user name does not match that a 404 (Not Found) response will occur.

A @Path value may or may not begin with a '/', it makes no difference. Likewise, by default, a @Path value may or may not end in a '/', it makes no difference, and thus request URLs that end or do not end in a '/' will both be matched.

4.1.2.  Annotate the class's methods to respond to HTTP requests using the corresponding JAX-RS annotations (@GET, @POST, etc.).

JAX-RS defines common HTTP methods using annotations: @GET, @POST, @PUT, @DELETE, @HEAD, and @OPTIONS. Only public methods may be exposed as resource methods. Listing below shows a customer resource exposing CRUD methods. Note that the createCustomer and updateCustomer methods take an InputStream as a parameter which represents the HTTP request body:


@Path("/customers")
public class CustomerResource {

    @GET
    public List<Customer> getListOfCustomers() {
        // ...
    }

    @POST
    @Consumes(MediaType.APPLICATION_XML)
    public Response createCustomer(InputStream is) {
        // ...
    }

    @PUT
    @Path("{customerId}")
    @Consumes(MediaType.APPLICATION_XML)
    public Response updateCustomer(@PathParam("customerId") String customerId, InputStream is) {
        // ...
    }

    @DELETE
    @Path("{customerId}")
    public void deleteCustomer(@PathParam("customerId") String customerId) {
        // ...
    }
}

						

4.1.3.  Use the JAX-RS @Produces and @Consumes annotations to specify the input and output resources for the RESTful web service.

@Produces

The @Produces annotation is used to specify the MIME media types of representations a resource can produce and send back to the client. In this example, the Java method will produce representations identified by the MIME media type "text/plain".

@Produces can be applied at both the class and method levels. Here's an example:

@Path("/myResource")
@Produces("text/plain")
public class SomeResource {
    @GET
    public String doGetAsPlainText() {
        ...
    }

    @GET
    @Produces("text/html")
    public String doGetAsHtml() {
        ...
    }
}
						

The doGetAsPlainText method defaults to the MIME type of the @Produces annotation at the class level. The doGetAsHtml method's @Produces annotation overrides the class-level @Produces setting, and specifies that the method can produce HTML rather than plain text.

If a resource class is capable of producing more that one MIME media type then the resource method chosen will correspond to the most acceptable media type as declared by the client. More specifically the Accept header of the HTTP request declared what is most acceptable. For example if the Accept header is:

Accept: text/plain
						

then the doGetAsPlainText method will be invoked.

Alternatively if the Accept header is:

Accept: text/plain; q=0.9, text/html
						

which declares that the client can accept media types of "text/plain" and "text/html" but prefers the latter, then the doGetAsHtml method will be invoked.

More than one media type may be declared in the same @Produces declaration, for example:

@GET
@Produces({"application/xml", "application/json"})
public String doGetAsXmlOrJson() {
    ...
}
						

The doGetAsXmlOrJson method will get invoked if either of the media types "application/xml" and "application/json" are acceptable. If both are equally acceptable then the former will be chosen because it occurs first.

The examples above refer explicitly to MIME media types for clarity. It is possible to refer to constant values, which may reduce typographical errors, see the constant field values of MediaType:

APPLICATION_ATOM_XML          "application/atom+xml"
APPLICATION_FORM_URLENCODED   "application/x-www-form-urlencoded"
APPLICATION_JSON              "application/json"
APPLICATION_OCTET_STREAM      "application/octet-stream"
APPLICATION_SVG_XML           "application/svg+xml"
APPLICATION_XHTML_XML         "application/xhtml+xml"
APPLICATION_XML               "application/xml"
MULTIPART_FORM_DATA           "multipart/form-data"
TEXT_HTML                     "text/html"
TEXT_PLAIN                    "text/plain"
TEXT_XML                      "text/xml"
WILDCARD                      "*/*"
						

@Consumes

The @Consumes annotation is used to specify the MIME media types of representations a resource can consume that were sent by the client. The above example can be modified to set the cliched message as follows:

@POST
@Consumes("text/plain")
public void postClichedMessage(String message) {
    // Store the message
}
						

In this example, the Java method will consume representations identified by the MIME media type "text/plain". Notice that the resource method returns void. This means no representation is returned and response with a status code of 204 (No Content) will be returned.

@Consumes can be applied at both the class and method levels and more than one media type may be declared in the same @Consumes declaration.

JAX-RS standard entity parameter types

JAX-RS requires certain parameters to be supported for virtually any content type. The following table lists the supported content types:

Table 4.1.  JAX-RS Standard Entity Parameter Types

Java TypeContent Type Supported

java.lang.String

*/*

byte[]

*/*

java.io.InputStream

*/*

java.io.Reader

*/*

java.io.File

*/*

javax.activation.DataSource

*/*

javax.xml.transform.Source

text/xml, application/xml, application/*+xml

javax.xml.bind.JAXBElement and JAXB classes

text/xml, application/xml, application/*+xml

javax.ws.rs.core.MultivaluedMap<String, String>

application/x-www-form-urlencoded

javax.ws.rs.core.StreamingOutput (as a writer only)

*/*


Developers can use the previous Java types as entity parameters for requests and responses:

@Path("/example")
public class RootResource {

    @GET
    @Produces("text/xml")
    public Response getInfo() {
        ...
    }

    @POST
    @Consumes("application/json")
    @Produces("application/json")
    public StreamingOutput createItem(InputStream requestBodyStream) {
        /* read the requestBodyStream like a normal input stream */
        ...
    }
}
						

4.1.4.  Implement the functionality of the JAX-WS resource's methods.

Extracting Request Parameters

Parameters of a resource method may be annotated with parameter-based annotations to extract information from a request.

The following example presents the use @PathParam to extract a path parameter (98342) from the path component of the request URL (http://java.boot.by/customers/98342) that matched the path declared in @Path:

@Path("/customers")
public class CustomerResource {
    @GET
    public Customer getCustomer(@PathParam("customerId") customerId) {
        // ...
    }
}
						

The @QueryParam annotation extracts the value of a URI query parameter. For example, the following code allows you to extract the ZIP code parameter (220119) out of the http://java.boot.by/customers?zip=220119 URI:

@Path("/customers")
public class CustomerResource {
    @GET
    public Customer getCustomerByZipCode(@QueryParam("zip") Long zip) {
        // ...
    }
}
						

With all parameters annotations, you can add a @DefaultValue annotation to define the default value for a parameter you are expecting. The default value is used if the corresponding metadata is not present in the request. In the following code, if the query parameter age is not in the request, the default value 50 is set:

@Path("/customers")
public class CustomerResource {
    @GET
    public Response getCustomers(@DefaultValue("50") @QueryParam("age") int age) {
        // ...
    }
}
						

If the @DefaultValue is not used on conjuction with @QueryParam and the query parameter is not present in the request then value will be an empty collection for List, Set or SortedSet, null for other object types, and the Java-defined default for primitive types.

The @PathParam and the other parameter-based annotations, @MatrixParam, @HeaderParam, @CookieParam and @FormParam obey the same rules as @QueryParam.

@MatrixParam extracts information from URL path segments (; is used as a delimiter instead of ?). For example, you will be able to extract the author's name (Mikalai) out of this URL: http://java.boot.by/products/books;author=Mikalai.

@HeaderParam extracts information from the HTTP headers. @CookieParam extracts information from the cookies declared in cookie related HTTP headers.

@FormParam is slightly special because it extracts information from a request representation that is of the MIME media type "application/x-www-form-urlencoded" and conforms to the encoding specified by HTML forms. This parameter is very useful for extracting information that is POSTed by HTML forms, for example the following extracts the form parameter named "name" from the POSTed form data:

@POST
@Consumes("application/x-www-form-urlencoded")
public void post(@FormParam("name") String name) {
    // Store the message
}
						

If it is necessary to obtain a general map of parameter name to values then, for query and path parameters it is possible to do the following:


@GET
public String get(@Context UriInfo ui) {
    MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
    MultivaluedMap<String, String> pathParams = ui.getPathParameters();
}

						

For header and cookie parameters the following:


@GET
public String get(@Context HttpHeaders hh) {
    MultivaluedMap<String, String> headerParams = hh.getRequestHeaders();
    Map<String, Cookie> pathParams = hh.getCookies();
}

						

In general @Context can be used to obtain contextual Java types related to the request or response. For form parameters it is possible to do the following:


@POST
@Consumes("application/x-www-form-urlencoded")
public void post(MultivaluedMap<String, String> formParams) {
    // Store the message
}

						

4.1.5.  Configure container role based access control via method-permissions in ejb-jar.xml or via access control annotations on EJB.

blah-blah

4.1.6.  Configure caller authentication (for access control protected methods) and message protection by Servlet Container via web.xml.

blah-blah

4.1.7.  Compile and package.

A JAX-RS application is packaged as a Servlet in a .war file. The Application subclass, resource classes, and providers are packaged in WEB-INF/classes, required libraries are packaged in WEB-INF/lib. Included libraries may also contain resource classes and providers as desired.

4.1.8.  Deploy the web service in a Java EE servlet container.

The resources and providers that make up a JAX-RS application are configured via an application-supplied subclass of Application. An implementation may provide alternate mechanisms for locating resource classes and providers (e.g. runtime class scanning) but use of javax.ws.rs.core.Application is the only portable means of configuration:


public abstract class Application {
    public abstract Set<Class<?>> getClasses();
    public Set<Object>getSingletons();
}

						

The getClasses() method returns a list of classes you want to deploy into the JAX-RS environment. They can be @Path annotated classes, in which case, you are specifying that you want to use the default per-request component model. The getSingletons() method returns actual instances that you create yourself within the implementation of your Application class. You use this method when you want to have control over instance creation of your resource classes and providers.

JAX-RS provides the deployment agnostic abstract class Application for declaring root resource and provider classes, and root resource and provider singleton instances. A web service may extend this class to declare root resource and provider classes. For example:


public class MyApplicaton extends Application {
    public Set<Class<?>> getClasses() {
        Set<Class<?>> s = new HashSet<Class<?>>();
        s.add(by.boot.java.HelloWorldResource.class);
        return s;
    }
}

						

Alternatively it is possible to reuse one of Jersey's implementations that scans for root resource and provider classes given a classpath or a set of package names. Such classes are automatically added to the set of classes that are returned by getClasses. For example, the following scans for root resource and provider classes in packages "by.boot.java", "by.boot.rest" and in any sub-packages of those two:

public class MyApplication extends PackagesResourceConfig {
    public MyApplication() {
        super("by.boot.java;by.boot.rest");
    }
}
						

For servlet deployments JAX-RS specifies that a class that implements Application may be declared instead of a servlet class in <servlet-class> element of a web.xml. This is supported for a web container implementing Servlet 3.0 as follows:


<web-app>
    <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>MyApplication</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/resources/*</url-pattern>
    </servlet-mapping>

    ....

</web-app>

						

If using Servlet 2.x then instead it is necessary to declare the Jersey specific servlet (or any other JAX-RS implementation-supplied Servlet class) and the application-supplied subclass of Application is identified using an init-param with a param-name of javax.ws.rs.Application as follows:


<web-app>

    <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>MyApplication</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/resources/*</url-pattern>
    </servlet-mapping>

    ....

</web-app>

						

Alternatively a simpler approach is to let Jersey choose the PackagesResourceConfig implementation automatically by declaring the packages as follows:


<web-app>

    <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>by.boot.java;by.boot.rest</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/resources/*</url-pattern>
    </servlet-mapping>

    ....

</web-app>

						

Professional hosting         'Oracle Certified Expert Web Services Developer 6' Quiz     Free SCDJWS 5.0 Guide