CIS 455/555: Internet and Web Systems
1. Background
A Web server represents a nice, limited-scale introduction to building a server system. It requires careful attention to
concurrency issues, and it requires well-designed programming abstractions to support extensibility. However, the
actual HTTP protocol is not incredibly complex. You’ll start by dealing with the issues of handling concurrency,
before building in a set of abstractions for future extensions.
In the early days of dynamic, Web server-based applications, interfaces such as Java servlets were commonplace.
However, servlets are heavyweight -- requiring fairly cumbersome frameworks such as Apache Tomcat or Eclipse
Jetty, having complex APIs, etc. Over time, other approaches were developed, particularly in server platforms for
other languages (e.g., Node.js for JavaScript, Django for Python). A key idea here was the notion of
programmatically mapping routes (paths and patterns) to callback or event handlers (functions called by the Web
server).
In recent years, the Spark Framework ported these same abstractions to Java, and it has gained significant traction
within the Web community. We’ll be implementing a framework that emulates the Spark APIs -- and gives you
insight into what is going on inside Spark (and also Jetty, which Spark runs at its core). Once you build out your
Homework 1 server and framework, you will ultimately use it to (1) serve HTML pages, e.g., for your search engine;
(2) supports actions in response to HTML forms, e.g., to trigger the actual search; (3) support REST Web service
calls, e.g., to build distributed crawling.
This assignment focuses on developing an application server, i.e., a Web (HTTP) server that runs Java-based
services, in two stages. In the first stage, you will implement a simple HTTP server for static content (i.e., files like
images, style sheets, and HTML pages). In the second stage, you will expand this work to handle Web service calls.
Web service calls are the underpinnings of many aspects of the cloud, and are used to invoke operations remotely.
One method for creating services in Java is through servlets, with which you might be familiar if you’ve taken CIS
450/550. Increasingly, however, there has been a movement towards lighter-weight RESTful services that expose
APIs over HTTP’s GET, POST, DELETE, PUT, and other operations. Such services are typically written by
attaching handlers to various routes or URL paths. In Java, perhaps the most popular framework for such operations
is the Spark Framework (not to be confused with Apache Spark). We will implement a microservices framework
that emulates the Spark API.
1.1. A Sample Program in the Spark Framework
This homework assignment consists of two milestones. Technically, you can build a one-off solution for Milestone 1,
then throw much of it away in order to do Milestone 2. That’s not what we recommend -- instead we want you to
understand where you are going with Milestone 2 before embarking on Milestone 1. If you implement Milestone 1
understand where you are going with Milestone 2 before embarking on Milestone 1. If you implement Milestone 1
using the same raw framework as Milestone 2 (not necessarily implementing every bell and whistle initially), you’ll
be much better positioned for the second milestone.
Thus, we begin by explaining the programming abstraction we are providing (cloning from open source) for this
project. Let’s look at a sample Spark Framework program, from their “Getting Started” documentation.
import static spark.Spark.*;
public class HelloWorld {
public static void main(String[] args) {
get("/hello", (req, res) -> "Hello World");
}
}
Upon a browser request (to localhost:8080/hello), it returns “Hello World.” Super simple, right?
Before diving into how it works, it’s worth noting two aspects of Java 8 that you may not have seen.
1. import static spark.Spark.* finds the spark.Spark class (in a JAR file) and imports all static functions in the
Spark class to the global scope. Thus, public static void Spark.get(...) is callable as get().
2. (req, res) -> “Hello World” is a lambda function that may be familiar to you if you have used functional
languages. It takes a pair of input parameters, req and res, and returns a string (“Hello World”). The types of
req, res, and the function return value were defined by the get() function itself: namely, they are of type
Route which takes a Request and a Response, returns a String, and optionally throws a HaltException if
the request results in an error.
Recall that HTTP defines a mechanism for the Web server (or a client) to make a request, and that the server must do
some computation and send back a response.
The call to get is registering a route handler for HTTP GET requests (to /hello). (Other kinds of HTTP requests,
such as POST, DELETE, HEAD, etc. are also mapped to route handlers using the same basic style.) This simple
route does not have any patterns or variables, but more complex routes can make define parameters in the path or as
an HTTP query string.