Comp 110 Chat110 - Packet Tool

Chat110 Packet Tool

READ THIS SECTION FIRST

In the next series of problem sets, we will build a chat app that allows you to send and receive messages with friends, peers, and the staff of COMP110.

Before you begin, there are a few things you should know in order to understand the pieces of this problem set as they come together.

Client - Server Overview

First, you will be building what is called a "client" program. It will connect to a "server" program over the internet, which we have built for the class. The server is just a Java program running on a computer connected to the internet that we provide. The server is a hub. You can think of it like a historical telephone switchboard operator. You and your peers' programs will form a connection with the server and send "packets" to the server. In the first part of this problem set there are 3 kinds of packets you can send.

1. The auth Packet - auth is short for authentication and it verifies the onyen connecting with a unique "key" we provide to you on your My110 page. If the key matches the ONYEN, the server will acknowledge you are authenticated and then you can begin sending other messages. Format:

auth:<onyen>:<key>

2. The who Packet - When you send the server a who packet, it will send you back a list of all other ONYENs currently connected and authenticated on the server. You can send dm packets (below) to any connected onyen.

who

3. The dm Packet - "direct message" - When you send the server a dm packet, which will contain the recipient's onyen, it will relay the message to the recipient. This is how chat messages will be sent between two people.

dm:<to>:<text>

Carolina Community Note: be pleasant when chatting with people you do not know in person. Each message is tied to your onyen. If we learn of offensive or aggressive interactions we have a means of verifying it. Should this come up, we will assign a failing grade for this series of assignments and submit the interaction to office of student conduct. Any attempt to authenticate with someone else's ONYEN is considered an honor code violation. You can try your hand at hacking in COMP435.

Your program's connection to the server will send and receive Strings formatted as above. It turns out this is how most internet-connected applications communicate! When you open a web page, your web browser forms a connection with a web server, sends it a specially formatted String, and the server responds back with a specially formatted String representing the web page. Your web browser then has code to process the String and construct all of the text, shapes, and layout that you see from it. Email, chat programs, and multiplayer games also work in this way.

Layered Architecture

We are providing support code for forming a Connection with the server, a class representing a Packet, and an interface others classes can implement that will allow them to plug into a Connection and be notified of when a Packet is sent or received over the connection.

In the latter half of this part of the problem set, you will write a "messages layer" which builds upon the "connection / packets layer". You will write code that converts a Packet object into a specific kind of message object, i.e. an AuthMessage object, a DirectMessage object, or a WhoMessage object. You will also write code that converts these objects back to Packets.

In the next part of the problem set, when we build the chat110 app user interface, it will depend only on the "messages layer" you build in this part. Your chat app's user interface will not know or care about "packets" because they are in a lower level layer. This design is an example of a "layered architecture" which leads to cleaner, less error-prone programs. In latter CS courses you will encounter many other examples of layering.

Getting Started: Import the PS5 Project

Do you want to try starting from _scratch_ rather than importing the starter materials? It's just a few extra steps and will show you how to start your own JavaFX projects outside of COMP110. If so, follow these steps here.

To get started, follow these steps to install the Support Code for PS5.

  1. Start Eclipse
  2. Select and copy the following link to your clipboard: https://github.com/comp110/17S-PS5-Chat110.git
  3. Click the File menu
  4. Select "Import"
  5. Double click "Git" to expand the folder
  6. Double click "Projects from Git"
  7. Double click "Clone URI".
  8. The "URI" field should already have the URI you copied in Step 2 in it. If it does not, paste the URI from step 2 in it. Click next.
  9. Ensure there's a check beside of "master". Click next.
  10. In the "Directory" field, click "Browse". Navigate to your documents folder and select the COMP110 folder. Click next.
  11. Ensure "Import existing Eclipse projects" is selected. Click next.
  12. Ensure the correct Problem Set folder is checked. Click finish.

Part A. Start the Packet Tool

