001/** 002 * 003 * Copyright 2003-2007 Jive Software. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.jivesoftware.smack.debugger; 019 020import java.awt.*; 021import java.awt.datatransfer.*; 022import java.awt.event.*; 023import java.io.*; 024 025import javax.swing.*; 026 027import org.jivesoftware.smack.*; 028import org.jivesoftware.smack.packet.*; 029import org.jivesoftware.smack.util.*; 030 031/** 032 * The LiteDebugger is a very simple debugger that allows to debug sent, received and 033 * interpreted messages. 034 * 035 * @author Gaston Dombiak 036 */ 037public class LiteDebugger implements SmackDebugger { 038 039 private static final String NEWLINE = "\n"; 040 041 private JFrame frame = null; 042 private XMPPConnection connection = null; 043 044 private PacketListener listener = null; 045 046 private Writer writer; 047 private Reader reader; 048 private ReaderListener readerListener; 049 private WriterListener writerListener; 050 051 public LiteDebugger(XMPPConnection connection, Writer writer, Reader reader) { 052 this.connection = connection; 053 this.writer = writer; 054 this.reader = reader; 055 createDebug(); 056 } 057 058 /** 059 * Creates the debug process, which is a GUI window that displays XML traffic. 060 */ 061 private void createDebug() { 062 frame = new JFrame("Smack Debug Window -- " + connection.getServiceName() + ":" + 063 connection.getPort()); 064 065 // Add listener for window closing event 066 frame.addWindowListener(new WindowAdapter() { 067 public void windowClosing(WindowEvent evt) { 068 rootWindowClosing(evt); 069 } 070 }); 071 072 // We'll arrange the UI into four tabs. The first tab contains all data, the second 073 // client generated XML, the third server generated XML, and the fourth is packet 074 // data from the server as seen by Smack. 075 JTabbedPane tabbedPane = new JTabbedPane(); 076 077 JPanel allPane = new JPanel(); 078 allPane.setLayout(new GridLayout(3, 1)); 079 tabbedPane.add("All", allPane); 080 081 // Create UI elements for client generated XML traffic. 082 final JTextArea sentText1 = new JTextArea(); 083 final JTextArea sentText2 = new JTextArea(); 084 sentText1.setEditable(false); 085 sentText2.setEditable(false); 086 sentText1.setForeground(new Color(112, 3, 3)); 087 sentText2.setForeground(new Color(112, 3, 3)); 088 allPane.add(new JScrollPane(sentText1)); 089 tabbedPane.add("Sent", new JScrollPane(sentText2)); 090 091 // Add pop-up menu. 092 JPopupMenu menu = new JPopupMenu(); 093 JMenuItem menuItem1 = new JMenuItem("Copy"); 094 menuItem1.addActionListener(new ActionListener() { 095 public void actionPerformed(ActionEvent e) { 096 // Get the clipboard 097 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 098 // Set the sent text as the new content of the clipboard 099 clipboard.setContents(new StringSelection(sentText1.getText()), null); 100 } 101 }); 102 103 JMenuItem menuItem2 = new JMenuItem("Clear"); 104 menuItem2.addActionListener(new ActionListener() { 105 public void actionPerformed(ActionEvent e) { 106 sentText1.setText(""); 107 sentText2.setText(""); 108 } 109 }); 110 111 // Add listener to the text area so the popup menu can come up. 112 MouseListener popupListener = new PopupListener(menu); 113 sentText1.addMouseListener(popupListener); 114 sentText2.addMouseListener(popupListener); 115 menu.add(menuItem1); 116 menu.add(menuItem2); 117 118 // Create UI elements for server generated XML traffic. 119 final JTextArea receivedText1 = new JTextArea(); 120 final JTextArea receivedText2 = new JTextArea(); 121 receivedText1.setEditable(false); 122 receivedText2.setEditable(false); 123 receivedText1.setForeground(new Color(6, 76, 133)); 124 receivedText2.setForeground(new Color(6, 76, 133)); 125 allPane.add(new JScrollPane(receivedText1)); 126 tabbedPane.add("Received", new JScrollPane(receivedText2)); 127 128 // Add pop-up menu. 129 menu = new JPopupMenu(); 130 menuItem1 = new JMenuItem("Copy"); 131 menuItem1.addActionListener(new ActionListener() { 132 public void actionPerformed(ActionEvent e) { 133 // Get the clipboard 134 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 135 // Set the sent text as the new content of the clipboard 136 clipboard.setContents(new StringSelection(receivedText1.getText()), null); 137 } 138 }); 139 140 menuItem2 = new JMenuItem("Clear"); 141 menuItem2.addActionListener(new ActionListener() { 142 public void actionPerformed(ActionEvent e) { 143 receivedText1.setText(""); 144 receivedText2.setText(""); 145 } 146 }); 147 148 // Add listener to the text area so the popup menu can come up. 149 popupListener = new PopupListener(menu); 150 receivedText1.addMouseListener(popupListener); 151 receivedText2.addMouseListener(popupListener); 152 menu.add(menuItem1); 153 menu.add(menuItem2); 154 155 // Create UI elements for interpreted XML traffic. 156 final JTextArea interpretedText1 = new JTextArea(); 157 final JTextArea interpretedText2 = new JTextArea(); 158 interpretedText1.setEditable(false); 159 interpretedText2.setEditable(false); 160 interpretedText1.setForeground(new Color(1, 94, 35)); 161 interpretedText2.setForeground(new Color(1, 94, 35)); 162 allPane.add(new JScrollPane(interpretedText1)); 163 tabbedPane.add("Interpreted", new JScrollPane(interpretedText2)); 164 165 // Add pop-up menu. 166 menu = new JPopupMenu(); 167 menuItem1 = new JMenuItem("Copy"); 168 menuItem1.addActionListener(new ActionListener() { 169 public void actionPerformed(ActionEvent e) { 170 // Get the clipboard 171 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 172 // Set the sent text as the new content of the clipboard 173 clipboard.setContents(new StringSelection(interpretedText1.getText()), null); 174 } 175 }); 176 177 menuItem2 = new JMenuItem("Clear"); 178 menuItem2.addActionListener(new ActionListener() { 179 public void actionPerformed(ActionEvent e) { 180 interpretedText1.setText(""); 181 interpretedText2.setText(""); 182 } 183 }); 184 185 // Add listener to the text area so the popup menu can come up. 186 popupListener = new PopupListener(menu); 187 interpretedText1.addMouseListener(popupListener); 188 interpretedText2.addMouseListener(popupListener); 189 menu.add(menuItem1); 190 menu.add(menuItem2); 191 192 frame.getContentPane().add(tabbedPane); 193 194 frame.setSize(550, 400); 195 frame.setVisible(true); 196 197 // Create a special Reader that wraps the main Reader and logs data to the GUI. 198 ObservableReader debugReader = new ObservableReader(reader); 199 readerListener = new ReaderListener() { 200 public void read(String str) { 201 int index = str.lastIndexOf(">"); 202 if (index != -1) { 203 receivedText1.append(str.substring(0, index + 1)); 204 receivedText2.append(str.substring(0, index + 1)); 205 receivedText1.append(NEWLINE); 206 receivedText2.append(NEWLINE); 207 if (str.length() > index) { 208 receivedText1.append(str.substring(index + 1)); 209 receivedText2.append(str.substring(index + 1)); 210 } 211 } 212 else { 213 receivedText1.append(str); 214 receivedText2.append(str); 215 } 216 } 217 }; 218 debugReader.addReaderListener(readerListener); 219 220 // Create a special Writer that wraps the main Writer and logs data to the GUI. 221 ObservableWriter debugWriter = new ObservableWriter(writer); 222 writerListener = new WriterListener() { 223 public void write(String str) { 224 sentText1.append(str); 225 sentText2.append(str); 226 if (str.endsWith(">")) { 227 sentText1.append(NEWLINE); 228 sentText2.append(NEWLINE); 229 } 230 } 231 }; 232 debugWriter.addWriterListener(writerListener); 233 234 // Assign the reader/writer objects to use the debug versions. The packet reader 235 // and writer will use the debug versions when they are created. 236 reader = debugReader; 237 writer = debugWriter; 238 239 // Create a thread that will listen for all incoming packets and write them to 240 // the GUI. This is what we call "interpreted" packet data, since it's the packet 241 // data as Smack sees it and not as it's coming in as raw XML. 242 listener = new PacketListener() { 243 public void processPacket(Packet packet) { 244 interpretedText1.append(packet.toXML().toString()); 245 interpretedText2.append(packet.toXML().toString()); 246 interpretedText1.append(NEWLINE); 247 interpretedText2.append(NEWLINE); 248 } 249 }; 250 } 251 252 /** 253 * Notification that the root window is closing. Stop listening for received and 254 * transmitted packets. 255 * 256 * @param evt the event that indicates that the root window is closing 257 */ 258 public void rootWindowClosing(WindowEvent evt) { 259 connection.removePacketListener(listener); 260 ((ObservableReader)reader).removeReaderListener(readerListener); 261 ((ObservableWriter)writer).removeWriterListener(writerListener); 262 } 263 264 /** 265 * Listens for debug window popup dialog events. 266 */ 267 private class PopupListener extends MouseAdapter { 268 JPopupMenu popup; 269 270 PopupListener(JPopupMenu popupMenu) { 271 popup = popupMenu; 272 } 273 274 public void mousePressed(MouseEvent e) { 275 maybeShowPopup(e); 276 } 277 278 public void mouseReleased(MouseEvent e) { 279 maybeShowPopup(e); 280 } 281 282 private void maybeShowPopup(MouseEvent e) { 283 if (e.isPopupTrigger()) { 284 popup.show(e.getComponent(), e.getX(), e.getY()); 285 } 286 } 287 } 288 289 public Reader newConnectionReader(Reader newReader) { 290 ((ObservableReader)reader).removeReaderListener(readerListener); 291 ObservableReader debugReader = new ObservableReader(newReader); 292 debugReader.addReaderListener(readerListener); 293 reader = debugReader; 294 return reader; 295 } 296 297 public Writer newConnectionWriter(Writer newWriter) { 298 ((ObservableWriter)writer).removeWriterListener(writerListener); 299 ObservableWriter debugWriter = new ObservableWriter(newWriter); 300 debugWriter.addWriterListener(writerListener); 301 writer = debugWriter; 302 return writer; 303 } 304 305 public void userHasLogged(String user) { 306 boolean isAnonymous = "".equals(StringUtils.parseName(user)); 307 String title = 308 "Smack Debug Window -- " 309 + (isAnonymous ? "" : StringUtils.parseBareAddress(user)) 310 + "@" 311 + connection.getServiceName() 312 + ":" 313 + connection.getPort(); 314 title += "/" + StringUtils.parseResource(user); 315 frame.setTitle(title); 316 } 317 318 public Reader getReader() { 319 return reader; 320 } 321 322 public Writer getWriter() { 323 return writer; 324 } 325 326 public PacketListener getReaderListener() { 327 return listener; 328 } 329 330 public PacketListener getWriterListener() { 331 return null; 332 } 333}