Rework Smack debugger.

Also fixes SMACK-728.
fixDocu
Florian Schmaus 2017-07-28 11:59:11 +02:00
parent 104146c5ed
commit b8ee8d808f
24 changed files with 328 additions and 390 deletions

View File

@ -53,7 +53,7 @@ service=example.org
```bash
service=example.org
serviceTlsPin=CERTSHA256:2F:92:C9:4D:30:58:E1:05:21:9A:57:59:5F:6E:25:9A:0F:BF:FF:64:1A:C3:4B:EC:06:7D:4A:6F:0A:D5:21:85
debug=true
debugger=console
```
### Framework properties
@ -72,7 +72,7 @@ debug=true
| accountTwoPassword | Password of the second XMPP account |
| accountThreeUsername | Username of the third XMPP account |
| accountThreePassword | Password of the third XMPP account |
| debug | 'true' to enable debug output |
| debugger | 'console' for console debugger, 'enhanced' for the enhanced debugger |
| enabledTests | List of enabled tests |
| disabledTests | List of disabled tests |
| testPackages | List of packages with tests |

View File

@ -16,9 +16,6 @@
*/
package org.jivesoftware.smackx.debugger.android;
import java.io.Reader;
import java.io.Writer;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.debugger.AbstractDebugger;
@ -37,8 +34,8 @@ import android.util.Log;
*/
public class AndroidDebugger extends AbstractDebugger {
public AndroidDebugger(XMPPConnection connection, Writer writer, Reader reader) {
super(connection, writer, reader);
public AndroidDebugger(XMPPConnection connection) {
super(connection);
}
@Override

View File

@ -161,16 +161,8 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
client.addBOSHClientResponseListener(new BOSHPacketReader());
// Initialize the debugger
if (config.isDebuggerEnabled()) {
if (debugger != null) {
initDebugger();
if (isFirstInitialization) {
if (debugger.getReaderListener() != null) {
addAsyncStanzaListener(debugger.getReaderListener(), null);
}
if (debugger.getWriterListener() != null) {
addPacketSendingListener(debugger.getWriterListener(), null);
}
}
}
// Send the session creation request

View File

@ -54,6 +54,7 @@ import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.compress.packet.Compress;
import org.jivesoftware.smack.compression.XMPPInputOutputStream;
import org.jivesoftware.smack.debugger.SmackDebugger;
import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
import org.jivesoftware.smack.filter.IQReplyFilter;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.filter.StanzaIdFilter;
@ -178,7 +179,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
/**
* The SmackDebugger allows to log and debug XML traffic.
*/
protected SmackDebugger debugger = null;
protected final SmackDebugger debugger;
/**
* The Reader which is used for the debugger.
@ -301,6 +302,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
protected AbstractXMPPConnection(ConnectionConfiguration configuration) {
saslAuthentication = new SASLAuthentication(this, configuration);
config = configuration;
SmackDebuggerFactory debuggerFactory = configuration.getDebuggerFactory();
if (debuggerFactory != null) {
debugger = debuggerFactory.create(this);
} else {
debugger = null;
}
// Notify listeners that a new connection has been established
for (ConnectionCreationListener listener : XMPPConnectionRegistry.getConnectionCreationListeners()) {
listener.connectionCreated(this);
@ -568,7 +575,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
// name we are now logged-in as.
// If DEBUG was set to true AFTER the connection was created the debugger
// will be null
if (config.isDebuggerEnabled() && debugger != null) {
if (debugger != null) {
debugger.userHasLogged(user);
}
callConnectionAuthenticatedListener(resumed);
@ -872,6 +879,11 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
*/
@SuppressWarnings("javadoc")
protected void firePacketSendingListeners(final Stanza packet) {
final SmackDebugger debugger = this.debugger;
if (debugger != null) {
debugger.onOutgoingStreamElement(packet);
}
final List<StanzaListener> listenersToNotify = new LinkedList<StanzaListener>();
synchronized (sendListeners) {
for (ListenerWrapper listenerWrapper : sendListeners.values()) {
@ -957,18 +969,10 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
throw new NullPointerException("Reader or writer isn't initialized.");
}
// If debugging is enabled, we open a window and write out all network traffic.
if (config.isDebuggerEnabled()) {
if (debugger == null) {
debugger = SmackConfiguration.createDebugger(this, writer, reader);
}
if (debugger == null) {
LOGGER.severe("Debugging enabled but could not find debugger class");
} else {
// Obtain new reader and writer from the existing debugger
reader = debugger.newConnectionReader(reader);
writer = debugger.newConnectionWriter(writer);
}
if (debugger != null) {
// Obtain new reader and writer from the existing debugger
reader = debugger.newConnectionReader(reader);
writer = debugger.newConnectionWriter(writer);
}
}
@ -1072,6 +1076,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
*/
protected void processStanza(final Stanza stanza) throws InterruptedException {
assert (stanza != null);
final SmackDebugger debugger = this.debugger;
if (debugger != null) {
debugger.onIncomingStreamElement(stanza);
}
lastStanzaReceived = System.currentTimeMillis();
// Deliver the incoming packet to listeners.
executorService.executeBlocking(new Runnable() {

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2003-2007 Jive Software.
* Copyright 2003-2007 Jive Software, 2017 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,6 +30,7 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.callback.CallbackHandler;
import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
import org.jivesoftware.smack.packet.Session;
import org.jivesoftware.smack.proxy.ProxyInfo;
import org.jivesoftware.smack.sasl.SASLMechanism;
@ -78,7 +79,7 @@ public abstract class ConnectionConfiguration {
*/
private final CallbackHandler callbackHandler;
private final boolean debuggerEnabled;
private final SmackDebuggerFactory debuggerFactory;
// Holds the socket factory that is used to generate the socket in the connection
private final SocketFactory socketFactory;
@ -158,7 +159,7 @@ public abstract class ConnectionConfiguration {
hostnameVerifier = builder.hostnameVerifier;
sendPresence = builder.sendPresence;
legacySessionDisabled = builder.legacySessionDisabled;
debuggerEnabled = builder.debuggerEnabled;
debuggerFactory = builder.debuggerFactory;
allowNullOrEmptyUsername = builder.allowEmptyOrNullUsername;
enabledSaslMechanisms = builder.enabledSaslMechanisms;
@ -281,13 +282,12 @@ public abstract class ConnectionConfiguration {
}
/**
* Returns true if the new connection about to be establish is going to be debugged. By
* default the value of {@link SmackConfiguration#DEBUG} is used.
* Returns the Smack debugger factory.
*
* @return true if the new connection about to be establish is going to be debugged.
* @return the Smack debugger factory or <code>null</code>
*/
public boolean isDebuggerEnabled() {
return debuggerEnabled;
public SmackDebuggerFactory getDebuggerFactory() {
return debuggerFactory;
}
/**
@ -519,7 +519,7 @@ public abstract class ConnectionConfiguration {
private boolean legacySessionDisabled = false;
private ProxyInfo proxy;
private CallbackHandler callbackHandler;
private boolean debuggerEnabled = SmackConfiguration.DEBUG;
private SmackDebuggerFactory debuggerFactory;
private SocketFactory socketFactory;
private DomainBareJid xmppServiceDomain;
private InetAddress hostAddress;
@ -531,6 +531,9 @@ public abstract class ConnectionConfiguration {
private X509TrustManager customX509TrustManager;
protected Builder() {
if (SmackConfiguration.DEBUG) {
enableDefaultDebugger();
}
}
/**
@ -808,15 +811,20 @@ public abstract class ConnectionConfiguration {
return getThis();
}
public B enableDefaultDebugger() {
this.debuggerFactory = SmackConfiguration.getDefaultSmackDebuggerFactory();
assert this.debuggerFactory != null;
return getThis();
}
/**
* Sets if the new connection about to be establish is going to be debugged. By
* default the value of {@link SmackConfiguration#DEBUG} is used.
*
* @param debuggerEnabled if the new connection about to be establish is going to be debugged.
* Set the Smack debugger factory used to construct Smack debuggers.
*
* @param debuggerFactory the Smack debugger factory.
* @return a reference to this builder.
*/
public B setDebuggerEnabled(boolean debuggerEnabled) {
this.debuggerEnabled = debuggerEnabled;
public B setDebuggerFactory(SmackDebuggerFactory debuggerFactory) {
this.debuggerFactory = debuggerFactory;
return getThis();
}
@ -965,4 +973,5 @@ public abstract class ConnectionConfiguration {
protected abstract B getThis();
}
}

View File

@ -17,8 +17,6 @@
package org.jivesoftware.smack;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -30,7 +28,6 @@ import javax.net.ssl.HostnameVerifier;
import org.jivesoftware.smack.compression.XMPPInputOutputStream;
import org.jivesoftware.smack.debugger.ReflectionDebuggerFactory;
import org.jivesoftware.smack.debugger.SmackDebugger;
import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
import org.jivesoftware.smack.parsing.ExceptionThrowingCallback;
import org.jivesoftware.smack.parsing.ParsingExceptionCallback;
@ -63,8 +60,6 @@ public final class SmackConfiguration {
static boolean smackInitialized = false;
private static SmackDebuggerFactory debuggerFactory = new ReflectionDebuggerFactory();
/**
* Value that indicates whether debugging is enabled. When enabled, a debug
* window will apear for each new connection that will contain the following
@ -80,6 +75,8 @@ public final class SmackConfiguration {
*/
public static boolean DEBUG = false;
private static SmackDebuggerFactory DEFAULT_DEBUGGER_FACTORY = ReflectionDebuggerFactory.INSTANCE;
/**
* The default parsing exception callback is {@link ExceptionThrowingCallback} which will
* throw an exception and therefore disconnect the active connection.
@ -148,6 +145,14 @@ public final class SmackConfiguration {
defaultPacketReplyTimeout = timeout;
}
public static void setDefaultSmackDebuggerFactory(SmackDebuggerFactory debuggerFactory) {
DEFAULT_DEBUGGER_FACTORY = Objects.requireNonNull(debuggerFactory, "Debugger factory must not be null");
}
public static SmackDebuggerFactory getDefaultSmackDebuggerFactory() {
return DEFAULT_DEBUGGER_FACTORY;
}
/**
* Gets the default max size of a stanza(/packet) collector before it will delete
* the older packets.
@ -190,43 +195,6 @@ public final class SmackConfiguration {
}
}
/**
* Sets Smack debugger factory.
*
* @param debuggerFactory new debugger factory implementation to be used by Smack
*/
public static void setDebuggerFactory(SmackDebuggerFactory debuggerFactory) {
SmackConfiguration.debuggerFactory = debuggerFactory;
}
/**
* Get the debugger factory.
*
* @return a debugger factory or <code>null</code>
*/
public static SmackDebuggerFactory getDebuggerFactory() {
return debuggerFactory;
}
/**
* Creates new debugger instance with given arguments as parameters. May
* return <code>null</code> if no DebuggerFactory is set or if the factory
* did not produce a debugger.
*
* @param connection
* @param writer
* @param reader
* @return a new debugger or <code>null</code>
*/
public static SmackDebugger createDebugger(XMPPConnection connection, Writer writer, Reader reader) {
SmackDebuggerFactory factory = getDebuggerFactory();
if (factory == null) {
return null;
} else {
return factory.create(connection, writer, reader);
}
}
/**
* Remove a SASL mechanism from the list to be used.
*

View File

@ -20,9 +20,8 @@ import java.io.Reader;
import java.io.Writer;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.TopLevelStreamElement;
import org.jivesoftware.smack.util.ObservableReader;
import org.jivesoftware.smack.util.ObservableWriter;
import org.jivesoftware.smack.util.ReaderListener;
@ -30,13 +29,10 @@ import org.jivesoftware.smack.util.WriterListener;
import org.jxmpp.jid.EntityFullJid;
public abstract class AbstractDebugger implements SmackDebugger {
public abstract class AbstractDebugger extends SmackDebugger {
public static boolean printInterpreted = false;
private final XMPPConnection connection;
private final StanzaListener listener;
private final ConnectionListener connListener;
private final ReaderListener readerListener;
private final WriterListener writerListener;
@ -44,8 +40,8 @@ public abstract class AbstractDebugger implements SmackDebugger {
private ObservableWriter writer;
private ObservableReader reader;
public AbstractDebugger(final XMPPConnection connection, Writer writer, Reader reader) {
this.connection = connection;
public AbstractDebugger(final XMPPConnection connection) {
super(connection);
// Create a special Reader that wraps the main Reader and logs data to the GUI.
this.reader = new ObservableReader(reader);
@ -67,18 +63,6 @@ public abstract class AbstractDebugger implements SmackDebugger {
};
this.writer.addWriterListener(writerListener);
// Create a thread that will listen for all incoming packets and write them to
// the GUI. This is what we call "interpreted" packet data, since it's the packet
// data as Smack sees it and not as it's coming in as raw XML.
listener = new StanzaListener() {
@Override
public void processStanza(Stanza packet) {
if (printInterpreted) {
log("RCV PKT (" + connection.getConnectionCounter() + "): " + packet.toXML());
}
}
};
connListener = new ConnectionListener() {
@Override
public void connected(XMPPConnection connection) {
@ -173,22 +157,15 @@ public abstract class AbstractDebugger implements SmackDebugger {
}
@Override
public Reader getReader() {
return reader;
public void onIncomingStreamElement(TopLevelStreamElement streamElement) {
if (printInterpreted) {
log("RCV PKT (" + connection.getConnectionCounter() + "): " + streamElement.toXML());
}
}
@Override
public Writer getWriter() {
return writer;
public void onOutgoingStreamElement(TopLevelStreamElement streamElement) {
// Does nothing (yet).
}
@Override
public StanzaListener getReaderListener() {
return listener;
}
@Override
public StanzaListener getWriterListener() {
return null;
}
}

View File

@ -17,9 +17,7 @@
package org.jivesoftware.smack.debugger;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
@ -39,8 +37,8 @@ import org.jivesoftware.smack.XMPPConnection;
public class ConsoleDebugger extends AbstractDebugger {
private final SimpleDateFormat dateFormatter = new SimpleDateFormat("HH:mm:ss");
public ConsoleDebugger(XMPPConnection connection, Writer writer, Reader reader) {
super(connection, writer, reader);
public ConsoleDebugger(XMPPConnection connection) {
super(connection);
}
@Override
@ -64,4 +62,17 @@ public class ConsoleDebugger extends AbstractDebugger {
log(logMessage + sw);
}
public static final class Factory implements SmackDebuggerFactory {
public static final SmackDebuggerFactory INSTANCE = new Factory();
private Factory() {
}
@Override
public SmackDebugger create(XMPPConnection connection) throws IllegalArgumentException {
return new ConsoleDebugger(connection);
}
}
}

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2014 Florian Schmaus
* Copyright 2014-2017 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,8 +16,6 @@
*/
package org.jivesoftware.smack.debugger;
import java.io.Reader;
import java.io.Writer;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -38,8 +36,8 @@ public class JulDebugger extends AbstractDebugger {
private static final Logger LOGGER = Logger.getLogger(JulDebugger.class.getName());
public JulDebugger(XMPPConnection connection, Writer writer, Reader reader) {
super(connection, writer, reader);
public JulDebugger(XMPPConnection connection) {
super(connection);
}
@Override

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2014 Vyacheslav Blinov
* Copyright 2014 Vyacheslav Blinov, 2017 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,12 +14,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.debugger;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Constructor;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -27,10 +23,15 @@ import java.util.logging.Logger;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.XMPPConnection;
public class ReflectionDebuggerFactory implements SmackDebuggerFactory {
public final class ReflectionDebuggerFactory implements SmackDebuggerFactory {
private static final Logger LOGGER = Logger.getLogger(ReflectionDebuggerFactory.class.getName());
private static final String DEBUGGER_CLASS_PROPERTY_NAME = "smack.debuggerClass";
public static final ReflectionDebuggerFactory INSTANCE = new ReflectionDebuggerFactory();
private ReflectionDebuggerFactory() {
}
/**
* Possible default debugger implementations. The order of enumeration is the one in which we try
* to instantiate these.
@ -76,14 +77,14 @@ public class ReflectionDebuggerFactory implements SmackDebuggerFactory {
}
@Override
public SmackDebugger create(XMPPConnection connection, Writer writer, Reader reader) throws IllegalArgumentException {
public SmackDebugger create(XMPPConnection connection) throws IllegalArgumentException {
Class<SmackDebugger> debuggerClass = getDebuggerClass();
if (debuggerClass != null) {
// Create a new debugger instance using 3arg constructor
try {
Constructor<SmackDebugger> constructor = debuggerClass
.getConstructor(XMPPConnection.class, Writer.class, Reader.class);
return constructor.newInstance(connection, writer, reader);
.getConstructor(XMPPConnection.class);
return constructor.newInstance(connection);
} catch (Exception e) {
throw new IllegalArgumentException("Can't initialize the configured debugger!", e);
}

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2003-2007 Jive Software.
* Copyright 2003-2007 Jive Software, 2017 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,7 +20,8 @@ package org.jivesoftware.smack.debugger;
import java.io.Reader;
import java.io.Writer;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.packet.TopLevelStreamElement;
import org.jxmpp.jid.EntityFullJid;
@ -33,7 +34,13 @@ import org.jxmpp.jid.EntityFullJid;
*
* @author Gaston Dombiak
*/
public interface SmackDebugger {
public abstract class SmackDebugger {
protected final XMPPConnection connection;
protected SmackDebugger(XMPPConnection connection) {
this.connection = connection;
}
/**
* Called when a user has logged in to the server. The user could be an anonymous user, this
@ -42,22 +49,9 @@ public interface SmackDebugger {
*
* @param user the user@host/resource that has just logged in
*/
// TODO: Should be replaced with a connection listener authenticed().
public abstract void userHasLogged(EntityFullJid user);
/**
* Returns the special Reader that wraps the main Reader and logs data to the GUI.
*
* @return the special Reader that wraps the main Reader and logs data to the GUI.
*/
public abstract Reader getReader();
/**
* Returns the special Writer that wraps the main Writer and logs data to the GUI.
*
* @return the special Writer that wraps the main Writer and logs data to the GUI.
*/
public abstract Writer getWriter();
/**
* Returns a new special Reader that wraps the new connection Reader. The connection
* has been secured so the connection is using a new reader and writer. The debugger
@ -79,20 +73,23 @@ public interface SmackDebugger {
public abstract Writer newConnectionWriter(Writer writer);
/**
* Returns the thread that will listen for all incoming packets and write them to the GUI.
* This is what we call "interpreted" stanza(/packet) data, since it's the stanza(/packet) data as Smack sees
* it and not as it's coming in as raw XML.
*
* @return the PacketListener that will listen for all incoming packets and write them to
* the GUI
* Used by the connection to notify about an incoming top level stream element.
* <p>
* This method is invoked right after the incoming stream was parsed.
* </p>
*
* @param streamElement the incoming top level stream element.
*/
public abstract StanzaListener getReaderListener();
public abstract void onIncomingStreamElement(TopLevelStreamElement streamElement);
/**
* Returns the thread that will listen for all outgoing packets and write them to the GUI.
*
* @return the PacketListener that will listen for all sent packets and write them to
* the GUI
* Used by the connection to notify about a outgoing top level stream element.
* <p>
* This method is invoked right before the element is serialized to XML and put into the outgoing stream.
* </p>
*
* @param streamElement the outgoing top level stream element.
*/
public abstract StanzaListener getWriterListener();
public abstract void onOutgoingStreamElement(TopLevelStreamElement streamElement);
}

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2014 Vyacheslav Blinov
* Copyright 2014 Vyacheslav Blinov, 2017 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,20 +14,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.debugger;
import java.io.Reader;
import java.io.Writer;
import org.jivesoftware.smack.XMPPConnection;
public interface SmackDebuggerFactory {
/**
* Initialize the new SmackDebugger instance.
*
* @param connection the XMPP connection this debugger is going to get attached to.
* @throws IllegalArgumentException if the SmackDebugger can't be loaded.
*/
SmackDebugger create(XMPPConnection connection, Writer writer, Reader reader) throws IllegalArgumentException;
SmackDebugger create(XMPPConnection connection) throws IllegalArgumentException;
}

View File

@ -17,9 +17,6 @@
package org.jivesoftware.smackx.debugger.slf4j;
import java.io.Reader;
import java.io.Writer;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.debugger.SmackDebugger;
import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
@ -27,9 +24,15 @@ import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
/**
* Implementation of SmackDebuggerFactory which always creates instance of SLF4JSmackDebugger.
*/
public class SLF4JDebuggerFactory implements SmackDebuggerFactory {
public final class SLF4JDebuggerFactory implements SmackDebuggerFactory {
public static final SLF4JDebuggerFactory INSTANCE = new SLF4JDebuggerFactory();
private SLF4JDebuggerFactory() {
}
@Override
public SmackDebugger create(XMPPConnection connection, Writer writer, Reader reader) throws IllegalArgumentException {
return new SLF4JSmackDebugger(connection, writer, reader);
public SmackDebugger create(XMPPConnection connection) throws IllegalArgumentException {
return new SLF4JSmackDebugger(connection);
}
}

View File

@ -1,41 +0,0 @@
/**
*
* Copyright 2014 Vyacheslav Blinov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.debugger.slf4j;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.packet.Stanza;
import org.slf4j.Logger;
class SLF4JLoggingPacketListener implements StanzaListener {
private final Logger logger;
private final String prefix;
public SLF4JLoggingPacketListener(Logger logger, String prefix) {
this.logger = Validate.notNull(logger);
this.prefix = Validate.notNull(prefix);
}
@Override
public void processStanza(Stanza packet) {
if (SLF4JSmackDebugger.printInterpreted.get() && logger.isDebugEnabled()) {
logger.debug("{}: PKT [{}] '{}'", prefix, packet.getClass().getName(), packet.toXML());
}
}
}

View File

@ -22,9 +22,9 @@ import java.io.Writer;
import java.util.concurrent.atomic.AtomicBoolean;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.debugger.SmackDebugger;
import org.jivesoftware.smack.packet.TopLevelStreamElement;
import org.jivesoftware.smack.util.ObservableReader;
import org.jivesoftware.smack.util.ObservableWriter;
@ -38,7 +38,8 @@ import org.slf4j.LoggerFactory;
* Use in conjunction with your SLF4J bindings of choice.
* See SLF4J manual for more details about bindings usage.
*/
public class SLF4JSmackDebugger implements SmackDebugger {
public class SLF4JSmackDebugger extends SmackDebugger {
public static final String LOGGER_NAME = "SMACK";
private static final Logger logger = LoggerFactory.getLogger(LOGGER_NAME);
public static final AtomicBoolean printInterpreted = new AtomicBoolean(true);
@ -46,10 +47,6 @@ public class SLF4JSmackDebugger implements SmackDebugger {
public static final String SENT_TAG = "SENT";
public static final String RECEIVED_TAG = "RECV";
private final XMPPConnection connection;
private final StanzaListener receivedListener = new SLF4JLoggingPacketListener(logger, RECEIVED_TAG);
private final StanzaListener sentListener = new SLF4JLoggingPacketListener(logger, SENT_TAG);
private final SLF4JRawXmlListener slf4JRawXmlListener = new SLF4JRawXmlListener(logger);
private ObservableWriter writer;
@ -59,17 +56,16 @@ public class SLF4JSmackDebugger implements SmackDebugger {
* Makes Smack use this Debugger.
*/
public static void enable() {
SmackConfiguration.setDebuggerFactory(new SLF4JDebuggerFactory());
SmackConfiguration.DEBUG = true;
SmackConfiguration.setDefaultSmackDebuggerFactory(SLF4JDebuggerFactory.INSTANCE);
}
/**
* Create new SLF4J Smack Debugger instance.
* @param connection Smack connection to debug
* @param writer connection data writer to observe
* @param reader connection data reader to observe
*/
public SLF4JSmackDebugger(XMPPConnection connection, Writer writer, Reader reader) {
this.connection = connection;
SLF4JSmackDebugger(XMPPConnection connection) {
super(connection);
this.writer = new ObservableWriter(writer);
this.writer.addWriterListener(slf4JRawXmlListener);
this.reader = new ObservableReader(Validate.notNull(reader));
@ -101,22 +97,17 @@ public class SLF4JSmackDebugger implements SmackDebugger {
}
@Override
public Reader getReader() {
return reader;
public void onIncomingStreamElement(TopLevelStreamElement streamElement) {
if (SLF4JSmackDebugger.printInterpreted.get() && logger.isDebugEnabled()) {
logger.debug("IN {}: {}", streamElement.getClass().getName(), streamElement.toXML());
}
}
@Override
public Writer getWriter() {
return writer;
public void onOutgoingStreamElement(TopLevelStreamElement streamElement) {
if (SLF4JSmackDebugger.printInterpreted.get() && logger.isDebugEnabled()) {
logger.debug("OUT {}: {}", streamElement.getClass().getName(), streamElement.toXML());
}
}
@Override
public StanzaListener getReaderListener() {
return receivedListener;
}
@Override
public StanzaListener getWriterListener() {
return sentListener;
}
}

View File

@ -72,13 +72,14 @@ import javax.xml.transform.stream.StreamSource;
import org.jivesoftware.smack.AbstractConnectionListener;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.debugger.SmackDebugger;
import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.TopLevelStreamElement;
import org.jivesoftware.smack.util.ObservableReader;
import org.jivesoftware.smack.util.ObservableWriter;
import org.jivesoftware.smack.util.ReaderListener;
@ -97,7 +98,7 @@ import org.jxmpp.jid.Jid;
*
* @author Gaston Dombiak
*/
public class EnhancedDebugger implements SmackDebugger {
public class EnhancedDebugger extends SmackDebugger {
private static final Logger LOGGER = Logger.getLogger(EnhancedDebugger.class.getName());
@ -149,10 +150,6 @@ public class EnhancedDebugger implements SmackDebugger {
private JFormattedTextField userField = null;
private JFormattedTextField statusField = null;
private XMPPConnection connection = null;
private StanzaListener packetReaderListener = null;
private StanzaListener packetWriterListener = null;
private ConnectionListener connListener = null;
private Writer writer;
@ -177,18 +174,9 @@ public class EnhancedDebugger implements SmackDebugger {
JTabbedPane tabbedPane;
public EnhancedDebugger(XMPPConnection connection, Writer writer, Reader reader) {
this.connection = connection;
this.writer = writer;
this.reader = reader;
createDebug();
EnhancedDebuggerWindow.addDebugger(this);
}
public EnhancedDebugger(XMPPConnection connection) {
super(connection);
/**
* Creates the debug process, which is a GUI window that displays XML traffic.
*/
private void createDebug() {
// We'll arrange the UI into six tabs. The first tab contains all data, the second
// client generated XML, the third server generated XML, the fourth allows to send
// ad-hoc messages and the fifth contains connection information.
@ -203,41 +191,6 @@ public class EnhancedDebugger implements SmackDebugger {
// Add the connection information panel
addInformationPanel();
// Create a thread that will listen for all incoming packets and write them to
// the GUI. This is what we call "interpreted" packet data, since it's the packet
// data as Smack sees it and not as it's coming in as raw XML.
packetReaderListener = new StanzaListener() {
SimpleDateFormat dateFormatter = new SimpleDateFormat("HH:mm:ss:SS");
@Override
public void processStanza(final Stanza packet) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
addReadPacketToTable(dateFormatter, packet);
}
});
}
};
// Create a thread that will listen for all outgoing packets and write them to
// the GUI.
packetWriterListener = new StanzaListener() {
SimpleDateFormat dateFormatter = new SimpleDateFormat("HH:mm:ss:SS");
@Override
public void processStanza(final Stanza packet) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
addSentPacketToTable(dateFormatter, packet);
}
});
}
};
// Create a thread that will listen for any connection closed event
connListener = new AbstractConnectionListener() {
@Override
@ -294,6 +247,8 @@ public class EnhancedDebugger implements SmackDebugger {
});
}
};
EnhancedDebuggerWindow.addDebugger(this);
}
private void addBasicPanels() {
@ -793,26 +748,6 @@ public class EnhancedDebugger implements SmackDebugger {
}
@Override
public Reader getReader() {
return reader;
}
@Override
public Writer getWriter() {
return writer;
}
@Override
public StanzaListener getReaderListener() {
return packetReaderListener;
}
@Override
public StanzaListener getWriterListener() {
return packetWriterListener;
}
/**
* Updates the statistics table
*/
@ -839,12 +774,21 @@ public class EnhancedDebugger implements SmackDebugger {
* @param dateFormatter the SimpleDateFormat to use to format Dates
* @param packet the read stanza(/packet) to add to the table
*/
private void addReadPacketToTable(final SimpleDateFormat dateFormatter, final Stanza packet) {
private void addReadPacketToTable(final SimpleDateFormat dateFormatter, final TopLevelStreamElement packet) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
String messageType;
Jid from = packet.getFrom();
Jid from;
String stanzaId;
if (packet instanceof Stanza) {
Stanza stanza = (Stanza) packet;
from = stanza.getFrom();
stanzaId = stanza.getStanzaId();
} else {
from = null;
stanzaId = "(Nonza)";
}
String type = "";
Icon packetTypeIcon;
receivedPackets++;
@ -885,7 +829,7 @@ public class EnhancedDebugger implements SmackDebugger {
packetReceivedIcon,
packetTypeIcon,
messageType,
packet.getStanzaId(),
stanzaId,
type,
"",
from});
@ -901,12 +845,21 @@ public class EnhancedDebugger implements SmackDebugger {
* @param dateFormatter the SimpleDateFormat to use to format Dates
* @param packet the sent stanza(/packet) to add to the table
*/
private void addSentPacketToTable(final SimpleDateFormat dateFormatter, final Stanza packet) {
private void addSentPacketToTable(final SimpleDateFormat dateFormatter, final TopLevelStreamElement packet) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
String messageType;
Jid to = packet.getTo();
Jid to;
String stanzaId;
if (packet instanceof Stanza) {
Stanza stanza = (Stanza) packet;
to = stanza.getTo();
stanzaId = stanza.getStanzaId();
} else {
to = null;
stanzaId = "(Nonza)";
}
String type = "";
Icon packetTypeIcon;
sentPackets++;
@ -947,7 +900,7 @@ public class EnhancedDebugger implements SmackDebugger {
packetSentIcon,
packetTypeIcon,
messageType,
packet.getStanzaId(),
stanzaId,
type,
to,
""});
@ -1006,8 +959,6 @@ public class EnhancedDebugger implements SmackDebugger {
*/
void cancel() {
connection.removeConnectionListener(connListener);
connection.removeAsyncStanzaListener(packetReaderListener);
connection.removePacketSendingListener(packetWriterListener);
((ObservableReader) reader).removeReaderListener(readerListener);
((ObservableWriter) writer).removeWriterListener(writerListener);
messagesTable = null;
@ -1099,4 +1050,40 @@ public class EnhancedDebugger implements SmackDebugger {
}
}
}
@Override
public void onIncomingStreamElement(final TopLevelStreamElement streamElement) {
final SimpleDateFormat dateFormatter = new SimpleDateFormat("HH:mm:ss:SS");
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
addReadPacketToTable(dateFormatter, streamElement);
}
});
}
@Override
public void onOutgoingStreamElement(final TopLevelStreamElement streamElement) {
final SimpleDateFormat dateFormatter = new SimpleDateFormat("HH:mm:ss:SS");
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
addSentPacketToTable(dateFormatter, streamElement);
}
});
}
public static final class Factory implements SmackDebuggerFactory {
public static final SmackDebuggerFactory INSTANCE = new Factory();
private Factory() {
}
@Override
public SmackDebugger create(XMPPConnection connection) throws IllegalArgumentException {
return new EnhancedDebugger(connection);
}
}
}

View File

@ -114,7 +114,7 @@ public final class EnhancedDebuggerWindow {
*
* @return the unique EnhancedDebuggerWindow instance
*/
public static EnhancedDebuggerWindow getInstance() {
public synchronized static EnhancedDebuggerWindow getInstance() {
if (instance == null) {
instance = new EnhancedDebuggerWindow();
}
@ -345,7 +345,7 @@ public final class EnhancedDebuggerWindow {
*
* @param evt the event that indicates that the root window is closing
*/
public void rootWindowClosing(WindowEvent evt) {
private synchronized void rootWindowClosing(WindowEvent evt) {
// Notify to all the debuggers to stop debugging
for (EnhancedDebugger debugger : debuggers) {
debugger.cancel();
@ -354,6 +354,8 @@ public final class EnhancedDebuggerWindow {
debuggers.clear();
// Release the default instance
instance = null;
frame = null;
notifyAll();
}
/**
@ -393,4 +395,14 @@ public final class EnhancedDebuggerWindow {
public boolean isVisible() {
return frame != null && frame.isVisible();
}
public synchronized void waitUntilClosed() throws InterruptedException {
if (frame == null) {
return;
}
while (frame != null) {
wait();
}
}
}

View File

@ -40,10 +40,9 @@ import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.debugger.SmackDebugger;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.TopLevelStreamElement;
import org.jivesoftware.smack.util.ObservableReader;
import org.jivesoftware.smack.util.ObservableWriter;
import org.jivesoftware.smack.util.ReaderListener;
@ -57,24 +56,22 @@ import org.jxmpp.jid.EntityFullJid;
*
* @author Gaston Dombiak
*/
public class LiteDebugger implements SmackDebugger {
public class LiteDebugger extends SmackDebugger {
private static final String NEWLINE = "\n";
private JFrame frame = null;
private XMPPConnection connection = null;
private final JTextArea interpretedText1 = new JTextArea();
private final JTextArea interpretedText2 = new JTextArea();
private StanzaListener listener = null;
private JFrame frame = null;
private Writer writer;
private Reader reader;
private ReaderListener readerListener;
private WriterListener writerListener;
public LiteDebugger(XMPPConnection connection, Writer writer, Reader reader) {
this.connection = connection;
this.writer = writer;
this.reader = reader;
public LiteDebugger(XMPPConnection connection) {
super(connection);
createDebug();
}
@ -181,8 +178,6 @@ public class LiteDebugger implements SmackDebugger {
menu.add(menuItem2);
// Create UI elements for interpreted XML traffic.
final JTextArea interpretedText1 = new JTextArea();
final JTextArea interpretedText2 = new JTextArea();
interpretedText1.setEditable(false);
interpretedText2.setEditable(false);
interpretedText1.setForeground(new Color(1, 94, 35));
@ -267,19 +262,6 @@ public class LiteDebugger implements SmackDebugger {
// and writer will use the debug versions when they are created.
reader = debugReader;
writer = debugWriter;
// Create a thread that will listen for all incoming packets and write them to
// the GUI. This is what we call "interpreted" packet data, since it's the packet
// data as Smack sees it and not as it's coming in as raw XML.
listener = new StanzaListener() {
@Override
public void processStanza(Stanza packet) {
interpretedText1.append(packet.toXML().toString());
interpretedText2.append(packet.toXML().toString());
interpretedText1.append(NEWLINE);
interpretedText2.append(NEWLINE);
}
};
}
/**
@ -289,7 +271,7 @@ public class LiteDebugger implements SmackDebugger {
* @param evt the event that indicates that the root window is closing
*/
public void rootWindowClosing(WindowEvent evt) {
connection.removeAsyncStanzaListener(listener);
// TODO: Remove debugger from connection.
((ObservableReader) reader).removeReaderListener(readerListener);
((ObservableWriter) writer).removeWriterListener(writerListener);
}
@ -348,22 +330,16 @@ public class LiteDebugger implements SmackDebugger {
}
@Override
public Reader getReader() {
return reader;
public void onIncomingStreamElement(TopLevelStreamElement streamElement) {
interpretedText1.append(streamElement.toXML().toString());
interpretedText2.append(streamElement.toXML().toString());
interpretedText1.append(NEWLINE);
interpretedText2.append(NEWLINE);
}
@Override
public Writer getWriter() {
return writer;
public void onOutgoingStreamElement(TopLevelStreamElement streamElement) {
// Does nothing.
}
@Override
public StanzaListener getReaderListener() {
return listener;
}
@Override
public StanzaListener getWriterListener() {
return null;
}
}

View File

@ -12,6 +12,7 @@ dependencies {
compile project(':smack-extensions')
compile project(':smack-experimental')
compile project(':smack-omemo')
compile project(':smack-debug')
compile 'org.reflections:reflections:0.9.9-RC1'
compile 'eu.geekplace.javapinning:java-pinning-java7:1.1.0-alpha1'
compile "junit:junit:$junitVersion"