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