001/** 002 * 003 * Copyright 2003-2007 Jive Software, 2014-2016 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 */ 017 018package org.jivesoftware.smack; 019 020import java.io.BufferedReader; 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.InputStreamReader; 024import java.lang.reflect.Field; 025import java.util.Collection; 026import java.util.List; 027import java.util.logging.Level; 028import java.util.logging.Logger; 029 030import org.jivesoftware.smack.compression.Java7ZlibInputOutputStream; 031import org.jivesoftware.smack.initializer.SmackInitializer; 032import org.jivesoftware.smack.packet.Bind; 033import org.jivesoftware.smack.provider.BindIQProvider; 034import org.jivesoftware.smack.provider.ProviderManager; 035import org.jivesoftware.smack.sasl.core.SASLAnonymous; 036import org.jivesoftware.smack.sasl.core.SASLXOauth2Mechanism; 037import org.jivesoftware.smack.sasl.core.SCRAMSHA1Mechanism; 038import org.jivesoftware.smack.sasl.core.ScramSha1PlusMechanism; 039import org.jivesoftware.smack.util.FileUtils; 040import org.jivesoftware.smack.util.StringUtils; 041 042import org.xmlpull.v1.XmlPullParser; 043import org.xmlpull.v1.XmlPullParserFactory; 044 045 046public final class SmackInitialization { 047 static final String SMACK_VERSION; 048 049 private static final String DEFAULT_CONFIG_FILE = "classpath:org.jivesoftware.smack/smack-config.xml"; 050 051 private static final Logger LOGGER = Logger.getLogger(SmackInitialization.class.getName()); 052 053 /** 054 * Loads the configuration from the smack-config.xml and system properties file. 055 * <p> 056 * So far this means that: 057 * 1) a set of classes will be loaded in order to execute their static init block 058 * 2) retrieve and set the current Smack release 059 * 3) set DEBUG 060 */ 061 static { 062 String smackVersion; 063 try { 064 BufferedReader reader = new BufferedReader(new InputStreamReader(FileUtils.getStreamForUrl("classpath:org.jivesoftware.smack/version", null), StringUtils.UTF8)); 065 smackVersion = reader.readLine(); 066 try { 067 reader.close(); 068 } catch (IOException e) { 069 LOGGER.log(Level.WARNING, "IOException closing stream", e); 070 } 071 } catch (Exception e) { 072 LOGGER.log(Level.SEVERE, "Could not determine Smack version", e); 073 smackVersion = "unknown"; 074 } 075 SMACK_VERSION = smackVersion; 076 077 String disabledClasses = System.getProperty("smack.disabledClasses"); 078 if (disabledClasses != null) { 079 String[] splitDisabledClasses = disabledClasses.split(","); 080 for (String s : splitDisabledClasses) SmackConfiguration.disabledSmackClasses.add(s); 081 } 082 try { 083 FileUtils.addLines("classpath:org.jivesoftware.smack/disabledClasses", SmackConfiguration.disabledSmackClasses); 084 } 085 catch (Exception e) { 086 throw new IllegalStateException(e); 087 } 088 089 try { 090 Class<?> c = Class.forName("org.jivesoftware.smack.CustomSmackConfiguration"); 091 Field f = c.getField("DISABLED_SMACK_CLASSES"); 092 String[] sa = (String[]) f.get(null); 093 if (sa != null) { 094 LOGGER.warning("Using CustomSmackConfig is deprecated and will be removed in a future release"); 095 for (String s : sa) 096 SmackConfiguration.disabledSmackClasses.add(s); 097 } 098 } 099 catch (ClassNotFoundException e1) { 100 } 101 catch (NoSuchFieldException e) { 102 } 103 catch (SecurityException e) { 104 } 105 catch (IllegalArgumentException e) { 106 } 107 catch (IllegalAccessException e) { 108 } 109 110 InputStream configFileStream; 111 try { 112 configFileStream = FileUtils.getStreamForUrl(DEFAULT_CONFIG_FILE, null); 113 } 114 catch (Exception e) { 115 throw new IllegalStateException(e); 116 } 117 118 try { 119 processConfigFile(configFileStream, null); 120 } 121 catch (Exception e) { 122 throw new IllegalStateException(e); 123 } 124 125 // Add the Java7 compression handler first, since it's preferred 126 SmackConfiguration.compressionHandlers.add(new Java7ZlibInputOutputStream()); 127 128 // Use try block since we may not have permission to get a system 129 // property (for example, when an applet). 130 try { 131 // Only overwrite DEBUG if it is set via the 'smack.debugEnabled' property. To prevent DEBUG_ENABLED 132 // = true, which could be set e.g. via a static block from user code, from being overwritten by the property not set 133 if (Boolean.getBoolean("smack.debugEnabled")) { 134 SmackConfiguration.DEBUG = true; 135 } 136 } 137 catch (Exception e) { 138 // Ignore. 139 } 140 141 SASLAuthentication.registerSASLMechanism(new SCRAMSHA1Mechanism()); 142 SASLAuthentication.registerSASLMechanism(new ScramSha1PlusMechanism()); 143 SASLAuthentication.registerSASLMechanism(new SASLXOauth2Mechanism()); 144 SASLAuthentication.registerSASLMechanism(new SASLAnonymous()); 145 146 ProviderManager.addIQProvider(Bind.ELEMENT, Bind.NAMESPACE, new BindIQProvider()); 147 148 SmackConfiguration.smackInitialized = true; 149 } 150 151 public static void processConfigFile(InputStream cfgFileStream, 152 Collection<Exception> exceptions) throws Exception { 153 processConfigFile(cfgFileStream, exceptions, SmackInitialization.class.getClassLoader()); 154 } 155 156 public static void processConfigFile(InputStream cfgFileStream, 157 Collection<Exception> exceptions, ClassLoader classLoader) throws Exception { 158 XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); 159 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); 160 parser.setInput(cfgFileStream, "UTF-8"); 161 int eventType = parser.getEventType(); 162 do { 163 if (eventType == XmlPullParser.START_TAG) { 164 if (parser.getName().equals("startupClasses")) { 165 parseClassesToLoad(parser, false, exceptions, classLoader); 166 } 167 else if (parser.getName().equals("optionalStartupClasses")) { 168 parseClassesToLoad(parser, true, exceptions, classLoader); 169 } 170 } 171 eventType = parser.next(); 172 } 173 while (eventType != XmlPullParser.END_DOCUMENT); 174 try { 175 cfgFileStream.close(); 176 } 177 catch (IOException e) { 178 LOGGER.log(Level.SEVERE, "Error while closing config file input stream", e); 179 } 180 } 181 182 private static void parseClassesToLoad(XmlPullParser parser, boolean optional, 183 Collection<Exception> exceptions, ClassLoader classLoader) 184 throws Exception { 185 final String startName = parser.getName(); 186 int eventType; 187 String name; 188 outerloop: do { 189 eventType = parser.next(); 190 name = parser.getName(); 191 if (eventType == XmlPullParser.START_TAG && "className".equals(name)) { 192 String classToLoad = parser.nextText(); 193 if (SmackConfiguration.isDisabledSmackClass(classToLoad)) { 194 continue outerloop; 195 } 196 197 try { 198 loadSmackClass(classToLoad, optional, classLoader); 199 } catch (Exception e) { 200 // Don't throw the exception if an exceptions collection is given, instead 201 // record it there. This is used for unit testing purposes. 202 if (exceptions != null) { 203 exceptions.add(e); 204 } else { 205 throw e; 206 } 207 } 208 } 209 } 210 while (!(eventType == XmlPullParser.END_TAG && startName.equals(name))); 211 } 212 213 private static void loadSmackClass(String className, boolean optional, ClassLoader classLoader) throws Exception { 214 Class<?> initClass; 215 try { 216 // Attempt to load and initialize the class so that all static initializer blocks of 217 // class are executed 218 initClass = Class.forName(className, true, classLoader); 219 } 220 catch (ClassNotFoundException cnfe) { 221 Level logLevel; 222 if (optional) { 223 logLevel = Level.FINE; 224 } 225 else { 226 logLevel = Level.WARNING; 227 } 228 LOGGER.log(logLevel, "A startup class '" + className + "' could not be loaded."); 229 if (!optional) { 230 throw cnfe; 231 } else { 232 return; 233 } 234 } 235 if (SmackInitializer.class.isAssignableFrom(initClass)) { 236 SmackInitializer initializer = (SmackInitializer) initClass.getConstructor().newInstance(); 237 List<Exception> exceptions = initializer.initialize(); 238 if (exceptions == null || exceptions.size() == 0) { 239 LOGGER.log(Level.FINE, "Loaded SmackInitializer " + className); 240 } else { 241 for (Exception e : exceptions) { 242 LOGGER.log(Level.SEVERE, "Exception in loadSmackClass", e); 243 } 244 } 245 } else { 246 LOGGER.log(Level.FINE, "Loaded " + className); 247 } 248 } 249}