Link Search Menu Expand Document

Project B


Released
30 September 2021
Submission
Deadline
15 October 2021 (for Feedback)
21 October 2021 @ 5 PM EDT
Where Web Submit
Name B
Files B.war
Version
1.4 14 October 2021 Revision to Section D. Revised this.
1.3 13 October 2021 Revision to Section C.4 Added this.
1.2 8 October 2021 Revision to Section C.4 Added this.
1.1 2 October 2021 Revision to Section D. Added Bullet 4 and this.
1.0 30 September 2021 Initial Release

Table of contents
  1. Executive Summary
  2. The Environment
    1. Adding External Libraries to Tomcat
  3. The Services
    1. FAuth — A Gateway Web Service
    2. GeoWeb — A Stateful Web Service
    3. Loc — An API Web Service
    4. Drone — A Composite Web Service
    5. OAuth — A Redirecting Web Service
  4. Service Implementation
  5. Testing & Deployment
  6. Submitting Your Work

Executive Summary

In this project, you will create a number of multithreaded WEB services which utilize HTTP over TCP. These services will build on the TCP services of Project A and expose a number of new concepts, such as industrial-strength app servers, session management, analytics, and federataed and open authentication. You will later use these services to build a shopping-cart web application.


The Environment

We will continue to use the remoteLab, but unlike Project A for which any editor or IDE will do, we will now leverage Eclipse with Java EE features. To set this up, do this:

  1. Launch Eclipse.
  2. The Servers view should be visible (normally in the bottom pane with the console). If not, enable it via Show View in the Window menu.
  3. Right-click anywhere inside the empty Servers view and choose New > Server.
  4. Add Tomcat v9.0 Server at localhost by pointing to its directory at: /eecs/home/<user>/4413/pkg/tomcat.
  5. Create a new Dynamic Web Project named B and accept all defaults.
  6. Right-click the project and select New > Servlet and name it Service (put it in the services package) and accept all defaults.
  7. One time only (since it gets cached), associate the Servlet Javadocs with Eclipse as follows:

    1. Right-click the project and select Build Path > Configure Build Path....
    2. Under the Libraries tab, expand open and select:

      Apache Tomcat v9.0 > servlet-api.jar > Source Attachment
      
    3. Select the External location option and set the location to

      /eecs/home/<user>/4413/pkg/tomcat/webapps/docs/servletapi/apache-tomcat-9.0.8-src.zip
      
    4. Click OK, then Apply and Apply and Close.

  8. Right-click Service, select Run > Run on Server, select Tomcat v9.0 Server at localhost and check Always use this server when running this project. Click Finish. You can now visit its URL from a browser. Use this servlet as a testbed to explore features and try out ideas.

Tomcat Tips

Once Tomcat is started, there is no need to click Run. It should automatically publish (re-synchronise) the servlets. Occassionally, you will get the error:

Several ports (5413, 4413, 3144, 6413) required by Tomcat v9.0 Server at localhost
are already in use. The server may already be running in another process, or a system
process may be using the port. To start this server you will need to stop the other
process or change the port number(s).

You may see this despite Eclipse reporting that the Tomcat server is stopped and there is nothing shown in the console. To manually stop Tomcat, open your terminal and run:

tomcatOff

Then retry starting the server.

To display Eclipse’s Tomcat configuration panel, open the Servers view and double-click on the Tomcat v9.0 Server at localhost entry. There you will the Tomcat configurations for automatic publishing, ports, timeouts, etc.

