Here's the code! It's not the prettiest just yet... I've been mangling and messing with it for quite a while, trying to resolve the issue.
/* SERVER.JAVA */
// Import statements make it possible to reference required packaged code.
import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
// This class defines a server that allows connection of multiple clients
// to access a shared file of employee data.
// ---- Written by: Jon Huhtala
// ---- Modified by: Chad Schultz
public class Server {
// Variables
String path = "c:/temp/employees.txt"; // Path of disk file
Map employees = new TreeMap(); // The employee collection
Employee emp; // The current Employee object
int port = 7000; // Port number
int loginNumber = 0; // For counting logins
ArrayList threads; // For storing
thread references
JPanel panel; // Component panel
TextArea message; // For Scrolling message area
JFrame frame; // Reference for main window
JButton exit; // EXIT button
// Main method to create a Server object and begin its processing.
public static void main(String[] args) {
new Server().go();
}
// This method builds the server GUI, reads data from disk and loads
// it into the collection, establish a port, and receive client logins.
public void go() {
// Build the GUI.
frame = new JFrame("Server");
frame.setSize(350, 275);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
panel = new JPanel();
panel.setLayout(new FlowLayout());
panel.add(new JLabel("Server Status and Messages"));
message = new TextArea("", 10, 40, TextArea.SCROLLBARS_VERTICAL_ONLY);
message.setBackground(Color.black);
message.setForeground(Color.yellow);
panel.add(message);
exit = new JButton("EXIT");
exit.addActionListener(new ExitListener());
panel.add(exit);
frame.getContentPane().add(panel);
frame.setVisible(true);
// Read data from disk and load the collection.
try {
FileReader fr = new FileReader(path);
BufferedReader br = new BufferedReader(fr);
String line = br.readLine();
while (line != null) {
StringTokenizer parser = new StringTokenizer(line, ",");
ArrayList fields = new ArrayList();
while (parser.hasMoreTokens()) {
fields.add(parser.nextToken());
}
System.out.println("Employee number " + fields.get(0) + ": " + fields.get(1));
employees.put(fields.get(0),fields.get(1));
line = br.readLine();
}
System.out.println("End of file");
br.close();
message.append("Collection loaded" + "\n");
}
catch (IOException err) {
System.out.println(err.getMessage());
}
// Establish a port and accept client logins.
try {
ServerSocket ss = new ServerSocket(port);
message.append("Server active" + "\n");
message.append("Address: " + InetAddress.getLocalHost() + "\n");
message.append("Port: " + port + "\n");
threads = new ArrayList();
while (true) {
Socket s = ss.accept();
// Create a thread to process requests from the client, add it
// to the list of threads, and start its processing.
ServerThread thread = new ServerThread(s, loginNumber);
threads.add(thread);
thread.start();
loginNumber++;
}
}
catch(IOException err) {
message.append(err.getMessage() + "\n");
}
}
// This inner class defines an action listener for the exit button.
class ExitListener implements ActionListener {
// When the button is clicked, all active threads are killed, data
// is written back to disk from the collection in ascending employee
// number order, and the server is shutdown.
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < threads.size(); i++) {
((ServerThread)threads.get(i)).kill();
threads.remove(i);
}
Iterator p = employees.keySet().iterator();
try {
FileWriter fw = new FileWriter(path);
BufferedWriter bw = new BufferedWriter(fw);
System.out.println("Writing employees to disk...");
while (p.hasNext()) {
emp = (Employee) employees.get(p.next());
System.out.println(emp.getNumber() + " - " + emp.getName());
bw.write(emp.getNumber() + "," + emp.getName());
bw.newLine();
}
bw.close();
System.out.println("Closed for output");
}
catch (IOException err) {
message.append(err.getMessage() + "\n");
}
frame.dispose();
System.exit(0);
}
}
// This inner class encapsulates the thread processing for a single client.
class ServerThread extends Thread {
// Variables
boolean alive; // For continuing thread processing
int threadNum; // For holding assigned thread number
Socket s; // Reference for the thread's Socket
ObjectOutputStream toClient; // For writing objects to the client
ObjectInputStream fromClient; // For reading objects from the client
// This constructor initializes the ServerThread object, creates
// object streams for communicating with the client, and sends the
// client its Integer thread number.
public ServerThread(Socket mySocket, int myThreadNum) {
s = mySocket;
threadNum = myThreadNum;
try {
toClient = new ObjectOutputStream(s.getOutputStream());
fromClient = new ObjectInputStream(s.getInputStream());
toClient.writeObject(new Integer(threadNum));
toClient.flush();
message.append("Thread " + threadNum + ": Connected" + "\n");
/*Employee e;
e = (Employee)fromClient.readObject();
System.out.println((String)e.getName());*/
}
catch(IOException err) {
message.append("Thread " + threadNum + ": " + err.getMessage() + "\n");
}
catch(Exception err) { System.out.println("Heh heh. Oops");}
}
// This method defines thread processing with the client. The thread is
// marked as being alive, then it loops to handle client requests until
// it is killed.
public void run() {
alive = true;
while (alive) {
try {
String request = (String)fromClient.readObject();
// Processing for a "FIND" request.
if (request.equals("FIND")) {
// Read the employee number from the client. If it matches that
// of an existing employee, get the Employee object from the
// collection and send it to the client. Otherwise, send a "dummy"
// Employee object to indicate that the requested employee was
// not found.
Integer empNum = (Integer)fromClient.readObject();
if (employees.containsKey(empNum)) {
emp = (Employee)employees.get(empNum);
toClient.writeObject(emp);
toClient.flush();
message.append("Thread " + threadNum + ": FOUND employee " +
emp.getNumber() + "\n");
}
else {
toClient.writeObject(new Employee(0, ""));
toClient.flush();
message.append("Thread " + threadNum +
": DID NOT FIND employee " +
empNum.intValue() + "\n");
}
}
// Processing for an "ADD" request.
else if (request.equals("ADD")) {
// Read the Employee object from the client. If an employee
// with a matching employee number already exists, send an error
// String to the client. Otherwise, put the Employee object into the
// collection and send a verification String to the client.
emp = (Employee)fromClient.readObject();
if (safeUpdate() == false) {
toClient.writeObject("NOT ADDED");
toClient.flush();
message.append("Thread " + threadNum +
": DID NOT ADD employee " +
emp.getNumber() + "\n");
}
else {
toClient.writeObject("ADDED");
toClient.flush();
message.append("Thread " + threadNum + ": ADDED employee " +
emp.getNumber() + "\n");
}
}
// Processing for a "DISCONNECT" request.
else if (request.equals("DISCONNECT")) {
alive = false;
message.append("Thread " + threadNum + ": DISCONNECTING" + "\n");
}
// Processing for an invalid request. The thread is killed due to
// the difficulty of re-synchronizing client communications.
else {
alive = false;
message.append("Thread " + threadNum +
": INVALID: " + request + "\n");
}
}
catch (Exception err) {
alive = false;
message.append("Thread " + threadNum + ": " +
err.getMessage() + "\n");
}
}
// Close the object streams as thread processing ends.
try {
fromClient.close();
toClient.close();
message.append("Thread " + threadNum + ": DISCONNECTED" + "\n");
}
catch (Exception err) {
message.append("Thread " + threadNum + ": " + err.getMessage() + "\n");
}
}
// This method can be called to kill the thread's processing loop.
public void kill() {
alive = false;
}
// This method can be called to safely update the collection in a
// multithreaded environment.
public synchronized boolean safeUpdate() {
if (employees.containsKey(new Integer(emp.getNumber()))) {
return false;
}
else {
employees.put(new Integer(emp.getNumber()), emp);
return true;
}
}
}
}
// This class encapsulates the data and processing of an employee.
// ---- Written by: Jon Huhtala
class Employee implements Serializable {
// Instance variables
private int number;
private String name;
// This constructor will initialize employee data to values supplied by
// the caller. No editing is performed (let's hope they get it right).
public Employee(int iNumber, String iName) {
number = iNumber;
name = iName;
}
// These "getter" methods will return the corresponding employee data.
public int getNumber() {
return number;
}
public String getName() {
return name;
}
}
/* CLIENT.JAVA */
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import javax.swing.*;
import javax.swing.event.*;
public class Client {
JFrame frame;
JPanel panel;
JTextField empNum;
JTextField empName;
JButton find;
JButton add;
JButton exit;
ObjectInputStream fromServer;
ObjectOutputStream toServer;
Employee emp;
boolean disconnect;
int threadNum;
public static void main(String[] args) {
Client me = new Client();
me.go();
}
public void go() {
disconnect = false;
frame = new JFrame("Client");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 120);
panel = new JPanel();
frame.getContentPane().add(panel);
panel.add(new JLabel("Employee number:"));
empNum = new JTextField(15);
panel.add(empNum);
panel.add(new JLabel("Employee name:"));
empName = new JTextField(16);
panel.add(empName);
find = new JButton("Find");
find.addActionListener(new FindListener());
panel.add(find);
add = new JButton("Add");
add.addActionListener(new AddListener());
panel.add(add);
exit = new JButton("Exit");
exit.addActionListener(new ExitListener());
panel.add(exit);
frame.setVisible(true);
try {
Socket s = new Socket("localhost", 7000);
ObjectInputStream fromServer = new ObjectInputStream(s.getInputStream());
ObjectOutputStream toServer = new ObjectOutputStream(s.getOutputStream());
threadNum = (Integer)fromServer.readObject();
int i = 0;
System.out.println("Thread number: " + threadNum);
System.out.println("Connected to server");
/*emp = new Employee(99,"HI Lois");
toServer.writeObject(emp);
toServer.flush();*/
do {
// i++;
} while (!disconnect);
// } while (i < 3);
fromServer.close();
toServer.close();
System.out.println("Connection is closed");
}
catch(Exception err) {
System.out.println(err.getMessage());
}
}
//Code for the "Find" button. This sends the employee number to the server
//and receives an Employee object, which it display unless the employee
//number is 0, representing that no match was found. In that case,
//an error message is displayed instead of the employee's name.
// -Chad Schultz
public class FindListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String cmd = new String("FIND");
Integer n;
try {
n = new Integer(Integer.parseInt(empNum.getText()));
}
catch (NumberFormatException err) {
empName.setText("Need a number in employee number field- like so");
empNum.setText("17");
n = 17;
}
//emp = new Employee(99,"HI Lois");
try {
toServer.close();
// toServer.writeObject(new Employee(5,"hello world"));
toServer.writeObject(cmd);
//toServer.writeObject(emp);
System.out.println("Sent cmd: " + cmd);
toServer.flush();
toServer.writeObject(n);
toServer.flush();
emp = (Employee)fromServer.readObject();
System.out.println("emp name: " + emp.getName());
System.out.println("emp number: " + emp.getNumber());
if (emp.getNumber() == 0)
empName.setText("NO MATCH FOUND");
else {
empNum.setText(""+emp.getNumber());
empName.setText(emp.getName());
}
}
catch(InvalidClassException err) {
System.out.println("InvalidClass");
}
catch(NotSerializableException err) {
System.out.println("Not serializable");
}
catch(IOException err) {
System.out.println("IO Exception");
}
catch(Exception err) {
System.out.println("Woopsie!");
System.out.println(err.getLocalizedMessage());
}
}
}
// -Chad Schultz
public class AddListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
try {
toServer.writeObject((String)"ADD");
// toServer.flush();
toServer.writeObject(new Employee(Integer.parseInt(empNum.getText()),empName.getText()));
toServer.flush();
empName.setText((String)fromServer.readObject());
}
catch(Exception err) {
System.out.println(err.getMessage());
}
}
}
// -Chad Schultz
public class ExitListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
disconnect = true;
try {
toServer.writeObject((String)"DISCONNECT");
/* fromServer.close();
toServer.close();
System.out.println("Connection is closed");*/
}
catch(Exception err) {
System.out.println(err.getMessage());
}
frame.dispose();
System.exit(0);
}
}
}
// This class encapsulates the data and processing of an employee.
// ---- Written by: Jon Huhtala
class Employee implements Serializable {
// Instance variables
private int number;
private String name;
// This constructor will initialize employee data to values supplied by
// the caller. No editing is performed (let's hope they get it right).
public Employee(int iNumber, String iName) {
number = iNumber;
name = iName;
}
// These "getter" methods will return the corresponding employee data.
public int getNumber() {
return number;
}
public String getName() {
return name;
}
}