SmackInitialization.java

  1. /**
  2.  *
  3.  * Copyright 2003-2007 Jive Software, 2014-2020 Florian Schmaus
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.jivesoftware.smack;

  18. import java.io.BufferedReader;
  19. import java.io.InputStream;
  20. import java.io.InputStreamReader;
  21. import java.nio.charset.StandardCharsets;
  22. import java.util.Collection;
  23. import java.util.List;
  24. import java.util.logging.Level;
  25. import java.util.logging.Logger;

  26. import org.jivesoftware.smack.bind2.Bind2ModuleDescriptor;
  27. import org.jivesoftware.smack.compress.provider.CompressedProvider;
  28. import org.jivesoftware.smack.compress.provider.FailureProvider;
  29. import org.jivesoftware.smack.compression.CompressionModuleDescriptor;
  30. import org.jivesoftware.smack.compression.Java7ZlibInputOutputStream;
  31. import org.jivesoftware.smack.compression.XmppCompressionManager;
  32. import org.jivesoftware.smack.compression.zlib.ZlibXmppCompressionFactory;
  33. import org.jivesoftware.smack.initializer.SmackInitializer;
  34. import org.jivesoftware.smack.isr.InstantStreamResumptionModuleDescriptor;
  35. import org.jivesoftware.smack.packet.Bind;
  36. import org.jivesoftware.smack.packet.Message;
  37. import org.jivesoftware.smack.provider.BindIQProvider;
  38. import org.jivesoftware.smack.provider.BodyElementProvider;
  39. import org.jivesoftware.smack.provider.MessageSubjectElementProvider;
  40. import org.jivesoftware.smack.provider.MessageThreadElementProvider;
  41. import org.jivesoftware.smack.provider.ProviderManager;
  42. import org.jivesoftware.smack.provider.SaslChallengeProvider;
  43. import org.jivesoftware.smack.provider.SaslFailureProvider;
  44. import org.jivesoftware.smack.provider.SaslSuccessProvider;
  45. import org.jivesoftware.smack.provider.TlsFailureProvider;
  46. import org.jivesoftware.smack.provider.TlsProceedProvider;
  47. import org.jivesoftware.smack.sasl.core.SASLAnonymous;
  48. import org.jivesoftware.smack.sasl.core.SASLXOauth2Mechanism;
  49. import org.jivesoftware.smack.sasl.core.SCRAMSHA1Mechanism;
  50. import org.jivesoftware.smack.sasl.core.ScramSha1PlusMechanism;
  51. import org.jivesoftware.smack.util.CloseableUtil;
  52. import org.jivesoftware.smack.util.FileUtils;
  53. import org.jivesoftware.smack.util.PacketParserUtils;
  54. import org.jivesoftware.smack.xml.XmlPullParser;

  55. public final class SmackInitialization {
  56.     static final String SMACK_VERSION;

  57.     private static final String DEFAULT_CONFIG_FILE = "org.jivesoftware.smack/smack-config.xml";

  58.     private static final Logger LOGGER = Logger.getLogger(SmackInitialization.class.getName());

  59.     /**
  60.      * Loads the configuration from the smack-config.xml and system properties file.
  61.      * <p>
  62.      * So far this means that:
  63.      * 1) a set of classes will be loaded in order to execute their static init block
  64.      * 2) retrieve and set the current Smack release
  65.      * 3) set DEBUG
  66.      */
  67.     static {
  68.         String smackVersion;
  69.         BufferedReader reader = null;
  70.         try {
  71.             reader = new BufferedReader(new InputStreamReader(FileUtils.getStreamForClasspathFile("org.jivesoftware.smack/version", null), StandardCharsets.UTF_8));
  72.             smackVersion = reader.readLine();
  73.         } catch (Exception e) {
  74.             LOGGER.log(Level.SEVERE, "Could not determine Smack version", e);
  75.             smackVersion = "unknown";
  76.         } finally {
  77.             CloseableUtil.maybeClose(reader, LOGGER);
  78.         }
  79.         SMACK_VERSION = smackVersion;

  80.         String disabledClasses = System.getProperty("smack.disabledClasses");
  81.         if (disabledClasses != null) {
  82.             String[] splitDisabledClasses = disabledClasses.split(",");
  83.             for (String s : splitDisabledClasses) SmackConfiguration.disabledSmackClasses.add(s);
  84.         }

  85.         InputStream configFileStream;
  86.         try {
  87.             configFileStream = FileUtils.getStreamForClasspathFile(DEFAULT_CONFIG_FILE, null);
  88.         }
  89.         catch (Exception e) {
  90.             throw new IllegalStateException("Could not load Smack configuration file", e);
  91.         }

  92.         try {
  93.             processConfigFile(configFileStream, null);
  94.         }
  95.         catch (Exception e) {
  96.             throw new IllegalStateException("Could not parse Smack configuration file", e);
  97.         }

  98.         // Add the Java7 compression handler first, since it's preferred
  99.         SmackConfiguration.addCompressionHandler(new Java7ZlibInputOutputStream());

  100.         XmppCompressionManager.registerXmppCompressionFactory(ZlibXmppCompressionFactory.INSTANCE);

  101.         // Use try block since we may not have permission to get a system
  102.         // property (for example, when an applet).
  103.         try {
  104.             // Only overwrite DEBUG if it is set via the 'smack.debugEnabled' property. To prevent DEBUG_ENABLED
  105.             // = true, which could be set e.g. via a static block from user code, from being overwritten by the property not set
  106.             if (Boolean.getBoolean("smack.debugEnabled")) {
  107.                 SmackConfiguration.DEBUG = true;
  108.             }
  109.         }
  110.         catch (Exception e) {
  111.             LOGGER.log(Level.FINE, "Could not handle debugEnable property on Smack initialization", e);
  112.         }

  113.         SASLAuthentication.registerSASLMechanism(new SCRAMSHA1Mechanism());
  114.         SASLAuthentication.registerSASLMechanism(new ScramSha1PlusMechanism());
  115.         SASLAuthentication.registerSASLMechanism(new SASLXOauth2Mechanism());
  116.         SASLAuthentication.registerSASLMechanism(new SASLAnonymous());

  117.         ProviderManager.addIQProvider(Bind.ELEMENT, Bind.NAMESPACE, new BindIQProvider());
  118.         ProviderManager.addExtensionProvider(Message.Body.ELEMENT, Message.Body.NAMESPACE, new BodyElementProvider());
  119.         ProviderManager.addExtensionProvider(Message.Thread.ELEMENT, Message.Thread.NAMESPACE, new MessageThreadElementProvider());
  120.         ProviderManager.addExtensionProvider(Message.Subject.ELEMENT, Message.Subject.NAMESPACE, new MessageSubjectElementProvider());

  121.         ProviderManager.addNonzaProvider(SaslChallengeProvider.INSTANCE);
  122.         ProviderManager.addNonzaProvider(SaslSuccessProvider.INSTANCE);
  123.         ProviderManager.addNonzaProvider(SaslFailureProvider.INSTANCE);
  124.         ProviderManager.addNonzaProvider(TlsProceedProvider.INSTANCE);
  125.         ProviderManager.addNonzaProvider(TlsFailureProvider.INSTANCE);
  126.         ProviderManager.addNonzaProvider(CompressedProvider.INSTANCE);
  127.         ProviderManager.addNonzaProvider(FailureProvider.INSTANCE);

  128.         SmackConfiguration.addModule(Bind2ModuleDescriptor.class);
  129.         SmackConfiguration.addModule(CompressionModuleDescriptor.class);
  130.         SmackConfiguration.addModule(InstantStreamResumptionModuleDescriptor.class);

  131.         SmackConfiguration.smackInitialized = true;
  132.     }

  133.     public static void processConfigFile(InputStream cfgFileStream,
  134.                     Collection<Exception> exceptions) throws Exception {
  135.         processConfigFile(cfgFileStream, exceptions, SmackInitialization.class.getClassLoader());
  136.     }

  137.     public static void processConfigFile(InputStream cfgFileStream,
  138.                     Collection<Exception> exceptions, ClassLoader classLoader) throws Exception {
  139.         XmlPullParser parser = PacketParserUtils.getParserFor(cfgFileStream);
  140.         XmlPullParser.Event eventType = parser.getEventType();
  141.         do {
  142.             if (eventType == XmlPullParser.Event.START_ELEMENT) {
  143.                 if (parser.getName().equals("startupClasses")) {
  144.                     parseClassesToLoad(parser, false, exceptions, classLoader);
  145.                 }
  146.                 else if (parser.getName().equals("optionalStartupClasses")) {
  147.                     parseClassesToLoad(parser, true, exceptions, classLoader);
  148.                 }
  149.             }
  150.             eventType = parser.next();
  151.         }
  152.         while (eventType != XmlPullParser.Event.END_DOCUMENT);
  153.         CloseableUtil.maybeClose(cfgFileStream, LOGGER);
  154.     }

  155.     private static void parseClassesToLoad(XmlPullParser parser, boolean optional,
  156.                     Collection<Exception> exceptions, ClassLoader classLoader)
  157.                     throws Exception {
  158.         final String startName = parser.getName();
  159.         XmlPullParser.Event eventType;
  160.         outerloop: do {
  161.             eventType = parser.next();
  162.             if (eventType == XmlPullParser.Event.START_ELEMENT && "className".equals(parser.getName())) {
  163.                 String classToLoad = parser.nextText();
  164.                 if (SmackConfiguration.isDisabledSmackClass(classToLoad)) {
  165.                     continue outerloop;
  166.                 }

  167.                 try {
  168.                     loadSmackClass(classToLoad, optional, classLoader);
  169.                 } catch (Exception e) {
  170.                     // Don't throw the exception if an exceptions collection is given, instead
  171.                     // record it there. This is used for unit testing purposes.
  172.                     if (exceptions != null) {
  173.                         exceptions.add(e);
  174.                     } else {
  175.                         throw e;
  176.                     }
  177.                 }
  178.             }
  179.         }
  180.         while (!(eventType == XmlPullParser.Event.END_ELEMENT && startName.equals(parser.getName())));
  181.     }

  182.     private static void loadSmackClass(String className, boolean optional, ClassLoader classLoader) throws Exception {
  183.         Class<?> initClass;
  184.         try {
  185.             // Attempt to load and initialize the class so that all static initializer blocks of
  186.             // class are executed
  187.             initClass = Class.forName(className, true, classLoader);
  188.         }
  189.         catch (ClassNotFoundException cnfe) {
  190.             Level logLevel;
  191.             if (optional) {
  192.                 logLevel = Level.FINE;
  193.             }
  194.             else {
  195.                 logLevel = Level.WARNING;
  196.             }
  197.             LOGGER.log(logLevel, "A startup class '" + className + "' could not be loaded.");
  198.             if (!optional) {
  199.                 throw cnfe;
  200.             } else {
  201.                 return;
  202.             }
  203.         }
  204.         if (SmackInitializer.class.isAssignableFrom(initClass)) {
  205.             SmackInitializer initializer = (SmackInitializer) initClass.getConstructor().newInstance();
  206.             List<Exception> exceptions = initializer.initialize();
  207.             if (exceptions == null || exceptions.size() == 0) {
  208.                 LOGGER.log(Level.FINE, "Loaded SmackInitializer " + className);
  209.             } else {
  210.                 for (Exception e : exceptions) {
  211.                     LOGGER.log(Level.SEVERE, "Exception in loadSmackClass", e);
  212.                 }
  213.             }
  214.         } else {
  215.             LOGGER.log(Level.FINE, "Loaded " + className);
  216.         }
  217.     }
  218. }