Adding External Libraries to Tomcat

  1. Right-click your Project in the Project Explorer.
  2. Navigate to:

    Run As > Run Configurations
    
  3. On the left, select:

    Apache Tomcat > Tomcat v9.0 Server at localhost
    
  4. Click the Classpath tab and select User Entries within the list hierarchy.
  5. Click Add External JARs.
  6. In the popup window, navigate to your Home directory and then the 4413 folder. Go to:

    /eecs/home/<user>/4413/lib
    
  7. Select all of the JARs files and click Open.
  8. Now you will see that the JARs listed under ``User Entries`.

    Note: derbyclient.jar seems to be missing dependencies. When you run the Tomcat server, this will result in a lot of FileNotFoundException exceptions being thrown in your console. You can ignore these or:

    1. Click derbyclient.jar and click Remove.
    2. Click Add External JARs and navigate to: /eecs/home/<user>/4413/pkg/derby/lib
    3. Select the copy of derbyclient.jar there and click Open.

  9. Click Apply and then Run to start the server.

You may need re-add the external libraries to your new Project. To do so, follow the same steps from Project A.


The Services

This Project asks that you develop and test five HTTP microservices with the following functional specifications:

FAuth — A Gateway Web Service

This Federated Authentication service receives two URL-encoded parameters username and password and returns OK or FAILURE in a text/plain payload based on the authentication of the Auth service of Project A. In other words, FAuth does not do any authentication itself. Instead, it simply delegate to Auth by turning its URL parameters to a TCP request line and by turning the TCP response line to an HTTP response.

GeoWeb — A Stateful Web Service

This service is the web counterpart of the stateful GEO2 service of Project A. It receives two URL-encoded parameters: lat and lng, containing the GPS coordinates of a place on Earth. If this is the first time this client has made such a request, then the return would be the text/plain payload “RECEIVED”. If not, then the return would be the text/plain payload:

The distance from (lat1, lng1) to (lat2, lng2) is: XXX km

where XXX is the geodesic distance between the previous place (lat1 and lng1, sent in the last request) and the current one (lat2 and lng2, sent in this request). This can continue (to a 3rd, 4th, … places) as long as the requests are made in the same session. All computations must be made by delegating to the Geo service, not Geo2, of Project A; i.e. this web service does not do any math, and it uses Tomcat’s session capabilities (rather than a 3rd parameter) to persist data.

Loc — An API Web Service

Given a street address (partial or complete) anywhere on Earth as a GET parameter, named location, this service returns in the HTTP payload a JSON object representing the address’s specs; most importantly, its latitude and longitude. Use the MapQuest’s API (http://www.mapquestapi.com/geocoding/v1/address?) and supply the address and your API key to perform this lookup and capture the latLng element in the returning JSON (see below). If MapQuest returns multiple results, return the first location’s latitude and longitude.

For example, the given request:

GET /loc?location=4700+Keele+Street+Toronto

Your service should response with:

{ "lat": 43.775124, "lng": -79.494075 }

If no location is found, your service should respond with:

{ "lat": null, "lng": null }

Getting a MapQuest API Key

You will need a MapQuest Developer account. To create a new account, register to here. Once you have completed the form and click Sign Me Up, you will be taken to your Profile.

Click Manage Keys on the left navigation to go to your API keys. MapQuest automatically creates you an API key when you first create your account. You can find it by expanding My Application where you will find your Consumer Key. This is your API key, you will use in your GET request.

Developer Tips

On the API documentation page, you will find an example GET request on the left:

GET http://www.mapquestapi.com/geocoding/v1/address?key=KEY&location=Washington,DC

Replace KEY with your API key and try it out in your web browser. You should get the same results as the Example Response. You can change response format by providing the optional query parameter outFormat in your request. The supported formats are: JSON, XML and CSV. JSON is the default format.

Whitespaces in the Location Query

Your location query parameter should permit whitespaces to be contained within it. However, URLs cannot contain whitespaces. To get around this, we encode our URLs. See here and here.

It may be one potential reason why your API request to MapQuest fails. To fix this, we could implement the encoding scheme ourselves, however, there are existing implementations of encoders we can use. One exists in the java.net.URLEncoder class.

String urlString = URLEncoder.encode(urlString, "UTF-8");

This method translates the given string into the application/x-www-form-urlencoded format using a specific encoding scheme. This method uses the supplied encoding scheme to obtain the bytes for unsafe characters.

Where’s the latLng object?

The exact path to latLng element in the returning JSON is…

Unfortunately, there is no standardized way to specify it without a verbose description, but there are draft proposals, such as JSONPath. In the JSONPath draft specification, the exact path to the latLng element is:

$.results[0].locations[0].latLng

where $ is the root of the JSON object. The rest, you read like how you would when accessing a nested Java object. Remember, you still need to use Gson’s getAsJsonArray and getAsJsonObject methods to walk the returning JSON object and the JsonArray#get(index) and JsonObject#get(key) methods to accessing the nested elements.

Pro Tip

You can also use an API explorer tool like hoppscotch.io to explore the MapQuest API. For security reasons, by default, Hoppscotch only works with HTTPS requests. To make HTTP requests, you will need to install the Hoppscotch Firefox plugin. Once installed, you should be able to test the MapQuest API and your own web services.

Drone — A Composite Web Service

Given two street addresses, this service returns an estimate of the drone delivery time in minutes for a shipment from one location to the other. Assume an average drone cruising speed of 150km/h. This service should receive the two addresses in the query parameters: source and destination. It should return the text/plain response:

The estimated delivery time is: XXX minutes.

where XXX is the estimated delivery time.

Hint

The GeoWeb, Drone and Loc services should all share the same model objects. There should only be a single implementation of code retrieving the MapQuest API and extracting the latitude and longitude values from the response JSON objects and a single implementation for connecting to the Geo service from Project A to get the geodesic distance. There should be no code duplication.

OAuth — A Redirecting Web Service

This Open Authentication service allows users to authenticate using a third-party website; i.e. it does not receive (and hence cannot “sniff” or “leak”) any credentials; i.e. it facilitates single sign-on. To authenticate, this service redirects the client (via response.sendRedirect) to this URL:

https://www.eecs.yorku.ca/~roumani/servers/auth/oauth.cgi

This website expects a URL parameter named back containing the URL of the OAuth servlet that originated the request. The URL prompts users for their Passport-York Credentials, and if the authentication succeeded, it redirects them back (using the back parameter) and sends the usernamee and full name as parameters named user and name. Otherwise, it issues a 401 Unauthorized and does not redirect back. If the oauth.cgi successfully redirects back to your servlet, print to your web browser the plain text message:

Hello, <name>. You are logged in as <user>.

where <name> and <user> are the query parameters returned to your servlet from oauth.cgi.


Service Implementation

Use the design patterns, methodologies, and hints demonstrated in lecture in order to speed up development and learn best-practice approaches.

  • Develop all 5 services in one project, but each in its own servlet.
  • The servlet name and its URL mapping should be the same as the service name.
  • Separate the model (business rules) from the controller (http communication). You can have one model class per service or one model for all three services. It is customary to put all servlet classes in one package named service and the model class(es) in one package named model.
  • Configurations, such as: the IP addresses and port numbers of the Geo and Auth services, your MapQuest API key, the MapQuest API URL, and the OAuth URL should be stored outside of your Java program in a configuration file, like a web.xml file stored in the WebContent/WEB-INF folder of your Project. (See below for details.)

About the web.xml Deployment Descriptors File

The web.xml file is a standard configuration file used to specify details and configuration regarding your server and its servlets. It allows your webapp to be portable, i.e. moved to a different server with different configurations or a different file path and still work with only minor changes to the configuration file. The webapp shouldn’t need to be re-compiled or edited to run in a different environment.

What is the web.xml file?

Java web applications use a deployment descriptor file to determine how URLs map to servlets, which URLs require authentication, and other information. This file is named web.xml, and resides in the app’s WAR under the WEB-INF/ directory. web.xml is part of the servlet standard for web applications.

A web application’s deployment descriptor describes the classes, resources and configuration of the application and how the web server uses them to serve web requests. When the web server receives a request for the application, it uses the deployment descriptor to map the URL of the request to the code that ought to handle the request.

Here is an example web.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<web-app
  xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  version="4.0"
>
  <display-name>Project B</display-name>

  <context-param>
    <param-name>MapQuestAPIKey</param-name>
    <param-value>12345abcdefghijklmnop</param-value>
  </context-param>
  ...

  <servlet>
    <servlet-name>FAuth</servlet-name>
    <servlet-class>services.FAuth</servlet-class>
    <init-param>
      <param-name>serviceAddress</param-name>
      <param-value>130.63.168.150</param-value>
    </init-param>
    <init-param>
      <param-name>servicePort</param-name>
      <param-value>40215</param-value>
    </init-param>
  </servlet>

  <servlet>
    <servlet-name>GeoWeb</servlet-name>
    <servlet-class>services.GeoWeb</servlet-class>
    <init-param>
      <param-name>serviceAddress</param-name>
      <param-value>130.63.168.99</param-value>
    </init-param>
    <init-param>
      <param-name>servicePort</param-name>
      <param-value>35155</param-value>
    </init-param>
  </servlet>
  ...
</web-app>

In your Java servlet, you can access these configurations, like this:

@WebServlet(
  name = "FAuth",         // It's critical that you specify the servlet name,
  urlPattern = {"/FAuth"} // and URL pattern here. You can specify URL pattern
                          // in the web.xml as <server-mapping>, but you must
                          // specify the servlet name here.
)
public class FAuth extends HttpServlet {
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    ...
    getServletContext().getInitParameter("MapQuestAPIKey"); // "12345abcdefghijklmnop"
    getInitParameter("serviceAddress"); // "130.63.168.150"
    getInitParameter("servicePort");    // "40215"
    ...
  }
}

@WebServlet(
  name = "GeoWeb",         // It's critical that you specify the servlet name,
  urlPattern = {"/GeoWeb"} // and URL pattern here. You can specify URL pattern
                           // in the web.xml as <server-mapping>, but you must
                           // specify the servlet name here.
)
public class GeoWeb extends HttpServlet {
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    ...
    getServletContext().getInitParameter("MapQuestAPIKey"); // "12345abcdefghijklmnop"
    getInitParameter("serviceAddress"); // "130.63.168.99"
    getInitParameter("servicePort");    // "35155"
    ...
  }
}

For more information about the web.xml Deployment Descriptors file, see here and here.

Note: Since both the GeoWeb and Drone services use the Geo service. It may make more sense to declare the IP and port number for Geo as context parameters instead of declaring them twice as initial parameters for both the GeoWeb and the Drone servlets.

  ...
  <context-param>
    <param-name>GeoServiceIP</param-name>
    <param-value>130.63.168.99</param-value>
  </context-param>
  <context-param>
    <param-name>GeoServicePort</param-name>
    <param-value>35155</param-value>
  </context-param>
  ...

Alternatively, you could use the Tomcat-specific context.xml configuration file (placed in your WebContent/META-INF folder) to specify the Context as well as database connections.

<?xml version="1.0" encoding="UTF-8" ?>
<Context privileged="true" reloadable="true">
    <Parameter name="MapQuestAPIKey"
               value="12345abcdefghijklmnop"
               override="true"/>
    <!--
      Equivalent to:

      <context-param>
        <param-name>MapQuestAPIKey</param-name>
        <param-value>12345abcdefghijklmnop</param-value>
      </context-param>

      in the web.xml.
    -->
    <Resource
        name="jdbc/EECS"
        factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
        type="javax.sql.DataSource"
        driverClassName="org.apache.derby.jdbc.ClientDriver"
        url="jdbc:derby://localhost:64413/EECS"
    />
    <ResourceLink
        global="jdbc/EECS"
        name="jdbc/EECS"
        type="javax.sql.DataSource"
    />
</Context>

For more information about the context.xml, see here.


Testing & Deployment

Test your services through a browser using http://localhost:4413/<project>/<service>/?<qs>, where project is the name of your web project, service is the name of your service (or its URL mapping) and qs is the query string. Once all is well, switch to the localhost address and test by deploying your services on one workstation and accessing it (via a browser) from a different workstation in the lab.


Submitting Your Work

Note that this process is different from that of Project A.

Follow these steps to persist / backup your work:

  1. Right-click your Project (in the Project Explorer) and select Export.
  2. Select WAR File.
  3. Provide a destination for the file (e.g. B.war on the Desktop).
  4. Check the box to Export the source files (extremely important)
  5. Now upload the created WAR file to the course cloud so you can use it during tests. (You can also upload it to your Google Drive, DropBox, S3, or some other cloud service). Via the submit command:

    $ submit 4413 B B.war
    

If you later need to restore this backup into a fresh workspace, do this:

  1. Launch Eclipse in a new workspace.
  2. Right-click anywhere in the Project Explorer and select Import.
  3. Select WAR file.
  4. Point to your WAR file and click Finish.

Try the above procedure end-to-end (by using two machines; by switching to a different workspace on the same machine; or by deleting your workspace after the WAR file has been created). Make sure you are comfortable backing up and restoring your course project. Do not wait until the day of the test to learn how to do this. Do not delay the backup until the work is done! Do it often (at least after every release). If you are familiar with Github then do use it but you still need to upload to the course cloud and to practice the above backup/restore procedure. Finally, note that the above WAR file approach is the best way to copy and/or rename your webapp.


Back to top

Copyright © 2021 Vincent Chu. Course materials based on and used with permission from Professor Hamzeh Roumani.
Last updated: 05 January 2022 at 06:16 PM EST