Before we move to working on the messages layer, let's build a little tool that allows us to see packets sent and received by a connection and allow us to send hand-written packets directly. You could call this a "wiretap" (with bunny quotes, of course).

This part of the problem set will be more like following a tutorial than problem solving so that you can get some additional practice and guidance setting up a JavaFX app on your own. Follow these steps carefully! We will begin by getting the four files "hooked up" and then continue by adding the real functionality once the basics are running.

A.1 Start the Controller Class

A.1.0 In the comp110.chat.tools package, add a new class named PacketToolController.

Begin by importing the Connection class: import comp110.chat.packets.Connection

A.1.1 Declare a field on the PacketToolController with the following characteristics:

Visibility:   private
Type:         Connection
Name:         _connection

A.1.2 Declare a constructor for the PacketToolController class with the following characteristics:

Visibility:   public
Parameters:   
    #1:    Type: Connection
           Name: connection

A.1.3 The constructor should initialize the _connection field by assigning the parameter it is provided to it.

A.2 Start the View FXML

A.2.1 Open SceneBuilder. In the Library pane, open the "Containers" tab. Find the "AnchorPane" control and drag it onto the DesignerPane.

A.2.2 In the Library pane, open the "Controls" tab. Find the "Label" control and drag it onto the AnchorPane. Change its text property to be "Packet Tool". You can style it however you'd like. We will not be grading for visual design in the Chat110 problem set so you can make your programs "look and feel" however you'd like.

Not feeling creative? Feel free to make yours look something like this, for now:


A.2.3 Important! Save your View's FXML file in the comp110.chat.tools package of the PS5 project. From Save, browse to the project on your computer. This may be in your Documents folder or the git folder. Once you find the PS5 project, you will need to browse to the src > comp110 > chat > tools folder. Save the file as PacketToolView.fxml.

A.2.4 Back in Eclipse, refresh your project by right clicking on it and selecting Refresh, or pressing F5. You should now see the FXML file in the comp110.chat.tools package like shown below. 


If you do not see PacketToolView.fxml here, do not continue to the next step until you do!

A.3 Add the Window Class

For COMP110's purposes, our Window classes will contain the pairing of a Controller and a View. Most of the code in our Window classes will be "boilerplate" code that makes use of the JavaFX framework to jump through a few steps to load the Controller and View bound together. You do not need to know or memorize this code.

A.3.1 In the comp110.chat.tools package, add a new class named PacketToolWindow.

You can replace the code Java Eclipse generated for the class with the following code. Note that the constructor of the Window requires we give it a Connection. You will need to make use of this later!

