HTTP Method and URI Matching
JAX-RS Injection
JAX-RS Content Handlers
Server Responses and Exception Handling
HATEOAS
Http Content Negotiation
Scaling JAX-RS Apps
@Target({ElementType.Method})
@HttpMethod(HttpMethod.GET)
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {
}
// Maps GET /orders/unpaid to getUnpaidOrders()
@Path("/orders")
public class OrderResource {
@GET
@Path("unpaid")
public String getUnpaidOrders() {
// ...
}
}
@Path("/customers")
public class CustomerDBHandler {
@Path("{database}-db")
public CustomerResource getDatabase(@PathParam("database") String db) {
return locateCustomerResource(db);
}
private CustomerResource locateCustomerResource(String db) {
// ...
}
}
public class CustomerResource {
@POST
@Consumes("application/xml")
public Response createCustomer(...) {
// ...
}
// ...
}
@Path("/a")
public class Resource1 {
@GET
@Path("/b")
public Response get(){}
}
@Path("/{any: .+}")
public class Resource2 {
@GET
public Response get(){}
@OPTIONS
public Response options(){}
}
@javax.ws.rs.HeaderParam
@javax.ws.rs.CookieParam
@javax.ws.rs.core.Context
@Path("/customers")
public class CustomerResource {
@Path("{id}")
@GET
@Produces("application/xml")
public StreamingOutput getCustomer(@PathParam("id") int id) {
// ...
}
@Path("{first}-{last}")
@GET
@Produces("application/xml")
public StreamingOutput getCustomer(
@PathParam("first") String firstName,
@PathParam("last") String lastName) {
// ...
}
}
public interface PathSegment {
String getPath();
MultivaluedMap<String, String> getMatrixParameters();
}
@Path("/cars/{make}")
public class CarResource {
@GET
@Path("/{model}/{year}")
@Produces("image/jpeg")
public Jpeg getPicture(
@PathParam("make") String make,
@PathParam("model") PathSegment car,
@PathParam("year") String year) {
String carColor = car.getMatrixParameters().getFirst("color");
}
}
@Path("/cars/{make}")
public class CarResource {
@GET
@Path("/{model : .+}/year/{year}")
@Produces("image/jpeg")
public Jpeg getPicture(
@PathParam("make") String make,
@PathParam("model") List<PathSegment> car,
@PathParam("year") String year) {
// ...
}
}
public interface UriInfo {
public String getPath();
public String getPath(boolean decode);
public List<PathSegment> getPathSegments();
public List<PathSegment> getPathSegments(boolean decode);
public MultivaluedMap<String, String> getPathParameters();
public MultivaluedMap<String, String> getPathParameters(boolean decode);
// ...
}
@Path("/cars/{make}")
public class CarResource {
@GET
@Path("/{model}/{year}")
@Produces("image/jpeg")
public Jpeg getPicture(@Context UriInfo info) {
String make = info.getPathParameters().getFirst("make");
PathSegment model = info.getPathSegments().get(1);
String color = model.getMatrixParameters().getFirst("color");
}
}
@Path("/{make}")
public class CarResource {
@GET
@Path("/{model}/{year}")
@Produces("image/jpeg")
public Jpeg getPicture(
@PathParam("make") String make,
@PathParam("model") String model,
@MatrixParam("color") String color) {
// ...
}
}
@Path("/customers")
public class CustomerResource {
@GET
@Produces("application/xml")
public String getCustomers(
@QueryParam("start") int start,
@QueryParam("size") int size) {
// ...
}
}
// Programmatic Query Param
@Path("/customers")
public class CustomerResource {
@GET
@Produces("application/xml")
public String getCustomers(@Context UriInfo info) {
String start = info.getQueryParameters().getFirst("start");
String size = info.getQueryParameters().getFirst("size");
// ...
}
}
@Path("/customers")
public class CustomerResource {
@POST
public void createCustomer(
@FormParam("firstname") String first,
@FormParam("lastname") String last) {
// ...
}
}
@Path("/myservice")
public class MyService {
@GET
@Produces("text/html")
public String get(@HeaderParam("Referer") String referer) {
// ...
}
}
public interface HttpHeaders {
public List<String> getRequestHeader(String name);
public MultivaluedMap<String, String> getRequestHeaders();
// ...
}
@Path("/myservice")
public class MyService {
@GET
@Produces("text/html")
public String get(@Context HttpHeaders headers) {
String referer = headers.getRequestHeader("Referer").get(0);
for (String header : headers.getRequestHeaders().keySet()) {
System.out.println("This header was set: " + header);
}
// ...
}
}
@Path("/myservice")
public class MyService {
@GET
@Produces("text/html")
public String get(@CookieParam("customerId") int custId) {
// ...
}
}
public class Cookie {
public String getName() {...}
public String getValue() {...}
public int getVersion() {...}
public String getDomain() {...}
public String getPath() {...}
// ...
}
@Path("/myservice")
public class MyService {
@GET
@Produces("text/html")
public String get(@CookieParam("customerId") Cookie custId) {
// ...
}
}
// Callback interface
public interface StreamingOutput {
void write(OutputStream os) throws IOException, WebApplicationException;
}
//
@Path("/")
public class ResourceX {
@GET
@Produces("text/plain")
StreamingOutput get(){
// anonymous inner class implementation
return new StreamingOutput() {
public void write(OutputStream os)
throws IOException, WebApplicationException {
os.write("Hi".getBytes());
}
}
}
}
@Path("/")
public class Resource {
@PUT
public void putInfo(InputStream inputStream) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1000];
int wasRead = 0;
do {
wasRead = inputStream.read(buffer);
if(wasRead > 0) {
baos.write(buffer, 0, wasRead);
}
} while(wasRead > -1);
byte[] bytes = boas.toByteArray();
String input = new String(bytes);
System.out.println(input);
}
}
@Path("/")
public class Resource {
@PUT
public void putInfo(Reader reader) {
LineNumberReader lineReader = new LineNumberReader(reader);
do {
String line = lineReader.readLine();
if(line != null) System.out.println(line);
} while(line != null);
}
@GET
@Path("{file: .+}")
@Produces("text/plain")
public InputStream getFile(@PathParam("file") String path) {
FileInputStream is = new FileInputStream(path);
return is;
}
}
@Path("/")
public class Resource {
@GET
@Path("{file: .+}")
@Produces("text/plain")
public InputStream getFile(@PathParam("file") String path) {
return new File(path);
}
@POST
public void post(File file) {
Reader reader = new Reader(new FileInputStream(file));
LineNumberReader lineReader = new LineNumberReader(reader);
do {
String line = lineReader.readLine();
if (line != null)
System.out.println(line);
} while (line != null);
}
}
@Path("/")
public class Resource {
@GET @Produces("text/plain")
public byte[] get() {
return "Hi".getBytes();
}
@POST
@Consumes("text/plain")
public void post(byte[] bytes) {
System.out.println(new String(bytes));
}
}
@Path("/")
public class Resource {
@GET
@Produces("application/xml")
public String get() {
return "<customer><name>Max Muster</name></customer>";
}
@POST
@Consumes("text/plain")
public void post(String str) {
System.out.println(str);
}
}
@Path("/")
public class Resource {
@POST
@Consumes("application/x-www-form-urlencoded")
@Produces("application/x-www-form-urlencoded")
public MultivaluedMap<String,String> post(
MultivaluedMap<String, String> form) {
return form;
}
}
@Path("/transform")
public class TransformationService {
@POST
@Consumes("application/xml")
@Produces("application/xml")
public String post(Source source) {
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer(new StreamSource("foo.xsl"));
StringWriter writer = new StringWriter();
transformer.transform(source, StreamResult(writer));
return writer.toString();
}
}
@XmlRootElement(name="customer")
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlAttribute
protected int id;
@XmlElement
protected String fullname;
public Customer() {}
public int getId() { return this.id; }
public void setId(int id) { this.id = id; }
public String getFullName() { return this.fullname; }
public void setFullName(String name} { this.fullname = name; }
}
Customer customer = new Customer();
customer.setId(42);
customer.setName("Max Muster");
JAXBContext ctx = JAXBContext.newInstance(Customer.class);
StringWriter writer = new StringWriter();
ctx.createMarshaller().marshal(customer, writer);
String custString = writer.toString();
customer = (Customer)ctx.createUnmarshaller().unmarshal(new StringReader(custString));
@Path("/customers")
public class CustomerResource {
@GET
@Path("{id}")
@Produces("application/xml")
public Customer getCustomer(@PathParam("id") int id) {
Customer cust = findCustomer(id);
return cust;
}
@POST
@Consumes("application/xml")
public void createCustomer(Customer cust) {
// ...
}
}
public abstract class Response {
public abstract Object getEntity();
public abstract int getStatus();
public abstract MultivaluedMap<String, Object> getMetadata();
// ...
}
public abstract class Response {
// ...
public static ResponseBuilder status(Status status) {...}
public static ResponseBuilder status(int status) {...}
public static ResponseBuilder ok() {...}
public static ResponseBuilder ok(Object entity) {...}
public static ResponseBuilder ok(Object entity, MediaType type) {...}
public static ResponseBuilder serverError() {...}
public static ResponseBuilder created(URI location) {...}
public static ResponseBuilder noContent() {...}
public static ResponseBuilder notModified() {...}
public static ResponseBuilder seeOther(URI location) {...}
public static ResponseBuilder temporaryRedirect(URI location) {...}
public static ResponseBuilder notAcceptable(List<Variant> variants) {...}
public static ResponseBuilder fromResponse(Response response) {...}
// ...
}
public static abstract class ResponseBuilder {
public abstract Response build();
public abstract ResponseBuilder clone();
public abstract ResponseBuilder status(int status);
public ResponseBuilder status(Status status) {...}
public abstract ResponseBuilder entity(Object entity);
public abstract ResponseBuilder type(MediaType type);
public abstract ResponseBuilder variant(Variant variant);
public abstract ResponseBuilder language(String language);
public abstract ResponseBuilder location(URI location);
public abstract ResponseBuilder contentLocation(URI location);
public abstract ResponseBuilder tag(String tag);
public abstract ResponseBuilder lastModified(Date lastModified);
public abstract ResponseBuilder cacheControl(CacheControl cacheControl);
public abstract ResponseBuilder expires(Date expires);
public abstract ResponseBuilder header(String name, Object value);
public abstract ResponseBuilder cookie(NewCookie... cookies);
}
@Path("/textbook")
public class TextBookService {
@GET
@Produces("text/plain")
public Response getBook() {
String book = "A book...";
ResponseBuilder builder = Response.ok(book);
builder.language("fr")
.header("Some-Header", "some value");
return builder.build();
}
}
public class NewCookie extends Cookie {
public static final int DEFAULT_MAX_AGE = −1;
public NewCookie(String name, String value) {}
public NewCookie(String name, String value, String path,
String domain, String comment,
int maxAge, boolean secure) {}
public NewCookie(String name, String value, String path,
String domain, int version, String comment,
int maxAge, boolean secure) {}
public NewCookie(Cookie cookie) {}
public NewCookie(Cookie cookie, String comment,
int maxAge, boolean secure) {}
public static NewCookie valueOf(String value)
throws IllegalArgumentException {}
public String getComment() {}
public int getMaxAge() {}
public boolean isSecure() {}
public Cookie toCookie() {}
}
@Path("/")
public class MyService {
@GET
public Response get() {
NewCookie cookie = new NewCookie("key", "value");
ResponseBuilder builder = Response.ok("hi", "text/plain");
return builder.cookie(cookie).build();
}
}
public enum Status {
OK(200, "OK"),
CREATED(201, "Created"),
ACCEPTED(202, "Accepted"),
NO_CONTENT(204, "No Content"),
MOVED_PERMANENTLY(301, "Moved Permanently"),
SEE_OTHER(303, "See Other"),
NOT_MODIFIED(304, "Not Modified"),
TEMPORARY_REDIRECT(307, "Temporary Redirect"),
BAD_REQUEST(400, "Bad Request"),
UNAUTHORIZED(401, "Unauthorized"),
FORBIDDEN(403, "Forbidden"),
NOT_FOUND(404, "Not Found"),
NOT_ACCEPTABLE(406, "Not Acceptable"),
CONFLICT(409, "Conflict"),
GONE(410, "Gone"),
PRECONDITION_FAILED(412, "Precondition Failed"),
UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"),
INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
SERVICE_UNAVAILABLE(503, "Service Unavailable");
public enum Family {
INFORMATIONAL, SUCCESSFUL, REDIRECTION,
CLIENT_ERROR, SERVER_ERROR, OTHER
}
public Family getFamily()
public int getStatusCode()
public static Status fromStatusCode(final int statusCode)
}
public class WebApplicationException extends RuntimeException {
public WebApplicationException() {...}
public WebApplicationException(Response response) {...}
public WebApplicationException(int status) {...}
public WebApplicationException(Response.Status status) {...}
public WebApplicationException(Throwable cause) {...}
public WebApplicationException(Throwable cause, Response response) {...}
public WebApplicationException(Throwable cause, int status) {...}
public WebApplicationException(Throwable cause, Response.Status status) {...}
public Response getResponse() {...}
}
@Path("/customers")
public class CustomerResource {
@GET
@Path("{id}")
@Produces("application/xml")
public Customer getCustomer(@PathParam("id") int id) {
Customer cust = findCustomer(id);
if (cust == null) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
return cust;
}
}
public interface ExceptionMapper<E extends Throwable> {
Response toResponse(E exception);
}
@Provider
public class EntityNotFoundMapper implements ExceptionMapper<EntityNotFoundException> {
public Response toResponse(EntityNotFoundException e) {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
BadRequestException 400 Malformed message
NotAuthorizedException 401 Authentication failure
ForbiddenException 403 Not permitted to access
NotFoundException 404 Couldn’t find resource
NotAllowedException 405 HTTP method not supported
NotAcceptableException 406 Client media type requested not supported
NotSupportedException 415 Client posted media type not supported
InternalServerErrorException 500 General server error
ServiceUnavailableException 503 Server is temporarily unavailable or busy
@Path("/customers")
public class CustomerResource {
@GET
@Path("{id}")
@Produces("application/xml")
public Customer getCustomer(@PathParam("id") int id) {
Customer cust = findCustomer(id);
if (cust == null) {
throw new NotFoundException());
}
return cust;
}
}
public ServiceUnavailableException(Long retryAfter) {}
public ServiceUnavailableException(Date retryAfter) {}
<customers>
<link rel="next"
href="http://example.com/customers?start=2&size=2" type="application/xml"/>
<customer id="123">
<name>Bill Burke</name>
</customer>
<customer id="332">
<name>Roy Fielding</name>
</customer>
</customers>
<customers>
<link rel="next"
href="http://example.com/customers?start=2&size=2" type="application/xml"/>
<customer id="123">
<name>Bill Burke</name>
</customer>
<customer id="332">
<name>Roy Fielding</name>
</customer>
</customers>
PUT /orders/333 HTTP/1.1
Content-Type: application/xml
<order id="333">
<customer id="123">...</customer>
<amount>$99.99</amount>
<cancelled>true</cancelled>
<order-entries>
...
</order-entries>
</order>
Better:
<order id="333">
<customer id="123">...</customer>
<amount>$99.99</amount>
<cancelled>false</cancelled>
<link rel="cancel"
href="http://example.com/orders/333/cancelled"/>
<order-entries>
...
</order-entries>
</order>
(Name) (Descr)
previous A URI that refers to the immediately preceding document
in a series of documents.
next A URI that refers to the immediately following document
in a series of documents.
edit A URI that can be retrieved, updated, and deleted.
payment A URI where payment is accepted. It is meant as a general way
to facilitate acts of payment.
...
HTTP/1.1 200 OK
Content-Type: application/xml
Link: <http://example.com/orders/333/cancelled>; rel=cancel
<order id="333">
...
</order>
public abstract class UriBuilder {
public static UriBuilder fromUri(URI uri)
throws IllegalArgumentException
public static UriBuilder fromUri(String uri)
throws IllegalArgumentException
public static UriBuilder fromPath(String path)
throws IllegalArgumentException
public static UriBuilder fromResource(Class<?> resource)
throws IllegalArgumentException
public static UriBuilder fromLink(Link link)
throws IllegalArgumentException
// ...
}
public abstract UriBuilder clone();
public abstract UriBuilder uri(URI uri)
throws IllegalArgumentException;
public abstract UriBuilder scheme(String scheme)
throws IllegalArgumentException;
public abstract UriBuilder schemeSpecificPart(String ssp)
throws IllegalArgumentException;
public abstract UriBuilder userInfo(String ui);
// ..
UriBuilder builder = UriBuilder.fromPath("/customers/{id}");
builder.scheme("http")
.host("{hostname}")
.queryParam("param={param}");
// http://{hostname}/customers/{id}?param={param}
UriBuilder clone = builder.clone();
URI uri = clone.build("example.com", "333", "value");
// http://example.com/customers/333?param=value
// Map usage
Map<String, Object> map = new HashMap<String, Object>();
map.put("hostname", "example.com");
map.put("id", 333);
map.put("param", "value");
UriBuilder clone = builder.clone();
URI uri = clone.buildFromMap(map);
@Path("/customers")
public class CustomerService {
@Path("{id}")
public Customer getCustomer(@PathParam("id") int id) {
//...
}
}
// ..
UriBuilder builder = UriBuilder.fromResource(CustomerService.class);
builder.host("{hostname}")
builder.path(CustomerService.class, "getCustomer");
// http://{hostname}/customers/{id}
URI uri = UriBuilder.fromUri("/{id}").build("a/b");
// /a%2Fb
// Encode methods
public abstract URI build(Object[] values, boolean encodeSlashInPath)
throws IllegalArgumentException, UriBuilderException
public abstract URI buildFromMap(Map<String, ?> values, boolean encodeSlashInPath)
throws IllegalArgumentException, UriBuilderException
// Template methods
public abstract UriBuilder resolveTemplate(String name, Object value);
public abstract UriBuilder resolveTemplate(String name, Object value,
boolean encodeSlashInPath);
public abstract UriBuilder resolveTemplateFromEncoded(String name,
Object value);
// ...
String original = "http://{host}/{id}";
String newTemplate = UriBuilder.fromUri(original)
.resolveTemplate("host", "localhost")
.toTemplate();
public interface UriInfo {
public URI getRequestUri();
public UriBuilder getRequestUriBuilder();
public URI getAbsolutePath();
public UriBuilder getAbsolutePathBuilder();
public URI getBaseUri();
public UriBuilder getBaseUriBuilder();
// ...
}
@Path("/customers")
public class CustomerService {
@GET
@Produces("application/xml")
public String getCustomers(@Context UriInfo uriInfo) {
UriBuilder nextLinkBuilder = uriInfo.getAbsolutePathBuilder();
nextLinkBuilder.queryParam("start", 5);
nextLinkBuilder.queryParam("size", 10);
URI next = nextLinkBuilder.build();
//... set up the rest of the document ...
}
// ...
}
// http://example.com/jaxrs/customers?start=5&size=10
public interface UriInfo {
// ...
public List<String> getMatchedURIs();
public List<String> getMatchedURIs(boolean decode);
}
@Path("/customers")
public class CustomerDatabaseResource {
@Path("{database}-db")
public CustomerResource getDatabase(@PathParam("database") String db) { // ... }
}
public class CustomerResource {
public CustomerResource(Map db) {}
@GET
@Path("{id}")
@Produces("application/xml")
public StreamingOutput getCustomer(@PathParam("id") int id) { // ...}
}
// getMatchedURIs
// http://example.com/customers
// http://example.com/customers/usa-db
// http://example.com/customers/usa-db/333
public abstract class Link {
public abstract URI getUri();
public abstract UriBuilder getUriBuilder();
public abstract String getRel();
public abstract List<String> getRels();
public abstract String getTitle();
public abstract String getType();
// ...
public static Builder fromUri(URI uri);
public static Builder fromUri(String uri);
public static Builder fromUriBuilder(UriBuilder uriBuilder);
// ...
}
Link link = Link.fromUri("http://{host}/root/customers/{id}")
.rel("update").type("text/plain").build("localhost", "1234");
// <http://localhost/root/customers/1234>; rel="update"; type="text/plain"
@Path
@GET
Response get() {
Link link = Link.fromUri("a/b/c").build();
Response response = Response.noContent()
.links(link)
.build();
return response;
}
// JAXB provides XmlAdapter for Link class
@XmlRootElement
public class Customer {
private String name;
private List<Link> links = new ArrayList<Link>();
@XmlElement
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@XmlElement(name="link")
@XmlJavaTypeAdapter(Link.JaxbAdapter.class)
public List<Link> getLinks() {
return links;
}
}
GET http://example.com/stuff
Accept: application/xml, application/json
GET http://example.com/stuff
Accept: text/*, text/html;level=1
GET http://example.com/stuff
Accept: text/*, text/html;level=1, */*, application/xml
GET http://example.com/stuff
Accept: text/*;q=0.9, */*;q=0.1, audio/mpeg, application/xml;q=0.5
// Language
GET http://example.com/stuff
Accept-Language: en-us, es, fr
GET http://example.com/stuff
Accept-Language: fr;q=1.0, es;q=1.0, en;q=0.1
// Encoding
GET http://example.com/stuff
Accept-Encoding: gzip, deflate
GET http://example.com/stuff
Accept-Encoding: gzip;q=1.0, compress;0.5; deflate;q=0.1
// Method Dispatching according to Accept header
@Path("/customers")
public class CustomerResource {
@GET
@Path("{id}")
@Produces("application/xml")
public Customer getCustomerXml(@PathParam("id") int id) {
// ...
}
@GET
@Path("{id}")
@Produces("text/plain")
public String getCustomerText(@PathParam("id") int id) {
// ...
}
@GET
@Path("{id}")
@Produces("application/json")
public Customer getCustomerJson(@PathParam("id") int id) {
// ...
}
}
@Path("/service")
public class MyService {
@GET
@Produces({"application/xml", "application/json"})
public Customer getCustomer(@PathParam("id") int id) {
// ...
}
}
public interface HttpHeaders {
public List<MediaType> getAcceptableMediaTypes();
public List<Locale> getAcceptableLanguages();
// ...
}
@Path("/myservice")
public class MyService {
@GET
public Response get(@Context HttpHeaders headers) {
MediaType type = headers.getAcceptableMediaTypes().get(0);
Locale language = headers.getAcceptableLanguages().get(0);
Object responseObject = ...;
Response.ResponseBuilder builder = Response.ok(responseObject, type);
builder.language(language);
return builder.build();
}
}
public interface Request {
Variant selectVariant(List<Variant> variants) throws IllegalArgumentException;
// ...
}
@Path("/myservice")
public class MyService {
@GET
Response getSomething(@Context Request request) {
List<Variant> variants = new ArrayList<Variant>();
variants.add(new Variant(
MediaType.APPLICATION_XML_TYPE,
"en", "deflate"));
variants.add(new Variant(
MediaType.APPLICATION_XML_TYPE,
"es", "deflate"));
// Pick the variant
Variant v = request.selectVariant(variants);
Object entity = ...; // get the object you want to return
ResponseBuilder builder = Response.ok(entity);
builder.type(v.getMediaType())
.language(v.getLanguage())
.header("Content-Encoding", v.getEncoding());
return builder.build();
}
}
@Path("/myservice")
public class MyService {
@GET
Response getSomething(@Context Request request) {
Variant.VariantListBuilder vb = Variant.VariantListBuilder.newInstance();
vb.mediaTypes(MediaType.APPLICATION_XML_TYPE,
MediaType.APPLICATION_JSON_TYPE)
.languages(new Locale("en"), new Locale("es"))
.encodings("deflate", "gzip").add();
List<Variant> variants = vb.build();
// Pick the variant
Variant v = request.selectVariant(variants);
Object entity = ...; // get the object you want to return
ResponseBuilder builder = Response.ok(entity);
builder.type(v.getMediaType())
.language(v.getLanguage())
.header("Content-Encoding", v.getEncoding());
return builder.build();
}
}
// /customers/en-US/xml/3323
// /customers/3323.xml.en-US
@Path("/customers/{id}.{type}.{language}")
@GET
public Customer getCustomer(@PathParam("id") int id,
@PathParam("type") String type, @PathParam("language") String language) {
// ...
}
@Path("/customers")
public class CustomerResource {
@Path("{id}")
@GET
@Produces("application/xml")
public Response getCustomer(@PathParam("id") int id) {
Customer cust = findCustomer(id);
ResponseBuilder builder = Response.ok(cust, "application/xml");
Date date = Calendar.getInstance(TimeZone.getTimeZone("GMT"))
.set(2014, 5, 15, 16, 0);
builder.expires(date);
return builder.build();
}
}
HTTP/1.1 200 OK
Content-Type: application/xml
Expires: Tue, 15 May 2014 16:00 GMT
<customer id="123">...</customers>
@Path("/customers")
public class CustomerResource {
@Path("{id}")
@GET
@Produces("application/xml")
public Response getCustomer(@PathParam("id") int id) {
Customer cust = findCustomer(id);
CacheControl cc = new CacheControl();
cc.setMaxAge(300);
cc.setPrivate(true);
cc.setNoStore(true);
ResponseBuilder builder = Response.ok(cust, "application/xml");
builder.cacheControl(cc);
return builder.build();
}
}
HTTP/1.1 200 OK
Content-Type: application/xml
Cache-Control: private, no-store, max-age=300
<customers>...</customers>
HTTP/1.1 200 OK
Content-Type: application/xml
Cache-Control: max-age=1000
Last-Modified: Tue, 15 May 2013 09:56 EST
<customer id="123">...</customer>
GET /customers/123 HTTP/1.1
If-Modified-Since: Tue, 15 May 2013 09:56 EST
HTTP/1.1 200 OK
Content-Type: application/xml
Cache-Control: max-age=1000
ETag: "3141271342554322343200"
<customer id="123">...</customer>
GET /customers/123 HTTP/1.1
If-None-Match: "3141271342554322343200"
public class EntityTag {
public EntityTag(String value) {
//...
}
public EntityTag(String value, boolean weak) {
//...
}
public static EntityTag valueOf(String value)
throws IllegalArgumentException {
//...
}
public boolean isWeak() {
//...
}
public String getValue() {
//...
}
}
@Path("/customers")
public class CustomerResource {
@Path("{id}")
@GET
@Produces("application/xml")
public Response getCustomer(@PathParam("id") int id,
@Context Request request) {
Customer cust = findCustomer(id);
EntityTag tag = new EntityTag(
Integer.toString(cust.hashCode()));
CacheControl cc = new CacheControl();
cc.setMaxAge(1000);
ResponseBuilder builder = request.evaluatePreconditions(tag);
if (builder != null) {
builder.cacheControl(cc);
return builder.build();
}
// Preconditions not met!
builder = Response.ok(cust, "application/xml");
builder.cacheControl(cc);
builder.tag(tag);
return builder.build();
}
}
HTTP/1.1 200 OK
Content-Type: application/xml
Cache-Control: max-age=1000
ETag: "3141271342554322343200"
Last-Modified: Tue, 15 May 2013 09:56 EST
<customer id="123">...</customer>
PUT /customers/123 HTTP/1.1
If-Match: "3141271342554322343200"
If-Unmodified-Since: Tue, 15 May 2013 09:56 EST
Content-Type: application/xml
<customer id="123">...</customer>
@Path("/customers")
public class CustomerResource {
@Path("{id}")
@PUT
@Consumes("application/xml")
public Response updateCustomer(@PathParam("id") int id,
@Context Request request, Customer update ) {
Customer cust = findCustomer(id);
EntityTag tag = new EntityTag(
Integer.toString(cust.hashCode()));
Date timestamp = ...; // get the timestamp
ResponseBuilder builder =
request.evaluatePreconditions(timestamp, tag);
if (builder != null) {
// Preconditions not met!
return builder.build();
}
... perform the update ...
builder = Response.noContent();
return builder.build();
}
}
RESTful Java with JAX-RS 2.0, Bill Burke
Thank you for your attention!