Main.java
001 /*
002  * @COPYRIGHT@
003  */
004 package demo.chatter;
005 
006 import com.tc.cluster.DsoCluster;
007 import com.tc.cluster.DsoClusterEvent;
008 import com.tc.cluster.DsoClusterListener;
009 
010 import java.awt.BorderLayout;
011 import java.awt.Color;
012 import java.awt.Container;
013 import java.awt.Dimension;
014 import java.awt.Font;
015 import java.awt.event.ActionEvent;
016 import java.awt.event.ActionListener;
017 import java.util.Random;
018 import java.util.concurrent.BlockingQueue;
019 import java.util.concurrent.LinkedBlockingQueue;
020 
021 import javax.swing.DefaultListModel;
022 import javax.swing.ImageIcon;
023 import javax.swing.JFrame;
024 import javax.swing.JLabel;
025 import javax.swing.JList;
026 import javax.swing.JPanel;
027 import javax.swing.JScrollPane;
028 import javax.swing.JTextField;
029 import javax.swing.JTextPane;
030 import javax.swing.ScrollPaneConstants;
031 import javax.swing.SwingConstants;
032 import javax.swing.SwingUtilities;
033 import javax.swing.text.BadLocationException;
034 import javax.swing.text.Document;
035 import javax.swing.text.Style;
036 import javax.swing.text.StyleConstants;
037 
038 /**
039  * Description of the Class
040  *
041  @author Terracotta, Inc.
042  */
043 public class Main extends JFrame implements ActionListener, ChatListener, DsoClusterListener {
044 
045   private DsoCluster       cluster;
046 
047   private final User       localUser;
048   private ChatManager      chatManager;
049   private MessageQueue     messageQueue;
050 
051   private final JTextPane  display   = new JTextPane();
052   private final JList      buddyList = new JList();
053   private final JTextField input     = new JTextField();
054 
055   private final Style      systemStyle;
056   private final Style      localUserStyle;
057   private final Style      remoteUserStyle;
058 
059   private final Object     lock      = new Object();
060 
061   public Main() throws Exception {
062     this.chatManager = new ChatManager();
063     this.localUser = new User(cluster.getCurrentNode());
064 
065     this.systemStyle = display.addStyle("systemStyle"null);
066     this.localUserStyle = display.addStyle("localUserStyle"null);
067     this.remoteUserStyle = display.addStyle("remoteUserStyle"null);
068   }
069 
070   private void init() throws Exception {
071     setupUI();
072 
073     synchronized (lock) {
074 
075       messageQueue = new MessageQueue(chatManager);
076       messageQueue.start();
077 
078       chatManager.registerUser(localUser);
079       chatManager.setLocalListener(this);
080 
081       cluster.addClusterListener(this);
082       populateCurrentUsers();
083     }
084   }
085 
086   private void setupUI() {
087     setDefaultLookAndFeelDecorated(true);
088     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
089     final Container content = getContentPane();
090 
091     display.setFont(new Font("Andale Mono", Font.PLAIN, 9));
092     display.setEditable(false);
093     display.setRequestFocusEnabled(false);
094 
095     StyleConstants.setItalic(localUserStyle, true);
096     StyleConstants.setForeground(localUserStyle, Color.LIGHT_GRAY);
097     StyleConstants.setFontSize(localUserStyle, 9);
098 
099     StyleConstants.setItalic(systemStyle, true);
100     StyleConstants.setForeground(systemStyle, Color.RED);
101 
102     input.setFont(new Font("Andale Mono", Font.PLAIN, 9));
103     input.addActionListener(this);
104     final JScrollPane scroll = new JScrollPane(display);
105     final Random r = new Random();
106     final JLabel avatar = new JLabel(localUser.getName() " (node id: " + localUser.getNode() ")",
107                                      new ImageIcon(getClass().getResource("/images/buddy" + r.nextInt(10".gif")),
108                                      SwingConstants.LEFT);
109     avatar.setForeground(Color.WHITE);
110     avatar.setFont(new Font("Georgia", Font.PLAIN, 16));
111     avatar.setVerticalTextPosition(SwingConstants.CENTER);
112     final JPanel buddypanel = new JPanel();
113     buddypanel.setBackground(Color.DARK_GRAY);
114     buddypanel.setLayout(new BorderLayout());
115     buddypanel.add(avatar, BorderLayout.CENTER);
116 
117     final JPanel buddyListPanel = new JPanel();
118     buddyListPanel.setBackground(Color.WHITE);
119     buddyListPanel.add(buddyList);
120     buddyList.setFont(new Font("Andale Mono", Font.BOLD, 9));
121 
122     content.setLayout(new BorderLayout());
123     content.add(buddypanel, BorderLayout.NORTH);
124     content.add(scroll, BorderLayout.CENTER);
125     content.add(input, BorderLayout.SOUTH);
126     JScrollPane scrollPane = new JScrollPane(buddyListPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
127                                              ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
128 
129     content.add(scrollPane, BorderLayout.EAST);
130     pack();
131 
132     setTitle("Chatter: " + localUser.getName());
133     setSize(new Dimension(600400));
134   }
135 
136   public void nodeJoined(final DsoClusterEvent event) {
137     // unused
138   }
139 
140   public void nodeLeft(final DsoClusterEvent event) {
141     synchronized (lock) {
142       chatManager.removeUser(event.getNode());
143       populateCurrentUsers();
144     }
145   }
146 
147   public void operationsEnabled(final DsoClusterEvent event) {
148     chatManager.retainNodes(cluster.getClusterTopology());
149 
150     synchronized (lock) {
151       systemMessage("The server is up.");
152 
153       SwingUtilities.invokeLater(new Runnable() {
154         public void run() {
155           toggleList(true);
156         }
157       });
158     }
159   }
160 
161   public void operationsDisabled(final DsoClusterEvent event) {
162     synchronized (lock) {
163       systemMessage("The server is down; all of your messages will be queued until the server comes back up again.");
164       SwingUtilities.invokeLater(new Runnable() {
165         public void run() {
166           toggleList(false);
167         }
168       });
169     }
170   }
171 
172   private void startup() {
173     setVisible(true);
174     input.requestFocus();
175   }
176 
177   public void actionPerformed(final ActionEvent e) {
178     final JTextField source = (JTextFielde.getSource();
179     final String message = source.getText();
180     source.setText("");
181 
182     synchronized (lock) {
183       Message msg = new Message(localUser, message, !cluster.areOperationsEnabled());
184 
185       messageQueue.enqueue(msg);
186 
187       if (!cluster.areOperationsEnabled()) {
188         displayMessage(message, localUserStyle);
189       }
190     }
191   }
192 
193   private void toggleList(final boolean on) {
194     this.buddyList.setVisible(on);
195     this.buddyList.setEnabled(on);
196   }
197 
198   private void handleNewUser(final String username) {
199     synchronized (lock) {
200       populateCurrentUsers();
201     }
202   }
203 
204   public void newMessage(final Message message) {
205     User source = message.getUser();
206     boolean local = source == localUser;
207 
208     if (local && message.wasAlreadyDisplayedLocally()) { return}
209 
210     String displayMessage = (local ? "" : source.getName() ": "+ message.getText();
211     displayMessage(displayMessage, local ? localUserStyle : remoteUserStyle);
212   }
213 
214   public void newUser(final String username) {
215     handleNewUser(username);
216   }
217 
218   private void displayMessage(final String message, final Style style) {
219     SwingUtilities.invokeLater(new Runnable() {
220       public void run() {
221         Document doc = display.getDocument();
222         try {
223           doc.insertString(doc.getLength(), message + "\n", style);
224         catch (BadLocationException ble) {
225           exit(ble);
226         }
227         display.setCaretPosition(doc.getLength());
228       }
229     });
230   }
231 
232   private void populateCurrentUsers() {
233     final DefaultListModel list = new DefaultListModel();
234     User[] currentUsers = chatManager.getCurrentUsers();
235     for (User currentUser : currentUsers) {
236       list.addElement(currentUser.getName());
237     }
238 
239     Runnable setList = new Runnable() {
240       public void run() {
241         buddyList.setModel(list);
242         buddyList.invalidate();
243         buddyList.repaint();
244       }
245     };
246 
247     if (SwingUtilities.isEventDispatchThread()) {
248       setList.run();
249     else {
250       SwingUtilities.invokeLater(setList);
251     }
252   }
253 
254   private void systemMessage(final String message) {
255     displayMessage(message, systemStyle);
256   }
257 
258   private static void exit(final Throwable t) {
259     t.printStackTrace();
260     System.exit(1);
261   }
262 
263   public static void main(final String[] argsthrows Exception {
264     final Main main = new Main();
265     main.init();
266 
267     javax.swing.SwingUtilities.invokeLater(new Runnable() {
268       public void run() {
269         main.startup();
270       }
271     });
272   }
273 
274   private static class MessageQueue extends Thread {
275     private final BlockingQueue<Message> msgQueue = new LinkedBlockingQueue<Message>(Integer.MAX_VALUE);
276     private final ChatManager            chatManager;
277 
278     MessageQueue(final ChatManager chatManager) {
279       this.chatManager = chatManager;
280       setDaemon(true);
281       setName("Offline Message Queue");
282     }
283 
284     void enqueue(final Message msg) {
285       try {
286         msgQueue.put(msg);
287       catch (InterruptedException e) {
288         exit(e);
289       }
290     }
291 
292     @Override
293     public void run() {
294       while (true) {
295         try {
296           Message msg = msgQueue.take();
297           chatManager.send(msg);
298         catch (InterruptedException e) {
299           exit(e);
300         }
301       }
302     }
303   }
304 }