SmackInitialization.java

  1. /**
  2.  *
  3.  * Copyright 2003-2007 Jive Software, 2014 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.IOException;
  20. import java.io.InputStream;
  21. import java.io.InputStreamReader;
  22. import java.lang.reflect.Field;
  23. import java.util.Collection;
  24. import java.util.List;
  25. import java.util.logging.Level;
  26. import java.util.logging.Logger;

  27. import org.jivesoftware.smack.compression.Java7ZlibInputOutputStream;
  28. import org.jivesoftware.smack.initializer.SmackInitializer;
  29. import org.jivesoftware.smack.packet.Bind;
  30. import org.jivesoftware.smack.provider.BindIQProvider;
  31. import org.jivesoftware.smack.provider.ProviderManager;
  32. import org.jivesoftware.smack.sasl.core.SASLXOauth2Mechanism;
  33. import org.jivesoftware.smack.sasl.core.SCRAMSHA1Mechanism;
  34. import org.jivesoftware.smack.util.FileUtils;
  35. import org.xmlpull.v1.XmlPullParser;
  36. import org.xmlpull.v1.XmlPullParserException;
  37. import org.xmlpull.v1.XmlPullParserFactory;


  38. public final class SmackInitialization {
  39.     static final String SMACK_VERSION;

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

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

  42.     /**
  43.      * Loads the configuration from the smack-config.xml and system properties file.
  44.      * <p>
  45.      * So far this means that:
  46.      * 1) a set of classes will be loaded in order to execute their static init block
  47.      * 2) retrieve and set the current Smack release
  48.      * 3) set DEBUG
  49.      */
  50.     static {
  51.         String smackVersion;
  52.         try {
  53.             BufferedReader reader = new BufferedReader(new InputStreamReader(FileUtils.getStreamForUrl("classpath:org.jivesoftware.smack/version", null)));
  54.             smackVersion = reader.readLine();
  55.             try {
  56.                 reader.close();
  57.             } catch (IOException e) {
  58.                 LOGGER.log(Level.WARNING, "IOException closing stream", e);
  59.             }
  60.         } catch(Exception e) {
  61.             LOGGER.log(Level.SEVERE, "Could not determine Smack version", e);
  62.             smackVersion = "unkown";
  63.         }
  64.         SMACK_VERSION = smackVersion;

  65.         String disabledClasses = System.getProperty("smack.disabledClasses");
  66.         if (disabledClasses != null) {
  67.             String[] splitDisabledClasses = disabledClasses.split(",");
  68.             for (String s : splitDisabledClasses) SmackConfiguration.disabledSmackClasses.add(s);
  69.         }
  70.         try {
  71.             FileUtils.addLines("classpath:org.jivesoftware.smack/disabledClasses", SmackConfiguration.disabledSmackClasses);
  72.         }
  73.         catch (Exception e) {
  74.             throw new IllegalStateException(e);
  75.         }

  76.         try {
  77.             Class<?> c = Class.forName("org.jivesoftware.smack.CustomSmackConfiguration");
  78.             Field f = c.getField("DISABLED_SMACK_CLASSES");
  79.             String[] sa = (String[]) f.get(null);
  80.             if (sa != null) {
  81.                 LOGGER.warning("Using CustomSmackConfig is deprecated and will be removed in a future release");
  82.                 for (String s : sa)
  83.                     SmackConfiguration.disabledSmackClasses.add(s);
  84.             }
  85.         }
  86.         catch (ClassNotFoundException e1) {
  87.         }
  88.         catch (NoSuchFieldException e) {
  89.         }
  90.         catch (SecurityException e) {
  91.         }
  92.         catch (IllegalArgumentException e) {
  93.         }
  94.         catch (IllegalAccessException e) {
  95.         }

  96.         InputStream configFileStream;
  97.         try {
  98.             configFileStream = FileUtils.getStreamForUrl(DEFAULT_CONFIG_FILE, null);
  99.         }
  100.         catch (Exception e) {
  101.             throw new IllegalStateException(e);
  102.         }

  103.         try {
  104.             processConfigFile(configFileStream, null);
  105.         }
  106.         catch (Exception e) {
  107.             throw new IllegalStateException(e);
  108.         }

  109.         // Add the Java7 compression handler first, since it's preferred
  110.         SmackConfiguration.compressionHandlers.add(new Java7ZlibInputOutputStream());

  111.         // Use try block since we may not have permission to get a system
  112.         // property (for example, when an applet).
  113.         try {
  114.             // Only overwrite DEBUG if it is set via the 'smack.debugEnabled' property. To prevent DEBUG_ENABLED
  115.             // = true, which could be set e.g. via a static block from user code, from being overwritten by the property not set
  116.             if (Boolean.getBoolean("smack.debugEnabled")) {
  117.                 SmackConfiguration.DEBUG = true;
  118.             }
  119.         }
  120.         catch (Exception e) {
  121.             // Ignore.
  122.         }

  123.         SASLAuthentication.registerSASLMechanism(new SCRAMSHA1Mechanism());
  124.         SASLAuthentication.registerSASLMechanism(new SASLXOauth2Mechanism());

  125.         ProviderManager.addIQProvider(Bind.ELEMENT, Bind.NAMESPACE, new BindIQProvider());

  126.         SmackConfiguration.smackInitialized = true;
  127.     }

  128.     public static void processConfigFile(InputStream cfgFileStream,
  129.                     Collection<Exception> exceptions) throws Exception {
  130.         processConfigFile(cfgFileStream, exceptions, SmackInitialization.class.getClassLoader());
  131.     }

  132.     public static void processConfigFile(InputStream cfgFileStream,
  133.                     Collection<Exception> exceptions, ClassLoader classLoader) throws Exception {
  134.         XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
  135.         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
  136.         parser.setInput(cfgFileStream, "UTF-8");
  137.         int eventType = parser.getEventType();
  138.         do {
  139.             if (eventType == XmlPullParser.START_TAG) {
  140.                 if (parser.getName().equals("startupClasses")) {
  141.                     parseClassesToLoad(parser, false, exceptions, classLoader);
  142.                 }
  143.                 else if (parser.getName().equals("optionalStartupClasses")) {
  144.                     parseClassesToLoad(parser, true, exceptions, classLoader);
  145.                 }
  146.             }
  147.             eventType = parser.next();
  148.         }
  149.         while (eventType != XmlPullParser.END_DOCUMENT);
  150.         try {
  151.             cfgFileStream.close();
  152.         }
  153.         catch (IOException e) {
  154.             LOGGER.log(Level.SEVERE, "Error while closing config file input stream", e);
  155.         }
  156.     }

  157.     private static void parseClassesToLoad(XmlPullParser parser, boolean optional,
  158.                     Collection<Exception> exceptions, ClassLoader classLoader)
  159.                     throws XmlPullParserException, IOException, 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.                 try {
  172.                     loadSmackClass(classToLoad, optional, classLoader);
  173.                 } catch (Exception e) {
  174.                     // Don't throw the exception if an exceptions collection is given, instead
  175.                     // record it there. This is used for unit testing purposes.
  176.                     if (exceptions != null) {
  177.                         exceptions.add(e);
  178.                     } else {
  179.                         throw e;
  180.                     }
  181.                 }
  182.             }
  183.         }
  184.         while (!(eventType == XmlPullParser.END_TAG && startName.equals(name)));
  185.     }

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