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