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