Lab 5 Details for MPCS 51205

Each lab will consist of a small problem and details of  how to proceed. Each lab is intended to give every student hands-on experience with the core technologies utilized during the course.  A student may concentrate, as a team member, on one technology over another for the final project, but labs are designed to give each and every student exposure to all the technologies that come into play.  You need to submit labs to the TAs for grading--see submission instructions below.  Generally, unless otherwise specified, you will have one week to complete each assigned lab.

See the syllabus for information on grading.  Turning in lab assignments on time is required, without exception, and all late deliveries will be penalized, regardless of cause.  Submit your assignments to the subversion repository according to the directions on the syllabus page.

You may write these solutions in either Java or C++...your choice.

Lab 5   Due: 5:00 pm, Monday, November 30, 2020

Problem 1:  Playing With Docker and PostgresSQL: 

BACKGROUND:

Like all programming problems, learning a new technology is not an exercise in reading but rather and exercise in typing.  This lab is designed to give you hands-on experience in (a) running a dockerized RMI client program that (b) communicates with an RMI server program in another docker container which then gets sample data from a PostgresSQL database and (b) returns that data to the dockerized RMI client program.  You will generally find the References section below helpful in addition to the required and recommended reading.  When we talk about "Docker", we are talking specifically about the Stable Community Edition of Docker, which is the version we will be using in this class.  The Stable Community Edition provides the basic container engine and built-in orchestration, networking, and security.

Note that this lab will focus on and exercise interprocess communication and accessing Postgres from a remote client via an RPC metaphor (Java RMI).  Please keep in mind that in your actual microservices architecture that you are creating for the final project deliverable, as each microservice has its own datastore, you will likely only be hitting Postgres from a local microservice, which is of course simpler, and not remotely...as shared persistence among microservices is generally a NO-NO.  Consider also that each microservice will likely have an instance of either PostgresSQL or MongoDB as their datastore, and will be making local calls to that datastore. 

WHAT YOU NEED TO DO:

STEP 1:

For this fifth lab, we are going to code a little microservice that will accept a synchronous call over RMI to a server with a backing PostgresSQL database.  That RMI Server will obtain data from the PostgresSQL database, and return it to an RMI Client running in a separate container.  You will not need to run a script capture on this lab, as you will be submitting your two client and server docker containers.

First, make sure docker is running either on your laptop (Option I from the first lab) (or in your VM if you are working in VirtualBox, etc.).

STEP 2:

Get Java RMI working in a Docker Container (CLIENT)

First, get down the Apache Httpd Server version 2.4 container.  Do this by executing the following command (make sure you understand the meaning and significance of -p 8080:8080):

docker run -dit -p 8080:8080 --name Lab5HttpdServer tomcat:9.0.27-jdk8-openjdk

Once you have that container down (and running), obtain a bash shell in that container by running docker exec into the container.  Once you are in that httpd container, install some software in the container. 
You will update the ubuntu OS, then install the vi editor, the Java 1.7.x JDK, and net-tools, which will allow you to run network-related utilities including ifconfig.

apt-get update
apt-get install -y vim
apt-get install -y default-jdk
apt-get install -y net-tools
apt-get install -y openssh-client

Create a subdirectory called "/src/lab5/RMI".  Next, download ~mark/pub/51205/src.labs/RMI.tgz and copy it into your container and untar the contents into your RMI subdirectory.  You could also scp it from the Linux cluster directly into your container as well.  Cat the README file and compile the java client and server.  Then execute the server and in a new terminal window execute the client, to make sure everything is running.  Refer again to the README file on how to run the client and server.   You can just use "localhost" for the <server host address> on the client command line.

This container will server as your "CLIENT" container for the synchronous exercise of this lab.  Don't be confused by the name of this container.  Although it is the httpd server and is named
Lab5HttpdServer, it will still function as our CLIENT for this lab.

STEP 3:

Get PostgresSQL working in a Docker Container (SERVER)

Next, in a separate terminal window get down a PostgresSQL version 10.10 container by running this command:

docker run --name Lab5PostgresServer -e POSTGRES_PASSWORD=mysecret -p 3306:3306 -p 5432:5432 -d postgres:10.10

Docker exec a bash shell into your new PostgresSQL server and poke around a bit.  Run the following updates in your Postgres_server:

apt-get update
apt-get install -y vim
apt-get install -y openjdk-8-jdk
apt-get install -y net-tools
apt-get install -y openssh-client
apt-get install -y unzip

