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.util.Collection; 025import java.util.List; 026import java.util.logging.Level; 027import java.util.logging.Logger; 028 029import org.jivesoftware.smack.compression.Java7ZlibInputOutputStream; 030import org.jivesoftware.smack.initializer.SmackInitializer; 031import org.jivesoftware.smack.packet.Bind; 032import org.jivesoftware.smack.packet.Message.Body; 033import org.jivesoftware.smack.provider.BindIQProvider; 034import org.jivesoftware.smack.provider.BodyElementProvider; 035import org.jivesoftware.smack.provider.ProviderManager; 036import org.jivesoftware.smack.sasl.core.SASLAnonymous; 037import org.jivesoftware.smack.sasl.core.SASLXOauth2Mechanism; 038import org.jivesoftware.smack.sasl.core.SCRAMSHA1Mechanism; 039import org.jivesoftware.smack.sasl.core.ScramSha1PlusMechanism; 040import org.jivesoftware.smack.util.FileUtils; 041import org.jivesoftware.smack.util.StringUtils; 042 043import org.xmlpull.v1.XmlPullParser; 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 = "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.getStreamForClasspathFile("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 = "unknown"; 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 084 InputStream configFileStream; 085 try { 086 configFileStream = FileUtils.getStreamForClasspathFile(DEFAULT_CONFIG_FILE, null); 087 } 088 catch (Exception e) { 089 throw new IllegalStateException("Could not load Smack configuration file", e); 090 } 091 092 try { 093 processConfigFile(configFileStream, null); 094 } 095 catch (Exception e) { 096 throw new IllegalStateException("Could not parse Smack configuration file", e); 097 } 098 099 // Add the Java7 compression handler first, since it's preferred 100 SmackConfiguration.compressionHandlers.add(new Java7ZlibInputOutputStream()); 101 102 // Use try block since we may not have permission to get a system 103 // property (for example, when an applet). 104 try { 105 // Only overwrite DEBUG if it is set via the 'smack.debugEnabled' property. To prevent DEBUG_ENABLED 106 // = true, which could be set e.g. via a static block from user code, from being overwritten by the property not set 107 if (Boolean.getBoolean("smack.debugEnabled")) { 108 SmackConfiguration.DEBUG = true; 109 } 110 } 111 catch (Exception e) { 112 LOGGER.log(Level.FINE, "Could not handle debugEnable property on Smack initialization", e); 113 } 114 115 SASLAuthentication.registerSASLMechanism(new SCRAMSHA1Mechanism()); 116 SASLAuthentication.registerSASLMechanism(new ScramSha1PlusMechanism()); 117 SASLAuthentication.registerSASLMechanism(new SASLXOauth2Mechanism()); 118 SASLAuthentication.registerSASLMechanism(new SASLAnonymous()); 119 120 ProviderManager.addIQProvider(Bind.ELEMENT, Bind.NAMESPACE, new BindIQProvider()); 121 ProviderManager.addExtensionProvider(Body.ELEMENT, Body.NAMESPACE, new BodyElementProvider()); 122 123 SmackConfiguration.smackInitialized = true; 124 } 125 126 public static void processConfigFile(InputStream cfgFileStream, 127 Collection<Exception> exceptions) throws Exception { 128 processConfigFile(cfgFileStream, exceptions, SmackInitialization.class.getClassLoader()); 129 } 130 131 public static void processConfigFile(InputStream cfgFileStream, 132 Collection<Exception> exceptions, ClassLoader classLoader) throws Exception { 133 XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); 134 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); 135 parser.setInput(cfgFileStream, "UTF-8"); 136 int eventType = parser.getEventType(); 137 do { 138 if (eventType == XmlPullParser.START_TAG) { 139 if (parser.getName().equals("startupClasses")) { 140 parseClassesToLoad(parser, false, exceptions, classLoader); 141 } 142 else if (parser.getName().equals("optionalStartupClasses")) { 143 parseClassesToLoad(parser, true, exceptions, classLoader); 144 } 145 } 146 eventType = parser.next(); 147 } 148 while (eventType != XmlPullParser.END_DOCUMENT); 149 try { 150 cfgFileStream.close(); 151 } 152 catch (IOException e) { 153 LOGGER.log(Level.SEVERE, "Error while closing config file input stream", e); 154 } 155 } 156 157 private static void parseClassesToLoad(XmlPullParser parser, boolean optional, 158 Collection<Exception> exceptions, ClassLoader classLoader) 159 throws Exception { 160 final String startName = parser.getName(); 161 int eventType; 162 String name; 163 outerloop: do { 164 eventType = parser.next(); 165 name = parser.getName(); 166 if (eventType == XmlPullParser.START_TAG && "className".equals(name)) { 167 String classToLoad = parser.nextText(); 168 if (SmackConfiguration.isDisabledSmackClass(classToLoad)) { 169 continue outerloop; 170 } 171 172 try { 173 loadSmackClass(classToLoad, optional, classLoader); 174 } catch (Exception e) { 175 // Don't throw the exception if an exceptions collection is given, instead 176 // record it there. This is used for unit testing purposes. 177 if (exceptions != null) { 178 exceptions.add(e); 179 } else { 180 throw e; 181 } 182 } 183 } 184 } 185 while (!(eventType == XmlPullParser.END_TAG && startName.equals(name))); 186 } 187 188 private static void loadSmackClass(String className, boolean optional, ClassLoader classLoader) throws Exception { 189 Class<?> initClass; 190 try { 191 // Attempt to load and initialize the class so that all static initializer blocks of 192 // class are executed 193 initClass = Class.forName(className, true, classLoader); 194 } 195 catch (ClassNotFoundException cnfe) { 196 Level logLevel; 197 if (optional) { 198 logLevel = Level.FINE; 199 } 200 else { 201 logLevel = Level.WARNING; 202 } 203 LOGGER.log(logLevel, "A startup class '" + className + "' could not be loaded."); 204 if (!optional) { 205 throw cnfe; 206 } else { 207 return; 208 } 209 } 210 if (SmackInitializer.class.isAssignableFrom(initClass)) { 211 SmackInitializer initializer = (SmackInitializer) initClass.getConstructor().newInstance(); 212 List<Exception> exceptions = initializer.initialize(); 213 if (exceptions == null || exceptions.size() == 0) { 214 LOGGER.log(Level.FINE, "Loaded SmackInitializer " + className); 215 } else { 216 for (Exception e : exceptions) { 217 LOGGER.log(Level.SEVERE, "Exception in loadSmackClass", e); 218 } 219 } 220 } else { 221 LOGGER.log(Level.FINE, "Loaded " + className); 222 } 223 } 224}