import javax.swing.*; import java.awt.event.*; import java.awt.*; import java.util.*; /** * JMSChat implements the JMS chat client. It is a multi-threaded * program that uses a pub/sub topic for broadcasting messages to all * subscribers to the topic. * It creates the user interface and several threads; one to send * messages, one to receive messages, and one to update the user * interface. * The threads for sending and receiving messages are implemented by * the JMSChatThread class. */ public class JMSChat extends JFrame { JFrame frame; CardPanel cardStackPanel; ChatPanel chatCard; LoginPanel loginCard; static String chatUser = null; static String chatTopic = null; JMSChatThread receiverThread = null; JMSChatThread senderThread = null; Thread updateThread = null; UpdateTextArea doUpdate; private ChatData controlData; final static String newline = "\n"; final static String loginButtonLabel = "Login"; final static String resetButtonLabel = "Reset"; final static String exitButtonLabel = "Exit"; final static String defaultTopic = "ChatTopic"; final static String defaultUserLabel = "Please enter a user name :"; final static String defaultTopicLabel = "Please enter a topic name (" + defaultTopic + " is default) :"; final static String errorUserLabel = "PLEASE ENTER A VALID USER NAME :"; final static String errorTopicLabel = "PLEASE ENTER A VALID TOPIC NAME :"; /** * Class constructor. * * @param controlData control flags and data */ public JMSChat(ChatData controlData) { this.controlData = controlData; frame = new JFrame("JMS Chat Client"); frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE); frame.setBackground(Color.lightGray); frame.setForeground(Color.white); frame.setSize(580,300); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { exitJMSChat(); } }); cardStackPanel = new CardPanel(); loginCard = new LoginPanel(frame); cardStackPanel.add(loginCard,"loginCard"); cardStackPanel.showCard("loginCard"); frame.getContentPane().add(cardStackPanel); frame.setVisible(true); /* * Start the thread to update the user interface. * This thread only uses the thread-safe append * method, so no "SwingWorker" thread is required. */ doUpdate = new UpdateTextArea(); updateThread = new Thread(doUpdate); updateThread.start(); } /** * Thread to update the user interface with messages * received from the JMS chat topic. */ class UpdateTextArea implements Runnable { public void run() { String message = null; while (true) { try { message = controlData.retrieveInbound(); if (message != null) { chatCard.receivedTextArea.append(message + newline); } } catch (Exception e) { System.out.println("Exception occurred: " + e.toString()); } } } } /** * The main entry point. */ public static void main(String[] args) { if (args.length != 0) { System.out.println( "Usage: java -Djms.properties= JMSChat"); System.exit(1); } ChatData controlData = new ChatData(); JMSChat myChat = new JMSChat(controlData); } /** * Exit routine. */ private void exitJMSChat() { // Set the exit flag controlData.setExitFlag(); // Interrupting sender thread triggers tidy up if (senderThread != null) { senderThread.interrupt(); } // Have to be more brutal with receiver thread if (receiverThread != null) { receiverThread.tidyUp(); } frame.setVisible(false); frame.dispose(); System.exit(0); } /** * User interface login panel. * Used to obtain user login name and chat topic name. */ private class LoginPanel extends JPanel { JButton loginButton; JButton resetButton; JLabel userLabel; JLabel topicLabel; JTextField userName; JTextField topicName; public LoginPanel(JFrame parent) { GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); setLayout(gridbag); setBorder(BorderFactory.createCompoundBorder( BorderFactory.createMatteBorder( 1,1,2,2,Color.black), BorderFactory.createEmptyBorder(5,5,5,5))); c.fill = GridBagConstraints.HORIZONTAL; c.anchor = GridBagConstraints.NORTH; c.gridwidth = GridBagConstraints.REMAINDER; c.gridheight = 1; userLabel = new JLabel(defaultUserLabel); gridbag.setConstraints(userLabel, c); add(userLabel); c.weighty = 1.0; userName = new JTextField(); userName.setEditable(true); userName.setText(""); gridbag.setConstraints(userName, c); add(userName); c.weightx = 0.0; c.weighty = 0.0; topicLabel = new JLabel(defaultTopicLabel); gridbag.setConstraints(topicLabel, c); add(topicLabel); c.weighty = 1.0; topicName = new JTextField(); topicName.setEditable(true); topicName.setText(defaultTopic); gridbag.setConstraints(topicName, c); add(topicName); c.weightx = 1.0; c.weighty = 0.0; c.gridwidth = 1; c.insets = new Insets(10, 10, 0, 10); c.gridwidth = GridBagConstraints.RELATIVE; loginButton = new JButton(loginButtonLabel); gridbag.setConstraints(loginButton, c); add(loginButton); c.gridwidth = GridBagConstraints.REMAINDER; resetButton = new JButton(resetButtonLabel); gridbag.setConstraints(resetButton, c); add(resetButton); loginButton.addActionListener((new ActionListener () { public void actionPerformed(ActionEvent ae) { chatUser = userName.getText(); chatTopic = topicName.getText(); if ((chatUser.length() == 0) || (chatTopic.length() == 0)) { // Invalid input, try again userName.setText(""); topicName.setText(defaultTopic); if (chatUser.length() == 0) { userLabel.setText(errorUserLabel); } else { userLabel.setText(defaultUserLabel); } if (chatTopic.length() == 0) { topicLabel.setText(errorTopicLabel); } else { topicLabel.setText(defaultTopicLabel); } cardStackPanel.showCard("loginCard"); } else { // Create the message sender and receiver // threads senderThread = new JMSChatThread(JMSChatThread.SENDER, controlData, chatUser, chatTopic); receiverThread = new JMSChatThread(JMSChatThread.RECEIVER, controlData, chatUser, chatTopic); senderThread.start(); receiverThread.start(); // Do not proceed until both threads connected controlData.waitUntilConnected(); chatCard = new ChatPanel(); cardStackPanel.add(chatCard,"chatCard"); cardStackPanel.showCard("chatCard"); } } })); resetButton.addActionListener((new ActionListener () { public void actionPerformed(ActionEvent ae) { userLabel.setText(defaultUserLabel); userName.setText(""); topicLabel.setText(defaultTopicLabel); topicName.setText(defaultTopic); cardStackPanel.showCard("loginCard"); } })); } } /** * User interface chat panel. * Used to input messages to send to the chat topic, and to * display messages received from the chat topic. */ private class ChatPanel extends JPanel { JTextArea receivedTextArea; JTextField sendTextField; JButton exitButton; // Constructor public ChatPanel() { JLabel l = null; GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); setLayout(gridbag); setBorder(BorderFactory.createCompoundBorder( BorderFactory.createMatteBorder( 1,1,2,2,Color.black), BorderFactory.createEmptyBorder(5,5,5,5))); c.fill = GridBagConstraints.HORIZONTAL; c.anchor = GridBagConstraints.NORTH; c.gridwidth = GridBagConstraints.REMAINDER; c.gridheight = 1; c.gridx = GridBagConstraints.REMAINDER; c.gridy = GridBagConstraints.RELATIVE; c.weightx = 10; c.weighty = 10; l = new JLabel("What " + chatUser + " receives from " + chatTopic + " :"); gridbag.setConstraints(l, c); add(l); c.fill = GridBagConstraints.BOTH; c.weighty = 100; c.gridheight = 6; receivedTextArea = new JTextArea(); receivedTextArea.setEditable(false); JScrollPane topScrollPane = new JScrollPane(receivedTextArea); topScrollPane.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); gridbag.setConstraints(topScrollPane, c); add(topScrollPane); c.fill = GridBagConstraints.HORIZONTAL; c.anchor = GridBagConstraints.SOUTH; c.weighty = 10; c.gridheight = 1; l = new JLabel("Enter text to send to " + chatTopic + " :"); gridbag.setConstraints(l, c); add(l); sendTextField = new JTextField(); sendTextField.setEditable(true); gridbag.setConstraints(sendTextField, c); add(sendTextField); c.weightx = 1.0; c.weighty = 0.0; c.insets = new Insets(10, 10, 0, 10); c.gridheight = GridBagConstraints.REMAINDER; c.gridwidth = GridBagConstraints.REMAINDER; exitButton = new JButton(exitButtonLabel); gridbag.setConstraints(exitButton, c); add(exitButton); sendTextField.addActionListener((new ActionListener () { public void actionPerformed(ActionEvent ae) { String textIn = sendTextField.getText(); if (textIn.length() != 0) { controlData.storeOutbound(textIn); } chatCard.sendTextField.setText(""); } })); exitButton.addActionListener((new ActionListener () { public void actionPerformed(ActionEvent ae) { exitJMSChat(); } })); } } }