1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-06-12 22:54:50 +02:00
Smack/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java
Vyacheslav Blinov 342ab65138 SmackDebugger initialization moved to dedicated factory.
You can set your custom debugger class as before, by using clear api
method ReflectionDebuggerFactory.setDebuggerClass, or you can set custom
debugger factory using SmackConfiguration.setDebuggerFactory if it's not
enough flexible for your needs
2014-08-18 12:03:54 +02:00

485 lines
17 KiB
Java

/**
*
* Copyright 2003-2007 Jive Software.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.HostnameVerifier;
import org.jivesoftware.smack.compression.Java7ZlibInputOutputStream;
import org.jivesoftware.smack.compression.XMPPInputOutputStream;
import org.jivesoftware.smack.debugger.ReflectionDebuggerFactory;
import org.jivesoftware.smack.debugger.SmackDebugger;
import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
import org.jivesoftware.smack.initializer.SmackInitializer;
import org.jivesoftware.smack.parsing.ExceptionThrowingCallback;
import org.jivesoftware.smack.parsing.ParsingExceptionCallback;
import org.jivesoftware.smack.util.FileUtils;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
/**
* Represents the configuration of Smack. The configuration is used for:
* <ul>
* <li> Initializing classes by loading them at start-up.
* <li> Getting the current Smack version.
* <li> Getting and setting global library behavior, such as the period of time
* to wait for replies to packets from the server. Note: setting these values
* via the API will override settings in the configuration file.
* </ul>
*
* Configuration settings are stored in org.jivesoftware.smack/smack-config.xml.
*
* @author Gaston Dombiak
*/
public final class SmackConfiguration {
private static final String SMACK_VERSION;
private static final String DEFAULT_CONFIG_FILE = "classpath:org.jivesoftware.smack/smack-config.xml";
private static final Logger LOGGER = Logger.getLogger(SmackConfiguration.class.getName());
private static int defaultPacketReplyTimeout = 5000;
private static int packetCollectorSize = 5000;
private static List<String> defaultMechs = new ArrayList<String>();
private static Set<String> disabledSmackClasses = new HashSet<String>();
private final static List<XMPPInputOutputStream> compressionHandlers = new ArrayList<XMPPInputOutputStream>(2);
private static SmackDebuggerFactory debuggerFactory = new ReflectionDebuggerFactory();
/**
* Value that indicates whether debugging is enabled. When enabled, a debug
* window will apear for each new connection that will contain the following
* information:<ul>
* <li> Client Traffic -- raw XML traffic generated by Smack and sent to the server.
* <li> Server Traffic -- raw XML traffic sent by the server to the client.
* <li> Interpreted Packets -- shows XML packets from the server as parsed by Smack.
* </ul>
* <p/>
* Debugging can be enabled by setting this field to true, or by setting the Java system
* property <tt>smack.debugEnabled</tt> to true. The system property can be set on the
* command line such as "java SomeApp -Dsmack.debugEnabled=true".
*/
public static boolean DEBUG_ENABLED = false;
/**
* Loads the configuration from the smack-config.xml and system properties file.
* <p>
* So far this means that:
* 1) a set of classes will be loaded in order to execute their static init block
* 2) retrieve and set the current Smack release
* 3) set DEBUG_ENABLED
*/
static {
String smackVersion;
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(FileUtils.getStreamForUrl("classpath:org.jivesoftware.smack/version", null)));
smackVersion = reader.readLine();
try {
reader.close();
} catch (IOException e) {
LOGGER.log(Level.WARNING, "IOException closing stream", e);
}
} catch(Exception e) {
LOGGER.log(Level.SEVERE, "Could not determine Smack version", e);
smackVersion = "unkown";
}
SMACK_VERSION = smackVersion;
String disabledClasses = System.getProperty("smack.disabledClasses");
if (disabledClasses != null) {
String[] splitDisabledClasses = disabledClasses.split(",");
for (String s : splitDisabledClasses) disabledSmackClasses.add(s);
}
try {
FileUtils.addLines("classpath:org.jivesoftware.smack/disabledClasses", disabledSmackClasses);
}
catch (Exception e) {
throw new IllegalStateException(e);
}
try {
Class<?> c = Class.forName("org.jivesoftware.smack.CustomSmackConfiguration");
Field f = c.getField("DISABLED_SMACK_CLASSES");
String[] sa = (String[]) f.get(null);
if (sa != null)
for (String s : sa)
disabledSmackClasses.add(s);
}
catch (ClassNotFoundException e1) {
}
catch (NoSuchFieldException e) {
}
catch (SecurityException e) {
}
catch (IllegalArgumentException e) {
}
catch (IllegalAccessException e) {
}
InputStream configFileStream;
try {
configFileStream = FileUtils.getStreamForUrl(DEFAULT_CONFIG_FILE, null);
}
catch (Exception e) {
throw new IllegalStateException(e);
}
try {
processConfigFile(configFileStream, null);
}
catch (Exception e) {
throw new IllegalStateException(e);
}
// Add the Java7 compression handler first, since it's preferred
compressionHandlers.add(new Java7ZlibInputOutputStream());
// Use try block since we may not have permission to get a system
// property (for example, when an applet).
try {
DEBUG_ENABLED = Boolean.getBoolean("smack.debugEnabled");
}
catch (Exception e) {
// Ignore.
}
}
/**
* The default parsing exception callback is {@link ExceptionThrowingCallback} which will
* throw an exception and therefore disconnect the active connection.
*/
private static ParsingExceptionCallback defaultCallback = new ExceptionThrowingCallback();
private static HostnameVerifier defaultHostnameVerififer;
/**
* Returns the Smack version information, eg "1.3.0".
*
* @return the Smack version information.
*/
public static String getVersion() {
return SMACK_VERSION;
}
/**
* Returns the number of milliseconds to wait for a response from
* the server. The default value is 5000 ms.
*
* @return the milliseconds to wait for a response from the server
*/
public static int getDefaultPacketReplyTimeout() {
// The timeout value must be greater than 0 otherwise we will answer the default value
if (defaultPacketReplyTimeout <= 0) {
defaultPacketReplyTimeout = 5000;
}
return defaultPacketReplyTimeout;
}
/**
* Sets the number of milliseconds to wait for a response from
* the server.
*
* @param timeout the milliseconds to wait for a response from the server
*/
public static void setDefaultPacketReplyTimeout(int timeout) {
if (timeout <= 0) {
throw new IllegalArgumentException();
}
defaultPacketReplyTimeout = timeout;
}
/**
* Gets the default max size of a packet collector before it will delete
* the older packets.
*
* @return The number of packets to queue before deleting older packets.
*/
public static int getPacketCollectorSize() {
return packetCollectorSize;
}
/**
* Sets the default max size of a packet collector before it will delete
* the older packets.
*
* @param collectorSize the number of packets to queue before deleting older packets.
*/
public static void setPacketCollectorSize(int collectorSize) {
packetCollectorSize = collectorSize;
}
/**
* Add a SASL mechanism to the list to be used.
*
* @param mech the SASL mechanism to be added
*/
public static void addSaslMech(String mech) {
if(! defaultMechs.contains(mech) ) {
defaultMechs.add(mech);
}
}
/**
* Add a Collection of SASL mechanisms to the list to be used.
*
* @param mechs the Collection of SASL mechanisms to be added
*/
public static void addSaslMechs(Collection<String> mechs) {
for(String mech : mechs) {
addSaslMech(mech);
}
}
/**
* Sets Smack debugger factory.
*
* @param debuggerFactory new debugger factory implementation to be used by Smack
*/
public static void setDebuggerFactory(SmackDebuggerFactory debuggerFactory) {
SmackConfiguration.debuggerFactory = debuggerFactory;
}
/**
* @return a debugger factory or <code>null</code>
*/
public static SmackDebuggerFactory getDebuggerFactory() {
return debuggerFactory;
}
/**
* Creates new debugger instance with given arguments as parameters. May
* return <code>null</code> if no DebuggerFactory is set or if the factory
* did not produce a debugger.
*
* @param connection
* @param writer
* @param reader
* @return a new debugger or <code>null</code>
*/
public static SmackDebugger createDebugger(XMPPConnection connection, Writer writer, Reader reader) {
SmackDebuggerFactory factory = getDebuggerFactory();
if (factory == null) {
return null;
} else {
return factory.create(connection, writer, reader);
}
}
/**
* Remove a SASL mechanism from the list to be used.
*
* @param mech the SASL mechanism to be removed
*/
public static void removeSaslMech(String mech) {
defaultMechs.remove(mech);
}
/**
* Remove a Collection of SASL mechanisms to the list to be used.
*
* @param mechs the Collection of SASL mechanisms to be removed
*/
public static void removeSaslMechs(Collection<String> mechs) {
defaultMechs.removeAll(mechs);
}
/**
* Returns the list of SASL mechanisms to be used. If a SASL mechanism is
* listed here it does not guarantee it will be used. The server may not
* support it, or it may not be implemented.
*
* @return the list of SASL mechanisms to be used.
*/
public static List<String> getSaslMechs() {
return Collections.unmodifiableList(defaultMechs);
}
/**
* Set the default parsing exception callback for all newly created connections
*
* @param callback
* @see ParsingExceptionCallback
*/
public static void setDefaultParsingExceptionCallback(ParsingExceptionCallback callback) {
defaultCallback = callback;
}
/**
* Returns the default parsing exception callback
*
* @return the default parsing exception callback
* @see ParsingExceptionCallback
*/
public static ParsingExceptionCallback getDefaultParsingExceptionCallback() {
return defaultCallback;
}
public static void addCompressionHandler(XMPPInputOutputStream xmppInputOutputStream) {
compressionHandlers.add(xmppInputOutputStream);
}
public static List<XMPPInputOutputStream> getCompresionHandlers() {
List<XMPPInputOutputStream> res = new ArrayList<XMPPInputOutputStream>(compressionHandlers.size());
for (XMPPInputOutputStream ios : compressionHandlers) {
if (ios.isSupported()) {
res.add(ios);
}
}
return res;
}
/**
* Set the default HostnameVerifier that will be used by XMPP connections to verify the hostname
* of a TLS certificate. XMPP connections are able to overwrite this settings by supplying a
* HostnameVerifier in their ConnecitonConfiguration with
* {@link ConnectionConfiguration#setHostnameVerifier(HostnameVerifier)}.
*/
public static void setDefaultHostnameVerifier(HostnameVerifier verifier) {
defaultHostnameVerififer = verifier;
}
/**
* Get the default HostnameVerifier
*
* @return the default HostnameVerifier or <code>null</code> if none was set
*/
static HostnameVerifier getDefaultHostnameVerifier() {
return defaultHostnameVerififer;
}
public static void processConfigFile(InputStream cfgFileStream,
Collection<Exception> exceptions) throws Exception {
processConfigFile(cfgFileStream, exceptions, SmackConfiguration.class.getClassLoader());
}
public static void processConfigFile(InputStream cfgFileStream,
Collection<Exception> exceptions, ClassLoader classLoader) throws Exception {
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(cfgFileStream, "UTF-8");
int eventType = parser.getEventType();
do {
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals("startupClasses")) {
parseClassesToLoad(parser, false, exceptions, classLoader);
}
else if (parser.getName().equals("optionalStartupClasses")) {
parseClassesToLoad(parser, true, exceptions, classLoader);
}
}
eventType = parser.next();
}
while (eventType != XmlPullParser.END_DOCUMENT);
try {
cfgFileStream.close();
}
catch (IOException e) {
LOGGER.log(Level.SEVERE, "Error while closing config file input stream", e);
}
}
private static void parseClassesToLoad(XmlPullParser parser, boolean optional,
Collection<Exception> exceptions, ClassLoader classLoader)
throws XmlPullParserException, IOException, Exception {
final String startName = parser.getName();
int eventType;
String name;
do {
eventType = parser.next();
name = parser.getName();
if (eventType == XmlPullParser.START_TAG && "className".equals(name)) {
String classToLoad = parser.nextText();
if (disabledSmackClasses.contains(classToLoad)) {
LOGGER.info("Not loading disabled Smack class " + classToLoad);
}
else {
try {
loadSmackClass(classToLoad, optional, classLoader);
}
catch (Exception e) {
// Don't throw the exception if an exceptions collection is given, instead
// record it there. This is used for unit testing purposes.
if (exceptions != null) {
exceptions.add(e);
}
else {
throw e;
}
}
}
}
}
while (!(eventType == XmlPullParser.END_TAG && startName.equals(name)));
}
private static void loadSmackClass(String className, boolean optional, ClassLoader classLoader) throws Exception {
Class<?> initClass;
try {
// Attempt to load and initialize the class so that all static initializer blocks of
// class are executed
initClass = Class.forName(className, true, classLoader);
}
catch (ClassNotFoundException cnfe) {
Level logLevel;
if (optional) {
logLevel = Level.FINE;
}
else {
logLevel = Level.WARNING;
}
LOGGER.log(logLevel, "A startup class '" + className + "' could not be loaded.");
if (!optional) {
throw cnfe;
} else {
return;
}
}
if (SmackInitializer.class.isAssignableFrom(initClass)) {
SmackInitializer initializer = (SmackInitializer) initClass.newInstance();
List<Exception> exceptions = initializer.initialize();
if (exceptions == null || exceptions.size() == 0) {
LOGGER.log(Level.FINE, "Loaded SmackInitializer " + className);
} else {
for (Exception e : exceptions) {
LOGGER.log(Level.SEVERE, "Exception in loadSmackClass", e);
}
}
} else {
LOGGER.log(Level.FINE, "Loaded " + className);
}
}
}