Due: Tuesday, Feb 11th by 11:59 PM

This is an individual assignment

Getting Started

Download the following zipfiles and import them into Eclipse:

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:

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:

Handle HTTP POST requests as follows:

Handle DELETE requests as follows:

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:

https://cs.ycp.edu/marmoset

Important: please do not submit the CS496_Jetty project as part of your submission. Only submit CS496_Assign01.