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(600, 400));
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 = (JTextField) e.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[] args) throws 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 }
|