blah-blah
blah-blah
@javax.ws.rs.PathParam
@PathParam allows you to inject the value of named URI path parameters that were defined in @Path expressions:
@Path("/employee")
public class EmployeeResource {
@Path("{id}")
@GET
public Employee getEmployee(@PathParam("id") int id) {
...
}
}
In the example above, we want to route HTTP GET requests to the relative URI pattern /employee/{id}. The getEmployee(...)
method extracts an Employee ID from the URI using @PathParam annotation. The value of the @PathParam annotation,
"id", matches the path parameter, {id}, that was defined in the @Path expression of
getEmployee(...).
While {id} represents a string segment in the request's URI, JAX-RS runtime automatically converts the value to an int
before it invokes the getEmployee(...) method. If the URI path parameter cannot be converted to an integer, the request is
considered a client error and the client will receive an HTTP 404, "Not Found" status code from the server.
@javax.ws.rs.QueryParam
The @QueryParam annotation allows you to inject individual URI query parameters into your Java parameters. For example, let's say
you wanted to query a employee database and retrieve a subset of all employees in the database. Your URI might look like this:
GET /employees?start=30&size=10
The start query parameter represents the employee index you want to start with and the size query parameter represents
how many employees you want returned. The JAX-RS service that implemented this might look like this:
@Path("/employees")
public class EmployeeResource {
@GET
@Produces("application/xml")
public String getEmployees(@QueryParam("start") int start,
@QueryParam("size") int size) {
...
}
}
The @QueryParam annotation was used to inject the URI query parameters "start" and "size" into the Java
parameters start and size. As with other annotation injection, JAX-RS runtime automatically converts the query
parameter's string into an integer.
@javax.ws.rs.MatrixParam
The @MatrixParam annotation extracts information from URL path segments.
@javax.ws.rs.HeaderParam
The @HeaderParam annotation is used to inject HTTP request header values. For example, what if your
application was interested in the web page that referred to or linked to your web service? You could access the HTTP
Referer header using the @HeaderParam annotation:
@Path("/myresource")
public class MyResource {
@GET
public String getData(@HeaderParam("Referer") String referer) {
...
}
}
The @HeaderParam annotation is pulling the Referer header directly from the HTTP
request and injecting it into the referer method parameter.
A very important aspects of REST is hyperlinks, URIs, in representations that clients can use to transition the web service to new application states (this is otherwise known as "hypermedia as the engine of application state"). HTML forms present a good example of this in practice.
Using Java API for RESTful Web Services (JAX-RS), you can use the UriInfo object to access request
headers. The UriInfo object provides methods to enable you to find or build URI information of a
request.
Using an injected UriInfo object by the JAX-RS runtime environment, the relative and absolute
uniform resource identifier (URI) information is known and available for modification. The
@javax.ws.rs.core.Context annotation indicates that a context object is injected. The
javax.ws.rs.core.UriInfo interface is the interface of the object that you want to inject. You
can use the UriInfo object to build absolute and relative URLs using the UriBuilder
class.
If a resource method signature can be modified, add the @javax.ws.rs.core.Context
javax.ws.rs.core.UriInfo parameter to the method. When the resource method is invoked, the
JAX-RS runtime environment passes an object that implements the UriInfo object; for example:
@Path("/resource")
public class RootResource {
@GET
public String getResource(@Context UriInfo uriInfo) {
// Client used this URI to reach this resource method
return uriInfo.getAbsolutePath().toASCIIString();
}
}
If a resource method signature cannot be modified and the class is a root resource, add the
@javax.ws.rs.core.Context javax.ws.rs.core.UriInfo field. When the resource is
instantiated for a request, an object that implements UriInfo is injected; for example:
@Path("/contextexample")
public class RootResource {
@Context
UriInfo uriInfo;
@GET
public String getResource() {
// Client used this URI to reach this resource method
return uriInfo.getAbsolutePath().toASCIIString();
}
}
Building URIs and building them safely is not easy with java.net.URI, which is why JAX-RS has the
UriBuilder class that makes it simple and easy to build URIs safely.
UriBuilder can be used to build new URIs or build from existing URIs. For resource classes it is more
than likely that URIs will be built from the base URI the web service is deployed at or from the request URI.
The class UriInfo provides such information (in addition to further information, see next section).
The following example shows URI building with UriInfo and UriBuilder:
@Path("/users/")
public class UsersResource {
@Context UriInfo uriInfo;
...
@GET
@Produces("application/json")
public JSONArray getUsersAsJsonArray() {
JSONArray uriArray = new JSONArray();
for (UserEntity userEntity : getUsers()) {
UriBuilder ub = uriInfo.getAbsolutePathBuilder();
URI userUri = ub
.path(userEntity.getUserid())
.build();
uriArray.put(userUri.toASCIIString());
}
return uriArray;
}
}
An instance of UriInfo can be injected into a class field or method parameter using the
@Context annotation. UriInfo provides both static and dynamic, per-request information,
about the components of a request URI. E.g. the following would return the names of any query parameters in a request:
@GET
@Produces{"text/plain"}
public String listQueryParamNames(@Context UriInfo info) {
StringBuilder buf = new StringBuilder();
for (String param: info.getQueryParameters().keySet()) {
buf.append(param);
buf.append("\n");
}
return buf.toString();
}
UriInfo can be used to obtain URIs and associated UriBuilder instances for the following
URIs: the base URI the application is deployed at; the request URI; and the absolute path URI, which is the
request URI minus any query components.
The getUsersAsJsonArray method constructs a JSONArrray where each element is a URI
identifying a specific user resource. The URI is built from the absolute path of the request URI by calling
uriInfo.getAbsolutePathBuilder(). A new path segment is added, which is the user ID, and then the
URI is built. Notice that it is not necessary to worry about the inclusion of '/' characters or that
the user ID may contain characters that need to be percent encoded. UriBuilder takes care of such details.
UriBuilder can be used to build/replace query or matrix parameters. URI templates can also be
declared, for example the following will build the URI:
http://localhost/segment?name=value
UriBuilder.fromUri("http://localhost/").
path("{a}").
queryParam("name", "{value}").
build("segment", "value");
The HTTP specification defines what HTTP response codes should be on a successful request. For example,
GET should return 200, OK and PUT should return 201, CREATED.
You can expect JAX-RS to return the same default response codes.
Sometimes, however, you need to specify your own response codes, or simply to add specific headers or cookies to
your HTTP response. JAX-RS provides a Response class for this:
@Path("/orders")
public class OrderEntryService {
@GET
@Path("{id}")
public Response getOrder(@PathParm("id") int orderId) {
Order order = OrderDAO.get(orderId);
ResponseBuilder builder = Response.ok(order);
Date expirationDate = new Date(System.currentTimeMillis() + 3000); // 3 secs
builder.expires(expirationDate);
return builder.build();
}
}
In this example, we still want to return a JAXB object with a 200 status code, but we want to add an
Expires header to the response. You use the ResponseBuilder class to build up the response,
and ResponseBuilder.build() to create the final Response instance.
Java API for RESTful Web Services (JAX-RS) enables developers to add a custom entity provider to the application. Use custom entity providers when you want to use Java types to represent incoming request message bodies as well as represent outgoing response message bodies. By adding a custom entity provider, you can deserialize custom Java types from message bodies and serialize any media type as message bodies.
A custom entity provider is created by annotating a class with a javax.ws.rs.ext.Provider annotation. The
class must implement the javax.ws.rs.ext.MessageBodyReader interface or the
javax.ws.rs.ext.MessageBodyWriter interface, or both. You must add the provider class to the list of
classes returned in the javax.ws.rs.core.Application subclass getClasses() method.
The MessageBodyReader interface represents a contract for a provider that supports the conversion of a
stream to a Java type. To add a MessageBodyReader implementation, annotate the implementation class with
@Provider. A MessageBodyReader implementation may be annotated with @Consumes
to restrict the media types for which it will be considered suitable:
package javax.ws.rs.ext;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
public interface MessageBodyReader<T extends Object> {
public boolean isReadable(Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType);
public T readFrom(Class<T> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String, String> httpHeaders,
InputStream entityStream) throws IOException, WebApplicationException;
}
The MessageBodyWriter interface represents a contract for a provider that supports the conversion of
a Java type to a stream. To add a MessageBodyWriter implementation, annotate the implementation class
with @Provider. A MessageBodyWriter implementation may be annotated with
Produces to restrict the media types for which it will be considered suitable:
package javax.ws.rs.ext;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
public interface MessageBodyWriter<T extends Object> {
public boolean isWriteable(Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType);
public long getSize(T t,
Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType);
public void writeTo(T t,
Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream) throws IOException, WebApplicationException;
}
Create a new Java class that is your custom entity provider. In this procedure, the example code creates
a reader and a writer for by.boot.java.Order types so that you can use the
by.boot.java.Order type as an incoming request entity parameter and as a return type to
contain the response entity:
public class OrderEntityProvider {
}
Add the @javax.ws.rs.ext.Provider annotation. Adding this annotation indicates to the
JAX-RS runtime environment that this class is a JAX-RS provider. If this Provider annotation
is not specified, the runtime environment does not detect that the class is a custom provider:
@javax.ws.rs.ext.Provider
public class OrderEntityProvider {
}
[optional] Add a @javax.ws.rs.Consumes and/or @javax.ws.rs.Produces annotation if
you want to limit the media types that the entity provider supports. In the following code snippet, the
provider is only invoked when the incoming Content-Type or outgoing
Content-Type is application/json:
@javax.ws.rs.ext.Provider
@javax.ws.rs.Consumes("application/json")
@javax.ws.rs.Produces("application/json")
public class OrderEntityProvider {
}
Implement javax.ws.rs.ext.MessageBodyReader<T> if the entity provider needs to
deserialize a message body.
You can use the generic type <T> to limit the types supported by the entity provider.
By defining the message body reader as
javax.ws.rs.ext.MessageBodyReader<by.boot.java.Order>, the JAX-RS runtime environment
knows that only by.boot.java.Order objects can be produced:
@javax.ws.rs.ext.Provider
@javax.ws.rs.Consumes("application/json")
@javax.ws.rs.Produces("application/json")
public class OrderEntityProvider implements MessageBodyReader<by.boot.java.Order> {
public boolean isReadable(Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType) {
return by.boot.java.Order.class == type;
}
public by.boot.java.Order readFrom(Class<by.boot.java.Order> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String, String> httpHeaders,
InputStream entityStream) throws IOException, WebApplicationException {
// This code reads from the entityStream and constructs the object.
by.boot.java.Order retObj = by.boot.java.Order.parse(entityStream);
return retObj;
}
}
If an entity provider needs to support a complex set of types, consider implementing
javax.ws.rs.ext.MessageBodyReader<Object>.
Implement javax.ws.rs.ext.MessageBodyWriter<T> if the entity provider needs to
serialize a message body. You can implement the MessageBodyReader<T> and
MessageBodyWriter<T> interfaces in the same Java class. In the following code
snippet, only the MessageBodyWriter implementation is shown:
@javax.ws.rs.ext.Provider
public class OrderEntityProvider implements MessageBodyWriter<by.boot.java.Order> {
public long getSize(by.boot.java.Order t,
Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType) {
// Return -1 if the content length cannot be determined
return -1;
}
public boolean isWriteable(Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType) {
return by.boot.java.Order.class == type;
}
public void writeTo(by.boot.java.Order t,
Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream) throws IOException, WebApplicationException {
entityStream.write(t.getBytes());
}
}
Add the custom entity provider to the javax.ws.rs.core.Application subclass and add the
provider to the set of classes returned from the getClasses() method. You can add multiple
custom entity providers to the set of classes returned:
public class MyApplication extends javax.ws.rs.core.Application {
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(OrderEntityProvider.class);
return classes;
}
}
JAX-RS has a RuntimeException class, WebApplicationException, that allows you to abort
your JAX-RS service method. It can take an HTTP status code or even a Response object as one of its
constructor parameters. For example:
@Path("/orders")
public class OrderEntryService {
@GET
@Path("{id}")
@Produces("application/xml")
public Order getOrder(@PathParm("id") int orderId) {
Order order = OrderDAO.get(orderId);
if (order == null) {
ResponseBuilder builder = Response.status(Status.NOT_FOUND);
builder.type("text/html");
builder.entity("<h3>Order Not Found</h3>");
throw new WebApplicationException(builder.build();
}
return order;
}
}
In this example, if the order is null, send a HTTP response code of NOT_FOUND with a HTML
encoded error message.
Beyond WebApplicationException, you can map non-JAX-RS exceptions that might be thrown by your
application to a Response object by registering implementations of the ExceptionMapper
class:
public interface ExceptionMapper<E extends Throwable> {
Response toResponse(E exception);
}
For example, lets say we were using JPA to locate our Order objects. We could map
javax.persistence.EntityNotFoundException to return a NOT_FOUND status code:
@Provider
public class EntityNotFoundMapper implements ExceptionMapper<EntityNotFoundException> {
Response toResponse(EntityNotFoundException exception) {
return Response.status(Status.NOT_FOUND);
}
}
or
@Provider
public class EntityNotFoundMapper implements ExceptionMapper<EntityNotFoundException> {
Response toResponse(EntityNotFoundException exception) {
return Response.status(Status.NOT_FOUND).
entity(exception.getMessage()).
type("text/plain").
build();
}
}
The above class is annotated with @Provider, this declares that the class is of interest to the
JAX-RS runtime. Such a class should be added to the set of classes of the Application instance that
is configured. When an application throws an EntityNotFoundException the toResponse method
of the EntityNotFoundMapper instance will be invoked.
Methods of a resource class that are annotated with @Path are either sub-resource methods
or sub-resource locators. Sub-resource methods handle an HTTP request directly whilst sub-resource locators
return an object that will handle an HTTP request. The presence or absence of a request method designator (e.g. @GET)
differentiates between the two:
Request method designator (@GET, @POST) presents:
Such methods, known as sub-resource methods, are treated like a normal resource method except the method is only invoked for request URIs that match a URI template created by concatenating the URI template of the resource class with the URI template of the method.
Example:
@Path("widgets")
public class WidgetsResource {
@GET
@Path("offers")
public WidgetList getDiscounted() {...}
}
In the above a GET request for the widgets/offers resource is handled directly by the getDiscounted()
sub-resource method of the resource class WidgetsResource.
Request method designator (@GET, @POST) absent:
Such methods, known as sub-resource locators, are used to dynamically resolve the object that will handle the request. Any returned object is treated as a resource class instance and used to either handle the request or to further resolve the object that will handle the request. An implementation MUST dynamically determine the class of object returned rather than relying on the static sub-resource locator return type since the returned instance may be a subclass of the declared type with potentially different annotations. Sub-resource locators may have all the same parameters as a normal resource method except that they MUST NOT have an entity parameter.
Example:
@Path("widgets")
public class WidgetsResource {
@Path("{id}")
public WidgetSubResource findWidget(@PathParam("id") String id) {
return new WidgetSubResource(id);
}
}
public class WidgetSubResource {
public WidgetSubResource(String id) {...}
@GET
public Widget getDetails() {...}
}
In the above a GET request for widgets/xxx is handled by the getDetails() method of the
WidgetSubResource resource class.
blah-blah
|
|
|