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}