001/**
002 *
003 * Copyright 2014 Vyacheslav Blinov
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 */
017
018package org.jivesoftware.smackx.debugger.slf4j;
019
020import java.io.Reader;
021import java.io.Writer;
022import java.util.concurrent.atomic.AtomicBoolean;
023
024import org.jivesoftware.smack.AbstractXMPPConnection;
025import org.jivesoftware.smack.ReconnectionManager;
026import org.jivesoftware.smack.SmackConfiguration;
027import org.jivesoftware.smack.StanzaListener;
028import org.jivesoftware.smack.XMPPConnection;
029import org.jivesoftware.smack.debugger.SmackDebugger;
030import org.jivesoftware.smack.util.ObservableReader;
031import org.jivesoftware.smack.util.ObservableWriter;
032
033import org.jxmpp.jid.EntityFullJid;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037
038/**
039 * Implementation of SmackDebugger that writes log messages using SLF4J API.
040 * Use in conjunction with your SLF4J bindings of choice.
041 * See SLF4J manual for more details about bindings usage.
042 */
043public class SLF4JSmackDebugger implements SmackDebugger  {
044
045    private static final java.util.logging.Logger LOGGER = java.util.logging.Logger.getLogger(SLF4JSmackDebugger.class.getName());
046
047    public static final String LOGGER_NAME = "SMACK";
048    private static final Logger logger = LoggerFactory.getLogger(LOGGER_NAME);
049    public static final AtomicBoolean printInterpreted = new AtomicBoolean(true);
050
051    public static final String SENT_TAG = "SENT";
052    public static final String RECEIVED_TAG = "RECV";
053
054    private final XMPPConnection connection;
055
056    private final StanzaListener receivedListener = new SLF4JLoggingPacketListener(logger, RECEIVED_TAG);
057    private final StanzaListener sentListener = new SLF4JLoggingPacketListener(logger, SENT_TAG);
058    private final SLF4JRawXmlListener slf4JRawXmlListener = new SLF4JRawXmlListener(logger);
059
060    private ObservableWriter writer;
061    private ObservableReader reader;
062
063    /**
064     * Makes Smack use this Debugger.
065     */
066    public static void enable() {
067        SmackConfiguration.setDebuggerFactory(new SLF4JDebuggerFactory());
068    }
069
070    /**
071     * Create new SLF4J Smack Debugger instance.
072     * @param connection Smack connection to debug
073     * @param writer connection data writer to observe
074     * @param reader connection data reader to observe
075     */
076    public SLF4JSmackDebugger(XMPPConnection connection, Writer writer, Reader reader) {
077        this.connection = connection;
078        this.writer = new ObservableWriter(writer);
079        this.writer.addWriterListener(slf4JRawXmlListener);
080        this.reader = new ObservableReader(Validate.notNull(reader));
081        this.reader.addReaderListener(slf4JRawXmlListener);
082
083        final SLF4JLoggingConnectionListener loggingConnectionListener = new SLF4JLoggingConnectionListener(connection, logger);
084        this.connection.addConnectionListener(loggingConnectionListener);
085
086        if (connection instanceof AbstractXMPPConnection) {
087            AbstractXMPPConnection abstractXmppConnection = (AbstractXMPPConnection) connection;
088            ReconnectionManager.getInstanceFor(abstractXmppConnection).addReconnectionListener(loggingConnectionListener);
089        } else {
090            LOGGER.info("The connection instance " + connection
091                            + " is not an instance of AbstractXMPPConnection, thus we can not install the ReconnectionListener");
092        }
093    }
094
095    @Override
096    public Reader newConnectionReader(Reader newReader) {
097        reader.removeReaderListener(slf4JRawXmlListener);
098        reader = new ObservableReader(newReader);
099        reader.addReaderListener(slf4JRawXmlListener);
100        return reader;
101    }
102
103    @Override
104    public Writer newConnectionWriter(Writer newWriter) {
105        writer.removeWriterListener(slf4JRawXmlListener);
106        writer = new ObservableWriter(newWriter);
107        writer.addWriterListener(slf4JRawXmlListener);
108        return writer;
109    }
110
111    @Override
112    public void userHasLogged(EntityFullJid user) {
113        if (logger.isDebugEnabled()) {
114            logger.debug("({}) User logged in {}", connection.hashCode(), user.toString());
115        }
116    }
117
118    @Override
119    public Reader getReader() {
120        return reader;
121    }
122
123    @Override
124    public Writer getWriter() {
125        return writer;
126    }
127
128    @Override
129    public StanzaListener getReaderListener() {
130        return receivedListener;
131    }
132
133    @Override
134    public StanzaListener getWriterListener() {
135        return sentListener;
136    }
137}