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