Scott Leberknight RESTful Web Services with Jersey
May 10, 2015
Scott Leberknight
RESTful Web Services with
Jersey
"Jersey RESTful Web Services framework is [an] open source, production quality framework for developing RESTful Web Services in Java..."
Jersey...
...produces & consumes RESTful web services
(in Java, mostly pain-free)
...is the JAX-RS reference implementation
(and extends JAX-RS with additional features)
JAX-RS?
Java API for RESTful Services
It's about the Resources,
stupid...
"A JAX-RS resource is an annotated POJO that provides so-called resource methods that are able to handle HTTP requests for URI paths that the resource is bound to."
@Path("/simple")public class SimpleResource {!
@GET @Produces("text/plain") public String get() { return "it's quite simple, you see?"; }}
resource class
Root Resources
POJO (plain-old Java object)
Annotated with @Path and/or method designator (e.g. @GET)
@Path
Specifies the URI at which a resource is located
URI template capability(via @PathParam)
URI path template
@Path("/users/{userid}")@GET@Produces("application/json")public User user(@PathParam("userid") String id) { return _userRepository.getUser(id);}
Method designators
Represent the HTTP method that resource methods respond to
@GET@Produces("text/plain")public String get() { return "this is it!";}
HTTP method designator
@Produces
Specifies the MIME type of representations that a resource
produces
Media Types
MediaType class contains constants for common MIME types...
Media Types
MediaType.TEXT_PLAIN!
MediaType.APPLICATION_JSON!
MediaType.APPLICATION_XML!
MediaType.MULTIPART_FORM_DATA!
// and more...
@Consumes
Specifies the MIME type of representations that a resource
can consume
@Consumes
@Path("/users")@POST@Consumes(MediaType.APPLICATION_JSON)public Response create(User user) { Long id = _userRepository.create(user); URI uri = URIBuilder.fromURI("/users") .path(id).build(); return Response.created(uri).build();}
Sub-Resources & Paths
methods annotated with @Path in root resource classes, or...
methods returning an (annotated) resource class
@Path("/myapp")public class UserResource { // root resource @Path("users") @GET @Produces(MediaType.TEXT_HTML) public String getUsersAsHtml() { ... } ! @Path("users.xml") @GET @Produces(MediaType.APPLICATION_XML) public String getUsersAsXml() { ... }! @Path("users.json") @GET @Produces(MediaType.APPLICATION_JSON) public String getUsersAsXml() { ... }}
Sub-resource methods
Sub-resource URIs
/myapp/users
/myapp/users.xml
/myapp/users.json
Injection
Use annotations to specify data to be injected...
Query & form parameters
Headers, cookies, etc.
Security context
...and more
@*Param
QueryParam
HeaderParam
MatrixParam
FormParam
CookieParam
BeanParam
@QueryParam
@Path("query-params")@GETpublic String colors (@QueryParam("red") int red, @QueryParam("green") int green, @QueryParam("blue") int blue) { return String.format("RGB(%d,%d,%d)", red, green, blue);}
@HeaderParam
@Path("header-params")@GETpublic String headers(@HeaderParam("Accept") String accept, @HeaderParam("X-Foo") String foo) { return String.format("Accept: %s, X-Foo: %s", accept, foo);}
@MatrixParam
@Path("matrix-params")@GETpublic String matrixParams(@MatrixParam("red") int red, @MatrixParam("green") int green, @MatrixParam("blue") int blue) { return String.format("RGB(%d,%d,%d)", red, green, blue);}
What's a "matrix param"?
http://www.w3.org/DesignIssues/MatrixURIs.html
Also, see Tim-Berners Lee on matrix param design issues circa 1996...
acme.com/rest/samples/color ;red=25;green=78;blue=192
Parameters separate by semi-colons, e.g.
Not widely used, supported (or known)
What if a parameter isn't supplied???
@DefaultValue
@Path("defaults")@GETpublic String defaults( @DefaultValue("Orange") @QueryParam("color") String color, @DefaultValue(MediaType.APPLICATION_JSON) @HeaderParam("Accept") String accept) {! return String.format("color: %s, Accept: %s", color, accept);}
@FormParam
@Path("form-params")@POST@Consumes(MediaType.APPLICATION_FORM_URLENCODED)public Response formParams(@FormParam("first") String firstName, @FormParam("last") String lastName) {! String entity = String.format("%s %s", firstName, lastName); return Response.ok(entity).build();}
@BeanParam
@Path("bean-params")@POSTpublic Response beanParams(@BeanParam User user) {! String entity = String.format("User: %s %s", user.getFirstName(), user.getLastName()); return Response.ok(entity).build();}
@BeanParam class
public class User {! @QueryParam("first") @DefaultValue("John") private String _firstName;! @QueryParam("last") @DefaultValue("Doe") private String _lastName;! public String getFirstName() { return _firstName; }! public String getLastName() { return _lastName; }}
@Context
Use to obtain information related to request/response
UriInfo
@Path("context-uri-info/{thing}")@GETpublic String uriInfo(@Context UriInfo info) { URI baseUri = info.getBaseUri(); MultivaluedMap<String, String> queryParams = info.getQueryParameters(); MultivaluedMap<String, String> pathParams = info.getPathParameters();! return String.format("base URI: %s, query params: %s, path params: %s", baseUri, queryParams.entrySet(), pathParams.entrySet());}
HttpHeaders
@Path("http-headers")@GETpublic String httpHeaders(@Context HttpHeaders headers) { List<MediaType> acceptableMediaTypes = headers.getAcceptableMediaTypes(); String xFoo = headers.getHeaderString("X-Foo");! return String.format("acceptableMediaTypes: %s, X-Foo: %s", acceptableMediaTypes, xFoo);}
Raw form parameters
@Path("raw-form")@POST@Consumes(MediaType.APPLICATION_FORM_URLENCODED)public Response rawForm(MultivaluedMap<String, String> formParams) {! String entity = formParams.entrySet().toString(); return Response.ok(entity).build();}
(Note: don't actually need @Context in above)
Resource Life Cycle
Scopes
Per-Request (default)new instance created on each request
Per-lookupnew instance created on each lookup (perhaps within same request)
Singletononly one instance for entire app
JAX-RS Application Model
Defines components of a JAX-RS application, i.e. resources
Application class
Independent of deployment environment
JAX-RS apps provide concrete implementation class
Simple Application
public class SampleApplication extends Application {! @Override public Set<Class<?>> getClasses() { Set<Class<?>> classes = new HashSet<>();! classes.add(SampleResource.class); classes.add(SimpleResource.class); classes.add(UserResource.class);! return classes; }}
Jersey's implementation of Application
Jersey ResourceConfig
Extend or create programmatically
Provides additional features like resource classpath scanning
Jersey ResourceConfig
public class SampleJerseyApp extends ResourceConfig {! public SampleJerseyApp() { // scan classpath for resources packages("com.acme.rest", "com.foo.services");! // register filters register(CsrfProtectionFilter.class); register(UriConnegFilter.class); register(HttpMethodOverrideFilter.class);! // other configuration, etc. }}
Deployment Options
JavaSE (e.g. Grizzly, Jetty, Simple, etc.)
Servlet container (e.g. Tomcat, Jetty)
JavaEE (e.g. JBoss, etc.)
OSGi
Using Grizzly HTTP Server
(JavaSE deployment)
public class Server { public static final String BASE_URI = "http://localhost:8080/rest/";! public static HttpServer startServer() { ResourceConfig config = new ResourceConfig() .packages("com.acme.rest") .register(CsrfProtectionFilter.class) .register(UriConnegFilter.class) .register(HttpMethodOverrideFilter.class);! // create a new Grizzly HTTP server rooted at BASE_URI return GrizzlyHttpServerFactory .createHttpServer(URI.create(BASE_URI), config); }! public static void main(String[] args) throws Exception { final HttpServer server = startServer(); System.out.printf("Jersey app started with WADL available at " + "%sapplication.wadl\nHit enter to stop it...\n", BASE_URI); System.in.read(); server.shutdownNow(); }}
Grizzly Server
Client API
Jersey provides a client API to consume RESTful services
Written in fluent-style (method chaining)
Supports URI templates, forms, etc.
Client client = ClientBuilder.newClient();WebTarget target = client.target("http://localhost:8080/rest") .path("sample/query-params");String response = target.queryParam("red", 0) .queryParam("green", 113)
.queryParam("blue", 195) .request() .get(String.class);
Client API example
Representations
...supports common media types like JSON, XML, etc.
...implementations provide ways to convert to/from various media
representations
JAX-RS...
...supplies support out-of-box for XML, JSON, etc.
...uses MOXy as the default provider for JSON and XML conversion
Jersey...
@Path("/")public class UserResource {! @Path("/users.json") @GET @Produces(MediaType.APPLICATION_JSON) public Collection<User> users() { return _userRepository.getAllUsers(); }! @Path("/users/{userid}.json") @GET @Produces(MediaType.APPLICATION_JSON) public User user(@PathParam("userid") Integer id) { return _userRepository.getUser(id);
}! @Path("/users.json") @POST @Consumes(MediaType.APPLICATION_JSON) public Response create(User user) throws Exception { Long id = _userRepository.save(user); URI uri = UriBuilder.fromUri("users").path(id).build(); return Response.created(uri).build(); }! // more resource methods...}
Automatic JSON support
Building Responses
@POST@Consumes("application/xml")public Response post(String content) { URI createdUri = buildUriFor(content); String createdContent = create(content); return Response.created(createdUri) .entity(Entity.text(createdContent)).build();}
& more to explore...
Security
JerseyTest
Asynchronous API
Filters & Interceptors
Bean Validation
MVC templates
...and more (see Jersey user guide)
References
https://jersey.java.netJersey web site
https://jersey.java.net/documentation/latest/index.htmlJersey user guide (latest)
https://grizzly.java.net/Project Grizzly web site
https://jersey.java.net/apidocs/latest/jersey/index.htmlJersey API docs
https://jax-rs-spec.java.netJAX-RS web site
My Info
twitter: sleberknight
www.sleberknight.com/blog
scott dot leberknight at gmail dot com