package Ninja;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.*;
public class NinjaServer implements Runnable {
public final static int NULL = 0;
public final static int DISCONNECTED = 1;
public final static int DISCONNECTING = 2;
public final static int BEGIN_CONNECT = 3;
public final static int CONNECTED = 4;
public final static String statusMessages[] = {
" Can't Connect!!! Don't Try Again!", " Disconnected",
" Disconnecting...", " Connecting... Please Wait", " Connected"
};
public final static NinjaServer NinjaObj = new NinjaServer();
public final static String END_CHAT_SESSION =
new Character((char)0).toString();
public static String hostIP = "localhost";
public static int port = 1337;
public static int connectionStatus = DISCONNECTED;
public static boolean isHost = true;
public static String statusString = statusMessages[connectionStatus];
public static StringBuffer toAppend = new StringBuffer("");
public static StringBuffer toSend = new StringBuffer("");
// Various GUI components and info
public static JFrame mainFrame = null;
public static JTextArea chatText = null;
public static JTextField chatLine = null;
public static JPanel statusBar = null;
public static JLabel statusField = null;
public static JTextField statusColor = null;
public static JTextField ipField = null;
public static JTextField portField = null;
public static JRadioButton hostOption = null;
public static JRadioButton guestOption = null;
public static JRadioButton NinjaOption = null;
public static JButton helpButton = null;
public static JButton connectButton = null;
public static JButton disconnectButton = null;
public static ServerSocket hostServer = null;
public static Socket socket = null;
public static BufferedReader in = null;
public static PrintWriter out = null;
private static JPanel initOptionsPane() {
JPanel pane = null;
ActionAdapter buttonListener = null;
JPanel optionsPane = new JPanel(new GridLayout(4, 1));
pane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
pane.add(new JLabel("Computer Name:"));
ipField = new JTextField(10); ipField.setText(hostIP);
ipField.setEnabled(false);
ipField.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent e) {
ipField.selectAll();
// Should be editable only when disconnected
if (connectionStatus != DISCONNECTED) {
changeStatusNTS(NULL, true);
}
else {
hostIP = ipField.getText();
}
}
});
pane.add(ipField);
optionsPane.add(pane);
// Port input
pane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
pane.add(new JLabel("Port Number:"));
portField = new JTextField(10); portField.setEditable(true);
portField.setText((new Integer(port)).toString());
portField.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent e) {
// should be editable only when disconnected
if (connectionStatus != DISCONNECTED) {
changeStatusNTS(NULL, true);
}
else {
int temp;
try {
temp = Integer.parseInt(portField.getText());
port = temp;
}
catch (NumberFormatException nfe) {
portField.setText((new Integer(port)).toString());
mainFrame.repaint();
}
}
}
});
pane.add(portField);
optionsPane.add(pane);
buttonListener = new ActionAdapter() {
public void actionPerformed(ActionEvent e) {
if (connectionStatus != DISCONNECTED) {
changeStatusNTS(NULL, true);
}
else {
isHost = e.getActionCommand().equals("host");
// Cannot supply host IP if host option is chosen
if (isHost) {
ipField.setEnabled(false);
ipField.setText("localhost");
hostIP = "localhost";
}
else {
ipField.setEnabled(true);
}
}
}
};
ButtonGroup bg = new ButtonGroup();
hostOption = new JRadioButton("Host", true);
hostOption.setMnemonic(KeyEvent.VK_H);
hostOption.setActionCommand("host");
hostOption.addActionListener(buttonListener);
guestOption = new JRadioButton("Guest", false);
guestOption.setMnemonic(KeyEvent.VK_G);
guestOption.setActionCommand("guest");
guestOption.addActionListener(buttonListener);
NinjaOption = new JRadioButton ("Ninja", false);
NinjaOption.setMnemonic(KeyEvent.VK_N);
NinjaOption.setActionCommand("Ninja");
NinjaOption.addActionListener(buttonListener);
bg.add(hostOption);
bg.add(guestOption);
bg.add(NinjaOption);
pane = new JPanel(new GridLayout(1, 2));
pane.add(hostOption);
pane.add(guestOption);
pane.add(NinjaOption);
optionsPane.add(pane);
JPanel buttonPane = new JPanel(new GridLayout(1, 2));
buttonListener = new ActionAdapter() {
public void actionPerformed(ActionEvent e) {
// Request a connection initiation
if (e.getActionCommand().equals("connect")) {
changeStatusNTS(BEGIN_CONNECT, true);
}
// Disconnect
else {
changeStatusNTS(DISCONNECTING, true);
}
}
};
connectButton = new JButton("Connect");
connectButton.setMnemonic(KeyEvent.VK_C);
connectButton.setActionCommand("connect");
connectButton.addActionListener(buttonListener);
connectButton.setEnabled(true);
disconnectButton = new JButton("Disconnect");
disconnectButton.setMnemonic(KeyEvent.VK_D);
disconnectButton.setActionCommand("disconnect");
disconnectButton.addActionListener(buttonListener);
disconnectButton.setEnabled(false);
buttonPane.add(connectButton);
buttonPane.add(disconnectButton);
optionsPane.add(buttonPane);
return optionsPane;
}
// Initialize all the GUI components and display the frame
private static void initGUI() {
// Set up the status bar
statusField = new JLabel();
statusField.setText(statusMessages[DISCONNECTED]);
statusColor = new JTextField(1);
statusColor.setBackground(Color.red);
statusColor.setEditable(false);
statusBar = new JPanel(new BorderLayout());
statusBar.add(statusColor, BorderLayout.EAST);
statusBar.add(statusField, BorderLayout.CENTER);
// Set up the options pane
JPanel optionsPane = initOptionsPane();
// Set up the chat pane
JPanel chatPane = new JPanel(new BorderLayout());
chatText = new JTextArea(15, 30);
chatText.setLineWrap(true);
chatText.setEditable(false);
chatText.setForeground(Color.green);
JScrollPane chatTextPane = new JScrollPane(chatText,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
chatLine = new JTextField();
chatLine.setEnabled(false);
chatLine.addActionListener(new ActionAdapter() {
public void actionPerformed(ActionEvent e) {
String s = chatLine.getText();
if (!s.equals("")) {
appendToChatBox("OUTGOING: " + s + "\n");
chatLine.selectAll();
// Send the string
sendString(s);
}
}
});
chatPane.add(chatLine, BorderLayout.SOUTH);
chatPane.add(chatTextPane, BorderLayout.CENTER);
chatPane.setPreferredSize(new Dimension(200, 200));
// Set up the main pane
JPanel mainPane = new JPanel(new BorderLayout());
mainPane.add(statusBar, BorderLayout.SOUTH);
mainPane.add(optionsPane, BorderLayout.WEST);
mainPane.add(chatPane, BorderLayout.CENTER);
// Set up the main frame
mainFrame = new JFrame("Simple Instant Messenger (SIM)");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setContentPane(mainPane);
mainFrame.setSize(mainFrame.getPreferredSize());
mainFrame.setLocation(1, 1);
mainFrame.pack();
mainFrame.setVisible(true);
}
/////////////////////////////////////////////////////////////////
// The thread-safe way to change the GUI components while
// changing state
private static void changeStatusTS(int newConnectStatus, boolean noError) {
// Change state if valid state
if (newConnectStatus != NULL) {
connectionStatus = newConnectStatus;
}
// If there is no error, display the appropriate status message
if (noError) {
statusString = statusMessages[connectionStatus];
}
// Otherwise, display error message
else {
statusString = statusMessages[NULL];
}
// Call the run() routine (Runnable interface) on the
// error-handling and GUI-update thread
SwingUtilities.invokeLater(NinjaObj);
}
/////////////////////////////////////////////////////////////////
// The non-thread-safe way to change the GUI components while
// changing state
private static void changeStatusNTS(int newConnectStatus, boolean noError) {
// Change state if valid state
if (newConnectStatus != NULL) {
connectionStatus = newConnectStatus;
}
// If there is no error, display the appropriate status message
if (noError) {
statusString = statusMessages[connectionStatus];
}
// Otherwise, display error message
else {
statusString = statusMessages[NULL];
}
// Call the run() routine (Runnable interface) on the
// current thread
NinjaObj.run();
}
/////////////////////////////////////////////////////////////////
// Thread-safe way to append to the chat box
private static void appendToChatBox(String s) {
synchronized (toAppend) {
toAppend.append(s);
}
}
/////////////////////////////////////////////////////////////////
// Add text to send-buffer
private static void sendString(String s) {
synchronized (toSend) {
toSend.append(s + "\n");
}
}
/////////////////////////////////////////////////////////////////
// Cleanup for disconnect
private static void cleanUp() {
try {
if (hostServer != null) {
hostServer.close();
hostServer = null;
}
}
catch (IOException e) { hostServer = null; }
try {
if (socket != null) {
socket.close();
socket = null;
}
}
catch (IOException e) { socket = null; }
try {
if (in != null) {
in.close();
in = null;
}
}
catch (IOException e) { in = null; }
if (out != null) {
out.close();
out = null;
}
}
/////////////////////////////////////////////////////////////////
// Checks the current state and sets the enables/disables
// accordingly
public void run() {
switch (connectionStatus) {
case DISCONNECTED:
connectButton.setEnabled(true);
disconnectButton.setEnabled(false);
ipField.setEnabled(true);
portField.setEnabled(true);
hostOption.setEnabled(true);
guestOption.setEnabled(true);
NinjaOption.setEnabled(true);
chatLine.setText(""); chatLine.setEnabled(false);
statusColor.setBackground(Color.red);
break;
case DISCONNECTING:
connectButton.setEnabled(false);
disconnectButton.setEnabled(false);
ipField.setEnabled(false);
portField.setEnabled(false);
hostOption.setEnabled(false);
guestOption.setEnabled(false);
NinjaOption.setEnabled(false);
chatLine.setEnabled(false);
statusColor.setBackground(Color.yellow);
break;
case CONNECTED:
connectButton.setEnabled(false);
disconnectButton.setEnabled(true);
ipField.setEnabled(false);
portField.setEnabled(false);
hostOption.setEnabled(false);
guestOption.setEnabled(false);
NinjaOption.setEnabled(false);
chatLine.setEnabled(true);
statusColor.setBackground(Color.green);
break;
case BEGIN_CONNECT:
connectButton.setEnabled(false);
disconnectButton.setEnabled(false);
ipField.setEnabled(false);
portField.setEnabled(false);
hostOption.setEnabled(false);
guestOption.setEnabled(false);
NinjaOption.setEnabled(false);
chatLine.setEnabled(false);
chatLine.grabFocus();
statusColor.setBackground(Color.yellow);
break;
}
// Make sure that the button/text field states are consistent
// with the internal states
ipField.setText(hostIP);
portField.setText((new Integer(port)).toString());
hostOption.setSelected(isHost);
guestOption.setSelected(!isHost);
NinjaOption.setSelected(!isHost);
statusField.setText(statusString);
chatText.append(toAppend.toString());
toAppend.setLength(0);
mainFrame.repaint();
}
/////////////////////////////////////////////////////////////////
// The main procedure
public static void main(String args[]) throws IOException {
String s;
initGUI();
while (true) {
try { // Poll every ~10 ms
Thread.sleep(10);
}
catch (InterruptedException e) {}
switch (connectionStatus) {
case BEGIN_CONNECT:
try {
// Try to set up a server if host
if (isHost) {
hostServer = new ServerSocket(port);
socket = hostServer.accept();
Thread t = new Thread();
t.start();
}
// If guest, try to connect to the server
else {
socket = new Socket(hostIP, port);
}
in = new BufferedReader(new
InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
changeStatusTS(CONNECTED, true);
}
// If error, clean up and output an error message
catch (IOException e) {
cleanUp();
changeStatusTS(DISCONNECTED, false);
}
break;
case CONNECTED:
try {
// Send data
if (toSend.length() != 0) {
out.print(toSend); out.flush();
toSend.setLength(0);
changeStatusTS(NULL, true);
}
// Receive data
if (in.ready()) {
s = in.readLine();
if ((s != null) && (s.length() != 0)) {
// Check if it is the end of a trasmission
if (s.equals(END_CHAT_SESSION)) {
changeStatusTS(DISCONNECTING, true);
}
// Otherwise, receive what text
else {
appendToChatBox("INCOMING: " + s + "\n");
changeStatusTS(NULL, true);
}
}
}
}
catch (IOException e) {
cleanUp();
changeStatusTS(DISCONNECTED, false);
}
break;
case DISCONNECTING:
// Tell other chatter to disconnect as well
out.print(END_CHAT_SESSION); out.flush();
// Clean up (close all streams/sockets)
cleanUp();
changeStatusTS(DISCONNECTED, true);
break;
default: break; // do nothing
}
}
}
}
////////////////////////////////////////////////////////////////// //
// Action adapter for easy event-listener coding
class ActionAdapter implements ActionListener {
public void actionPerformed(ActionEvent e) {}
}
////////////////////////////////////////////////////////////////// //
I have two problems with this instant messenger at the current moment.
First : I still can't figure out how to get multiple clients to connect to the host. It says connected, but there is no actual Incoming and Outgoing messages.
Second : When I connect to the server as guest, once connected, it switches over to Ninja