/** *Class MagnificentChatApplet * *Version 1.0 * *@author Martin Carlsson * *Internet Programming 2 - Course **/ import java.awt.*; import java.awt.event.*; import javax.swing.event.*; import java.io.*; import java.net.*; import java.util.*; import javax.swing.*; import javax.swing.text.*; import javax.swing.border.*; import com.oreilly.servlet.HttpMessage; /** *Class MagnificentChatApplet is a threaded GUI class of the Magnificent *Chat application that is a subclass to JApplet. *It implements the Runnable, ActionListener and CaretListener interfaces. * *@version 1.0 * *@author Martin Carlsson * *@see javax.swing.JApplet */ public class MagnificentChatApplet extends JApplet implements Runnable, ActionListener, CaretListener { private JTextArea privateInputArea; private JButton privateSendButton, privateCancelButton; private String servletNameAndSession ="/A_8"; //initially the name of the servlet private String host;// Host to connect to private Thread thisActivity;// this thread protected static int ADD = 0, MSG = 1, REMOVE = 2, PRIVATE = 3;// Flags for the different kind of messages private boolean looping = true;//flag for the run() method private String nameTaken = "***nameTaken***";// If the servlet sends this the name is taken //Message area and it's scroll pane private JTextArea area; private JScrollPane areaPane; //List of online users private JList list; private JScrollPane listPane; //Input area and it's scroll pane private JTextArea input; private JScrollPane inputPane; //Private message input area and it's scroll pane private JTextArea privateMsgInput; private JScrollPane privateMsgInputPane; //North, east and west panels private JPanel northPanel; private JPanel eastPanel; private JLabel listLabel; private JMenuBar menuBar; private JMenu privateMenu; private JMenu helpMenu; private JMenuItem privateMsgItem = new JMenuItem("Send message to.."); private JMenuItem listPrivateMsgItem = new JMenuItem("Send message to.. "); private JMenuItem chatHelpItem = new JMenuItem("How to use..."); private JMenuItem aboutItem = new JMenuItem("About"); private JButton send = new JButton(" SEND "); protected String user = ""; private String[] users; private JLabel nickNameLabel; private JLabel chatTitle = new JLabel("Magnificent Web Chat"); //Help text private String helpText = " BROADCAST\n"+ "Type in your message and press the\n"+ "send button to make a broadcast\n"+ "to all online users.\n\n"+ " UNICAST\n"+ "Use the \"Send message\" option under\n"+ "the private menu - or click on a \n"+ "username in the list of users - to\n"+ "send a message to a specific user.\n"; //About text private String aboutText = "Magnificent Web Chat Version 1.0\n"+ "Created by Martin Carlsson. 2005\n"; // Menu that pops up when a client rightclicks in the input field JPopupMenu editPopup, listPopup; JMenuItem deleteItem = new JMenuItem("Delete"); JMenuItem selectAllItem = new JMenuItem("Select All"); JMenuItem cutItem; JMenuItem copyItem; JMenuItem pasteItem; PrivateDialog p; public void init() { SetLookAndFeel(); privateSendButton = new JButton("Send"); privateCancelButton = new JButton("Cancel"); privateSendButton.addActionListener(this); privateCancelButton.addActionListener(this); //Container to put everything in Container con = getContentPane(); con.setPreferredSize(new Dimension(100, 100)); con.setLayout(new BorderLayout()); //Message area area = new JTextArea(); area.setToolTipText("Messages sent or received"); area.setLineWrap(true); area.setBorder(BorderFactory.createEmptyBorder(4,4,4,4));//Onsynlig border runt meddelandeytan area.setEditable(false); //Holds the scrollbar to the bottom DefaultCaret caret = (DefaultCaret) area.getCaret(); caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); //Scroll and message area areaPane = new JScrollPane(area); areaPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); areaPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); //Input area input = new JTextArea(2,20); input.setToolTipText("Type message here"); DefaultCaret inputCaret = (DefaultCaret) input.getCaret(); inputCaret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); input.setLineWrap(true);//wraps long lines input.addKeyListener(kl); //listens to keys pressed input.setEditable(true); //made editable inputPane = new JScrollPane(input); input.addCaretListener(this); //Private message input area privateMsgInput = new JTextArea(2,20); privateMsgInput.setToolTipText("Type message here"); DefaultCaret privateMsgInputCaret = (DefaultCaret) privateMsgInput.getCaret(); privateMsgInputCaret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); privateMsgInput.setLineWrap(true);//wraps long lines privateMsgInput.addKeyListener(kl); //listens to keys pressed privateMsgInput.setEditable(true); //made editable privateMsgInputPane = new JScrollPane(privateMsgInput); //Policys for vertical and horizontal scrolling inputPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); inputPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); //Creates and adds default actions ActionMap aMap = input.getActionMap(); Action cut = aMap.get(DefaultEditorKit.cutAction); Action copy = aMap.get(DefaultEditorKit.copyAction); Action paste = aMap.get(DefaultEditorKit.pasteAction); //Popup menu for rightclicks in the input area editPopup = new JPopupMenu(); cutItem = editPopup.add(cut); copyItem = editPopup.add(copy); pasteItem = editPopup.add(paste); editPopup.add(deleteItem); editPopup.addSeparator(); editPopup.add(selectAllItem); //Sets the text for the default action menu choises cutItem.setText("Cut"); copyItem.setText("Copy"); pasteItem.setText("Paste"); //Adds listeners deleteItem.addActionListener(this); selectAllItem.addActionListener(this); privateMsgItem.addActionListener(this); listPrivateMsgItem.addActionListener(this); chatHelpItem.addActionListener(this); aboutItem.addActionListener(this); //Short commands for menu choises cutItem.setAccelerator(KeyStroke.getKeyStroke (KeyEvent.VK_X, ActionEvent.CTRL_MASK)); copyItem.setAccelerator(KeyStroke.getKeyStroke (KeyEvent.VK_C, ActionEvent.CTRL_MASK)); pasteItem.setAccelerator(KeyStroke.getKeyStroke (KeyEvent.VK_V, ActionEvent.CTRL_MASK)); selectAllItem.setAccelerator(KeyStroke.getKeyStroke (KeyEvent.VK_A, ActionEvent.CTRL_MASK)); deleteItem.setAccelerator(KeyStroke.getKeyStroke (KeyEvent.VK_D, ActionEvent.CTRL_MASK)); //South panel JPanel southPanel = new JPanel(); southPanel.setLayout(new BorderLayout()); southPanel.add( inputPane,BorderLayout.CENTER); send.setPreferredSize(new Dimension(120,30)); send.setEnabled(false); southPanel.add(send,BorderLayout.EAST); //Fill out panel JPanel southWestFill = new JPanel(); southWestFill.setBackground(Color.decode("#003366")); southPanel.add(southWestFill,BorderLayout.WEST); //Adds the central message area and the south panel con.add(areaPane,BorderLayout.CENTER); con.add(southPanel,BorderLayout.SOUTH); //Fill out panel JPanel southFill = new JPanel(); southFill.setBackground(Color.decode("#003366")); southPanel.add(southFill, BorderLayout.SOUTH); //Mouselistener for the input area and send button input.addMouseListener(mL); send.addActionListener(this); area.addMouseListener(mL); //List of online users list = new JList(); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.addMouseListener(mL); list.setModel(new DefaultListModel()); list.setBackground(Color.black); list.setForeground(Color.white); listPane = new JScrollPane(list); listPane.setBorder(new BevelBorder(BevelBorder.LOWERED)); DefaultCaret listCaret = (DefaultCaret) area.getCaret(); listCaret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); //East panel eastPanel = new JPanel(); eastPanel.setLayout(new BorderLayout()); eastPanel.setPreferredSize(new Dimension(120,getSize().height)); eastPanel.setBackground(Color.decode("#003366")); con.add(eastPanel,BorderLayout.EAST); //Label to show the number of online users listLabel = new JLabel(); listLabel.setHorizontalAlignment(JLabel.CENTER); listLabel.setForeground(Color.decode("#ff9900")); listLabel.setBackground(Color.decode("#003366")); eastPanel.add(listLabel,BorderLayout.NORTH); eastPanel.add(listPane,BorderLayout.CENTER); //Fill out panels JPanel eastRightFill = new JPanel(); eastRightFill.setBackground(Color.decode("#003366")); eastPanel.add(eastRightFill, BorderLayout.EAST); JPanel eastLeftFill = new JPanel(); eastLeftFill.setBackground(Color.decode("#003366")); eastPanel.add(eastLeftFill, BorderLayout.WEST); JPanel eastBottomFill = new JPanel(); eastBottomFill.setBackground(Color.decode("#003366")); eastPanel.add(eastBottomFill, BorderLayout.SOUTH); //North panel northPanel = new JPanel(); northPanel.setBackground(Color.decode("#003366")); northPanel.setLayout(new BorderLayout()); chatTitle.setFont(new Font("SansSerif", Font.BOLD,25)); chatTitle.setForeground(Color.white); northPanel.add(chatTitle,BorderLayout.CENTER); JPanel northLeftPanel = new JPanel(); northLeftPanel.setBackground(Color.decode("#003366")); northPanel.add(northLeftPanel,BorderLayout.WEST); con.add(northPanel, BorderLayout.NORTH); //Fill out panel JPanel northEastPanel = new JPanel(); //Label for the users name nickNameLabel = new JLabel("- Your nickname: TestNickName"); nickNameLabel.setFont(new Font("SansSerif", Font.BOLD,11)); nickNameLabel.setForeground(Color.white); nickNameLabel.setPreferredSize(new Dimension(300,15)); northEastPanel.add(nickNameLabel,BorderLayout.EAST); northEastPanel.setBackground(Color.decode("#003366")); northPanel.add(northEastPanel,BorderLayout.EAST); //Menu bar menuBar = new JMenuBar(); menuBar.setBackground(Color.decode("#003366")); privateMenu = new JMenu("Private"); privateMenu.setForeground(Color.decode("#ff9900")); helpMenu = new JMenu("Help"); helpMenu.setForeground(Color.decode("#ff9900")); setJMenuBar(menuBar); //Items for the menu bar privateMenu.add(privateMsgItem); helpMenu.add(chatHelpItem); helpMenu.add(aboutItem); menuBar.add(privateMenu); menuBar.add(helpMenu); //Pops up when a user click in the list of online users listPopup = new JPopupMenu("Private Message"); listPopup.setDefaultLightWeightPopupEnabled(false); listPopup.add(listPrivateMsgItem); //West panel JPanel westPanel = new JPanel(); westPanel.setBackground(Color.decode("#003366")); con.add(westPanel, BorderLayout.WEST); thisActivity = new Thread(this); }//end init() /** *Called right after the init(). Makes an internal *call to handleUserWelcoming and starts this thread. */ public void start() { servletNameAndSession = handleUserWelcoming(); thisActivity.start(); input.requestFocus(); } /** *Takes care of the welcoming. The client is given the opportunity to log in with any name that is not taken. *The name is sent to the servlet to ensure this. *Eventually the server sends a valid username and a session back. *@return string with the servlet name and the user session id */ public String handleUserWelcoming() { String serverResponse = null; String[] responseArr = null; try { user = JOptionPane.showInputDialog(this,"Please enter a nickname(1 - 12 characters)","Welcome to MagnificentChat!",JOptionPane.QUESTION_MESSAGE); if(user == null || user.equals("") || user.length() == 0) user = "guest"; else if(user != null && user.length() > 12) while(user.length() > 12 && user != null && user.length() != 0 && !user.equals("")) { user = JOptionPane.showInputDialog(this,"Please try again with no more than 12 characters","Bad input!",JOptionPane.ERROR_MESSAGE); if(user == null || user.equals("") || user.length() == 0) { user = "guest"; break; } } while(true) { InputStream in = sendToServer(user,ADD); BufferedReader data = new BufferedReader(new InputStreamReader(in)); serverResponse = data.readLine();//Reads the answer from the servlet if(serverResponse != null && serverResponse.equals(nameTaken)) {// If the name is taken user = JOptionPane.showInputDialog(this,"Try again or leave empty if u wish to be anonymous\n(1 - 12 characters)",user + " is already in use!",JOptionPane.QUESTION_MESSAGE); if(user == null || user.equals("") || user.length() == 0 || user.length() > 12) user = "guest"; } else if(serverResponse != null) { responseArr=serverResponse.split(","); user=responseArr[0]; nickNameLabel.setText("- Your nickname: " + user); break; } }//while } //try catch (SocketException sE) { String problem = "Can't establish a connection to host: \n" + sE.getMessage()+"\n"; area.append(problem); System.err.println(problem); try { Thread.sleep(5000); } catch (InterruptedException iE) { } } catch (FileNotFoundException fNfE) { String problem = "Resource not found in handleWelcoming: " + fNfE.getMessage(); area.append(problem); System.err.println(problem); try { Thread.sleep(5000); } catch (InterruptedException iE) { } } catch (Exception e) { String problem = "Unknown exception in handleWelcoming: " + e.getClass().getName() + ": " + e.getMessage(); area.append(problem); try { Thread.sleep(3000); } catch (InterruptedException iE) { } } return responseArr[1]; } // end handlewelcoming /** *Gets a message from the servlet *@return message from the servlet */ String getFromServer() { String msgFromServer = null; while (msgFromServer == null) { try { URL url = new URL(getCodeBase(), servletNameAndSession); HttpMessage msg = new HttpMessage(url); // No caching msg.setHeader("Expires", "Sat, 6 May 1995 12:00:00 GMT"); msg.setHeader("Cache-Control", "no-store, no-cache, must-revalidate"); msg.setHeader("Pragma", "no-cache"); InputStream in = msg.sendGetMessage(); BufferedReader data = new BufferedReader(new InputStreamReader(in)); msgFromServer = data.readLine();//Reads the message } catch (SocketException sE) { area.append("Can't establish a connection to host:\n" + sE.getMessage()+"\n" + sE.getStackTrace()); try { Thread.sleep(5000); } catch (InterruptedException iE) { area.append("Connection has timed out"); } } catch (FileNotFoundException fNfE) { area.append("Resource not found\n: " + fNfE.getMessage()); try { Thread.sleep(5000); } catch (InterruptedException iE) { } } catch (Exception e) { area.append("Unknown Exception:\n" + e.getClass().getName() + ": " + e.getMessage()); try { Thread.sleep(3000); } catch (InterruptedException iE) { } } } return msgFromServer; } //end getFromServer /** *Makes a call at each loop to the getFromServer() method to get the next message. */ public void run() { while (looping) { try { synchronized(this) { while (!looping) wait(); } } catch (InterruptedException e) { } String m = getFromServer(); if(m.startsWith("users,")) { users = m.replaceFirst("users,","").split(","); users = sortList(users); for(int i=0; i < users.length;i++) users[i] = " ".concat(users[i]); list.setListData(users); listLabel.setText("ONLINE: "+ users.length); } else if(m.startsWith("private message")) { String[] myPrivateMsg = m.split(","); Frame frame = JOptionPane.getFrameForComponent(this); p = new PrivateDialog (frame,myPrivateMsg[2],myPrivateMsg[1],false, this); } else if(m.startsWith("No message sent")) { showNoMessageSent(m); } else { area.append(m +"\n"); area.setCaretPosition(area.getText().length()); } } }//end run() /** *Sorts the list of users *@param arr array of list elements *@return the list sorted */ private String[] sortList(String[] arr) { for(int i=0; i0) { String temp=arr[index]; arr[index]=arr[start]; arr[start]=temp; } start++; } } return arr; } /** *Verifies if the users list of online users. *@param user the user to look for in the list *@return true or false, i.e. is the user in the list */ public boolean isUserOnline(String user) { boolean online = false; for(int i = 0;i < users.length; i++) { if(online = users[i].trim().equals(user)) break; } return online; } /** *Sends private messages */ void sendPrivateMessage() { String privateMessage=""; String initialValue = (String)list.getSelectedValue(); JOptionPane jO = new JOptionPane(); if(initialValue==null || initialValue.trim().equals(user)) initialValue=users[0]; String sendTo = (String)JOptionPane.showInputDialog(northPanel,"Who do you wish to send a message to?","Send Private Message!",JOptionPane.QUESTION_MESSAGE, null, users, initialValue); sendTo = sendTo.trim(); if(sendTo != null && sendTo !="" && sendTo.length() > 0 && !sendTo.equals(user.trim())) privateMessage = jO.showInputDialog(northPanel,"Type your message here:","Private Message!",JOptionPane.PLAIN_MESSAGE); if(!isUserOnline(sendTo)) { Toolkit.getDefaultToolkit().beep(); JOptionPane.showMessageDialog(northPanel, "No message sent" + " to " + sendTo + ". " + sendTo + " is not online!", "Alert", JOptionPane.ERROR_MESSAGE); } else if(privateMessage != null && privateMessage !="" && privateMessage.length() > 0) sendToServer(sendTo + ","+privateMessage,PRIVATE); } void showNoMessageSent(String m) { String[] msg = m.split(","); if(!msg[1].trim().equals(user.trim())) { Toolkit.getDefaultToolkit().beep(); JOptionPane.showMessageDialog(northPanel, msg[0] + " to " + msg[1] + ". " + msg[1] + " is not online!", "Alert", JOptionPane.ERROR_MESSAGE); } } /** Flag method to start or stop the thread */ public synchronized void setLooping(boolean b) { looping = b; if (looping) notify(); } /** *Sends a post message to the server/servlet *@param toSend message to send *@param int flag that can be MSG REMOVE ADD or PRIVATE *@return inputstream to read the response from the server/servlet */ InputStream sendToServer(String toSend,int flag) { try { URL url = new URL(getCodeBase(), servletNameAndSession); HttpMessage msg = new HttpMessage(url); Properties props = new Properties(); if(flag == MSG) props.setProperty("message",toSend); else if(flag == REMOVE) props.setProperty("removeUser", toSend); else if(flag == ADD) props.setProperty("addUser", toSend); else if(flag == PRIVATE) props.setProperty("privateMsg", toSend); return msg.sendPostMessage(props); } catch (SocketException e) { // Couldn't connect to host area.append("Can't connect to host: " + e.getMessage()); return null; } catch (FileNotFoundException e) { // Couldn't find servlet String problem = "Server/servlet not found: " + e.getMessage(); area.append(problem); System.err.println(problem); return null; } catch (Exception e) { // Unknown String problem = "Unknown problem: " + e.getClass().getName() + ": " + e.getMessage(); area.append(problem); System.err.println(problem); return null; } } /** *Handles events *@param event event generated by a client */ public void actionPerformed(ActionEvent event) { String inputText = input.getText(); if (event.getSource() == send) { if(inputText.equals("")) { Toolkit.getDefaultToolkit().beep(); } else { sendToServer(inputText,MSG); input.setText(""); } input.requestFocus(); } else if(event.getSource() == privateMsgItem || event.getSource() == listPrivateMsgItem) sendPrivateMessage(); else if(event.getSource() == chatHelpItem) { JOptionPane.showMessageDialog(this,helpText, // show help text "How to use this chat", JOptionPane.INFORMATION_MESSAGE); } else if(event.getSource() == aboutItem) { JOptionPane.showMessageDialog(this,aboutText, // show about text "About", JOptionPane.INFORMATION_MESSAGE); } else if(event.getSource() == deleteItem) { int start = input.getSelectionStart(); int end = input.getSelectionEnd(); input.replaceRange("",start,end); } else if(event.getSource() == selectAllItem) input.selectAll(); } /** *Mouse listener adapter class that to listen to client mouse clicks. @see java.awt.event.MouseAdapter */ MouseListener mL = new MouseAdapter() { /** *Listens to the input area, message area and the list of online users. *Pops up an edit menu if a client righclick in the input area. *Changes the caret position if a client click in the message area. *Pops up a menu for sending private messages *if a client clicks in the list of online users *@param mE event generated by a client/user */ public void mousePressed(MouseEvent mE) { if(mE.getSource()==input) { if(SwingUtilities.isRightMouseButton(mE)) editPopup.show(mE.getComponent(),mE.getX(),mE.getY()-120); } else if(mE.getSource() == area) area.setCaretPosition(area.getText().length()); else if(mE.getSource() == list) { if(SwingUtilities.isRightMouseButton(mE) || SwingUtilities.isLeftMouseButton(mE)) { listPopup.setLabel("Private menu"); listPopup.show(list,list.getX()-45,mE.getY()+10); } } } }; /** *Adapter class that listens to when a user pressed a key @see java.awt.event.KeyAdapter */ KeyListener kl = new KeyAdapter() { String inputText = ""; /** *Listens to the input area. *The message is sent to the servlet when a user press Enter. @param e event generated by a user */ public void keyPressed(KeyEvent kE) { if(kE.getKeyCode() == KeyEvent.VK_ENTER) { inputText = input.getText().trim(); if(inputText.equals("")) Toolkit.getDefaultToolkit().beep(); else { sendToServer(inputText,MSG); input.setCaretPosition(0); input.setText(""); } } } /** *Listens to the input area for when a key is released. *@param kE händelse genererad när en tangent släpps */ public void keyReleased(KeyEvent kE) { if(kE.getKeyCode() == KeyEvent.VK_ENTER) input.setText(""); } }; /** *Listends to the input area. Used for enabling *the send button if there is any text *in the input area, and - if not - disabling it. *@param CaretEvent caret händelse */ public void caretUpdate(CaretEvent cE) { if(cE.getMark()==0) send.setEnabled(false); else if(cE.getMark()==1) send.setEnabled(true); } /** *Method for setting a cross platform look and feel. */ public void SetLookAndFeel() { try { UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName() ); SwingUtilities.updateComponentTreeUI(this); } catch (Exception e) { e.printStackTrace(); } } /** *Sends a message to the servlet that this user is leaving. */ public void stop() { sendToServer(user, REMOVE); setLooping(false); thisActivity = null; } } // End class MagnificentChatApplet