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
018
019package org.jivesoftware.smack.debugger;
020
021import java.io.Reader;
022import java.io.Writer;
023import java.lang.reflect.Constructor;
024import java.util.logging.Level;
025import java.util.logging.Logger;
026
027import org.jivesoftware.smack.SmackConfiguration;
028import org.jivesoftware.smack.XMPPConnection;
029
030public class ReflectionDebuggerFactory implements SmackDebuggerFactory {
031    private static final Logger LOGGER = Logger.getLogger(ReflectionDebuggerFactory.class.getName());
032    private static final String DEBUGGER_CLASS_PROPERTY_NAME = "smack.debuggerClass";
033
034    /**
035     * Possible default debugger implementations. The order of enumeration is the one in which we try
036     * to instantiate these.
037     */
038    private static final String[] DEFAULT_DEBUGGERS = new String[] {
039            "org.jivesoftware.smackx.debugger.EnhancedDebugger",
040            "org.jivesoftware.smackx.debugger.android.AndroidDebugger",
041            "org.jivesoftware.smack.debugger.ConsoleDebugger",
042            "org.jivesoftware.smack.debugger.LiteDebugger",
043            "org.jivesoftware.smack.debugger.JulDebugger",
044            };
045
046    /**
047     * Sets custom debugger class to be created by this factory.
048     * @param debuggerClass class to be used by this factory
049     */
050    public static void setDebuggerClass(Class<? extends SmackDebugger> debuggerClass) {
051        if (debuggerClass == null) {
052            System.clearProperty(DEBUGGER_CLASS_PROPERTY_NAME);
053        } else {
054            System.setProperty(DEBUGGER_CLASS_PROPERTY_NAME, debuggerClass.getCanonicalName());
055        }
056    }
057
058    /**
059     * Returns debugger class used by this factory.
060     * @return debugger class that will be used for instantiation by this factory
061     */
062    @SuppressWarnings("unchecked")
063    public static Class<SmackDebugger> getDebuggerClass() {
064        String customDebuggerClassName = getCustomDebuggerClassName();
065        if (customDebuggerClassName == null) {
066            return getOneOfDefaultDebuggerClasses();
067        } else {
068            try {
069                return (Class<SmackDebugger>) Class.forName(customDebuggerClassName);
070            } catch (Exception e) {
071                LOGGER.log(Level.WARNING, "Unable to instantiate debugger class " + customDebuggerClassName, e);
072            }
073        }
074        // no suitable debugger class found - give up
075        return null;
076    }
077
078    @Override
079    public SmackDebugger create(XMPPConnection connection, Writer writer, Reader reader) throws IllegalArgumentException {
080        Class<SmackDebugger> debuggerClass = getDebuggerClass();
081        if (debuggerClass != null) {
082            // Create a new debugger instance using 3arg constructor
083            try {
084                Constructor<SmackDebugger> constructor = debuggerClass
085                        .getConstructor(XMPPConnection.class, Writer.class, Reader.class);
086                return constructor.newInstance(connection, writer, reader);
087            } catch (Exception e) {
088                throw new IllegalArgumentException("Can't initialize the configured debugger!", e);
089            }
090        }
091        return null;
092    }
093
094    private static String getCustomDebuggerClassName() {
095        try {
096            // Use try block since we may not have permission to get a system
097            // property (for example, when an applet).
098            return System.getProperty(DEBUGGER_CLASS_PROPERTY_NAME);
099        } catch (Throwable t) {
100            // Ignore.
101            return null;
102        }
103    }
104
105    @SuppressWarnings("unchecked")
106    private static Class<SmackDebugger> getOneOfDefaultDebuggerClasses() {
107        for (String debugger : DEFAULT_DEBUGGERS) {
108            if (SmackConfiguration.isDisabledSmackClass(debugger)) {
109                continue;
110            }
111            try {
112                return (Class<SmackDebugger>) Class.forName(debugger);
113            } catch (ClassNotFoundException cnfe) {
114                LOGGER.fine("Did not find debugger class '" + debugger + "'");
115            } catch (ClassCastException ex) {
116                LOGGER.warning("Found debugger class that does not appears to implement SmackDebugger interface");
117            } catch (Exception ex) {
118                LOGGER.warning("Unable to instantiate either Smack debugger class");
119            }
120        }
121        // did not found any of default debuggers - give up
122        return null;
123    }
124}