Due: Tuesday, Feb 11th by 11:59 PM
This is an individual assignment
Getting Started
Download the following zipfiles and import them into Eclipse:
- CS496_Jetty.zip: the Jetty web application server and associated libraries (such as Jackson for JSON)
- CS496_Assign01.zip: the implementation of the web service using Java servlets
The assignment consists of creating a web service to manage a basic inventory system.
Your Task
The goal is to implement a RESTful inventory tracking web service. Clients can request information about the entire inventory, or a single item. Resource identifiers are assigned as follows:
- "/inventory" refers to the entire inventory
- "/inventory/Apples" refers to the item named "Apples" in the inventory
The data accepted and generated by the web service is encoded using JSON.
You can start the web service by running the main method of the Main class in the edu.ycp.cs496.fruit.main package. The web service listens for requests on port 8081. So, for example, a GET request to the URL
http://localhost:8081/inventory
should return the initial inventory as a JSON array, which is
[
{
"name": "Apples",
"quantity": 3
},
{
"name": "Oranges",
"quantity": 7
},
{
"name": "Pomegranates",
"quantity": 55
}
]
The starting point code includes a handler for the HTTP GET method which allows the client to access the entire inventory, or a single item. So, for example, the URL
http://localhost:8081/inventory/Apples
should return a single item as a JSON object:
{
"name": "Apples",
"quantity": 3
}
Your Task
Handle HTTP PUT requests as follows:
- If resource identifier is "/inventory", replaces the entire inventory with the inventory encoded in the JSON document found in the body of the request
- If the resource identifier is "/inventory/itemname", replaces the item with the given item name, replacing it with the item that is encoded in the JSON document found in the body of the request
Handle HTTP POST requests as follows:
- If the resource identifier is "/inventory", then a single item is ready from the JSON document encoded in the body of the request, and the item is added to the inventory.
Handle DELETE requests as follows:
- If the resource identifier is "/inventory", all items are deleted from the inventory
- If the resource identifier is "/inventory/itemname", the named item is deleted
Note that delete requests have no body.
"Persistence"
A real inventory web service would almost certainly use a database to store the inventory information. However, in web applications and web services, it is generally useful to have a "persistence layer" abstraction that shields the application from knowledge of how data is being stored.
In this assignment, the IDatabase interface describes the persistence operations:
/**
* Persistence operations for fruit web service.
*/
public interface IDatabase {
/**
* Get a single {@link Item} for the given item name.
*
* @param itemName the item name
* @return the {@link Item}, or null if there is no such item
*/
public Item getItem(String itemName);
/**
* Get the current inventory (list of {@link Item}s.
*
* @return the current inventory (list of {@link Item}s
*/
public List<Item> getInventory();
}
Because this is an interface, it can be implemented in a variety of ways. For this assignment, it is implemented by a FakeDatabase class, which simply stores the items in the inventory in an ArrayList:
/**
* Implementation of the {@link IDatabase} interface that stores
* objects using in-memory data structures. It doesn't
* provide actual persistence, but is useful as a proof
* of concept.
*/
public class FakeDatabase implements IDatabase {
private List<Item> inventory;
public FakeDatabase() {
inventory = new ArrayList<Item>();
// Populate initial inventory
inventory.add(new Item("Apples", 3));
inventory.add(new Item("Oranges", 7));
inventory.add(new Item("Pomegranates", 55));
}
@Override
public Item getItem(String itemName) {
for (Item item : inventory) {
if (item.getName().equals(itemName)) {
// return a copy
return new Item(item.getName(), item.getQuantity());
}
}
// no such item
return null;
}
@Override
public List<Item> getInventory() {
// return a copy
return new ArrayList<Item>(inventory);
}
}
The Database class provides access to the object that implements the IDatabase interface:
/**
* Allow access to the singleton {@link IDatabase} implementation.
*/
public class Database {
private static final IDatabase theInstance = new FakeDatabase();
/**
* Get the singleton {@link IDatabase} implementation.
*
* @return the singleton {@link IDatabase} implementation
*/
public static IDatabase getInstance() {
return theInstance;
}
}
The idea is that the single instance of IDatabase can be changed from being a FakeDatabase object to a real database at some future point without affecting any of the code that uses the persistence layer. (You won't need to do this for this assignment.)
Testing
You can use the curl program to test your implementation. (You can't use a web browser for testing because a web browser cannot generate a PUT request.)
Let's say that you have a text file called replaceApples.txt with the following JSON data:
{
"name": "Apples",
"quantity": 16
}
You can send this as the body of a PUT request using the following curl command:
curl -X PUT -d @replaceApples.txt http://localhost:8081/inventory/Apples
Note that you must run the command from the directory containing replaceApples.txt.
Note that using the -X POST option will send a POST request.
Hints
Note that the correct content type for JSON-encoded data (according to RFC 4627) is application/json. So, when returning JSON data in an HTTP response, make sure to call
resp.setContentType("application/json");
where resp is the HttpServletResponse object.
The Jackson library has a class called ObjectMapper which transparently converts between POJOs ("Plain Old Java Objects") and JSON representations. You can use the JSON.getObjectMapper() to get a singleton instance of the ObjectMapper class.
Reading an JSON object (corresponding to an instance of a model class, in this case an Item object) encoded in the body of an HTTP request:
Reader reader = req.getReader();
Item item = JSON.getObjectMapper().readValue(req.getReader(), Item.class);
Writing a Java object as a JSON object to the body of an HTTP response (assumes that resp is an HttpServletResponse):
// Assume that item is a POJO representing an inventory item
Item item = ...
Writer writer = resp.getWriter();
JSON.getObjectMapper().writeValue(writer, item);
Writing an array of Java objects as a JSON array (in this case, taking a List of Item objects, converting it to a Java array, and writing the Java array as JSON):
List<Item> itemList = ...
Item[] itemListAsArray = itemList.toArray(new Item[itemList.size()]);
Writer writer = resp.getWriter();
JSON.getObjectMapper().writeValue(writer, itemListAsArray);
Submitting
Select the CS496_Assign01 project, then click the blue up arrow icon, and enter your Marmoset username and password when prompted.
Alternatively, export the CS496_Assign01 project to a zipfile, and upload the zipfile to Marmoset as assign01: