Sunday, April 06, 2014

REST: JAX-RS by Example

REST

Annotation
Description
@PATH(your_path)
Sets the path to base URL + /your_path. The base URL is based on your application name, the servlet and the URL pattern from the web.xmlconfiguration file.
@POST, @GET, @PUT, @DELETE
Indicates that the following method will answer to an HTTP POST/GET/PUT/DELETE request, respectively.
@Produces(MediaType.TEXT_PLAIN[, more-types])
@Produces defines which MIME type is delivered by a method annotated with @GET.
@Consumes(type[, more-types])
@Consumes defines which MIME type is consumed by this method.
@PathParam
Used to inject values from the URL into a method parameter. This way you inject, for example, the ID of a resource into the method to get the correct object.

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

//Plain old Java Object it does not extend as class or implements
//an interface

//The class registers its methods for the HTTP GET request using the @GET annotation.
//Using the @Produces annotation, it defines that it can deliver several MIME types,
//text, XML and HTML.

//The browser requests per default the HTML MIME type.

//Sets the path to base URL + /hello
@Path("/hello")
public class Hello {
// This method is called if TEXT_PLAIN is request
 @GET
 @Produces(MediaType.TEXT_PLAIN)
 public String sayPlainTextHello() {
   return "Hello Jersey";
 }

 // This method is called if XML is request
 @GET
 @Produces(MediaType.TEXT_XML)
 public String sayXMLHello() {
   return "<?xml version=\"1.0\"?>" + "<hello> Hello Jersey" + "</hello>";
 }

 // This method is called if HTML is request
 @GET
 @Produces(MediaType.TEXT_HTML)
 public String sayHtmlHello() {
   return "<html> " + "<title>" + "Hello Jersey" + "</title>"
       + "<body><h1>" + "Hello Jersey" + "</body></h1>" + "</html> ";
 }

}

import static org.junit.Assert.assertEquals;

import java.net.URI;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;

import org.junit.BeforeClass;
import org.junit.Test;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;

public class HelloTest {

          private static WebResource service;

          @BeforeClass
          public static void setUpBeforeClass() throws Exception {
                   ClientConfig config = new DefaultClientConfig();
                   Client client = Client.create(config);
                   service = client.resource(getBaseURI());
          }

          @Test
          public void testSayPlainTextHello() {
                   String msg = service.path("rest").path("hello")
                                      .accept(MediaType.TEXT_PLAIN).get(ClientResponse.class)
                                      .toString();
                   System.out.println(msg);
                   assertEquals(true, msg.contains("200"));
          }

          private static URI getBaseURI() {
                   return UriBuilder.fromUri("http://localhost:8080/rest").build();
          }

}

A more complete example will be a todo service:
package rest.todo;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
// JAX-RS supports an automatic mapping from JAXB annotated class to XML and
// JSON
public class Todo {
private String id;
private String summary;
private String description;

public Todo() {

}

public Todo(String id, String summary) {
this.id = id;
this.summary = summary;
}

public String getSummary() {
return summary;
}

public void setSummary(String summary) {
this.summary = summary;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

}

package rest.todo;

import java.util.HashMap;
import java.util.Map;

public enum TodoDao {
instance;

private Map contentProvider = new HashMap();

private TodoDao() {

Todo todo = new Todo("1", "Learn REST");
todo.setDescription("Read http://www.vogella.com/tutorials/REST/article.html");
contentProvider.put("1", todo);
todo = new Todo("2", "Do something");
todo.setDescription("Read complete http://www.vogella.com");
contentProvider.put("2", todo);

}

public Map getModel() {
return contentProvider;
}

}

package rest.todo;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBElement;

public class TodoResource {
@Context
UriInfo uriInfo;
@Context
Request request;
String id;

public TodoResource(UriInfo uriInfo, Request request, String id) {
this.uriInfo = uriInfo;
this.request = request;
this.id = id;
}

// Application integration
@GET
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Todo getTodo() {
Todo todo = TodoDao.instance.getModel().get(id);
if (todo == null)
throw new RuntimeException("Get: Todo with " + id + " not found");
return todo;
}

// for the browser
@GET
@Produces(MediaType.TEXT_XML)
public Todo getTodoHTML() {
Todo todo = TodoDao.instance.getModel().get(id);
if (todo == null)
throw new RuntimeException("Get: Todo with " + id + " not found");
return todo;
}

@PUT
@Consumes(MediaType.APPLICATION_XML)
public Response putTodo(JAXBElement todo) {
Todo c = todo.getValue();
return putAndGetResponse(c);
}

@DELETE
public void deleteTodo() {
Todo c = TodoDao.instance.getModel().remove(id);
if (c == null)
throw new RuntimeException("Delete: Todo with " + id + " not found");
}

private Response putAndGetResponse(Todo todo) {
Response res;
if (TodoDao.instance.getModel().containsKey(todo.getId())) {
res = Response.noContent().build();
} else {
res = Response.created(uriInfo.getAbsolutePath()).build();
}
TodoDao.instance.getModel().put(todo.getId(), todo);
return res;
}
}

package rest.todo;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.UriInfo;

//Will map the resource to the URL todos
@Path("/todos")
public class TodosResource {
// Allows to insert contextual objects into the class,
// e.g. ServletContext, Request, Response, UriInfo
@Context
UriInfo uriInfo;
@Context
Request request;

// Return the list of todos to the user in the browser
@GET
@Produces(MediaType.TEXT_XML)
public List getTodosBrowser() {
List todos = new ArrayList();
todos.addAll(TodoDao.instance.getModel().values());
return todos;
}

// Return the list of todos for applications
@GET
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public List getTodos() {
List todos = new ArrayList();
todos.addAll(TodoDao.instance.getModel().values());
return todos;
}

// to get the total number of records
@GET
@Path("count")
@Produces(MediaType.TEXT_PLAIN)
public String getCount() {
int count = TodoDao.instance.getModel().size();
return String.valueOf(count);
}

@POST
@Produces(MediaType.TEXT_HTML)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void newTodo(@FormParam("id") String id,
@FormParam("summary") String summary,
@FormParam("description") String description,
@Context HttpServletResponse servletResponse) throws IOException {
Todo todo = new Todo(id, summary);
if (description != null) {
todo.setDescription(description);
}
TodoDao.instance.getModel().put(id, todo);

servletResponse.sendRedirect("../create_todo.html");
}

// Defines that the next path parameter after todos is
// treated as a parameter and passed to the TodoResources
// Allows to type http://localhost:8080/de.vogella.jersey.todo/rest/todos/1
// 1 will be treaded as parameter todo and passed to TodoResource
@Path("{todo}")
public TodoResource getTodo(@PathParam("todo") String id) {
return new TodoResource(uriInfo, request, id);
}
}

And finally a test client:

package rest.todo;

import java.net.URI;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;

import org.junit.BeforeClass;
import org.junit.Test;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;

public class TodoTest {

private static WebResource service;

@BeforeClass
public static void setUpBeforeClass() throws Exception {
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
service = client.resource(getBaseURI());
}

@Test
public void test() {
// Get XML
System.out.println(service.path("rest").path("todo")
.accept(MediaType.TEXT_XML).get(String.class));
// Get XML for application
System.out.println(service.path("rest").path("todo")
.accept(MediaType.APPLICATION_JSON).get(String.class));
// Get JSON for application
System.out.println(service.path("rest").path("todo")
.accept(MediaType.APPLICATION_XML).get(String.class));
}

private static URI getBaseURI() {
return UriBuilder.fromUri(
"http://localhost:8080/rest").build();
}

}


No comments: