diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java index 2de9535f4..2c3546f01 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -19,7 +19,6 @@ package org.jivesoftware.smack; import java.io.IOException; import java.io.Reader; import java.io.Writer; -import java.lang.reflect.Constructor; import java.util.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -54,16 +53,6 @@ import org.jivesoftware.smack.rosterstore.RosterStore; public abstract class AbstractXMPPConnection implements XMPPConnection { private static final Logger LOGGER = Logger.getLogger(AbstractXMPPConnection.class.getName()); - /** - * Possible default debugger implementations. The order of enumeration is the one in which we try - * to instantiate these. - */ - private static final String[] DEBUGGERS = new String[] { - "org.jivesoftware.smackx.debugger.EnhancedDebugger", - "org.jivesoftware.smackx.debugger.android.AndroidDebugger", - "org.jivesoftware.smack.debugger.LiteDebugger", - "org.jivesoftware.smack.debugger.ConsoleDebugger" }; - /** * Counter to uniquely identify connections that are created. */ @@ -798,62 +787,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { // If debugging is enabled, we open a window and write out all network traffic. if (config.isDebuggerEnabled()) { if (debugger == null) { - // Detect the debugger class to use. - String className = null; - // Use try block since we may not have permission to get a system - // property (for example, when an applet). - try { - className = System.getProperty("smack.debuggerClass"); - } - catch (Throwable t) { - // Ignore. - } - Class debuggerClass = null; - if (className != null) { - try { - debuggerClass = Class.forName(className); - } - catch (Exception e) { - LOGGER.warning("Unabled to instantiate debugger class " + className); - } - } - if (debuggerClass == null) { - for (String debugger : DEBUGGERS) { - try { - debuggerClass = Class.forName(debugger); - } - catch (ClassNotFoundException cnfe) { - LOGGER.fine("Did not find debugger class '" + debugger + "'"); - } - catch (Exception ex) { - LOGGER.warning("Unabled to instantiate either Smack debugger class"); - } - if (debuggerClass != null) { - // We found a debugger, let's use it - break; - } - } - } - if (debuggerClass != null) { - // Create a new debugger instance. If an exception occurs then disable the - // debugging - // option - try { - Constructor constructor = debuggerClass.getConstructor( - XMPPConnection.class, Writer.class, Reader.class); - debugger = (SmackDebugger) constructor.newInstance(this, writer, reader); - reader = debugger.getReader(); - writer = debugger.getWriter(); - } - catch (Exception e) { - throw new IllegalArgumentException( - "Can't initialize the configured debugger!", e); - } - } else { - LOGGER.severe("Debugging enabled but could not find debugger class"); - } + debugger = SmackConfiguration.createDebugger(this, writer, reader); } - else { + + 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); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java index f2ebbc01f..9664603c5 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java @@ -21,6 +21,8 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.Reader; +import java.io.Writer; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; @@ -35,7 +37,9 @@ import javax.net.ssl.HostnameVerifier; import org.jivesoftware.smack.compression.Java7ZlibInputOutputStream; 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.initializer.SmackInitializer; import org.jivesoftware.smack.parsing.ExceptionThrowingCallback; import org.jivesoftware.smack.parsing.ParsingExceptionCallback; @@ -73,6 +77,8 @@ public final class SmackConfiguration { private final static List compressionHandlers = new ArrayList(2); + 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 @@ -258,10 +264,38 @@ public final class SmackConfiguration { } /** - * Sets smack debugger class + * Sets Smack debugger factory. + * + * @param debuggerFactory new debugger factory implementation to be used by Smack */ - public static void setDebugger(Class debuggerClass) { - System.setProperty("smack.debuggerClass", debuggerClass.getCanonicalName()); + public static void setDebuggerFactory(SmackDebuggerFactory debuggerFactory) { + SmackConfiguration.debuggerFactory = debuggerFactory; + } + + /** + * @return a debugger factory or null + */ + public static SmackDebuggerFactory getDebuggerFactory() { + return debuggerFactory; + } + + /** + * Creates new debugger instance with given arguments as parameters. May + * return null 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 null + */ + 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); + } } /** diff --git a/smack-core/src/main/java/org/jivesoftware/smack/debugger/ReflectionDebuggerFactory.java b/smack-core/src/main/java/org/jivesoftware/smack/debugger/ReflectionDebuggerFactory.java new file mode 100644 index 000000000..4244aa0f3 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/debugger/ReflectionDebuggerFactory.java @@ -0,0 +1,119 @@ +/** + * + * 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.smack.debugger; + +import org.jivesoftware.smack.XMPPConnection; + +import java.io.Reader; +import java.io.Writer; +import java.lang.reflect.Constructor; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class ReflectionDebuggerFactory implements SmackDebuggerFactory { + private static final Logger LOGGER = Logger.getLogger(ReflectionDebuggerFactory.class.getName()); + private static final String DEBUGGER_CLASS_PROPERTY_NAME = "smack.debuggerClass"; + + /** + * Possible default debugger implementations. The order of enumeration is the one in which we try + * to instantiate these. + */ + private static final String[] DEFAULT_DEBUGGERS = new String[] { + "org.jivesoftware.smackx.debugger.EnhancedDebugger", + "org.jivesoftware.smackx.debugger.android.AndroidDebugger", + "org.jivesoftware.smack.debugger.LiteDebugger", + "org.jivesoftware.smack.debugger.ConsoleDebugger" }; + + + /** + * Sets custom debugger class to be created by this factory + * @param debuggerClass class to be used by this factory + */ + public static void setDebuggerClass(Class debuggerClass) { + if (debuggerClass == null) { + System.clearProperty(DEBUGGER_CLASS_PROPERTY_NAME); + } else { + System.setProperty(DEBUGGER_CLASS_PROPERTY_NAME, debuggerClass.getCanonicalName()); + } + } + + /** + * Returns debugger class used by this factory + * @return debugger class that will be used for instantiation by this factory + */ + @SuppressWarnings("unchecked") + public static Class getDebuggerClass() { + String customDebuggerClassName = getCustomDebuggerClassName(); + if (customDebuggerClassName == null) { + return getOneOfDefaultDebuggerClasses(); + } else { + try { + return (Class) Class.forName(customDebuggerClassName); + } catch (Exception e) { + LOGGER.log(Level.WARNING, "Unable to instantiate debugger class " + customDebuggerClassName, e); + } + } + // no suitable debugger class found - give up + return null; + } + + @Override + public SmackDebugger create(XMPPConnection connection, Writer writer, Reader reader) throws IllegalArgumentException { + Class debuggerClass = getDebuggerClass(); + if (debuggerClass != null) { + // Create a new debugger instance using 3arg constructor + try { + Constructor constructor = debuggerClass + .getConstructor(XMPPConnection.class, Writer.class, Reader.class); + return constructor.newInstance(connection, writer, reader); + } catch (Exception e) { + throw new IllegalArgumentException("Can't initialize the configured debugger!", e); + } + } + return null; + } + + private static String getCustomDebuggerClassName() { + try { + // Use try block since we may not have permission to get a system + // property (for example, when an applet). + return System.getProperty(DEBUGGER_CLASS_PROPERTY_NAME); + } catch (Throwable t) { + // Ignore. + return null; + } + } + + @SuppressWarnings("unchecked") + private static Class getOneOfDefaultDebuggerClasses() { + for (String debugger : DEFAULT_DEBUGGERS) { + try { + return (Class) Class.forName(debugger); + } catch (ClassNotFoundException cnfe) { + LOGGER.fine("Did not find debugger class '" + debugger + "'"); + } catch (ClassCastException ex) { + LOGGER.warning("Found debugger class that does not appears to implement SmackDebugger interface"); + } catch (Exception ex) { + LOGGER.warning("Unable to instantiate either Smack debugger class"); + } + } + // did not found any of default debuggers - give up + return null; + } +} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/debugger/SmackDebuggerFactory.java b/smack-core/src/main/java/org/jivesoftware/smack/debugger/SmackDebuggerFactory.java new file mode 100644 index 000000000..cad0dd155 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/debugger/SmackDebuggerFactory.java @@ -0,0 +1,33 @@ +/** + * + * 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.smack.debugger; + +import org.jivesoftware.smack.XMPPConnection; + +import java.io.Reader; +import java.io.Writer; + +public interface SmackDebuggerFactory { + /** + * Initialize the new SmackDebugger instance. + * + * @throws IllegalArgumentException if the SmackDebugger can't be loaded. + */ + SmackDebugger create(XMPPConnection connection, Writer writer, Reader reader) throws IllegalArgumentException; +} diff --git a/smack-debug-slf4j/src/main/java/org/jivesoftware/smackx/debugger/slf4j/SLF4JDebuggerFactory.java b/smack-debug-slf4j/src/main/java/org/jivesoftware/smackx/debugger/slf4j/SLF4JDebuggerFactory.java new file mode 100644 index 000000000..11e9842bd --- /dev/null +++ b/smack-debug-slf4j/src/main/java/org/jivesoftware/smackx/debugger/slf4j/SLF4JDebuggerFactory.java @@ -0,0 +1,35 @@ +/** + * + * 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.XMPPConnection; +import org.jivesoftware.smack.debugger.SmackDebugger; +import org.jivesoftware.smack.debugger.SmackDebuggerFactory; + +import java.io.Reader; +import java.io.Writer; + +/** + * Implementation of SmackDebuggerFactory which always creates instance of SLF4JSmackDebugger + */ +public class SLF4JDebuggerFactory implements SmackDebuggerFactory { + @Override + public SmackDebugger create(XMPPConnection connection, Writer writer, Reader reader) throws IllegalArgumentException { + return new SLF4JSmackDebugger(connection, writer, reader); + } +} diff --git a/smack-debug-slf4j/src/main/java/org/jivesoftware/smackx/debugger/slf4j/SLF4JSmackDebugger.java b/smack-debug-slf4j/src/main/java/org/jivesoftware/smackx/debugger/slf4j/SLF4JSmackDebugger.java index a95b1ce12..0f5fe14c0 100644 --- a/smack-debug-slf4j/src/main/java/org/jivesoftware/smackx/debugger/slf4j/SLF4JSmackDebugger.java +++ b/smack-debug-slf4j/src/main/java/org/jivesoftware/smackx/debugger/slf4j/SLF4JSmackDebugger.java @@ -58,7 +58,7 @@ public class SLF4JSmackDebugger implements SmackDebugger { * Makes Smack use this Debugger */ public static void enable() { - SmackConfiguration.setDebugger(SLF4JSmackDebugger.class); + SmackConfiguration.setDebuggerFactory(new SLF4JDebuggerFactory()); } /**