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