Go here: http://www.postgresqltutorial.com/postgresql-sample-database/
and click on the red "Download DVD Rental Sample Database" button and follow the instructions to download and load your Postgres instance with the sample employees database.  You may find that obtaining the dvdrental.tar file onto your host system, and then using docker cp to copy it into your container will be easiest.  You may also find it easier to obtain the tarball of this sample database off my pub directory ~mark/pub/51205/src.labs/dvdrental.tar.  It's contents are identical to the zip file referred to on the www.postgresqltutorial.com website above.   Once you've retrieved the sample DVD rental database file, run the following instructions  to install the sample database into your Postgres container:

1:  Get into a postgres shell: 

    docker exec -it Lab5PostgresServer /bin/bash
    psql --username postgres

2:  At the postgres shell prompt (postgres=#), create a new database called dvdrental and then exit out of your postgres shell (\q):
    create database dvdrental;
   

    dvdrental=# create database dvdrental;  //this will create the dvdrental database:  DO NOT FORGET the terminating ';' !
    dvdrental=# \q

3:  Now run gp_restore to load the dvdrental.tar SQL commands to create the set of tables in your dvdrental database you just created.  From your container's prompt, run:

    pg_restore -U postgres -d dvdrental ./dvdrental.tar

You should see no output and no errors.  At this point, you can hop back into your postgres shell and take a look at the new tables you've created (it will match the logical diagram of the dvd rental sample database here:  http://www.postgresqltutorial.com/wp-content/uploads/2018/03/printable-postgresql-sample-database-diagram.pdf):

psql --username postgres   //this command from the container prompt will put you back in the PostgresSQL shell
dvdrental=# \c dvdrental;     //this will change to the dvdrental database
dvdrental=# \l                //this will list all the database instances in your PostgresSQL instance (you should see "dvdrental")
dvdrental=# \dt               //this will list (dump) all the tables in the dvdrental database (this should match the logical diagram linked above)

Now, issue a few SQL commands to investigate the data (don't type the bold prompt and make no changes of course!):

dvdrental=# select count(*) from customer;
dvdrental=# select count(*) from film;
dvdrental=# select * from customer limit 10;
dvdrental=# select customer_id, first_name, last_name from customer limit 10;


 
Examine the output from these commands carefully, especially the output from the last.  Now exit out of psql:

dvdrental=# \q

Create a new subdirectory called "/src/lab5/RMI" in your Lab5PostgresServer container.  Next, download ~mark/pub/51205/src.labs/RMI.tgz and copy it into your container and untar the contents into your RMI subdirectory.  You could also scp it from the Linux cluster directly into your container as well.  Cat the README file and compile the java client and server.  Then execute the server and in a new terminal window execute the client, again making sure everything is running.  Refer again to the README file on how to run the client and server.  Again you can use "localhost" for the <server host address> on the client command line.

Are you having fun yet? 

NOTE:  There is a free GUI front end for MacOS for PostgresSQL that is available for download here called PSequel.  You may choose to install it.  Here's a snapshot of PSequel executing that last select query above (hitting our dvdrental database in our PostgresSQL container):



There may be other tools for other OSs similar to PSequel on the mac.

This container will serve as your SERVER container for the synchronous exercise of this lab.


STEP 4:

Verify the Java JDK in the PostgresSQL working in the Docker Client Container

Exec into your httpd CLIENT container
Lab5HttpdServer (if you're not already there).

Now, let's make sure that our Java programs can hit our Postgres server.  Get the Postgres java driver down.  You can scp it from inside your container from my pub server at ~mark/pub/51205/src.labs/postgresql-42.2.8.jar.  Store it in the root directory of
your Lab5HttpdServer container.  Now, set your CLASSPATH so that it includes this directory, something like:

export CLASSPATH=/postgresql-42.2.8.jar:.

Make sure that you don't forget the final ":." in that line above!

Now, get JdbcTest.java from the cluster at ~mark/pub/51205/src.labs/JdbcTest.java, and copy it into your Lab5HttpdServer container, maybe into the same subdirectory you're using for you RMI code.  Now compile JdbcTest.java.  Make sure whatever terminal you use to execute javac or java has the CLASSPATH set above. 

Find out the IP address of the Server container.  Inside your Lab5PostgresServer container, type ifconfig.  Note the IP address of the eth0 interface in that container (it should be something like "inet addr:172.17.0.2" or something like that). Pay particular attention to the proper IP address of your Postgres server container.  It may be 172.17.0.2 or 172.17.0.3 or something else.  You may need to replace the connectionUrl string's IP address in your JdbcTest.java file with the correct actual server IP.  Note that the root user and password secret lines should be fine.  Recompile JdbcTest.java and run it.  Hopefully, you will get output that matches the same output from the last query command you typed in the PostgresSQL shell above, namely, you'll see Jared Ely, Mary Smith, Patricia Johnson, all good friends of mine.  If so, you're in great shape.  Your JdbcTest java test program running on the CLIENT just hit your Postgres instance in your SERVER.

You are indeed a Boss.

STEP 5:

Verify that your RMIClient program running in your docker client can talk to the RMIServer program running in your docker server. 

Find out the IP address of the Server container.  Inside your Lab5PostgresServer container, type ifconfig.  Note the IP address of the eth0 interface in that container (it should be something like "inet addr:172.17.0.3" or something like that).

Now we need to connect our RMIClient running on our CLIENT (
Lab5HttpdServer) to the RMIServer running on our SERVER (Lab5PostgresServer).  To do this, on your Lab5PostgresServer SERVER execute the RMIServer program.

On your
Lab5PostgresServer SERVER, execute java RMIServer.   Note the "using address" line, especaially the IP address.

Now, in your CLIENT container (
(Lab5HttpdServer), execute your client against your server, using a command similar to this:  "java RMIClient 172.17.0.3 12345 hi".  Of course you would use the correct IP address.  Make sure that your RMIServer prints out the string  you passed in.  Once that's done, you're almost finished.  You can stop your RMIServer program.

STEP 6:

Modify your RMIServer and RMIClient to execute a remote database access call and return the result to the remote client. 

On your Lab5PostgresServer SERVER that is running your Postgres instance, use vi (or whatever you've installed as its alternative) to modify the RemoteInterface.java code.  Change the RemoteInterface classes's recieveMessage method so that instead of returning void, it returns an int. 

Then, modify on the CLIENT the source code for RemoteInterface.java, and make the same change to the return value of the receiveMessage() call from void to int, as your new receiveMessage() function will be returning an int instead of a void. 

Then, create a function in your RMIServer.java source (very similar to the main() function in JdbcTest class in JdbcTest.java) on your SERVER that when called and passed in a String tableName, hits the (local to the container) PostgresSQL database table whose name was passed in, and executes a SELECT COUNT(*) on that table, and returns the numeric result of the number of records in the table returned from the SELECT call.  So, for example, if you pass in the table name "customer" to the client, the remote server would return the int 599 to the client, because there are 599 records in the customer table.  If you were to pass in the table name "film" to the client (running on CLIENT
(Lab5HttpdServer), such as:

# java RMIClient 172.17.0.3 12345 film

the remote server would return 1000, because there are 1000 records in the film table on the server.  So the prototype of the new function might be something like:

    public int execute_it(String tableName)
    {
        //this code will hit the local Postgres database table "tableName" and execute a query similar to
"select * from departments",
        //and will return the result set's result, in this case, the int "9" because there are 9 records in the departments table...

    }

Then simply have your receiveMessage function that implements RemoteInterface call your new function and return the result, such as:

    public int receiveMessage(String tableName) throws RemoteException
    {
        System.out.println("In RMIServer's receiveMessage method.  Received TableName: " + tableName);
        int y = execute_it(tableName);  // the return value is the number of rows in tableName
        return y; 
    }



STEP 7:

Run it all. 

Your output should look something like this (doesn't have to exactly match this but this is the idea):

CLIENT:

root@3f2c68786aae:/src/lab5/RMI# java RMIClient 172.17.0.7 12345 film
sending film to 172.17.0.7:12345
returned count: 1000
root@3f2c68786aae:/src/lab5/RMI# java RMIClient 172.17.0.7 12345 customer
sending customer to 172.17.0.7:12345
returned count: 599

SERVER:

root@62007982f963:/src/lab5/RMI# java RMIServer
using address=62007982f963/172.17.0.7,port=12345
In RMIServer's receiveMessage method.  Received TableName: film
In RMIServer's receiveMessage method.  Received TableName: customer

Once you have your new changes all executing properly, you're done.  Submit as instructed below.

References:

You may find the following references helpful (in addition to the links from previous labs):

Ubuntu package commands
The docker page for Postgres

General Docker Tutorial Links

Docker Cheat Sheet

Submitting:

Use the folder named "lab_5" in your Subversion repository.  Upload your Lab 5 CLIENT and SERVER tarballs (docker export) to this repo.  gzip each tarball before submitting.  Please include a README text file that contains any instructions for the TAs to assist with grading (especially with instructions on how to run/launch your containers), and design notes are often a useful thing to provide.