1 /** 2 * $RCSfile$ 3 * $Revision$ 4 * $Date$ 5 * 6 * Copyright 2003-2007 Jive Software. 7 * 8 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 package org.jivesoftware.smack; 22 23 import java.io.InputStream; 24 import java.net.URL; 25 import java.util.ArrayList; 26 import java.util.Collection; 27 import java.util.Enumeration; 28 import java.util.List; 29 import java.util.Vector; 30 31 import org.xmlpull.v1.XmlPullParserFactory; 32 import org.xmlpull.v1.XmlPullParser; 33 34 /** 35 * Represents the configuration of Smack. The configuration is used for: 36 * <ul> 37 * <li> Initializing classes by loading them at start-up. 38 * <li> Getting the current Smack version. 39 * <li> Getting and setting global library behavior, such as the period of time 40 * to wait for replies to packets from the server. Note: setting these values 41 * via the API will override settings in the configuration file. 42 * </ul> 43 * 44 * Configuration settings are stored in META-INF/smack-config.xml (typically inside the 45 * smack.jar file). 46 * 47 * @author Gaston Dombiak 48 */ 49 public final class SmackConfiguration { 50 51 private static final String SMACK_VERSION = "3.2.2"; 52 53 private static int packetReplyTimeout = 5000; 54 private static Vector<String> defaultMechs = new Vector<String>(); 55 56 private static boolean localSocks5ProxyEnabled = true; 57 private static int localSocks5ProxyPort = 7777; 58 private static int packetCollectorSize = 5000; 59 60 /** 61 * defaultPingInterval (in seconds) 62 */ 63 private static int defaultPingInterval = 1800; // 30 min (30*60) 64 65 /** 66 * This automatically enables EntityCaps for new connections if it is set to true 67 */ 68 private static boolean autoEnableEntityCaps = false; 69 70 private SmackConfiguration() { 71 } 72 73 /** 74 * Loads the configuration from the smack-config.xml file.<p> 75 * 76 * So far this means that: 77 * 1) a set of classes will be loaded in order to execute their static init block 78 * 2) retrieve and set the current Smack release 79 */ 80 static { 81 try { 82 // Get an array of class loaders to try loading the providers files from. 83 ClassLoader[] classLoaders = getClassLoaders(); 84 for (ClassLoader classLoader : classLoaders) { 85 Enumeration<URL> configEnum = classLoader.getResources("META-INF/smack-config.xml"); 86 while (configEnum.hasMoreElements()) { 87 URL url = configEnum.nextElement(); 88 InputStream systemStream = null; 89 try { 90 systemStream = url.openStream(); 91 XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); 92 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); 93 parser.setInput(systemStream, "UTF-8"); 94 int eventType = parser.getEventType(); 95 do { 96 if (eventType == XmlPullParser.START_TAG) { 97 if (parser.getName().equals("className")) { 98 // Attempt to load the class so that the class can get initialized 99 parseClassToLoad(parser); 100 } 101 else if (parser.getName().equals("packetReplyTimeout")) { 102 packetReplyTimeout = parseIntProperty(parser, packetReplyTimeout); 103 } 104 else if (parser.getName().equals("mechName")) { 105 defaultMechs.add(parser.nextText()); 106 } 107 else if (parser.getName().equals("localSocks5ProxyEnabled")) { 108 localSocks5ProxyEnabled = Boolean.parseBoolean(parser.nextText()); 109 } 110 else if (parser.getName().equals("localSocks5ProxyPort")) { 111 localSocks5ProxyPort = parseIntProperty(parser, localSocks5ProxyPort); 112 } 113 else if (parser.getName().equals("packetCollectorSize")) { 114 packetCollectorSize = parseIntProperty(parser, packetCollectorSize); 115 } 116 else if (parser.getName().equals("defaultPingInterval")) { 117 defaultPingInterval = parseIntProperty(parser, defaultPingInterval); 118 } 119 else if (parser.getName().equals("autoEnableEntityCaps")) { 120 autoEnableEntityCaps = Boolean.parseBoolean(parser.nextText()); 121 } 122 } 123 eventType = parser.next(); 124 } 125 while (eventType != XmlPullParser.END_DOCUMENT); 126 } 127 catch (Exception e) { 128 e.printStackTrace(); 129 } 130 finally { 131 try { 132 systemStream.close(); 133 } 134 catch (Exception e) { 135 // Ignore. 136 } 137 } 138 } 139 } 140 } 141 catch (Exception e) { 142 e.printStackTrace(); 143 } 144 } 145 146 /** 147 * Returns the Smack version information, eg "1.3.0". 148 * 149 * @return the Smack version information. 150 */ 151 public static String getVersion() { 152 return SMACK_VERSION; 153 } 154 155 /** 156 * Returns the number of milliseconds to wait for a response from 157 * the server. The default value is 5000 ms. 158 * 159 * @return the milliseconds to wait for a response from the server 160 */ 161 public static int getPacketReplyTimeout() { 162 // The timeout value must be greater than 0 otherwise we will answer the default value 163 if (packetReplyTimeout <= 0) { 164 packetReplyTimeout = 5000; 165 } 166 return packetReplyTimeout; 167 } 168 169 /** 170 * Sets the number of milliseconds to wait for a response from 171 * the server. 172 * 173 * @param timeout the milliseconds to wait for a response from the server 174 */ 175 public static void setPacketReplyTimeout(int timeout) { 176 if (timeout <= 0) { 177 throw new IllegalArgumentException(); 178 } 179 packetReplyTimeout = timeout; 180 } 181 182 /** 183 * Gets the default max size of a packet collector before it will delete 184 * the older packets. 185 * 186 * @return The number of packets to queue before deleting older packets. 187 */ 188 public static int getPacketCollectorSize() { 189 return packetCollectorSize; 190 } 191 192 /** 193 * Sets the default max size of a packet collector before it will delete 194 * the older packets. 195 * 196 * @param The number of packets to queue before deleting older packets. 197 */ 198 public static void setPacketCollectorSize(int collectorSize) { 199 packetCollectorSize = collectorSize; 200 } 201 202 /** 203 * Add a SASL mechanism to the list to be used. 204 * 205 * @param mech the SASL mechanism to be added 206 */ 207 public static void addSaslMech(String mech) { 208 if(! defaultMechs.contains(mech) ) { 209 defaultMechs.add(mech); 210 } 211 } 212 213 /** 214 * Add a Collection of SASL mechanisms to the list to be used. 215 * 216 * @param mechs the Collection of SASL mechanisms to be added 217 */ 218 public static void addSaslMechs(Collection<String> mechs) { 219 for(String mech : mechs) { 220 addSaslMech(mech); 221 } 222 } 223 224 /** 225 * Remove a SASL mechanism from the list to be used. 226 * 227 * @param mech the SASL mechanism to be removed 228 */ 229 public static void removeSaslMech(String mech) { 230 if( defaultMechs.contains(mech) ) { 231 defaultMechs.remove(mech); 232 } 233 } 234 235 /** 236 * Remove a Collection of SASL mechanisms to the list to be used. 237 * 238 * @param mechs the Collection of SASL mechanisms to be removed 239 */ 240 public static void removeSaslMechs(Collection<String> mechs) { 241 for(String mech : mechs) { 242 removeSaslMech(mech); 243 } 244 } 245 246 /** 247 * Returns the list of SASL mechanisms to be used. If a SASL mechanism is 248 * listed here it does not guarantee it will be used. The server may not 249 * support it, or it may not be implemented. 250 * 251 * @return the list of SASL mechanisms to be used. 252 */ 253 public static List<String> getSaslMechs() { 254 return defaultMechs; 255 } 256 257 /** 258 * Returns true if the local Socks5 proxy should be started. Default is true. 259 * 260 * @return if the local Socks5 proxy should be started 261 */ 262 public static boolean isLocalSocks5ProxyEnabled() { 263 return localSocks5ProxyEnabled; 264 } 265 266 /** 267 * Sets if the local Socks5 proxy should be started. Default is true. 268 * 269 * @param localSocks5ProxyEnabled if the local Socks5 proxy should be started 270 */ 271 public static void setLocalSocks5ProxyEnabled(boolean localSocks5ProxyEnabled) { 272 SmackConfiguration.localSocks5ProxyEnabled = localSocks5ProxyEnabled; 273 } 274 275 /** 276 * Return the port of the local Socks5 proxy. Default is 7777. 277 * 278 * @return the port of the local Socks5 proxy 279 */ 280 public static int getLocalSocks5ProxyPort() { 281 return localSocks5ProxyPort; 282 } 283 284 /** 285 * Sets the port of the local Socks5 proxy. Default is 7777. If you set the port to a negative 286 * value Smack tries the absolute value and all following until it finds an open port. 287 * 288 * @param localSocks5ProxyPort the port of the local Socks5 proxy to set 289 */ 290 public static void setLocalSocks5ProxyPort(int localSocks5ProxyPort) { 291 SmackConfiguration.localSocks5ProxyPort = localSocks5ProxyPort; 292 } 293 294 /** 295 * Returns the default ping interval (seconds) 296 * 297 * @return 298 */ 299 public static int getDefaultPingInterval() { 300 return defaultPingInterval; 301 } 302 303 /** 304 * Sets the default ping interval (seconds). Set it to '-1' to disable the periodic ping 305 * 306 * @param defaultPingInterval 307 */ 308 public static void setDefaultPingInterval(int defaultPingInterval) { 309 SmackConfiguration.defaultPingInterval = defaultPingInterval; 310 } 311 312 /** 313 * Check if Entity Caps are enabled as default for every new connection 314 * @return 315 */ 316 public static boolean autoEnableEntityCaps() { 317 return autoEnableEntityCaps; 318 } 319 320 /** 321 * Set if Entity Caps are enabled or disabled for every new connection 322 * 323 * @param true if Entity Caps should be auto enabled, false if not 324 */ 325 public static void setAutoEnableEntityCaps(boolean b) { 326 autoEnableEntityCaps = b; 327 } 328 329 private static void parseClassToLoad(XmlPullParser parser) throws Exception { 330 String className = parser.nextText(); 331 // Attempt to load the class so that the class can get initialized 332 try { 333 Class.forName(className); 334 } 335 catch (ClassNotFoundException cnfe) { 336 System.err.println("Error! A startup class specified in smack-config.xml could " + 337 "not be loaded: " + className); 338 } 339 } 340 341 private static int parseIntProperty(XmlPullParser parser, int defaultValue) 342 throws Exception 343 { 344 try { 345 return Integer.parseInt(parser.nextText()); 346 } 347 catch (NumberFormatException nfe) { 348 nfe.printStackTrace(); 349 return defaultValue; 350 } 351 } 352 353 /** 354 * Returns an array of class loaders to load resources from. 355 * 356 * @return an array of ClassLoader instances. 357 */ 358 private static ClassLoader[] getClassLoaders() { 359 ClassLoader[] classLoaders = new ClassLoader[2]; 360 classLoaders[0] = SmackConfiguration.class.getClassLoader(); 361 classLoaders[1] = Thread.currentThread().getContextClassLoader(); 362 // Clean up possible null values. Note that #getClassLoader may return a null value. 363 List<ClassLoader> loaders = new ArrayList<ClassLoader>(); 364 for (ClassLoader classLoader : classLoaders) { 365 if (classLoader != null) { 366 loaders.add(classLoader); 367 } 368 } 369 return loaders.toArray(new ClassLoader[loaders.size()]); 370 } 371 } 372