001/** 002 * 003 * Copyright the original author or authors 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 */ 017package org.jivesoftware.smack.debugger; 018 019import org.jivesoftware.smack.ConnectionListener; 020import org.jivesoftware.smack.StanzaListener; 021import org.jivesoftware.smack.XMPPConnection; 022import org.jivesoftware.smack.packet.Stanza; 023import org.jivesoftware.smack.util.ObservableReader; 024import org.jivesoftware.smack.util.ObservableWriter; 025import org.jivesoftware.smack.util.ReaderListener; 026import org.jivesoftware.smack.util.WriterListener; 027import org.jxmpp.jid.EntityFullJid; 028 029import java.io.Reader; 030import java.io.Writer; 031 032public abstract class AbstractDebugger implements SmackDebugger { 033 034 public static boolean printInterpreted = false; 035 036 private final XMPPConnection connection; 037 038 private final StanzaListener listener; 039 private final ConnectionListener connListener; 040 private final ReaderListener readerListener; 041 private final WriterListener writerListener; 042 043 private ObservableWriter writer; 044 private ObservableReader reader; 045 046 public AbstractDebugger(final XMPPConnection connection, Writer writer, Reader reader) { 047 this.connection = connection; 048 049 // Create a special Reader that wraps the main Reader and logs data to the GUI. 050 this.reader = new ObservableReader(reader); 051 readerListener = new ReaderListener() { 052 @Override 053 public void read(String str) { 054 log("RECV (" + connection.getConnectionCounter() + "): " + str); 055 } 056 }; 057 this.reader.addReaderListener(readerListener); 058 059 // Create a special Writer that wraps the main Writer and logs data to the GUI. 060 this.writer = new ObservableWriter(writer); 061 writerListener = new WriterListener() { 062 @Override 063 public void write(String str) { 064 log("SENT (" + connection.getConnectionCounter() + "): " + str); 065 } 066 }; 067 this.writer.addWriterListener(writerListener); 068 069 // Create a thread that will listen for all incoming packets and write them to 070 // the GUI. This is what we call "interpreted" packet data, since it's the packet 071 // data as Smack sees it and not as it's coming in as raw XML. 072 listener = new StanzaListener() { 073 @Override 074 public void processStanza(Stanza packet) { 075 if (printInterpreted) { 076 log("RCV PKT (" + connection.getConnectionCounter() + "): " + packet.toXML()); 077 } 078 } 079 }; 080 081 connListener = new ConnectionListener() { 082 @Override 083 public void connected(XMPPConnection connection) { 084 log("XMPPConnection connected (" 085 + connection + ")"); 086 } 087 @Override 088 public void authenticated(XMPPConnection connection, boolean resumed) { 089 String logString = "XMPPConnection authenticated (" + connection + ")"; 090 if (resumed) { 091 logString += " and resumed"; 092 } 093 log(logString); 094 } 095 @Override 096 public void connectionClosed() { 097 log( 098 "XMPPConnection closed (" + 099 connection + 100 ")"); 101 } 102 103 @Override 104 public void connectionClosedOnError(Exception e) { 105 log( 106 "XMPPConnection closed due to an exception (" + 107 connection + 108 ")", e); 109 } 110 @Override 111 public void reconnectionFailed(Exception e) { 112 log( 113 "Reconnection failed due to an exception (" + 114 connection + 115 ")", e); 116 } 117 @Override 118 public void reconnectionSuccessful() { 119 log( 120 "XMPPConnection reconnected (" + 121 connection + 122 ")"); 123 } 124 @Override 125 public void reconnectingIn(int seconds) { 126 log( 127 "XMPPConnection (" + 128 connection + 129 ") will reconnect in " + seconds); 130 } 131 }; 132 } 133 134 protected abstract void log(String logMessage); 135 136 protected abstract void log(String logMessage, Throwable throwable); 137 138 @Override 139 public Reader newConnectionReader(Reader newReader) { 140 reader.removeReaderListener(readerListener); 141 ObservableReader debugReader = new ObservableReader(newReader); 142 debugReader.addReaderListener(readerListener); 143 reader = debugReader; 144 return reader; 145 } 146 147 @Override 148 public Writer newConnectionWriter(Writer newWriter) { 149 writer.removeWriterListener(writerListener); 150 ObservableWriter debugWriter = new ObservableWriter(newWriter); 151 debugWriter.addWriterListener(writerListener); 152 writer = debugWriter; 153 return writer; 154 } 155 156 @Override 157 public void userHasLogged(EntityFullJid user) { 158 String localpart = user.getLocalpart().toString(); 159 boolean isAnonymous = "".equals(localpart); 160 String title = 161 "User logged (" + connection.getConnectionCounter() + "): " 162 + (isAnonymous ? "" : localpart) 163 + "@" 164 + connection.getXMPPServiceDomain() 165 + ":" 166 + connection.getPort(); 167 title += "/" + user.getResourcepart(); 168 log(title); 169 // Add the connection listener to the connection so that the debugger can be notified 170 // whenever the connection is closed. 171 connection.addConnectionListener(connListener); 172 } 173 174 @Override 175 public Reader getReader() { 176 return reader; 177 } 178 179 @Override 180 public Writer getWriter() { 181 return writer; 182 } 183 184 @Override 185 public StanzaListener getReaderListener() { 186 return listener; 187 } 188 189 @Override 190 public StanzaListener getWriterListener() { 191 return null; 192 } 193}