001: /*
002: @COPYRIGHT@
003: */
004: package demo.chatter;
005:
006: import java.awt.BorderLayout;
007: import java.awt.Color;
008: import java.awt.Container;
009: import java.awt.Dimension;
010: import java.awt.Font;
011: import java.awt.event.ActionEvent;
012: import java.awt.event.ActionListener;
013: import java.awt.event.WindowEvent;
014: import java.awt.event.WindowListener;
015: import java.util.Random;
016:
017: import javax.management.MBeanServer;
018: import javax.management.MBeanServerFactory;
019: import javax.management.MBeanServerNotification;
020: import javax.management.Notification;
021: import javax.management.NotificationFilter;
022: import javax.management.NotificationListener;
023: import javax.management.ObjectName;
024: import javax.swing.DefaultListModel;
025: import javax.swing.ImageIcon;
026: import javax.swing.JFrame;
027: import javax.swing.JLabel;
028: import javax.swing.JList;
029: import javax.swing.JPanel;
030: import javax.swing.JScrollPane;
031: import javax.swing.JTextField;
032: import javax.swing.JTextPane;
033: import javax.swing.text.Document;
034: import javax.swing.text.Style;
035: import javax.swing.text.StyleConstants;
036:
037: /**
038: * Description of the Class
039: *
040: *@author Terracotta, Inc.
041: */
042: public class Main extends JFrame implements ActionListener,
043: ChatterDisplay, WindowListener {
044:
045: private final ChatManager chatManager = new ChatManager();
046:
047: private final JTextPane display = new JTextPane();
048:
049: private User user;
050:
051: private final JList buddyList = new JList();
052:
053: private boolean isServerDown = false;
054: private static final String CHATTER_SYSTEM = "SYSTEM";
055:
056: public Main() {
057: try {
058: final String nodeId = registerForNotifications();
059: user = new User(nodeId, this );
060: populateCurrentUsers();
061: login();
062: } catch (final Exception e) {
063: throw new RuntimeException(e);
064: }
065:
066: addWindowListener(this );
067: setDefaultLookAndFeelDecorated(true);
068: setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
069: final Container content = getContentPane();
070:
071: display.setFont(new Font("Andale Mono", Font.PLAIN, 9));
072: display.setEditable(false);
073: display.setRequestFocusEnabled(false);
074:
075: final JTextField input = new JTextField();
076: input.setFont(new Font("Andale Mono", Font.PLAIN, 9));
077: input.addActionListener(this );
078: final JScrollPane scroll = new JScrollPane(display);
079: final Random r = new Random();
080: final JLabel avatar = new JLabel(user.getName() + " (node id: "
081: + user.getNodeId() + ")",
082: new ImageIcon(getClass().getResource(
083: "/images/buddy" + r.nextInt(10) + ".gif")),
084: JLabel.LEFT);
085: avatar.setForeground(Color.WHITE);
086: avatar.setFont(new Font("Georgia", Font.PLAIN, 16));
087: avatar.setVerticalTextPosition(JLabel.CENTER);
088: final JPanel buddypanel = new JPanel();
089: buddypanel.setBackground(Color.DARK_GRAY);
090: buddypanel.setLayout(new BorderLayout());
091: buddypanel.add(avatar, BorderLayout.CENTER);
092:
093: final JPanel buddyListPanel = new JPanel();
094: buddyListPanel.setBackground(Color.WHITE);
095: buddyListPanel.add(buddyList);
096: buddyList.setFont(new Font("Andale Mono", Font.BOLD, 9));
097:
098: content.setLayout(new BorderLayout());
099: content.add(buddypanel, BorderLayout.NORTH);
100: content.add(scroll, BorderLayout.CENTER);
101: content.add(input, BorderLayout.SOUTH);
102: content.add(new JScrollPane(buddyListPanel), BorderLayout.EAST);
103: pack();
104:
105: setTitle("Chatter: " + user.getName());
106: setSize(new Dimension(600, 400));
107: setVisible(true);
108: input.requestFocus();
109: }
110:
111: public void actionPerformed(final ActionEvent e) {
112: final JTextField input = (JTextField) e.getSource();
113: final String message = input.getText();
114: input.setText("");
115: (new Thread() {
116: public void run() {
117: chatManager.send(user, message);
118: }
119: }).start();
120:
121: if (isServerDown) {
122: updateMessage(user.getName(), message, true);
123: }
124: }
125:
126: public void handleConnectedServer() {
127: isServerDown = false;
128: javax.swing.SwingUtilities.invokeLater(new Runnable() {
129: public void run() {
130: Main.this .buddyList.setVisible(true);
131: Main.this .buddyList.setEnabled(true);
132: }
133: });
134: }
135:
136: public void handleDisconnectedServer() {
137: isServerDown = true;
138: updateMessage("The server is down; all of your messages will be queued until the server goes back up again.");
139: javax.swing.SwingUtilities.invokeLater(new Runnable() {
140: public void run() {
141: Main.this .buddyList.setVisible(false);
142: Main.this .buddyList.setEnabled(false);
143: }
144: });
145: }
146:
147: public synchronized void handleDisconnectedUser(final String nodeId) {
148: chatManager.removeUser(nodeId);
149: populateCurrentUsers();
150: }
151:
152: public synchronized void handleNewUser(final String username) {
153: populateCurrentUsers();
154: }
155:
156: public void updateMessage(final String username,
157: final String message, final boolean isOwnMessage) {
158: javax.swing.SwingUtilities.invokeLater(new Runnable() {
159: public void run() {
160: try {
161: final Document doc = display.getDocument();
162: final Style style = display.addStyle("Style", null);
163:
164: if (isOwnMessage) {
165: StyleConstants.setItalic(style, true);
166: StyleConstants.setForeground(style,
167: Color.LIGHT_GRAY);
168: StyleConstants.setFontSize(style, 9);
169: }
170:
171: if (username.equals(CHATTER_SYSTEM)) {
172: StyleConstants.setItalic(style, true);
173: StyleConstants.setForeground(style, Color.RED);
174: } else {
175: StyleConstants.setBold(style, true);
176: doc.insertString(doc.getLength(), username
177: + ": ", style);
178: }
179:
180: StyleConstants.setBold(style, false);
181: doc.insertString(doc.getLength(), message, style);
182: doc.insertString(doc.getLength(), "\n", style);
183:
184: display.setCaretPosition(doc.getLength());
185: } catch (final javax.swing.text.BadLocationException ble) {
186: System.err.println(ble.getMessage());
187: }
188: }
189: });
190: }
191:
192: public void windowClosing(WindowEvent e) {
193: handleDisconnectedUser(user.getNodeId());
194: }
195:
196: public void windowActivated(WindowEvent e) {
197: // TODO Auto-generated method stub
198: }
199:
200: public void windowClosed(WindowEvent e) {
201: // TODO Auto-generated method stub
202: }
203:
204: public void windowDeactivated(WindowEvent e) {
205: // TODO Auto-generated method stub
206: }
207:
208: public void windowDeiconified(WindowEvent e) {
209: // TODO Auto-generated method stub
210: }
211:
212: public void windowIconified(WindowEvent e) {
213: // TODO Auto-generated method stub
214: }
215:
216: public void windowOpened(WindowEvent e) {
217: // TODO Auto-generated method stub
218: }
219:
220: void login() {
221: // ****************************************************
222: // Uncomment this section if you want incoming clients
223: // to see the history of messages.
224: // ****************************************************
225: // --- CODE BEGINS HERE ---
226: // final Message[] messages = chatManager.getMessages();
227: // for (int i = 0; i < messages.length; i++) {
228: // user.newMessage(messages[i]);
229: // }
230: // --- CODE ENDS HERE ---
231: synchronized (chatManager) {
232: chatManager.registerUser(user);
233: }
234: }
235:
236: private synchronized void populateCurrentUsers() {
237: DefaultListModel list = new DefaultListModel();
238: final Object[] currentUsers = chatManager.getCurrentUsers();
239: for (int i = 0; i < currentUsers.length; i++) {
240: list.addElement(new String(((User) currentUsers[i])
241: .getName()));
242: }
243: buddyList.setModel(list);
244: }
245:
246: private void updateMessage(final String message) {
247: updateMessage(CHATTER_SYSTEM, message, true);
248: }
249:
250: /**
251: * Registers this client for JMX notifications.
252: *
253: *@return Description of the Returned Value
254: *@exception Exception Description of Exception
255: *@returns This clients Node ID
256: */
257: private String registerForNotifications() throws Exception {
258: final java.util.List servers = MBeanServerFactory
259: .findMBeanServer(null);
260: if (servers.size() == 0) {
261:
262: System.err
263: .println("WARNING: No JMX servers found, unable to register for notifications.");
264: return "0";
265: }
266:
267: final MBeanServer server = (MBeanServer) servers.get(0);
268: final ObjectName clusterBean = new ObjectName(
269: "org.terracotta:type=Terracotta Cluster,name=Terracotta Cluster Bean");
270: final ObjectName delegateName = ObjectName
271: .getInstance("JMImplementation:type=MBeanServerDelegate");
272: final java.util.List clusterBeanBag = new java.util.ArrayList();
273:
274: // listener for newly registered MBeans
275: final NotificationListener listener0 = new NotificationListener() {
276: public void handleNotification(Notification notification,
277: Object handback) {
278: synchronized (clusterBeanBag) {
279: clusterBeanBag.add(handback);
280: clusterBeanBag.notifyAll();
281: }
282: }
283: };
284:
285: // filter to let only clusterBean passed through
286: final NotificationFilter filter0 = new NotificationFilter() {
287: public boolean isNotificationEnabled(
288: Notification notification) {
289: if (notification.getType().equals(
290: "JMX.mbean.registered")
291: && ((MBeanServerNotification) notification)
292: .getMBeanName().equals(clusterBean)) {
293: return true;
294: }
295: return false;
296: }
297: };
298:
299: // add our listener for clusterBean's registration
300: server.addNotificationListener(delegateName, listener0,
301: filter0, clusterBean);
302:
303: // because of race condition, clusterBean might already have registered
304: // before we registered the listener
305: final java.util.Set allObjectNames = server.queryNames(null,
306: null);
307:
308: if (!allObjectNames.contains(clusterBean)) {
309: synchronized (clusterBeanBag) {
310: while (clusterBeanBag.isEmpty()) {
311: clusterBeanBag.wait();
312: }
313: }
314: }
315:
316: // clusterBean is now registered, no need to listen for it
317: server.removeNotificationListener(delegateName, listener0);
318:
319: // listener for clustered bean events
320: final NotificationListener listener1 = new NotificationListener() {
321: public void handleNotification(Notification notification,
322: Object handback) {
323: String nodeId = notification.getMessage();
324: if (notification.getType()
325: .endsWith("thisNodeConnected")) {
326: handleConnectedServer();
327: }
328: if (notification.getType().endsWith(
329: "thisNodeDisconnected")) {
330: handleDisconnectedServer();
331: } else if (notification.getType().endsWith(
332: "nodeDisconnected")) {
333: handleDisconnectedUser(nodeId);
334: }
335: }
336: };
337:
338: // now that we have the clusterBean, add listener for membership events
339: server.addNotificationListener(clusterBean, listener1, null,
340: clusterBean);
341: return (server.getAttribute(clusterBean, "NodeId")).toString();
342: }
343:
344: public static void main(final String[] args) {
345: javax.swing.SwingUtilities.invokeLater(new Runnable() {
346: public void run() {
347: new Main();
348: }
349: });
350: }
351: }
|