Create a GUI Controller: this will have instructions, and buttons to start the s
ID: 3832980 • Letter: C
Question
Create a GUI Controller: this will have instructions, and buttons to start the server, to start each client, and to exit.
Instructions will be several labels
Start Server Button:
This button will have a mnemonic and a tooltip.
When this button is selected, create a new ChatServerExec.
Then call the ChatServerExec.startServer method with the port number as an argument if the server has not yet been created.
The GUI controller will maintain a sentinel that specifies whether or not the server has been started yet, and display a JOptionPane message if it has.
Start Client Button:
This button will have a mnemonic and a tooltip.
Each time the “Start Client” button is selected in the GUI, create a new ChatClientExec.
Then call the ChatClientExec.startClient method with the port number as an argument
Exit Button:
This button will have a mnemonic and a tooltip.
When selected, the server and any clients, along with the controller GUI, will close.
Client – The Chat application for each user. The client will consist of an executive (ChatClientExec) and ChatClient classes.
Start ChatClientExec from the GUI after the Server is running.
ChatClientExec will create a new ChatClient and run it in its own thread.
The ChatClient process will create a new Stage, which will produce a separate GUI for each client.
The user will be asked to enter a screen name
When the screen name is accepted, the client’s chat textbox will be enabled. When the user types into the textbox, the client will transmit the message to the server.
When the client receives messages from the server, it will be displayed in the client’s textarea.
Server – Manages the clients.
Maintain a list of screen names in use in this session and check new names for duplication.
Maintain a list of PrintWriter objects, and iterate through them to echo-print clients’ messages.
Start this class before the client.
Assumptions:
It is assumed that the GUI, the server, and clients will be run on the same computer.
It is assumed that synchronization of objects with locks or conditions will not be needed
Explanation / Answer
//GUIController.java
import javax.swing.JOptionPane;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.geometry.Insets;
public class GUIController extends Application {
// Declares variables to hold the Labels for the instructions that will be displayed
Label instructionTitleLabel;
Label step1InstructionLabel;
Label step2InstructionLabel;
Label step3InstructionLabel;
Label step4InstructionLabel;
Label step5InstructionLabel;
Button startServerButton; // Declares variable to hold the "Start the Server" button
Button startClientButton; // Declares variable to hold the "Start each Client" button
Button exitButton; // Declares variable to hold the "Exit" button
// Counter to make sure the server can only be started once
int counter = 0;
/**
* Sets the layout of the GUI, with all the labels, textfields, and buttons that the chat room application will need to perform all its functions.
*/
@Override
public void start(Stage stage)
{
// Create the Labels that will be used to display the instructions.
instructionTitleLabel = new Label("Chat Room Controller");
instructionTitleLabel.setFont(new Font(18));
step1InstructionLabel = new Label("1. Start the server.");
step2InstructionLabel = new Label("2. Start a client.");
step3InstructionLabel = new Label("3. Enter a screen name in the clients's GUI ");
step4InstructionLabel = new Label("4. Start more clients.");
step5InstructionLabel = new Label("5. Enter a message in a client's GUI.");
// Create the buttons that will be used by the Chat Room Controller to setup the server and start each client.
startServerButton = new Button("Start the _Server");
// Assign the letter S in the word Server as the mnemonic for the "startServerButton" button
startServerButton.setMnemonicParsing(true);
// Add a tooltip to the "startServerButton" button
startServerButton.setTooltip(new Tooltip("Click here to start the server."));
startClientButton = new Button("Start each _Client");
// Assign the letter C in the word Client as the mnemonic for the "startClientButton" button
startClientButton.setMnemonicParsing(true);
// Add a tooltip to the "startServerButton" button
startClientButton.setTooltip(new Tooltip("Click here to start a client."));
exitButton = new Button("_Exit");
// Assign the E key as the mnemonic for the "Exit" button
exitButton.setMnemonicParsing(true);
// Add a tooltip to the "Exit" button
exitButton.setTooltip(new Tooltip("Click here to exit."));
//exitButton.setPadding(new Insets(5, 18, 5, 18));
// Sets event handlers on each button, which will perform different actions, depending on which button the user clicks.
startServerButton.setOnAction(new StartServerButtonEventHandler());
startClientButton.setOnAction(new StartClientButtonEventHandler());
exitButton.setOnAction(new ExitButtonEventHandler());
// Create vertical box to place the labels that will display the instructions
VBox instructionTitlePane = new VBox();
instructionTitlePane.setAlignment(Pos.CENTER_LEFT);
instructionTitlePane.getChildren().addAll(instructionTitleLabel);
// Create vertical box to place the labels that will display the instructions
VBox instructionPane = new VBox(2);
instructionPane.setAlignment(Pos.CENTER_LEFT);
instructionPane.setPadding(new Insets(10,180,15,70));
instructionPane.setStyle("-fx-border-color: gray;");
// Add the buttons to the children of the addActorPane vertical box
instructionPane.getChildren().addAll(instructionTitlePane, step1InstructionLabel, step2InstructionLabel, step3InstructionLabel, step4InstructionLabel, step5InstructionLabel);
HBox buttonPane = new HBox(15);
buttonPane.setAlignment(Pos.CENTER);
buttonPane.setPadding(new Insets(15, 0, 15, 0));
// Add the buttons to the children of the buttonPane horizontal box
buttonPane.getChildren().addAll(startServerButton, startClientButton, exitButton);
// Create a vertical box that will nest the addActorBoxPane on top, followed by the addMovieBoxPane at the bottom.
VBox contentPane = new VBox();
contentPane.setAlignment(Pos.CENTER);
//contentPane.setPadding(new Insets(15, 50, 0, 50));
contentPane.getChildren().addAll(instructionPane, buttonPane);
// Create a BorderPane to place contentPane into the center of the GUI display.
// contentPane contains all the nested Hbox's and Vbox's that were created to properly organize and display the contents of the GUI application.
BorderPane displayPane = new BorderPane();
// Place the contentPane in the center region of the BorderPane.
displayPane.setCenter(contentPane);
// Set displayPane as root of scene and set the scene on the stage
Scene scene = new Scene(displayPane);
stage.setTitle("Chat Room Controller");
stage.setScene(scene);
stage.show();
}
// An event handler for the Start Server button, which will start the server which will be used to communicate with the clients of the chatroom.
class StartServerButtonEventHandler implements EventHandler<ActionEvent>
{
@Override
public void handle(ActionEvent event)
{
// if no server has been started yet, then start the server
if(counter < 1)
{
ChatServerExec server = new ChatServerExec();
// I just choose a random port number of 9001 to start the server in.
server.startServer(9001);
// display JOption Pane message if server has started
JOptionPane.showMessageDialog(null, "The server has been started.");
counter++;
}
// if a server has already been started, then display message saying another one cannot be started if the user clicks the button again.
else
{
JOptionPane.showMessageDialog(null, "Cannot Start more than one server.");
}
}
}
// An event handler for the Start Client button, which will open up the chat room if a client is started successfully.
class StartClientButtonEventHandler implements EventHandler<ActionEvent>
{
@Override
public void handle(ActionEvent event)
{
// can only start a client if server has been stared first
if(counter >= 1)
{
//Each time the Start Client button is selected in the GUI, create a new ChatClientExec.
ChatClientExec client = new ChatClientExec();
try
{
client.startClient();
}
catch (Exception e)
{
e.printStackTrace();
}
}
// if the server has not been started, then display message saying to start it first.
else
{
JOptionPane.showMessageDialog(null, "Must start server first.");
}
}
}
// Will exit the program, if user clicks the "Exit" button.
class ExitButtonEventHandler implements EventHandler<ActionEvent>
{
@Override
public void handle(ActionEvent event)
{
System.exit(0);
}
}
/**
* Will launch the GUI for the Actor Graph application.
*/
public static void main(String[] args) {
launch(args);
}
}
==========================================================================
//ChatClient.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class ChatClient implements ChatClientInterface
{
private BufferedReader in;
private PrintWriter out;
private JFrame chatBox = new JFrame("Chat Room");
private JTextField messageTextField = new JTextField(40);
private JTextArea messageBox = new JTextArea(15, 50);
private Socket clientSocket;
private static int PORT;
public ChatClient()
{
// I just choose a random port number of 9001 to start the server in.
PORT = 9001;
// The ChatClient class file provided for this assignment had the Java Swing package imported, so I will be
// using Java Swing to create the chat box and textfields for the application.
chatBox.setVisible(true);
messageTextField.setEditable(false);
messageBox.setEditable(false);
chatBox.getContentPane().add(messageTextField, "North");
chatBox.getContentPane().add(new JScrollPane(messageBox), "Center");
chatBox.pack();
// Add Listeners
messageTextField.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
out.println(messageTextField.getText());
messageTextField.setText("");
}
});
}
/**
* Prompt for and return the desired screen name.
*
* @return the screen name the user wishes to use
*/
@Override
public String getName()
{
return JOptionPane.showInputDialog(chatBox, "Choose a screen name:", "Screen name selection", JOptionPane.PLAIN_MESSAGE);
}
/**
* The port number that the server is using
*
* @return the port number for the client to connect to the server
*/
@Override
public int getServerPort()
{
return PORT;
}
/**
* Connects to the server then enters the processing loop.
*/
public void run()
{
try
{
// Make the connection to the server using a socket
// I just choose a random port number of 9001 to start the server in.
clientSocket = new Socket("localhost", getServerPort());
//Use the input and output streams attached to the socket to communicate with the server
in = new BufferedReader(new InputStreamReader( clientSocket.getInputStream()));
out = new PrintWriter(clientSocket.getOutputStream(), true);
// read the messages from the server and perform the appropriate actions
while (true)
{
//When the server sends "SUBMITNAME" the client replies with the desired screen name.
String line = in.readLine();
if (line.startsWith("SUBMITNAME"))
{
// Use the getName() method to open a JOption pane box to allow the user to enter a desired screen name.
out.println(getName());
}
// Once a screen name has been accepted, then enable the chat textbox in the GUI for the user to enter messages
else if (line.startsWith("NAMEACCEPTED"))
{
messageTextField.setEditable(true);
}
// When the client receives messages from the server, it will be displayed in the clients textarea
else if (line.startsWith("MESSAGE"))
{
messageBox.append(line.substring(8) + " ");
}
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
==============================================================================
//ChatClientExec .java
public class ChatClientExec implements ChatClientExecInterface
{
/**
* Runs the client in its own thread
*/
@Override
public void startClient() throws Exception
{
//System.out.println("Client Connected");
//Will create a new ChatClient and run it in its own thread.
Thread client = new Thread()
{
@Override
public void run()
{
while (true)
{
ChatClient chatClient = new ChatClient();
chatClient.run();
}
}
};
client.start();
}
}
===========================================================================
//ChatClientExecInterface.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public interface ChatClientExecInterface {
/**
* Runs the client in its own thread
*/
public void startClient() throws Exception;
}
======================================================================
//ChatClientInterface.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public interface ChatClientInterface extends Runnable {
/**
* Prompt for and return the desired screen name.
*/
public String getName();
public int getServerPort();
}
=============================================================================
//ChatServerExec.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
public class ChatServerExec implements ChatServerExecInterface
{
private static int PORT;
private static HashSet<String> names = new HashSet<String>(); // Maintain a list of screen names in use in this session, to allow checking for duplicate screen names
private static HashSet<PrintWriter> writers = new HashSet<PrintWriter>(); // Maintain a list of output streams in order so each message can be retransmitted to all the clients.
public void startServer(int port)
{
PORT = port;
// Will create the server in a new thread in order not to block the GUI
Thread server = new Thread()
{
@Override
public void run()
{
try
{
ServerSocket serverSocket = new ServerSocket(PORT);
//System.out.println("Waiting for clients to connect...");
while (true)
{
try
{
// starts a new thread for each client that is added to the chat room.
while (true)
{
NewClientThread obj = new NewClientThread(serverSocket.accept());
obj.start();
}
}
finally
{
serverSocket.close();
}
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
};
server.start();
}
/**
* A Thread class which will be used every time a new client is added into the chat room
*/
private static class NewClientThread extends Thread
{
private Socket serverSocket;
private BufferedReader in;
private PrintWriter out;
private String screenName;
/**
* Constructs a handler thread, squirreling away the socket.
* All the interesting work is done in the run method.
*/
public NewClientThread(Socket socket)
{
this.serverSocket = socket;
}
/**
* Services this thread's client by repeatedly requesting a
* screen name until a unique one has been submitted, then
* acknowledges the name and registers the output stream for
* the client in a global set, then repeatedly gets inputs and
* broadcasts them.
*/
public void run()
{
try
{
//Use the input and output streams attached to the socket to communicate with the client
in = new BufferedReader(new InputStreamReader( serverSocket.getInputStream()));
out = new PrintWriter(serverSocket.getOutputStream(), true);
//If the name is already in use in this session, then an error message is given, and the user is re-asked for a screen name.
// If the user selects Cancel, the user is re-asked for a screen name.
while (true)
{
out.println("SUBMITNAME");
screenName = in.readLine();
if (screenName == null)
{
return;
}
synchronized (names)
{
// If the screen name is not already being used, then add it to the HashSet
if (!names.contains(screenName))
{
names.add(screenName);
break;
}
}
}
// If the screen name is accepted, then send it to the client, so that the chat textbox can be enabled.
out.println("NAMEACCEPTED");
writers.add(out);
// When the user types into the textbox, the client will transmit the message to the server.
while (true)
{
String input = in.readLine();
if (input == null)
{
return;
}
for (PrintWriter messages : writers)
{
messages.println("MESSAGE " + screenName + ": " + input);
}
}
}
catch (IOException e)
{
System.out.println(e);
}
finally
{
try
{
serverSocket.close();
}
catch (IOException e)
{
System.out.println(e);
}
}
}
}
}
=============================================================================
//ChatServerExecInterface.java
public interface ChatServerExecInterface {
/**
* Starts an instance of a server in a thread so that GUI thread can continue to operate asynchronously
* @param port
*/
public void startServer(int port);
}
============================================================================