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}