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.XMPPConnection;
028import org.jivesoftware.smack.debugger.SmackDebugger;
029import org.jivesoftware.smack.packet.TopLevelStreamElement;
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 extends 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 SLF4JRawXmlListener slf4JRawXmlListener = new SLF4JRawXmlListener(logger);
055
056    private ObservableWriter writer;
057    private ObservableReader reader;
058
059    /**
060     * Makes Smack use this Debugger.
061     */
062    public static void enable() {
063        SmackConfiguration.DEBUG = true;
064        SmackConfiguration.setDefaultSmackDebuggerFactory(SLF4JDebuggerFactory.INSTANCE);
065    }
066
067    /**
068     * Create new SLF4J Smack Debugger instance.
069     * @param connection Smack connection to debug
070     */
071    SLF4JSmackDebugger(XMPPConnection connection) {
072        super(connection);
073        this.writer = new ObservableWriter(writer);
074        this.writer.addWriterListener(slf4JRawXmlListener);
075        this.reader = new ObservableReader(Validate.notNull(reader));
076        this.reader.addReaderListener(slf4JRawXmlListener);
077
078        final SLF4JLoggingConnectionListener loggingConnectionListener = new SLF4JLoggingConnectionListener(connection, logger);
079        this.connection.addConnectionListener(loggingConnectionListener);
080
081        if (connection instanceof AbstractXMPPConnection) {
082            AbstractXMPPConnection abstractXmppConnection = (AbstractXMPPConnection) connection;
083            ReconnectionManager.getInstanceFor(abstractXmppConnection).addReconnectionListener(loggingConnectionListener);
084        } else {
085            LOGGER.info("The connection instance " + connection
086                            + " is not an instance of AbstractXMPPConnection, thus we can not install the ReconnectionListener");
087        }
088    }
089
090    @Override
091    public Reader newConnectionReader(Reader newReader) {
092        reader.removeReaderListener(slf4JRawXmlListener);
093        reader = new ObservableReader(newReader);
094        reader.addReaderListener(slf4JRawXmlListener);
095        return reader;
096    }
097
098    @Override
099    public Writer newConnectionWriter(Writer newWriter) {
100        writer.removeWriterListener(slf4JRawXmlListener);
101        writer = new ObservableWriter(newWriter);
102        writer.addWriterListener(slf4JRawXmlListener);
103        return writer;
104    }
105
106    @Override
107    public void userHasLogged(EntityFullJid user) {
108        if (logger.isDebugEnabled()) {
109            logger.debug("({}) User logged in {}", connection.hashCode(), user.toString());
110        }
111    }
112
113    @Override
114    public void onIncomingStreamElement(TopLevelStreamElement streamElement) {
115        if (SLF4JSmackDebugger.printInterpreted.get() && logger.isDebugEnabled()) {
116            logger.debug("IN {}: {}", streamElement.getClass().getName(), streamElement.toXML(null));
117        }
118    }
119
120    @Override
121    public void onOutgoingStreamElement(TopLevelStreamElement streamElement) {
122        if (SLF4JSmackDebugger.printInterpreted.get() && logger.isDebugEnabled()) {
123            logger.debug("OUT {}: {}", streamElement.getClass().getName(), streamElement.toXML(null));
124        }
125    }
126
127}