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.util.XmppStringUtils;
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            public void read(String str) {
053                log("RECV (" + connection.getConnectionCounter() + "): " + str);
054            }
055        };
056        this.reader.addReaderListener(readerListener);
057
058        // Create a special Writer that wraps the main Writer and logs data to the GUI.
059        this.writer = new ObservableWriter(writer);
060        writerListener = new WriterListener() {
061            public void write(String str) {
062                log("SENT (" + connection.getConnectionCounter() + "): " + str);
063            }
064        };
065        this.writer.addWriterListener(writerListener);
066
067        // Create a thread that will listen for all incoming packets and write them to
068        // the GUI. This is what we call "interpreted" packet data, since it's the packet
069        // data as Smack sees it and not as it's coming in as raw XML.
070        listener = new StanzaListener() {
071            public void processPacket(Stanza packet) {
072                if (printInterpreted) {
073                    log("RCV PKT (" + connection.getConnectionCounter() + "): " + packet.toXML());
074                }
075            }
076        };
077
078        connListener = new ConnectionListener() {
079            public void connected(XMPPConnection connection) {
080                log("XMPPConnection connected ("
081                                + connection.getConnectionCounter() + ")");
082            }
083            public void authenticated(XMPPConnection connection, boolean resumed) {
084                String logString = "XMPPConnection authenticated (" + connection.getConnectionCounter() + ")";
085                if (resumed) {
086                    logString += " and resumed";
087                }
088                log(logString);
089            }
090            public void connectionClosed() {
091                log(
092                       "XMPPConnection closed (" +
093                        connection.getConnectionCounter() +
094                        ")");
095            }
096
097            public void connectionClosedOnError(Exception e) {
098                log(
099                        "XMPPConnection closed due to an exception (" +
100                        connection.getConnectionCounter() +
101                        ")");
102                e.printStackTrace();
103            }
104            public void reconnectionFailed(Exception e) {
105                log(
106                        "Reconnection failed due to an exception (" +
107                        connection.getConnectionCounter() +
108                        ")");
109                e.printStackTrace();
110            }
111            public void reconnectionSuccessful() {
112                log(
113                        "XMPPConnection reconnected (" +
114                        connection.getConnectionCounter() +
115                        ")");
116            }
117            public void reconnectingIn(int seconds) {
118                log(
119                        "XMPPConnection (" +
120                        connection.getConnectionCounter() +
121                        ") will reconnect in " + seconds);
122            }
123        };
124    }
125
126    protected abstract void log(String logMessage);
127
128    public Reader newConnectionReader(Reader newReader) {
129        reader.removeReaderListener(readerListener);
130        ObservableReader debugReader = new ObservableReader(newReader);
131        debugReader.addReaderListener(readerListener);
132        reader = debugReader;
133        return reader;
134    }
135
136    public Writer newConnectionWriter(Writer newWriter) {
137        writer.removeWriterListener(writerListener);
138        ObservableWriter debugWriter = new ObservableWriter(newWriter);
139        debugWriter.addWriterListener(writerListener);
140        writer = debugWriter;
141        return writer;
142    }
143
144    public void userHasLogged(String user) {
145        String localpart = XmppStringUtils.parseLocalpart(user);
146        boolean isAnonymous = "".equals(localpart);
147        String title =
148                "User logged (" + connection.getConnectionCounter() + "): "
149                + (isAnonymous ? "" : localpart)
150                + "@"
151                + connection.getServiceName()
152                + ":"
153                + connection.getPort();
154        title += "/" + XmppStringUtils.parseResource(user);
155        log(title);
156        // Add the connection listener to the connection so that the debugger can be notified
157        // whenever the connection is closed.
158        connection.addConnectionListener(connListener);
159    }
160
161    public Reader getReader() {
162        return reader;
163    }
164
165    public Writer getWriter() {
166        return writer;
167    }
168
169    public StanzaListener getReaderListener() {
170        return listener;
171    }
172
173    public StanzaListener getWriterListener() {
174        return null;
175    }
176}