REST Web Services with Jersey

This is a "first steps" presentation of Jersey REST web services.

It is assumed you want to deploy on a regular Java application server. Client sample is provided as well, however it is well known the REST services are easy to consume without any convoluted technology.

Library

Of course you can implement REST manually, however there are already libraries that do the job for you. The most popular Java implementation is Jersey and you can find it here.

Server

So you typically use a Java application server like tomcat. In order to make your application to be Jersey aware, use the following web.xml

  1.  
  2. <web-app>
  3. <servlet>
  4. <servlet-name>Jersey Web Application</servlet-name>
  5. <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
  6. <init-param>
  7. <param-name>jersey.config.server.provider.packages</param-name>
  8. <param-value>com.tester.alfa</param-value>
  9. </init-param>
  10. <load-on-startup>1</load-on-startup>
  11. </servlet>
  12. <servlet-mapping>
  13. <servlet-name>Jersey Web Application</servlet-name>
  14. <url-pattern>/info/*</url-pattern>
  15. </servlet-mapping>
  16.  
  17. <welcome-file>index.html</welcome-file>
  18.  
  19. </web-app>
  20.  
  21.  
  22.  

See the servlet is called ServletContainer, that is common to many implementations. Now depending on what implementation you use, it can reside in a different package. Double check your fully qualified class name

The parameter jersey.config.server.provider.packages tells Jersey which packages are to be scanned for annotations. This one can have a different name as well depending on the implementation. If you have multiple packages that need to be scanned and are not related, mention all of them in an element, comma separated while keeping one element.

The Class

After this is done, simply add a number of classes to your application that have the format below. Same path can trigger different methods to be called if different http operations or content types are used.

  1.  
  2. package com.tester.alfa;
  3.  
  4. import javax.ws.rs.Consumes;
  5. import javax.ws.rs.FormParam;
  6. import javax.ws.rs.GET;
  7. import javax.ws.rs.POST;
  8. import javax.ws.rs.Path;
  9. import javax.ws.rs.PathParam;
  10. import javax.ws.rs.Produces;
  11. import javax.ws.rs.core.MediaType;
  12. import javax.ws.rs.core.Response;
  13. import javax.ws.rs.core.Response.ResponseBuilder;
  14. import javax.xml.bind.JAXBException;
  15.  
  16. @Path(value="/info/alfa")
  17. public class AlfaInfo {
  18.  
  19. @GET
  20. @Path(value="/another/{userId}")
  21. @Produces(value=MediaType.TEXT_PLAIN)
  22. @Consumes(value=MediaType.TEXT_PLAIN)
  23. public Response get(@PathParam(value="userId") String userId){
  24.  
  25. return Response.ok("the provided value: " + userId).build();
  26. }
  27.  
  28. @POST
  29. @Path(value="/post")
  30. @Produces(value=MediaType.APPLICATION_FORM_URLENCODED)
  31. @Consumes(value=MediaType.APPLICATION_FORM_URLENCODED)
  32. public Response post(@FormParam("first") String first, @FormParam("second") String second){
  33. ResponseBuilder successful = Response.ok("the used parameters: " + first + "/" + second);
  34. Response response = successful.build();
  35.  
  36. return response;
  37. }
  38.  
  39. @POST
  40. @Path(value="/post")
  41. @Produces(value=MediaType.APPLICATION_JSON)
  42. @Consumes(value=MediaType.APPLICATION_JSON)
  43. public Alfa postJson() throws JAXBException{
  44. return new Alfa("first", "second");
  45. }
  46.  
  47.  
  48. }
  49.  
  50.  

Notes

  • There is a path on the class and a path on each method. The path on the method is called subpath and is added to the path associated with the class
  • The method shows how to map a parameter in the URI to one of the method parameters by the use of @PathParam annotation
  • The method post shows how to pass and use the form related parameters. Note the @Produces and @Consumes values as they are important
  • The method postJson gets and produces a JSON. You see that it returns a variable of type Alfa. The Alfa class is merely a value object. We created a MessageBodyWriter that "knows" how to process the information from Json to Alfa and viceversa

This is the class Alfa.

  1.  
  2. package com.tester.alfa;
  3.  
  4. import javax.xml.bind.annotation.XmlElement;
  5. import javax.xml.bind.annotation.XmlRootElement;
  6.  
  7. @XmlRootElement
  8. public class Alfa {
  9. private String afirst;
  10. private String asecond;
  11.  
  12. public Alfa(){}
  13.  
  14. public Alfa(String first, String second) {
  15. super();
  16.  
  17. this.afirst = first;
  18. this.asecond = second;
  19. }
  20.  
  21. @XmlElement
  22. public String getFirst() {
  23. return afirst;
  24. }
  25.  
  26. public void setFirst(String first) {
  27. this.afirst = first;
  28. }
  29.  
  30. @XmlElement
  31. public String getSecond() {
  32. return asecond;
  33. }
  34.  
  35. public void setSecond(String second) {
  36. this.asecond = second;
  37. }
  38.  
  39. @Override
  40. public String toString() {
  41. StringBuilder builder = new StringBuilder();
  42. builder.append("Alfa [first=");
  43. builder.append(afirst);
  44. builder.append(", second=");
  45. builder.append(asecond);
  46. builder.append("]");
  47. return builder.toString();
  48. }
  49.  
  50.  
  51. }
  52.  

And this is the formatter. Please note the annotationss @Provider and @Produces

  1.  
  2. package com.tester.alfa;
  3.  
  4. import java.io.IOException;
  5. import java.io.OutputStream;
  6. import java.lang.annotation.Annotation;
  7. import java.lang.reflect.Type;
  8.  
  9. import javax.ws.rs.Produces;
  10. import javax.ws.rs.WebApplicationException;
  11. import javax.ws.rs.core.MediaType;
  12. import javax.ws.rs.core.MultivaluedMap;
  13. import javax.ws.rs.ext.MessageBodyWriter;
  14. import javax.ws.rs.ext.Provider;
  15.  
  16. @Produces(value=MediaType.APPLICATION_JSON)
  17. public class AlfaJsonFormatter implements MessageBodyWriter<Alfa> {
  18.  
  19. @Override
  20. public long getSize(Alfa arg0, Class<?> arg1, Type arg2, Annotation[] arg3, MediaType arg4) {
  21. return -1;
  22. }
  23.  
  24. @Override
  25. public boolean isWriteable(Class<?> cls, Type type, Annotation[] annotations, MediaType mediaType) {
  26. return Alfa.class.isAssignableFrom(cls);
  27. }
  28.  
  29. @Override
  30. public void writeTo(Alfa alfa, Class<?> arg1, Type arg2, Annotation[] arg3, MediaType mediaType, MultivaluedMap<String, Object> values, OutputStream stream) throws IOException, WebApplicationException {
  31. // definitely you will do it more efficient probably by using JAXB. This is only an example
  32.  
  33. String res = "{'first':'" + alfa.getFirst() + "', 'second': '" + alfa.getSecond() + "'}";
  34. stream.write(res.getBytes());
  35.  
  36. stream.flush();
  37. }
  38.  
  39. }
  40.  

Client

As mentioned, if you know how to create a HttpRequest with the right URI or body or form parameters, you know how to call the service. However this is an example of how to do that in Jersey.

So I call all three methods in the service, the GET method and the two POST methods that one accept a form post and the other one requests a JSON

  1.  
  2. package com.tester.alfa;
  3.  
  4. import javax.ws.rs.client.Client;
  5. import javax.ws.rs.client.ClientBuilder;
  6. import javax.ws.rs.client.Entity;
  7. import javax.ws.rs.client.Invocation;
  8. import javax.ws.rs.client.Invocation.Builder;
  9. import javax.ws.rs.client.WebTarget;
  10. import javax.ws.rs.core.Form;
  11. import javax.ws.rs.core.MediaType;
  12. import javax.ws.rs.core.Response;
  13.  
  14. public class Consumer {
  15. public static void main(String[] args){
  16. try {
  17. Consumer consumer = new Consumer();
  18. consumer.doGet();
  19. consumer.doPost();
  20. consumer.doJson();
  21. }
  22. catch(Exception e){
  23. e.printStackTrace();
  24. }
  25. }
  26.  
  27. private void doPost() {
  28. Client client = ClientBuilder.newClient();
  29. WebTarget target = client.target("http://localhost:8081/alfa/info/info/alfa/post");
  30.  
  31. Builder builder = target.request(MediaType.APPLICATION_FORM_URLENCODED);
  32.  
  33. Form form = new Form();
  34. form.param("first", "the value for first");
  35. form.param("second", "the second value");
  36.  
  37. Invocation getInvocation = builder.buildPost(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED));
  38. Response response = getInvocation.invoke();
  39.  
  40. String res = response.readEntity(String.class);
  41. int status = response.getStatus();
  42.  
  43. System.out.println("" + status + "/" + res);
  44. }
  45.  
  46. private void doGet() {
  47. Client client = ClientBuilder.newClient();
  48. WebTarget target = client.target("http://localhost:8080/alfa/info/info/alfa/another/testuser");
  49.  
  50. Builder builder = target.request(MediaType.TEXT_PLAIN);
  51. Invocation getInvocation = builder.buildGet();
  52. Response response = getInvocation.invoke();
  53.  
  54. String res = response.readEntity(String.class);
  55. int status = response.getStatus();
  56.  
  57. System.out.println("" + status + "/" + res);
  58. }
  59.  
  60. private void doJson(){
  61. Client client = ClientBuilder.newClient();
  62. WebTarget target = client.target("http://localhost:8081/alfa/info/info/alfa/post");
  63.  
  64. Builder builder = target.request(MediaType.APPLICATION_JSON);
  65.  
  66. Invocation getInvocation = builder.buildPost(null);
  67. Response response = getInvocation.invoke();
  68.  
  69. String res = response.readEntity(String.class);
  70. int status = response.getStatus();
  71.  
  72. System.out.println("" + status + "/" + res);
  73. }
  74. }
  75.  

Results

Just ran the client with the server mentioned above with the following result:

200/the provided value: testuser
200/the used parameters: the value for first/the second value
200/{'first':'first', 'second': 'second'}