package comp110.chat.tools;
import java.io.IOException;
import comp110.chat.packets.Connection;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class PacketToolWindow {
    public PacketToolWindow(Connection connection) {
        try {
            // Setup a Controller and some variables that plug-in to the boilerplate below
            PacketToolController controller = new PacketToolController(connection);
            String viewFile = "PacketToolView.fxml";
            String windowTitle = "Packet Tool";
            // Setup the View's FXML Loader
            FXMLLoader loader = new FXMLLoader(this.getClass().getResource(viewFile));
            loader.setController(controller);
            // Show the window
            Stage stage = new Stage();
            stage.setScene(new Scene(loader.load()));
            stage.setTitle(windowTitle);
            stage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

A.4 In the ToolsApp, form a Connection and show the PacketToolWindow

Open ToolsApp.java in the comp110.chat.tools package. This is the class that contains the program's main method and declares it is a JavaFX app. Remember, JavaFX apps launch in a special way. From the main method we call JavaFX's Application.launch method. This causes JavaFX to move through some steps to begin and ultimately it constructs a ToolsApp object for you and calls its start method. The start method is where your work will begin!

A.4.1 Connect to the Chat110 server

In the start method, under the first TODO, declare a Connection variable and initialize it to a new Connection object. Using it's connect method, connect to the Chat110 server whose address is "ws://comp110.com/chat110". Read through the Connection class to see how to call its connect method. (Note: do not change the Connection class.)

A.4.2 Show the PacketTool Window

Under the next TODO, after your connection is established in A.4.1, construct a new PacketToolWindow object. Remember that the PacketToolWindow requires a Connection argument. Pass it the connection object you setup in A.4.1.

You're now ready to run your project for the first time! Fingers crossed and you should see the window appear with your view loaded. Phew. That's a lot of steps just to get started!

Part B. Building the Packet Tool

The purpose of this tool is to have a user interface like the one pictured below. It will allow us to test sending String representations of Packets to the server and receive them back.


Your design does not need to look exactly like above, however, it will need to have the required controls listed below.

B.1 The Controller should implement the ConnectionObserver interface

A Connection allows us to "plug-in" an object that implements the comp110.chat.packets.ConnectionObserver interface. The ConnectionObserver interface declares two methods: packetSent, packetReceived. Once a ConnectionObserver is "plugged-in" to the connection, it will have its packetSent and packetReceived methods called every time a packet is sent or received over the connection.

For a refresher on Java interfaces, see lecture 18.

B.1.1 Import comp110.chat.packets.ConnectionObserver and comp110.chat.packets.Packet into the PacketToolController class then declare PacketToolController implements the ConnectionObserver interface.

public class PacketToolController implements ConnectionObserver

B.1.2 Implement the required methods of ConnectionObserver in the PacketToolController class. For now, each should simply print its packet parameter using System.out.println as shown below.

    public void packetSent(Packet packet) {
        System.out.println(packet);
    }

    public void packetReceived(Packet packet) {
        System.out.println(packet);
    }

B.1.3 "Plug-in" the Controller as an observer of the connection.

Now that the PacketToolController class implements ConnectionObserver, any PacketToolController object can be registered with a connection in order to be notified (via method calls) each time a packet is sent or received. This is a programming design pattern called "observer/observable" which you will learn the full glory of in COMP401.

In the constructor, add the controller being constructed as an observer of the connection, as shown below.

    public PacketToolController(Connection connection) {
        _connection = connection;
        _connection.addObserver(this);
    }

We're almost ready to test sending packets to the server through the Packet Tool!

First let's add 

B.1.4 Declare a _packetField and Implement a send method

When the user presses enter or the send button, we will want to send whatever is typed into the Packet text field to the server. We will need to do two more things in the Controller before we can hook up our view:

First, we'll need to declare an FXML field for the TextField that where the user can type a packet String in. You will need to import javafx.fxml.FXML as well as javafx.scene.control.TextField. Declare the field below in the PacketToolController, with the @FXML annotation above it.

    @FXML
    private TextField _packetField;

Second, we'll need to declare a method to handle the event of a user pressing enter or clicking the "Send" button. This will read the text in the text field, clear the text field, and send a packet to the server. Declare and implement the following send method in the PacketToolController class.

    public void send() {
        Packet packet = new Packet(_packetField.getText()); // Construct a packet from the text field's text
        _packetField.setText(""); // Clear the text field
        _connection.send(packet); // Send the packet to the server
    }

B.2 Adding Send Controls to your View

In SceneBuilder, add the following controls to your View's design so that we can try sending packets. You will find each Control in the Control's tab of the Library pane. Drag them onto your View.

You can also add a label (as shown in the screenshot below) to help indicate what the text field represents.

B.2.1 - TextField - This is where you will type in a packet String. We will hook up its "on action" method to call the send method.

Properties Tab
  Prompt Text: Enter a Packet String
Code Tab
  fx:id - _packetField
  On Action - send

B.2.2 - Button

Properties Tab
  Text: Send
Code Tab
  On Action - send


Remember to save in SceneBuilder then refresh in Eclipse. Try running again. You should not have any errors at this point.

Part C. Interacting with the Protocol

We should now be able to test sending and receiving String packets to and from the server! Remember, the Controller is registered to receive notifications every time a packet is sent/received from the server. All we are currently doing with those packets is printing them out to the console.

To begin with, try sending me a direct message. Enter the following packet String into the text field and press send:

dm:krisj:hello

In your console window in Eclipse, you should now see printed out:

-> dm:krisj:hello
<- error:401:You have not not authenticated via an auth packet

Ah! Whenever we connect the first packet we must send is an auth packet. It's format is like this:

auth:<onyen>:<key>

The onyen will be your onyen (*not* your PID!). Your key is unique to your ONYEN and found on your My110 page's sidebar. It is a 16-character String of letters and numbers. Don't worry, you do not need to memorize this! Go find your key now and copy it into your clipboard.

Now try sending an auth packet. If it's successful your console should look something like:

-> auth:onyen:keykeykeykeykeyy
<- auth:onyen:hello

Once you are authenticated, you can see who else is currently connected to the server by sending a who packet, which is simply the word 'who'. The server will respond with the list of onyens currently connected.

-> who
<- who:krisj:rameses

Choose an ONYEN from the response list (they're separated by :'s) and try sending them a direct message. You *can* direct message yourself.

When you send a direct message you will not get a packet until the other person sends you one. You *can* direct message yourself so that your message is immediately delivered to you for testing.

-> dm:krisj:test message
<- dm:krisj:test message
-> dm:carol:hey bae
<- error:404:carol is offline

We only allow you to have one connection open per ONYEN, however, you can add a special suffix to your ONYEN and use your same key to authenticate, so that you can try sending messages between two running versions of your program. To see this in action, try running a *second* Packet Tool window. In it, try authenticating with <onyen>-test as your onyen. You should now be able to dm back and forth between <onyen> and <onyen>-test

C.1 - Always authenticate when your app starts up

Let's have the app automatically send an auth packet when it begins. In the ToolsApp class' start method, after you've constructed your PacketToolWindow and under the TODO, go ahead and send an auth packet so that you do not need to repeat this step manually each time you test:

// TODO: Authenticate with Server
connection.send(new Packet("auth:onyen:keykeykeykey"));

When you start your app up again, you should now see it automatically authenticating on startup.

Part D. Adding List Views for Packet History

Printing messages to and from the console was just a short term step to ensure everything was connected. Let's make it such that our Packet history is visible in the View.

To do this we will use a control called a ListView. You've encountered these in apps where you are presented with a list of items. We will have one to show a history of packets sent and another for received.

D.1 - In the PacketToolController, import the class javafx.scene.control.ListView. Then add the following FXML fields:

 @FXML
 private ListView<Packet> _sent, _received;

We will learn what the <Packet> notation means in lecture 19. You can read this as a ListView who works with Packet objects.

D.2 - Next, in SceneBuilder, we will need to add two ListView controls whose names correspond with those in the FXML field. We arranged ours like the screenshot below, however you can design yours any way you'd like. Labels are optional but encouraged.

ListView - Add one to your view that will be for our sent Packets  

Code Tab
  fx:id - _sent

ListView - Add one to your view that will be for our received Packets  

Code Tab
  fx:id - _received


Save in SceneBuilder, refresh in Eclipse!

D.3 - Add packets to their respective ListViews

In the methods that are currently simply printing out packets as they are sent/received, we will instead add them to the ListViews we just setup in our View and Controller. Change your methods to do the following:

    public void packetSent(Packet packet) {
        _sent.getItems().add(packet); // Add the packet to the _sent ListView's items
        _sent.scrollTo(packet); // Scroll to it (once the list gets long)
    }

    public void packetReceived(Packet packet) {
        _received.getItems().add(packet);
        _received.scrollTo(packet);
    }

Try running your app again and your Packets should no longer print to console. Instead they should begin showing up in your ListViews.

This portion of the problem set was more tutorial-like than the typical problem set in order to give you more experience with the connections between App-Window-Controller-View classes in JavaFX and to get you comfortable with the Chat110 protocol.

In the next part of this problem set you will be tasked with transforming generic packet objects into specific message objects. Finally, in the subsequent part after that, you will build a real chat-like user interface with a friends list and conversation windows.