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.util.concurrent.atomic.AtomicBoolean;
021
022import org.jivesoftware.smack.AbstractXMPPConnection;
023import org.jivesoftware.smack.ReconnectionManager;
024import org.jivesoftware.smack.SmackConfiguration;
025import org.jivesoftware.smack.XMPPConnection;
026import org.jivesoftware.smack.debugger.SmackDebugger;
027import org.jivesoftware.smack.packet.TopLevelStreamElement;
028import org.jivesoftware.smack.util.ObservableReader;
029import org.jivesoftware.smack.util.ObservableWriter;
030
031import org.jxmpp.jid.EntityFullJid;
032import org.slf4j.Logger;
033import org.slf4j.LoggerFactory;
034
035
036/**
037 * Implementation of SmackDebugger that writes log messages using SLF4J API.
038 * Use in conjunction with your SLF4J bindings of choice.
039 * See SLF4J manual for more details about bindings usage.
040 */
041public class SLF4JSmackDebugger extends SmackDebugger  {
042
043    private static final java.util.logging.Logger LOGGER = java.util.logging.Logger.getLogger(SLF4JSmackDebugger.class.getName());
044
045    public static final String LOGGER_NAME = "SMACK";
046    private static final Logger logger = LoggerFactory.getLogger(LOGGER_NAME);
047    public static final AtomicBoolean printInterpreted = new AtomicBoolean(true);
048
049    public static final String SENT_TAG = "SENT";
050    public static final String RECEIVED_TAG = "RECV";
051
052    private final SLF4JRawXmlListener slf4JRawXmlListener = new SLF4JRawXmlListener(logger);
053
054    private ObservableWriter writer;
055    private ObservableReader reader;
056
057    /**
058     * Makes Smack use this Debugger.
059     */
060    public static void enable() {
061        SmackConfiguration.DEBUG = true;
062        SmackConfiguration.setDefaultSmackDebuggerFactory(SLF4JDebuggerFactory.INSTANCE);
063    }
064
065    /**
066     * Create new SLF4J Smack Debugger instance.
067     * @param connection Smack connection to debug
068     */
069    SLF4JSmackDebugger(XMPPConnection connection) {
070        super(connection);
071        this.writer = new ObservableWriter(writer);
072        this.writer.addWriterListener(slf4JRawXmlListener);
073        this.reader = new ObservableReader(Validate.notNull(reader));
074        this.reader.addReaderListener(slf4JRawXmlListener);
075
076        final SLF4JLoggingConnectionListener loggingConnectionListener = new SLF4JLoggingConnectionListener(connection, logger);
077        this.connection.addConnectionListener(loggingConnectionListener);
078
079        if (connection instanceof AbstractXMPPConnection) {
080            AbstractXMPPConnection abstractXmppConnection = (AbstractXMPPConnection) connection;
081            ReconnectionManager.getInstanceFor(abstractXmppConnection).addReconnectionListener(loggingConnectionListener);
082        } else {
083            LOGGER.info("The connection instance " + connection
084                            + " is not an instance of AbstractXMPPConnection, thus we can not install the ReconnectionListener");
085        }
086    }
087
088    @Override
089    public void outgoingStreamSink(CharSequence outgoingCharSequence) {
090        slf4JRawXmlListener.write(outgoingCharSequence.toString());
091    }
092
093    @Override
094    public void incomingStreamSink(CharSequence incomingCharSequence) {
095        slf4JRawXmlListener.read(incomingCharSequence.toString());
096    }
097
098    @Override
099    public void userHasLogged(EntityFullJid user) {
100        if (logger.isDebugEnabled()) {
101            logger.debug("({}) User logged in {}", connection.hashCode(), user.toString());
102        }
103    }
104
105    @Override
106    public void onIncomingStreamElement(TopLevelStreamElement streamElement) {
107        if (SLF4JSmackDebugger.printInterpreted.get() && logger.isDebugEnabled()) {
108            logger.debug("IN {}: {}", streamElement.getClass().getName(), streamElement.toXML());
109        }
110    }
111
112    @Override
113    public void onOutgoingStreamElement(TopLevelStreamElement streamElement) {
114        if (SLF4JSmackDebugger.printInterpreted.get() && logger.isDebugEnabled()) {
115            logger.debug("OUT {}: {}", streamElement.getClass().getName(), streamElement.toXML());
116        }
117    }
118
119}