diff --git a/build.gradle b/build.gradle index 4d9eea2fc..f1f99e7cb 100644 --- a/build.gradle +++ b/build.gradle @@ -220,6 +220,24 @@ Smack core components. compression compressionJar dns dnsJar } + task createVersionResource(type: CreateFileTask) { + fileContent = version + outputFile = new File(projectDir, 'src/main/resources/org.jivesoftware.smack/version') + } + compileJava.dependsOn(createVersionResource) +} + +class CreateFileTask extends DefaultTask { + @Input + String fileContent + + @OutputFile + File outputFile + + @TaskAction + def createFile() { + outputFile.text = fileContent + } } project(':compression-jzlib') { diff --git a/core/.gitignore b/core/.gitignore new file mode 100644 index 000000000..ae0e243db --- /dev/null +++ b/core/.gitignore @@ -0,0 +1 @@ +src/main/resources/org.jivesoftware.smack/version diff --git a/core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java b/core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java index b245fa95b..8d7d3498f 100644 --- a/core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java +++ b/core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java @@ -19,6 +19,7 @@ package org.jivesoftware.smack; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -26,11 +27,13 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +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.mxp1.MXParser; import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; /** * Represents the configuration of Smack. The configuration is used for: @@ -42,21 +45,20 @@ import org.xmlpull.v1.XmlPullParser; * via the API will override settings in the configuration file. * * - * Configuration settings are stored in META-INF/smack-config.xml (typically inside the + * Configuration settings are stored in org.jivesoftware.smack/smack-config.xml (typically inside the * smack.jar file). * * @author Gaston Dombiak */ public final class SmackConfiguration { - private static final String SMACK_VERSION = "3.4.0"; - private static final String DEFAULT_CONFIG_FILE = "classpath:META-INF/smack-config.xml"; + private static final String SMACK_VERSION; + private static final String DEFAULT_CONFIG_FILE = "classpath:org.jivesoftware.smack/smack-config.xml"; private static final Logger log = Logger.getLogger(SmackConfiguration.class.getName()); private static InputStream configFileStream; private static int packetReplyTimeout = 5000; - private static int keepAliveInterval = 30000; private static List defaultMechs = new ArrayList(); private static boolean localSocks5ProxyEnabled = true; @@ -65,6 +67,20 @@ public final class SmackConfiguration { private static boolean initialized = false; + static { + String smackVersion; + try { + InputStream is = FileUtils.getStreamForUrl("classpath:org.jivesoftware.smack/version", null); + byte[] buf = new byte[1024]; + is.read(buf); + smackVersion = new String(buf, Charset.forName("UTF-8")); + } catch(Exception e) { + log.log(Level.SEVERE, "Could not determine Smack version", e); + smackVersion = "unkown"; + } + SMACK_VERSION = smackVersion; + } + /** * The default parsing exception callback is {@link ExceptionThrowingCallback} which will * throw an exception and therefore disconnect the active connection. @@ -88,7 +104,7 @@ public final class SmackConfiguration { */ /** - * Sets the location of the config file on the classpath. Only required if changing from the default location of classpath:META-INF/smack-config.xml. + * Sets the location of the config file on the classpath. Only required if changing from the default location of classpath:org.jivesoftware.smack/smack-config.xml. * *

* This method must be called before accessing any other class in Smack. @@ -320,8 +336,21 @@ public final class SmackConfiguration { return defaultCallback; } - private static void parseClassToLoad(XmlPullParser parser) throws Exception { - String className = parser.nextText(); + public static void parseClassesToLoad(XmlPullParser parser, boolean optional) 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(); + loadSmackClass(classToLoad, optional); + } + } while (! (eventType == XmlPullParser.END_TAG && startName.equals(name))); + } + + public static void loadSmackClass(String className, boolean optional) throws Exception { // Attempt to load the class so that the class can get initialized try { Class initClass = Class.forName(className); @@ -332,7 +361,17 @@ public final class SmackConfiguration { } } catch (ClassNotFoundException cnfe) { - log.log(Level.WARNING, "A startup class [" + className + "] specified in smack-config.xml could not be loaded: "); + Level logLevel; + if (optional) { + logLevel = Level.FINE; + } + else { + logLevel = Level.WARNING; + } + log.log(logLevel, "A startup class [" + className + + "] specified in smack-config.xml could not be loaded: "); + if (!optional) + throw cnfe; } } @@ -373,63 +412,63 @@ public final class SmackConfiguration { configFileStream = FileUtils.getStreamForUrl(DEFAULT_CONFIG_FILE, null); } catch (Exception e) { - log.log(Level.INFO, "Could not create input stream for default config file [" + DEFAULT_CONFIG_FILE + "]", e); + throw new IllegalStateException(e); } } if (configFileStream != null) { - readFile(configFileStream); + try { + readFile(configFileStream); + } + catch (Exception e) { + throw new IllegalStateException(e); + } } else { log.log(Level.INFO, "No configuration file found"); } } - private static void readFile(InputStream cfgFileStream) { - try { - XmlPullParser parser = new MXParser(); - 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("className")) { - // Attempt to load the class so that the class can get initialized - parseClassToLoad(parser); - } - else if (parser.getName().equals("packetReplyTimeout")) { - packetReplyTimeout = parseIntProperty(parser, packetReplyTimeout); - } - else if (parser.getName().equals("keepAliveInterval")) { - keepAliveInterval = parseIntProperty(parser, keepAliveInterval); - } - else if (parser.getName().equals("mechName")) { - defaultMechs.add(parser.nextText()); - } - else if (parser.getName().equals("localSocks5ProxyEnabled")) { - localSocks5ProxyEnabled = Boolean.parseBoolean(parser.nextText()); - } - else if (parser.getName().equals("localSocks5ProxyPort")) { - localSocks5ProxyPort = parseIntProperty(parser, localSocks5ProxyPort); - } - else if (parser.getName().equals("packetCollectorSize")) { - packetCollectorSize = parseIntProperty(parser, packetCollectorSize); - } - else if (parser.getName().equals("autoEnableEntityCaps")) { - autoEnableEntityCaps = Boolean.parseBoolean(parser.nextText()); - } + private static void readFile(InputStream cfgFileStream) throws Exception { + XmlPullParser parser = new MXParser(); + 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); + } + else if (parser.getName().equals("optionalStartupClasses")) { + parseClassesToLoad(parser, true); + } + else if (parser.getName().equals("packetReplyTimeout")) { + packetReplyTimeout = parseIntProperty(parser, packetReplyTimeout); + } + else if (parser.getName().equals("mechName")) { + defaultMechs.add(parser.nextText()); + } + else if (parser.getName().equals("localSocks5ProxyEnabled")) { + localSocks5ProxyEnabled = Boolean.parseBoolean(parser.nextText()); + } + else if (parser.getName().equals("localSocks5ProxyPort")) { + localSocks5ProxyPort = parseIntProperty(parser, localSocks5ProxyPort); + } + else if (parser.getName().equals("packetCollectorSize")) { + packetCollectorSize = parseIntProperty(parser, packetCollectorSize); + } + else if (parser.getName().equals("autoEnableEntityCaps")) { + autoEnableEntityCaps = Boolean.parseBoolean(parser.nextText()); } - eventType = parser.next(); - } while (eventType != XmlPullParser.END_DOCUMENT); - } catch (Exception e) { - log.log(Level.SEVERE, "Error occurred while reading config file", e); - } - finally { - try { - cfgFileStream.close(); - } catch (IOException e) { - log.log(Level.INFO, "Error while closing config file input stream", e); } + eventType = parser.next(); + } + while (eventType != XmlPullParser.END_DOCUMENT); + try { + cfgFileStream.close(); + } + catch (IOException e) { + log.log(Level.SEVERE, "Error while closing config file input stream", e); } } } diff --git a/core/src/main/java/org/jivesoftware/smack/provider/CoreInitializer.java b/core/src/main/java/org/jivesoftware/smack/initializer/CoreInitializer.java similarity index 61% rename from core/src/main/java/org/jivesoftware/smack/provider/CoreInitializer.java rename to core/src/main/java/org/jivesoftware/smack/initializer/CoreInitializer.java index b39aa0f82..c821192f4 100644 --- a/core/src/main/java/org/jivesoftware/smack/provider/CoreInitializer.java +++ b/core/src/main/java/org/jivesoftware/smack/initializer/CoreInitializer.java @@ -1,6 +1,5 @@ -package org.jivesoftware.smack.provider; +package org.jivesoftware.smack.initializer; -import org.jivesoftware.smack.SmackInitializer; /** * Loads the default provider file for the Smack core on initialization. @@ -10,6 +9,6 @@ import org.jivesoftware.smack.SmackInitializer; */ public class CoreInitializer extends UrlProviderFileInitializer implements SmackInitializer { protected String getFilePath() { - return "classpath:META-INF/core.providers"; + return "classpath:org.jivesoftware.smack/core.providers"; } } diff --git a/core/src/main/java/org/jivesoftware/smack/LoggingInitializer.java b/core/src/main/java/org/jivesoftware/smack/initializer/LoggingInitializer.java similarity index 59% rename from core/src/main/java/org/jivesoftware/smack/LoggingInitializer.java rename to core/src/main/java/org/jivesoftware/smack/initializer/LoggingInitializer.java index ddc49d461..02bc136a4 100644 --- a/core/src/main/java/org/jivesoftware/smack/LoggingInitializer.java +++ b/core/src/main/java/org/jivesoftware/smack/initializer/LoggingInitializer.java @@ -1,5 +1,8 @@ -package org.jivesoftware.smack; +package org.jivesoftware.smack.initializer; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; @@ -15,14 +18,22 @@ import org.jivesoftware.smack.util.FileUtils; public class LoggingInitializer implements SmackInitializer { private static Logger log = Logger.getLogger(LoggingInitializer.class.getName()); + + private List exceptions = new LinkedList(); @Override public void initialize() { try { - LogManager.getLogManager().readConfiguration(FileUtils.getStreamForUrl("classpath:META-INF/jul.properties", null)); + LogManager.getLogManager().readConfiguration(FileUtils.getStreamForUrl("classpath:org.jivesofware.smack/jul.properties", null)); } catch (Exception e) { log .log(Level.WARNING, "Could not initialize Java Logging from default file.", e); + exceptions.add(e); } } + + @Override + public List getExceptions() { + return Collections.unmodifiableList(exceptions); + } } diff --git a/core/src/main/java/org/jivesoftware/smack/SmackInitializer.java b/core/src/main/java/org/jivesoftware/smack/initializer/SmackInitializer.java similarity index 64% rename from core/src/main/java/org/jivesoftware/smack/SmackInitializer.java rename to core/src/main/java/org/jivesoftware/smack/initializer/SmackInitializer.java index d22aebc4b..7efbbaf70 100644 --- a/core/src/main/java/org/jivesoftware/smack/SmackInitializer.java +++ b/core/src/main/java/org/jivesoftware/smack/initializer/SmackInitializer.java @@ -1,4 +1,8 @@ -package org.jivesoftware.smack; +package org.jivesoftware.smack.initializer; + +import java.util.List; + +import org.jivesoftware.smack.SmackConfiguration; /** * Defines an initialization class that will be instantiated and invoked by the {@link SmackConfiguration} class during initialization. @@ -11,4 +15,5 @@ package org.jivesoftware.smack; */ public interface SmackInitializer { void initialize(); + List getExceptions(); } diff --git a/core/src/main/java/org/jivesoftware/smack/provider/UrlProviderFileInitializer.java b/core/src/main/java/org/jivesoftware/smack/initializer/UrlProviderFileInitializer.java similarity index 64% rename from core/src/main/java/org/jivesoftware/smack/provider/UrlProviderFileInitializer.java rename to core/src/main/java/org/jivesoftware/smack/initializer/UrlProviderFileInitializer.java index 68cbfe5c6..b5e746069 100644 --- a/core/src/main/java/org/jivesoftware/smack/provider/UrlProviderFileInitializer.java +++ b/core/src/main/java/org/jivesoftware/smack/initializer/UrlProviderFileInitializer.java @@ -1,15 +1,19 @@ -package org.jivesoftware.smack.provider; +package org.jivesoftware.smack.initializer; +import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import org.jivesoftware.smack.SmackInitializer; +import org.jivesoftware.smack.provider.ProviderFileLoader; +import org.jivesoftware.smack.provider.ProviderManager; import org.jivesoftware.smack.util.FileUtils; /** @@ -21,6 +25,8 @@ import org.jivesoftware.smack.util.FileUtils; public abstract class UrlProviderFileInitializer implements SmackInitializer { private static final Logger log = Logger.getLogger(UrlProviderFileInitializer.class.getName()); + private List exceptions = new LinkedList(); + @Override public void initialize() { String filePath = getFilePath(); @@ -30,17 +36,26 @@ public abstract class UrlProviderFileInitializer implements SmackInitializer { if (is != null) { log.log(Level.INFO, "Loading providers for file [" + filePath + "]"); - ProviderManager.getInstance().addLoader(new ProviderFileLoader(is)); + ProviderFileLoader pfl = new ProviderFileLoader(is); + ProviderManager.getInstance().addLoader(pfl); + exceptions.addAll(pfl.getLoadingExceptions()); } else { log.log(Level.WARNING, "No input stream created for " + filePath); + exceptions.add(new IOException("No input stream created for " + filePath)); } } catch (Exception e) { log.log(Level.SEVERE, "Error trying to load provider file " + filePath, e); + exceptions.add(e); } } + @Override + public List getExceptions() { + return Collections.unmodifiableList(exceptions); + } + protected abstract String getFilePath(); /** diff --git a/core/src/main/java/org/jivesoftware/smack/provider/VmArgInitializer.java b/core/src/main/java/org/jivesoftware/smack/initializer/VmArgInitializer.java similarity index 82% rename from core/src/main/java/org/jivesoftware/smack/provider/VmArgInitializer.java rename to core/src/main/java/org/jivesoftware/smack/initializer/VmArgInitializer.java index 54e1be9cf..3b5f29af0 100644 --- a/core/src/main/java/org/jivesoftware/smack/provider/VmArgInitializer.java +++ b/core/src/main/java/org/jivesoftware/smack/initializer/VmArgInitializer.java @@ -1,4 +1,6 @@ -package org.jivesoftware.smack.provider; +package org.jivesoftware.smack.initializer; + +import org.jivesoftware.smack.provider.ProviderManager; /** diff --git a/core/src/main/java/org/jivesoftware/smack/provider/ProviderFileLoader.java b/core/src/main/java/org/jivesoftware/smack/provider/ProviderFileLoader.java index 57c9916fb..4e8f210f3 100644 --- a/core/src/main/java/org/jivesoftware/smack/provider/ProviderFileLoader.java +++ b/core/src/main/java/org/jivesoftware/smack/provider/ProviderFileLoader.java @@ -3,6 +3,9 @@ package org.jivesoftware.smack.provider; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -24,6 +27,7 @@ public class ProviderFileLoader implements ProviderLoader { private Collection iqProviders; private Collection extProviders; private InputStream providerStream; + private List exceptions = new LinkedList(); public ProviderFileLoader(InputStream providerFileInputStream) { setInputStream(providerFileInputStream); @@ -43,6 +47,10 @@ public class ProviderFileLoader implements ProviderLoader { initialize(); return extProviders; } + + public List getLoadingExceptions() { + return Collections.unmodifiableList(exceptions); + } @SuppressWarnings("unchecked") private synchronized void initialize() { @@ -112,11 +120,13 @@ public class ProviderFileLoader implements ProviderLoader { } catch (ClassNotFoundException cnfe) { log.log(Level.SEVERE, "Could not find provider class", cnfe); + exceptions.add(cnfe); } } } catch (IllegalArgumentException illExc) { log.log(Level.SEVERE, "Invalid provider type found [" + typeName + "] when expecting iqProvider or extensionProvider", illExc); + exceptions.add(illExc); } } eventType = parser.next(); diff --git a/core/src/main/java/org/jivesoftware/smack/provider/ProviderManager.java b/core/src/main/java/org/jivesoftware/smack/provider/ProviderManager.java index 46c3eea22..85272c483 100644 --- a/core/src/main/java/org/jivesoftware/smack/provider/ProviderManager.java +++ b/core/src/main/java/org/jivesoftware/smack/provider/ProviderManager.java @@ -41,7 +41,7 @@ import org.jivesoftware.smack.packet.IQ; * * Because many more IQ types are part of XMPP and its extensions, a pluggable IQ parsing * mechanism is provided. IQ providers are registered programatically or by creating a - * smack.providers file in the META-INF directory of your JAR file. The file is an XML + * providers file. The file is an XML * document that contains one or more iqProvider entries, as in the following example: * *

diff --git a/core/src/main/resources/core.providers b/core/src/main/resources/org.jivesoftware.smack/core.providers
similarity index 58%
rename from core/src/main/resources/core.providers
rename to core/src/main/resources/org.jivesoftware.smack/core.providers
index 614a2d8d7..6af31515f 100644
--- a/core/src/main/resources/core.providers
+++ b/core/src/main/resources/org.jivesoftware.smack/core.providers
@@ -1,7 +1,7 @@
  
 
  
-    
+
     
     
         query
@@ -9,11 +9,4 @@
         org.jivesoftware.smack.provider.PrivacyProvider
     
 
-    
-    
-        ping
-        urn:xmpp:ping
-        org.jivesoftware.smack.ping.provider.PingProvider
-    
-
 
diff --git a/core/src/main/resources/jul.properties b/core/src/main/resources/org.jivesoftware.smack/jul.properties
similarity index 100%
rename from core/src/main/resources/jul.properties
rename to core/src/main/resources/org.jivesoftware.smack/jul.properties
diff --git a/core/src/main/resources/org.jivesoftware.smack/smack-config.xml b/core/src/main/resources/org.jivesoftware.smack/smack-config.xml
new file mode 100644
index 000000000..85c788546
--- /dev/null
+++ b/core/src/main/resources/org.jivesoftware.smack/smack-config.xml
@@ -0,0 +1,35 @@
+
+
+
+
+    
+    5000
+
+    
+    true
+
+    
+    7777
+
+    
+    10000
+    
+    
+    false
+    
+    
+    
+        org.jivesoftware.smack.initializer.CoreInitializer
+        org.jivesoftware.smack.initializer.VmArgInitializer
+        org.jivesoftware.smack.PrivacyListManager
+        org.jivesoftware.smack.ReconnectionManager
+    
+
+    
+        org.jivesoftware.smack.util.dns.JavaxResolver
+        org.jivesoftware.smackx.ExtensionsProviderInitializer
+        org.jivesoftware.smackx.ExtensionsStartupClasses
+        org.jivesoftware.smackx.ExperimentalProviderInitializer
+        org.jivesoftware.smackx.WorkgroupProviderInitializer
+    
+
diff --git a/core/src/main/resources/smack-config.xml b/core/src/main/resources/smack-config.xml
deleted file mode 100644
index b660fcff5..000000000
--- a/core/src/main/resources/smack-config.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-    
-    5000
-
-    
-    true
-
-    
-    7777
-
-    
-    10000
-    
-    
-    false
-    
-    
-    
-        org.jivesoftware.smack.provider.CoreInitializer
-        org.jivesoftware.smack.provider.VmArgInitializer
-        org.jivesoftware.smack.PrivacyListManager        
-        org.jivesoftware.smackx.ExtensionInitializer
-        org.jivesoftware.smackx.disco.ServiceDiscoveryManager
-        org.jivesoftware.smackx.xhtmlim.XHTMLManager
-        org.jivesoftware.smackx.muc.MultiUserChat
-        org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager
-        org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager    
-        org.jivesoftware.smackx.filetransfer.FileTransferManager
-        org.jivesoftware.smackx.iqlast.LastActivityManager
-        org.jivesoftware.smack.ReconnectionManager    
-        org.jivesoftware.smackx.commands.AdHocCommandManager    
-        org.jivesoftware.smack.util.dns.JavaxResolver
-        org.jivesoftware.smackx.ping.PingManager
-    
-
-
diff --git a/core/src/test/java/org/jivesoftware/smack/SmackConfigurationTest.java b/core/src/test/java/org/jivesoftware/smack/SmackConfigurationTest.java
new file mode 100644
index 000000000..470fd7c3b
--- /dev/null
+++ b/core/src/test/java/org/jivesoftware/smack/SmackConfigurationTest.java
@@ -0,0 +1,17 @@
+package org.jivesoftware.smack;
+
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+public class SmackConfigurationTest {
+
+    @Test
+    public void testSmackConfiguration() {
+        try {
+            SmackConfiguration.getPacketReplyTimeout();
+        } catch (Throwable t) {
+            fail("SmackConfiguration threw Throwable");
+        }
+    }
+}
diff --git a/core/src/test/java/org/jivesoftware/smack/initializer/CoreInitializerTest.java b/core/src/test/java/org/jivesoftware/smack/initializer/CoreInitializerTest.java
new file mode 100644
index 000000000..7f8fe9211
--- /dev/null
+++ b/core/src/test/java/org/jivesoftware/smack/initializer/CoreInitializerTest.java
@@ -0,0 +1,17 @@
+package org.jivesoftware.smack.initializer;
+
+import static org.junit.Assert.assertTrue;
+
+import org.jivesoftware.smack.initializer.CoreInitializer;
+import org.junit.Test;
+
+public class CoreInitializerTest {
+
+    @Test
+    public void testCoreInitializer() {
+        CoreInitializer ci = new CoreInitializer();
+        ci.initialize();
+        assertTrue(ci.getExceptions().size() == 0);
+    }
+
+}
diff --git a/experimental/src/main/java/org/jivesoftware/smackx/experimental/ExperimentalProviderInitializer.java b/experimental/src/main/java/org/jivesoftware/smackx/ExperimentalProviderInitializer.java
similarity index 80%
rename from experimental/src/main/java/org/jivesoftware/smackx/experimental/ExperimentalProviderInitializer.java
rename to experimental/src/main/java/org/jivesoftware/smackx/ExperimentalProviderInitializer.java
index 6d8abbc20..5a58890a2 100644
--- a/experimental/src/main/java/org/jivesoftware/smackx/experimental/ExperimentalProviderInitializer.java
+++ b/experimental/src/main/java/org/jivesoftware/smackx/ExperimentalProviderInitializer.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package org.jivesoftware.smackx.experimental;
+package org.jivesoftware.smackx;
 
-import org.jivesoftware.smack.provider.UrlProviderFileInitializer;
+import org.jivesoftware.smack.initializer.UrlProviderFileInitializer;
 
 /**
  * Initializes the providers in the experimental code stream.
@@ -28,6 +28,6 @@ public class ExperimentalProviderInitializer  extends UrlProviderFileInitializer
 
     @Override
     protected String getFilePath() {
-        return "classpath:META-INF/experimental.providers";
+        return "classpath:org.jivesoftware.smackx/experimental.providers";
     }
 }
diff --git a/experimental/src/main/java/org/jivesoftware/smackx/WorkgroupProviderInitializer.java b/experimental/src/main/java/org/jivesoftware/smackx/WorkgroupProviderInitializer.java
new file mode 100644
index 000000000..52ebb6577
--- /dev/null
+++ b/experimental/src/main/java/org/jivesoftware/smackx/WorkgroupProviderInitializer.java
@@ -0,0 +1,11 @@
+package org.jivesoftware.smackx;
+
+import org.jivesoftware.smack.initializer.UrlProviderFileInitializer;
+
+public class WorkgroupProviderInitializer  extends UrlProviderFileInitializer {
+
+    @Override
+    protected String getFilePath() {
+        return "classpath:org.jivesoftware.smackx/workgroup.providers";
+    }
+}
diff --git a/experimental/src/main/java/org/jivesoftware/smackx/workgroup/WorkgroupProviderInitializer.java b/experimental/src/main/java/org/jivesoftware/smackx/workgroup/WorkgroupProviderInitializer.java
deleted file mode 100644
index a198a32e6..000000000
--- a/experimental/src/main/java/org/jivesoftware/smackx/workgroup/WorkgroupProviderInitializer.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.jivesoftware.smackx.workgroup;
-
-import org.jivesoftware.smack.provider.UrlProviderFileInitializer;
-
-public class WorkgroupProviderInitializer  extends UrlProviderFileInitializer {
-
-    @Override
-    protected String getFilePath() {
-        return "classpath:META-INF/workgroup.providers";
-    }
-}
diff --git a/experimental/src/main/resources/experimental.providers b/experimental/src/main/resources/org.jivesoftware.smackx/experimental.providers
similarity index 100%
rename from experimental/src/main/resources/experimental.providers
rename to experimental/src/main/resources/org.jivesoftware.smackx/experimental.providers
diff --git a/experimental/src/main/resources/workgroup.providers b/experimental/src/main/resources/org.jivesoftware.smackx/workgroup.providers
similarity index 100%
rename from experimental/src/main/resources/workgroup.providers
rename to experimental/src/main/resources/org.jivesoftware.smackx/workgroup.providers
diff --git a/experimental/src/test/java/org/jivesoftware/smackx/ExperimentalProviderInitializerTest.java b/experimental/src/test/java/org/jivesoftware/smackx/ExperimentalProviderInitializerTest.java
new file mode 100644
index 000000000..187a67247
--- /dev/null
+++ b/experimental/src/test/java/org/jivesoftware/smackx/ExperimentalProviderInitializerTest.java
@@ -0,0 +1,15 @@
+package org.jivesoftware.smackx;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class ExperimentalProviderInitializerTest {
+
+    @Test
+    public void testExperimentalProviderInitialzer() {
+        ExperimentalProviderInitializer epi = new ExperimentalProviderInitializer();
+        epi.initialize();
+        assertTrue(epi.getExceptions().size() == 0);
+    }
+}
diff --git a/experimental/src/test/java/org/jivesoftware/smackx/WorkgroupProviderInitializerTest.java b/experimental/src/test/java/org/jivesoftware/smackx/WorkgroupProviderInitializerTest.java
new file mode 100644
index 000000000..d9d8d0872
--- /dev/null
+++ b/experimental/src/test/java/org/jivesoftware/smackx/WorkgroupProviderInitializerTest.java
@@ -0,0 +1,15 @@
+package org.jivesoftware.smackx;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class WorkgroupProviderInitializerTest {
+
+    @Test
+    public void testWorkgroupProviderInitializer() {
+        WorkgroupProviderInitializer wpi = new WorkgroupProviderInitializer();
+        wpi.initialize();
+        assertTrue(wpi.getExceptions().size() == 0);
+    }
+}
diff --git a/extensions/src/main/java/org/jivesoftware/smackx/ExtensionInitializer.java b/extensions/src/main/java/org/jivesoftware/smackx/ExtensionInitializer.java
deleted file mode 100644
index cc3b585ff..000000000
--- a/extensions/src/main/java/org/jivesoftware/smackx/ExtensionInitializer.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.jivesoftware.smackx;
-
-import org.jivesoftware.smack.provider.UrlProviderFileInitializer;
-
-/**
- * Loads the default provider file for the Smack extensions on initialization.
- * 
- * @author Robin Collier
- *
- */
-public class ExtensionInitializer extends UrlProviderFileInitializer {
-    @Override
-    protected String getFilePath() {
-        return "classpath:META-INF/extension.providers";
-    }
-}
diff --git a/extensions/src/main/java/org/jivesoftware/smackx/ExtensionsProviderInitializer.java b/extensions/src/main/java/org/jivesoftware/smackx/ExtensionsProviderInitializer.java
new file mode 100644
index 000000000..b5e43779d
--- /dev/null
+++ b/extensions/src/main/java/org/jivesoftware/smackx/ExtensionsProviderInitializer.java
@@ -0,0 +1,16 @@
+package org.jivesoftware.smackx;
+
+import org.jivesoftware.smack.initializer.UrlProviderFileInitializer;
+
+/**
+ * Loads the default provider file for the Smack extensions on initialization.
+ * 
+ * @author Robin Collier
+ *
+ */
+public class ExtensionsProviderInitializer extends UrlProviderFileInitializer {
+    @Override
+    protected String getFilePath() {
+        return "classpath:org.jivesoftware.smackx/extensions.providers";
+    }
+}
\ No newline at end of file
diff --git a/extensions/src/main/java/org/jivesoftware/smackx/ExtensionsStartupClasses.java b/extensions/src/main/java/org/jivesoftware/smackx/ExtensionsStartupClasses.java
new file mode 100644
index 000000000..30d6f7cf9
--- /dev/null
+++ b/extensions/src/main/java/org/jivesoftware/smackx/ExtensionsStartupClasses.java
@@ -0,0 +1,65 @@
+package org.jivesoftware.smackx;
+
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.jivesoftware.smack.SmackConfiguration;
+import org.jivesoftware.smack.initializer.SmackInitializer;
+import org.jivesoftware.smack.util.FileUtils;
+import org.xmlpull.mxp1.MXParser;
+import org.xmlpull.v1.XmlPullParser;
+
+public class ExtensionsStartupClasses implements SmackInitializer {
+
+    private static final String EXTENSIONS_XML = "classpath:org.jivesoftware.smackx/extensions.xml";
+
+    private List exceptions = new LinkedList();
+    // TODO log
+
+    @Override
+    public void initialize() {
+        InputStream is;
+        XmlPullParser parser;
+        int eventType;
+        try {
+            is = FileUtils.getStreamForUrl(EXTENSIONS_XML, null);
+            parser = new MXParser();
+            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+            parser.setInput(is, "UTF-8");
+            eventType = parser.getEventType();
+        }
+        catch (Exception e) {
+            exceptions.add(e);
+            return;
+        }
+        try {
+            do {
+                String name = parser.getName();
+                if (eventType == XmlPullParser.START_TAG) {
+                    if ("startupClasses".equals(name)) {
+                        try {
+                            SmackConfiguration.parseClassesToLoad(parser, false);
+                        }
+                        catch (Exception e) {
+                            exceptions.add(e);
+                        }
+                    }
+                }
+                eventType = parser.next();
+            }
+            while (eventType != XmlPullParser.END_DOCUMENT);
+            is.close();
+        }
+        catch (Exception e) {
+            exceptions.add(e);
+        }
+    }
+
+    @Override
+    public List getExceptions() {
+        return Collections.unmodifiableList(exceptions);
+    }
+
+}
diff --git a/extensions/src/main/resources/extension.providers b/extensions/src/main/resources/org.jivesoftware.smackx/extensions.providers
similarity index 96%
rename from extensions/src/main/resources/extension.providers
rename to extensions/src/main/resources/org.jivesoftware.smackx/extensions.providers
index 54b99d989..1dcb2183d 100644
--- a/extensions/src/main/resources/extension.providers
+++ b/extensions/src/main/resources/org.jivesoftware.smackx/extensions.providers
@@ -442,4 +442,11 @@
 		org.jivesoftware.smackx.forward.provider.ForwardedProvider
 	
 
+    
+    
+        ping
+        urn:xmpp:ping
+        org.jivesoftware.smackx.ping.provider.PingProvider
+    
+
 
diff --git a/extensions/src/main/resources/org.jivesoftware.smackx/extensions.xml b/extensions/src/main/resources/org.jivesoftware.smackx/extensions.xml
new file mode 100644
index 000000000..497d27470
--- /dev/null
+++ b/extensions/src/main/resources/org.jivesoftware.smackx/extensions.xml
@@ -0,0 +1,13 @@
+
+    
+        org.jivesoftware.smackx.disco.ServiceDiscoveryManager
+        org.jivesoftware.smackx.xhtmlim.XHTMLManager
+        org.jivesoftware.smackx.muc.MultiUserChat
+        org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager
+        org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager
+        org.jivesoftware.smackx.filetransfer.FileTransferManager
+        org.jivesoftware.smackx.iqlast.LastActivityManager
+        org.jivesoftware.smackx.commands.AdHocCommandManager
+        org.jivesoftware.smackx.ping.PingManager
+    
+
\ No newline at end of file
diff --git a/extensions/src/test/java/org/jivesoftware/smackx/ExtensionsProviderInitializerTest.java b/extensions/src/test/java/org/jivesoftware/smackx/ExtensionsProviderInitializerTest.java
new file mode 100644
index 000000000..61459331e
--- /dev/null
+++ b/extensions/src/test/java/org/jivesoftware/smackx/ExtensionsProviderInitializerTest.java
@@ -0,0 +1,16 @@
+package org.jivesoftware.smackx;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class ExtensionsProviderInitializerTest {
+
+    @Test
+    public void testExtensionProviderInitializer() {
+        ExtensionsProviderInitializer ei = new ExtensionsProviderInitializer();
+        ei.initialize();
+        assertTrue(ei.getExceptions().size() == 0);
+    }
+
+}
diff --git a/extensions/src/test/java/org/jivesoftware/smackx/ExtensionsStartupClassesTest.java b/extensions/src/test/java/org/jivesoftware/smackx/ExtensionsStartupClassesTest.java
new file mode 100644
index 000000000..d46bf2b4b
--- /dev/null
+++ b/extensions/src/test/java/org/jivesoftware/smackx/ExtensionsStartupClassesTest.java
@@ -0,0 +1,16 @@
+package org.jivesoftware.smackx;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class ExtensionsStartupClassesTest {
+
+    @Test
+    public void testExtensiosnStartupClasses() {
+        ExtensionsStartupClasses esc = new ExtensionsStartupClasses();
+        esc.initialize();
+        assertTrue(esc.getExceptions().size() == 0);
+    }
+
+}