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.PacketListener;
021import org.jivesoftware.smack.XMPPConnection;
022import org.jivesoftware.smack.packet.Packet;
023import org.jivesoftware.smack.util.*;
024
025import java.io.Reader;
026import java.io.Writer;
027import java.text.SimpleDateFormat;
028import java.util.Date;
029
030/**
031 * Very simple debugger that prints to the console (stdout) the sent and received stanzas. Use
032 * this debugger with caution since printing to the console is an expensive operation that may
033 * even block the thread since only one thread may print at a time.<p>
034 * <p/>
035 * It is possible to not only print the raw sent and received stanzas but also the interpreted
036 * packets by Smack. By default interpreted packets won't be printed. To enable this feature
037 * just change the <tt>printInterpreted</tt> static variable to <tt>true</tt>.
038 *
039 * @author Gaston Dombiak
040 */
041public class ConsoleDebugger implements SmackDebugger {
042
043    public static boolean printInterpreted = false;
044    private SimpleDateFormat dateFormatter = new SimpleDateFormat("hh:mm:ss aaa");
045
046    private XMPPConnection connection = null;
047
048    private PacketListener listener = null;
049    private ConnectionListener connListener = null;
050
051    private Writer writer;
052    private Reader reader;
053    private ReaderListener readerListener;
054    private WriterListener writerListener;
055
056    public ConsoleDebugger(XMPPConnection connection, Writer writer, Reader reader) {
057        this.connection = connection;
058        this.writer = writer;
059        this.reader = reader;
060        createDebug();
061    }
062
063    /**
064     * Creates the listeners that will print in the console when new activity is detected.
065     */
066    private void createDebug() {
067        // Create a special Reader that wraps the main Reader and logs data to the GUI.
068        ObservableReader debugReader = new ObservableReader(reader);
069        readerListener = new ReaderListener() {
070            public void read(String str) {
071                System.out.println(
072                        dateFormatter.format(new Date()) + " RCV  (" + connection.hashCode() +
073                        "): " +
074                        str);
075            }
076        };
077        debugReader.addReaderListener(readerListener);
078
079        // Create a special Writer that wraps the main Writer and logs data to the GUI.
080        ObservableWriter debugWriter = new ObservableWriter(writer);
081        writerListener = new WriterListener() {
082            public void write(String str) {
083                System.out.println(
084                        dateFormatter.format(new Date()) + " SENT (" + connection.hashCode() +
085                        "): " +
086                        str);
087            }
088        };
089        debugWriter.addWriterListener(writerListener);
090
091        // Assign the reader/writer objects to use the debug versions. The packet reader
092        // and writer will use the debug versions when they are created.
093        reader = debugReader;
094        writer = debugWriter;
095
096        // Create a thread that will listen for all incoming packets and write them to
097        // the GUI. This is what we call "interpreted" packet data, since it's the packet
098        // data as Smack sees it and not as it's coming in as raw XML.
099        listener = new PacketListener() {
100            public void processPacket(Packet packet) {
101                if (printInterpreted) {
102                    System.out.println(
103                            dateFormatter.format(new Date()) + " RCV PKT (" +
104                            connection.hashCode() +
105                            "): " +
106                            packet.toXML());
107                }
108            }
109        };
110
111        connListener = new ConnectionListener() {
112            public void connected(XMPPConnection connection) {
113                System.out.println(dateFormatter.format(new Date()) + " XMPPConnection connected ("
114                                + connection.hashCode() + ")");
115            }
116            public void authenticated(XMPPConnection connection) {
117                System.out.println(dateFormatter.format(new Date())
118                                + " XMPPConnection authenticated (" + connection.hashCode() + ")");
119            }
120            public void connectionClosed() {
121                System.out.println(
122                        dateFormatter.format(new Date()) + " XMPPConnection closed (" +
123                        connection.hashCode() +
124                        ")");
125            }
126
127            public void connectionClosedOnError(Exception e) {
128                System.out.println(
129                        dateFormatter.format(new Date()) +
130                        " XMPPConnection closed due to an exception (" +
131                        connection.hashCode() +
132                        ")");
133                e.printStackTrace();
134            }
135            public void reconnectionFailed(Exception e) {
136                System.out.println(
137                        dateFormatter.format(new Date()) +
138                        " Reconnection failed due to an exception (" +
139                        connection.hashCode() +
140                        ")");
141                e.printStackTrace();
142            }
143            public void reconnectionSuccessful() {
144                System.out.println(
145                        dateFormatter.format(new Date()) + " XMPPConnection reconnected (" +
146                        connection.hashCode() +
147                        ")");
148            }
149            public void reconnectingIn(int seconds) {
150                System.out.println(
151                        dateFormatter.format(new Date()) + " XMPPConnection (" +
152                        connection.hashCode() +
153                        ") will reconnect in " + seconds);
154            }
155        };
156    }
157
158    public Reader newConnectionReader(Reader newReader) {
159        ((ObservableReader)reader).removeReaderListener(readerListener);
160        ObservableReader debugReader = new ObservableReader(newReader);
161        debugReader.addReaderListener(readerListener);
162        reader = debugReader;
163        return reader;
164    }
165
166    public Writer newConnectionWriter(Writer newWriter) {
167        ((ObservableWriter)writer).removeWriterListener(writerListener);
168        ObservableWriter debugWriter = new ObservableWriter(newWriter);
169        debugWriter.addWriterListener(writerListener);
170        writer = debugWriter;
171        return writer;
172    }
173
174    public void userHasLogged(String user) {
175        boolean isAnonymous = "".equals(StringUtils.parseName(user));
176        String title =
177                "User logged (" + connection.hashCode() + "): "
178                + (isAnonymous ? "" : StringUtils.parseBareAddress(user))
179                + "@"
180                + connection.getServiceName()
181                + ":"
182                + connection.getPort();
183        title += "/" + StringUtils.parseResource(user);
184        System.out.println(title);
185        // Add the connection listener to the connection so that the debugger can be notified
186        // whenever the connection is closed.
187        connection.addConnectionListener(connListener);
188    }
189
190    public Reader getReader() {
191        return reader;
192    }
193
194    public Writer getWriter() {
195        return writer;
196    }
197
198    public PacketListener getReaderListener() {
199        return listener;
200    }
201
202    public PacketListener getWriterListener() {
203        return null;
204    }
205}