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