Home
>>     < >




In-class notes for 01/26/2018

CS 284 (MCA), Interim 2018

RESTful Java server code

Java source code

Files with name that begin with ExampleRest... illustrate how to use the HTTP REST API support

  • ExampleRestServer.java

    • This is the sample server code, illustrating both what you would include in the main thread for a server (typically containing the accept() call on a ServerSocket) and what would typically go into a Worker thread (receiving the message, parsing it, sending a reply).

    • This and other code files are documented with Javadoc, which is good for a detailed look but could obscure the big picture. See ExampleRestServer.java_nodoc for a version with Javadoc removed.

    • The line

          // define API handlers
          ExampleRestModel model = new ExampleRestModel();
      
      is the main addition necessary in the main server code. See below for a discussion of models in this sense.

    • (This code also starts a second thread commandThread that processes standard input, as opposed to socket I/O, This isn't necessary for the HTTP demo, but it provides for an orderly shut down of the server using the EXIT command entered through standard input.)

    • The code

      	/***** BEGIN section to implement within a Worker thread *****/
      
      	try {
      	  ...
      	}
      
      	/***** END section to implement within a Worker thread *****/
      
      would typically be executed by a Worker thread.

      • Create streams inStream and outStream for a newly accepted socket, and read a message from inStream into a byte array inBuff[]. That message will contain an entire HTTP request corresponding to a REST API call.

      • The lines

        	  HttpParser parser = new HttpParser(inBuff, 0, count);  
        	  int code = parser.parseRequest();
        
        create a parser object for the HTTP request message in inBuff. This analyzes the content of that message and extracts

        • the HTTP method for the request, such as POST, GET, PATCH, or DELETE,

        • the API URL for the message on that server, such as /names for the "names" server example presented earlier in the term,

        • the parameters and headers in the message,

        • etc.

      • The call

            reply = model.handle(parser);
        
        forwards the parsed results for handling in the Model object defined above. If parsing returns an error code (anything other than 200), a reply is prepared based on that error code without further handling operations.

      • The return value from model.handle() is a fully formed HTTP reply message, ready to send to the client in the call

            outStream.write(reply.getBytes());
        

    • Note: The Model object model should be passed to each Worker thread, typically in an argument of the Worker constructor, so each Worker can call model's methods when processing an incoming HTTP request.

  • ExampleRestModel.java

    The class ExampleRestModel illustrates how to implement services offered by a REST API.

    • Note: The name "Model" refers to the Model/View/Control pattern for implementing applications with user interfaces.

      • The data model prescribes how data is organized and managed in an application.

      • The view refers to the visible elements of a user interface, including screens, buttons, images, etc.

      • The control consists of the algorithm logic for relating events in the view with the data stored in the model.

      For example, when user clicks on a button in the view, the windowing system activates the control code associated with that button, which may modify and/or retrieve data in the model, perhaps leading to changes in the view (a new screen, an updated image, etc.)

    • ExampleRestModel.java_nodoc provides a version of ExampleRestModel.java without Javadoc, in case that helps reveal the code structure.

    • The constructor for ExampleRestData

        public ExampleRestModel() {
          addHandler("/", new CountHandler());
          addHandler("/names", new NamesHandler());
        }
      
      contains calls to set up "API handlers" for implementing REST operations associated with a particular URL. These handlers implement the services associated with the URLs / and /names associated with this server.

    • Note: Ordinarily, the data model uses database tables in a DBMS to store, manage, and retrieve data. In this simplified example, we will store information in local variables, rather than the safer and more robust database tables.

      Consequently, you would ordinarily pass an open database connection (typically set up once in the server's main thread) as an argument to the Model constructor, so it can perform database interactions.

      This design strategy locates all the database interactions in a Model class, separate from the setup and connection operations in the Server's main thread and from the communication management in the Worker thread class. This separation of concerns makes it easier to manage the code development and maintenance.

    • The two addHandler() calls above create objects of type CountHandler and NamesHandler, respectively. These types provide the custom implementation for the server's API services. In the example, these classes are implemented as inner classes within ExampleRestModel, but they could also be implemented in separate code modules, as anonymous classes, etc. (compare to GUI component adapters discussed in an early lab).

    • The class CountHandler extends the base class RestApiHandler, which provides default implementations of four methods for handling REST API calls:

      • doPost() for POST requests (Create);

      • doGet() for GET requests (Read);

      • doPatch() for PATCH requests (Update); and

      • doDelete() for DELETE requests (Delete).

      The default behavior in RestApiHandler is for all of these handler methods to generate an HTTP reply with the code 501 - Not Implemented.

      The subclass CountHandler overrides three of these methods, each of which modifies or obtains the (local) data and creates an appropriate HTTP reply message (to be sent by the Worker thread). These custom methods doGet(), etc., implement the REST API operations.

    • Likewise, the subclass NamesHandler defines the /names API operations

The other three Java source files define the classes used in the ExampleRest....java files. You shouldn't need to change these unless you want to add some new capability.

  • HttpParser.java implements parsing of HTTP requests and generation of HTTP reply messages.

  • RestApiHandler.java is the base class for API Handlers such as CountHandler and NamesHandler.

  • RestApiModel.java is the base class for Models such as ExampleRestModel. This base class implements the method handle() that looks up an appropriate API handler for a parsed HTTP request and calls the appropriate do...() method for that request.

Java source code

  1. Copy the Java source files above into an appropriate subdirectory D in your working directory on the Link, and compile using

    javac ExampleRestServer.java
    

  2. Copy the files Test/ExampleRestServer.sh, Test/ExampleRestServer.out, and Test/tweak into a subdirectory Test of D. Issue the shell commands

    chmod +x Test/tweak Test/ExampleRestServer.sh
    
    Also, modify Test/ExampleRestServer.sh to use one of your assigned ports.

  3. Enter the shell command

    Test/ExampleRestServer.sh
    
    to start your server. You should not see any lines of output (these are captured by the script), nor should you see another shell prompt yet.

  4. On your laptop or another machine with a node setup, , copy the files Test/example-client.js and Test/example-client.out to a directory set up for running javascript code. (These files don't need to be copied to a subdirectory Test.)

    Edit example-client.js to enter the Link machine and port you started your ExampleRestServer on. (You can find the numerical IP address for a Link machine by entering

    arp -n rns20m-n.cs.stolaf.edu
    
    on any link machine, where m and n indicate the Link machine your server is running on.

  5. To run the client, enter

    node example-client.js > example-client.out.new
    
    on a shell window on your computer. (You may need to be on the St. Olaf network for this to succeed.)

    Your program should return and you should soon see a new shell prompt. Then enter

    diff example-client.out.new example-client.out
    
    to see the differences in output between your client run and mine. The only difference should be the first line, indicating which server and port you used.

  6. Finally, return to your window where you started your ExampleRestServer on a Link machine. Enter the command

    EXIT
    
    The script will immediately print the differences between your run and my prior run. The only difference should be in the host/port and in the date headers.

  7. Note: The files ending in .out hold the expected output from client and server.




< >