import java.util.*; /** Base class for customizing REST API handlers. Create a subclass of RestApiModel that defines Handler classes (subclasses of RestApiHandler, which have custom handler methods doGet(), doPost(), etc.) to handle the four conventional REST requests: We refer to the Model classes, since they typically interact with database tables (the term "Model" refers to the Model/View/Control pattern for UI programming). Locate database interactions for API calls in model and handler objects; other concerns such as network communications, details of parsing requests and constructing replies, finding the right handler method for an HTTP request, etc., are implemented elsewhere. */ public class RestApiModel { /** if non-null, print debug output prefixed by DEBUG */ private static String DEBUG = "DEVEL "; /** data structure of all Handler objects. See addHandler() to add a handler to this data structure. */ public Hashtable handlers = initHandlers(); /** helper method for constructing a new RestApiModel object @return An initial value for handlers */ private Hashtable initHandlers() { Hashtable tmp = new Hashtable(); RestApiHandler h = new RestApiHandler(); h.recordApi("", false); tmp.put("", h); return tmp; } /** Respond to a parsed REST-style HTTP request. @param p Parser object after parsing a new HTTP request @return HTTP reply message for that request, according to the API */ public String handle(HttpParser p) { RestApiHandler h = getHandler(makeKey(p.getURLBase(), !p.getURLId().equals(""))); if (DEBUG != null) { System.out.println(DEBUG + "handle(): " + h); System.out.println(DEBUG + "handle(): " + p.getMethod()); System.out.println(DEBUG + "handle() urlId: \"" + p.getURLId()+ "\""); } if (p.getMethod().equals("POST")) return h.doPost(p); else if (p.getMethod().equals("GET")) return h.doGet(p); else if (p.getMethod().equals("PATCH")) return h.doPatch(p); else if (p.getMethod().equals("DELETE")) return h.doDelete(p); else // method not supported return p.makeReply(501, "Unsupported HTTP method " + p.getMethod() + "."); } /** helper method for generating Hashtable keys for handlers data structure */ private String makeKey(String url, boolean id) { return new String(url + (id ? " #" : "")); } /** helper method for generating Hashtable keys for handlers data structure */ private String makeKey(String url) { return url; } /** Add or replace a handler for a new API component. @param url Base URL for the new API component @param id True if API component expects a final integer path component (typically representing a record ID number), false if no such final integer should be used @param handlr Handler object providing methods such as doGet(), doPost(), etc., for implementing a REST-style API. handlr is typically an object in a subclass of RestApiHandler that implements some or all of those four doX() methods. */ public void addHandler(String url, boolean id, RestApiHandler handlr) { handlr.recordApi(url, id); handlers.put(makeKey(url,id), handlr); } /** Add or replace a handler for a new API component without a final integer in the URL. @param url Base URL for the new API component @param handlr Handler object providing methods such as doGet(), doPost(), etc., for implementing a REST-style API. handlr is typically an object in a subclass of RestApiHandler that implements some or all of those four doX() methods. */ public void addHandler(String url, RestApiHandler handlr) { addHandler(url, false, handlr); } /** Retrieve a handler object for a particular API URL, with or without a final integer ID component @param url Base URL for an API request @param id True if API component expects a final integer path component (typically representing a record ID number), false if no such final integer should be used @return Handler object for that url/id combination, or default handler (no services implemented) if no such handler exists in handlers data structure */ public RestApiHandler getHandler(String url, boolean id) { RestApiHandler h = handlers.get(makeKey(url,id)); if (h == null) { System.out.println("Warning: REST API handler for URL \"" + url + (id ? "/#" : "") + "\"\n" + "not found, using default handler"); h = handlers.get(""); } return h; } /** Retrieve a handler object for a particular API URL, with no final integer ID component @param url Base URL for an API request @return Handler object for that url and no final integer ID component, or default handler (no services implemented) if no such handler exists in handlers data structure */ public RestApiHandler getHandler(String url) { return getHandler(url, false); } /** retrieve all keys in handlers data structure. @return A space-separated list of keys in the handlers data structure. If an API url expects a final integer component in its path, the base URL is followed by a space and the character # in this return string. */ public String getHandlerKeys() { String ret = new String(); for (Enumeration e = handlers.keys(); e.hasMoreElements(); ) { ret += " " + e.nextElement(); } return ret; } /** Retrieve String representations of all handler values stored in handlers data structure. @return A space-separated list of string representations of the handlers in the handlers data structure. */ public String getHandlerValues() { String ret = new String(); for (Enumeration e = handlers.elements(); e.hasMoreElements(); ) { ret += " " + e.nextElement(); } return ret; } }