diff --git a/build/build.xml b/build/build.xml
index 653f8569c..4e2111a7e 100644
--- a/build/build.xml
+++ b/build/build.xml
@@ -31,11 +31,13 @@
+
+
@@ -46,10 +48,13 @@
+
+
+
@@ -151,6 +156,23 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -285,6 +307,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sent
+ urn:xmpp:carbons:2
+ org.jivesoftware.smackx.carbons.provider.CarbonManagerProvider
+
+
+ received
+ urn:xmpp:carbons:2
+ org.jivesoftware.smackx.carbons.provider.CarbonManagerProvider
+
+
+
diff --git a/experimental/source/org/jivesoftware/smackx/carbons/Carbon.java b/experimental/source/org/jivesoftware/smackx/carbons/Carbon.java
new file mode 100644
index 000000000..39c188851
--- /dev/null
+++ b/experimental/source/org/jivesoftware/smackx/carbons/Carbon.java
@@ -0,0 +1,117 @@
+/**
+ * Copyright 2013 Georg Lukas
+ *
+ * All rights reserved. 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.smackx.carbons;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.forward.Forwarded;
+
+/**
+ * Packet extension for XEP-0280: Message Carbons. The extension
+ * XEP-0280 is
+ * meant to synchronize a message flow to multiple presences of a user.
+ *
+ *
+ * It accomplishes this by wrapping a {@link Forwarded} packet in a sent
+ * or received element
+ *
+ * @author Georg Lukas
+ */
+public class Carbon implements PacketExtension {
+ public static final String NAMESPACE = "urn:xmpp:carbons:2";
+
+ private Direction dir;
+ private Forwarded fwd;
+
+ /**
+ * Construct a Carbon message extension.
+ *
+ * @param dir Determines if the carbon is being sent/received
+ * @param fwd The forwarded message.
+ */
+ public Carbon(Direction dir, Forwarded fwd) {
+ this.dir = dir;
+ this.fwd = fwd;
+ }
+
+ /**
+ * Get the direction (sent or received) of the carbon.
+ *
+ * @return the {@link Direction} of the carbon.
+ */
+ public Direction getDirection() {
+ return dir;
+ }
+
+ /**
+ * Get the forwarded packet.
+ *
+ * @return the {@link Forwarded} message contained in this Carbon.
+ */
+ public Forwarded getForwarded() {
+ return fwd;
+ }
+
+ @Override
+ public String getElementName() {
+ return dir.toString();
+ }
+
+ @Override
+ public String getNamespace() {
+ return NAMESPACE;
+ }
+
+ @Override
+ public String toXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<").append(getElementName()).append(" xmlns=\"")
+ .append(getNamespace()).append("\">");
+
+ buf.append(fwd.toXML());
+
+ buf.append("").append(getElementName()).append(">");
+ return buf.toString();
+ }
+
+ /**
+ * Defines the direction of a {@link Carbon} message.
+ */
+ public static enum Direction {
+ received,
+ sent
+ }
+
+ /**
+ * Packet extension indicating that a message may not be carbon-copied. Adding this
+ * extension to any message will disallow that message from being copied.
+ */
+ public static class Private implements PacketExtension {
+ public static final String ELEMENT = "private";
+
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ public String getNamespace() {
+ return Carbon.NAMESPACE;
+ }
+
+ public String toXML() {
+ return "<" + ELEMENT + " xmlns=\"" + Carbon.NAMESPACE + "\"/>";
+ }
+ }
+}
diff --git a/experimental/source/org/jivesoftware/smackx/carbons/CarbonManager.java b/experimental/source/org/jivesoftware/smackx/carbons/CarbonManager.java
new file mode 100644
index 000000000..9217860ce
--- /dev/null
+++ b/experimental/source/org/jivesoftware/smackx/carbons/CarbonManager.java
@@ -0,0 +1,213 @@
+/**
+ * Copyright 2013 Georg Lukas
+ *
+ * All rights reserved. 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.smackx.carbons;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.ConnectionCreationListener;
+import org.jivesoftware.smack.PacketCollector;
+import org.jivesoftware.smack.PacketListener;
+import org.jivesoftware.smack.SmackConfiguration;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.filter.PacketIDFilter;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Message;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smackx.ServiceDiscoveryManager;
+import org.jivesoftware.smackx.packet.DiscoverInfo;
+
+/**
+ * Packet extension for XEP-0280: Message Carbons. This class implements
+ * the manager for registering {@link Carbon} support, enabling and disabling
+ * message carbons.
+ *
+ * You should call enableCarbons() before sending your first undirected
+ * presence.
+ *
+ * @author Georg Lukas
+ */
+public class CarbonManager {
+
+ private static Map instances =
+ Collections.synchronizedMap(new WeakHashMap());
+
+ static {
+ Connection.addConnectionCreationListener(new ConnectionCreationListener() {
+ public void connectionCreated(Connection connection) {
+ new CarbonManager(connection);
+ }
+ });
+ }
+
+ private Connection connection;
+ private volatile boolean enabled_state = false;
+
+ private CarbonManager(Connection connection) {
+ ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
+ sdm.addFeature(Carbon.NAMESPACE);
+ this.connection = connection;
+ instances.put(connection, this);
+ }
+
+ /**
+ * Obtain the CarbonManager responsible for a connection.
+ *
+ * @param connection the connection object.
+ *
+ * @return a CarbonManager instance
+ */
+ public static CarbonManager getInstanceFor(Connection connection) {
+ CarbonManager carbonManager = instances.get(connection);
+
+ if (carbonManager == null) {
+ carbonManager = new CarbonManager(connection);
+ }
+
+ return carbonManager;
+ }
+
+ private IQ carbonsEnabledIQ(final boolean new_state) {
+ IQ setIQ = new IQ() {
+ public String getChildElementXML() {
+ return "<" + (new_state? "enable" : "disable") + " xmlns='" + Carbon.NAMESPACE + "'/>";
+ }
+ };
+ setIQ.setType(IQ.Type.SET);
+ return setIQ;
+ }
+
+ /**
+ * Returns true if XMPP Carbons are supported by the server.
+ *
+ * @return true if supported
+ */
+ public boolean isSupportedByServer() {
+ try {
+ DiscoverInfo result = ServiceDiscoveryManager
+ .getInstanceFor(connection).discoverInfo(connection.getServiceName());
+ return result.containsFeature(Carbon.NAMESPACE);
+ }
+ catch (XMPPException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Notify server to change the carbons state. This method returns
+ * immediately and changes the variable when the reply arrives.
+ *
+ * You should first check for support using isSupportedByServer().
+ *
+ * @param new_state whether carbons should be enabled or disabled
+ */
+ public void sendCarbonsEnabled(final boolean new_state) {
+ IQ setIQ = carbonsEnabledIQ(new_state);
+
+ connection.addPacketListener(new PacketListener() {
+ public void processPacket(Packet packet) {
+ IQ result = (IQ)packet;
+ if (result.getType() == IQ.Type.RESULT) {
+ enabled_state = new_state;
+ }
+ connection.removePacketListener(this);
+ }
+ }, new PacketIDFilter(setIQ.getPacketID()));
+
+ connection.sendPacket(setIQ);
+ }
+
+ /**
+ * Notify server to change the carbons state. This method blocks
+ * some time until the server replies to the IQ and returns true on
+ * success.
+ *
+ * You should first check for support using isSupportedByServer().
+ *
+ * @param new_state whether carbons should be enabled or disabled
+ *
+ * @return true if the operation was successful
+ */
+ public boolean setCarbonsEnabled(final boolean new_state) {
+ if (enabled_state == new_state)
+ return true;
+
+ IQ setIQ = carbonsEnabledIQ(new_state);
+
+ PacketCollector collector =
+ connection.createPacketCollector(new PacketIDFilter(setIQ.getPacketID()));
+ connection.sendPacket(setIQ);
+ IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+ collector.cancel();
+
+ if (result != null && result.getType() == IQ.Type.RESULT) {
+ enabled_state = new_state;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Helper method to enable carbons.
+ *
+ * @return true if the operation was successful
+ */
+ public boolean enableCarbons() {
+ return setCarbonsEnabled(true);
+ }
+
+ /**
+ * Helper method to disable carbons.
+ *
+ * @return true if the operation was successful
+ */
+ public boolean disableCarbons() {
+ return setCarbonsEnabled(false);
+ }
+
+ /**
+ * Check if carbons are enabled on this connection.
+ */
+ public boolean getCarbonsEnabled() {
+ return this.enabled_state;
+ }
+
+ /**
+ * Obtain a Carbon from a message, if available.
+ *
+ * @param msg Message object to check for carbons
+ *
+ * @return a Carbon if available, null otherwise.
+ */
+ public static Carbon getCarbon(Message msg) {
+ Carbon cc = (Carbon)msg.getExtension("received", Carbon.NAMESPACE);
+ if (cc == null)
+ cc = (Carbon)msg.getExtension("sent", Carbon.NAMESPACE);
+ return cc;
+ }
+
+ /**
+ * Mark a message as "private", so it will not be carbon-copied.
+ *
+ * @param msg Message object to mark private
+ */
+ public static void disableCarbons(Message msg) {
+ msg.addExtension(new Carbon.Private());
+ }
+}
diff --git a/experimental/source/org/jivesoftware/smackx/carbons/provider/CarbonManagerProvider.java b/experimental/source/org/jivesoftware/smackx/carbons/provider/CarbonManagerProvider.java
new file mode 100644
index 000000000..f76f33b18
--- /dev/null
+++ b/experimental/source/org/jivesoftware/smackx/carbons/provider/CarbonManagerProvider.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2013 Georg Lukas
+ *
+ * All rights reserved. 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.smackx.carbons.provider;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.jivesoftware.smackx.carbons.Carbon;
+import org.jivesoftware.smackx.carbons.Carbon.Direction;
+import org.jivesoftware.smackx.forward.Forwarded;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * This class implements the {@link PacketExtensionProvider} to parse
+ * cabon copied messages from a packet. It will return a {@link Carbon} packet extension.
+ *
+ * @author Georg Lukas
+ *
+ */
+public class CarbonManagerProvider implements PacketExtensionProvider {
+
+ public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+ Direction dir = Direction.valueOf(parser.getName());
+ Forwarded fwd = null;
+
+ boolean done = false;
+ while (!done) {
+ int eventType = parser.next();
+ if (eventType == XmlPullParser.START_TAG && parser.getName().equals("forwarded")) {
+ fwd = (Forwarded) PacketParserUtils.parsePacketExtension(Forwarded.ELEMENT_NAME, Forwarded.NAMESPACE, parser);
+ }
+ else if (eventType == XmlPullParser.END_TAG && dir == Direction.valueOf(parser.getName()))
+ done = true;
+ }
+ if (fwd == null)
+ throw new Exception("sent/received must contain exactly one tag");
+ return new Carbon(dir, fwd);
+ }
+}
diff --git a/experimental/source/org/jivesoftware/smackx/experimental/ExperimentalProviderInitializer.java b/experimental/source/org/jivesoftware/smackx/experimental/ExperimentalProviderInitializer.java
new file mode 100644
index 000000000..70c8e8760
--- /dev/null
+++ b/experimental/source/org/jivesoftware/smackx/experimental/ExperimentalProviderInitializer.java
@@ -0,0 +1,11 @@
+package org.jivesoftware.smackx.experimental;
+
+import org.jivesoftware.smack.provider.UrlProviderFileInitializer;
+
+public class ExperimentalProviderInitializer extends UrlProviderFileInitializer {
+
+ @Override
+ protected String getFilePath() {
+ return "classpath:META-INF/experimental.providers";
+ }
+}
diff --git a/experimental/test/org/jivesoftware/smackx/carbons/CarbonTest.java b/experimental/test/org/jivesoftware/smackx/carbons/CarbonTest.java
new file mode 100644
index 000000000..f07afba57
--- /dev/null
+++ b/experimental/test/org/jivesoftware/smackx/carbons/CarbonTest.java
@@ -0,0 +1,110 @@
+/**
+ * All rights reserved. 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.smackx.carbons;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Properties;
+
+import org.jivesoftware.smack.provider.ProviderManager;
+import org.jivesoftware.smack.test.util.TestUtils;
+import org.jivesoftware.smackx.carbons.provider.CarbonManagerProvider;
+import org.jivesoftware.smackx.forward.Forwarded;
+import org.jivesoftware.smackx.forward.provider.ForwardedProvider;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+
+import com.jamesmurty.utils.XMLBuilder;
+
+public class CarbonTest {
+
+ private static Properties outputProperties = new Properties();
+ static {
+ outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
+ }
+
+ @BeforeClass
+ public static void setup() {
+ ProviderManager.getInstance().addExtensionProvider("forwarded", "urn:xmpp:forward:0", new ForwardedProvider());
+ }
+
+ @Test
+ public void carbonSentTest() throws Exception {
+ XmlPullParser parser;
+ String control;
+ Carbon cc;
+ Forwarded fwd;
+
+ control = XMLBuilder.create("sent")
+ .e("forwarded")
+ .a("xmlns", "urn:xmpp:forwarded:0")
+ .e("message")
+ .a("from", "romeo@montague.com")
+ .asString(outputProperties);
+
+ parser = TestUtils.getParser(control, "sent");
+ cc = (Carbon) new CarbonManagerProvider().parseExtension(parser);
+ fwd = cc.getForwarded();
+
+ // meta
+ assertEquals(Carbon.Direction.sent, cc.getDirection());
+
+ // no delay in packet
+ assertEquals(null, fwd.getDelayInfo());
+
+ // check message
+ assertEquals("romeo@montague.com", fwd.getForwardedPacket().getFrom());
+
+ // check end of tag
+ assertEquals(XmlPullParser.END_TAG, parser.getEventType());
+ assertEquals("sent", parser.getName());
+ }
+
+ @Test
+ public void carbonReceivedTest() throws Exception {
+ XmlPullParser parser;
+ String control;
+ Carbon cc;
+
+ control = XMLBuilder.create("received")
+ .e("forwarded")
+ .a("xmlns", "urn:xmpp:forwarded:0")
+ .e("message")
+ .a("from", "romeo@montague.com")
+ .asString(outputProperties);
+
+ parser = TestUtils.getParser(control, "received");
+ cc = (Carbon) new CarbonManagerProvider().parseExtension(parser);
+
+ assertEquals(Carbon.Direction.received, cc.getDirection());
+
+ // check end of tag
+ assertEquals(XmlPullParser.END_TAG, parser.getEventType());
+ assertEquals("received", parser.getName());
+ }
+
+ @Test(expected=Exception.class)
+ public void carbonEmptyTest() throws Exception {
+ XmlPullParser parser;
+ String control;
+
+ control = XMLBuilder.create("sent")
+ .a("xmlns", "urn:xmpp:forwarded:0")
+ .asString(outputProperties);
+
+ parser = TestUtils.getParser(control, "sent");
+ new CarbonManagerProvider().parseExtension(parser);
+ }
+}
diff --git a/test-unit/org/jivesoftware/smack/DummyConnection.java b/test-unit/org/jivesoftware/smack/DummyConnection.java
index 57753a1ba..308644a49 100644
--- a/test-unit/org/jivesoftware/smack/DummyConnection.java
+++ b/test-unit/org/jivesoftware/smack/DummyConnection.java
@@ -26,6 +26,14 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.ConnectionConfiguration;
+import org.jivesoftware.smack.ConnectionCreationListener;
+import org.jivesoftware.smack.ConnectionListener;
+import org.jivesoftware.smack.PacketCollector;
+import org.jivesoftware.smack.Roster;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.Connection.ListenerWrapper;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
diff --git a/test-unit/org/jivesoftware/smack/keepalive/KeepaliveTest.java b/test-unit/org/jivesoftware/smack/keepalive/KeepaliveTest.java
index 937bcebbb..870f9b937 100644
--- a/test-unit/org/jivesoftware/smack/keepalive/KeepaliveTest.java
+++ b/test-unit/org/jivesoftware/smack/keepalive/KeepaliveTest.java
@@ -13,7 +13,6 @@ import org.jivesoftware.smack.DummyConnection;
import org.jivesoftware.smack.PacketInterceptor;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.SmackConfiguration;
-import org.jivesoftware.smack.TestUtils;
import org.jivesoftware.smack.ThreadedDummyConnection;
import org.jivesoftware.smack.filter.IQTypeFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
@@ -22,6 +21,7 @@ import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.ping.PingFailedListener;
import org.jivesoftware.smack.ping.packet.Ping;
+import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.junit.After;
import org.junit.Before;
diff --git a/test-unit/org/jivesoftware/smack/packet/StreamErrorTest.java b/test-unit/org/jivesoftware/smack/packet/StreamErrorTest.java
index 9c3e9dfc6..302ef0e7d 100644
--- a/test-unit/org/jivesoftware/smack/packet/StreamErrorTest.java
+++ b/test-unit/org/jivesoftware/smack/packet/StreamErrorTest.java
@@ -2,8 +2,8 @@ package org.jivesoftware.smack.packet;
import static org.junit.Assert.*;
-import org.jivesoftware.smack.TestUtils;
import org.jivesoftware.smack.packet.StreamError;
+import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.junit.Test;
import org.xmlpull.v1.XmlPullParser;
diff --git a/test-unit/org/jivesoftware/smack/parsing/ParsingExceptionTest.java b/test-unit/org/jivesoftware/smack/parsing/ParsingExceptionTest.java
index fd674ae0c..121d1e2a1 100644
--- a/test-unit/org/jivesoftware/smack/parsing/ParsingExceptionTest.java
+++ b/test-unit/org/jivesoftware/smack/parsing/ParsingExceptionTest.java
@@ -3,11 +3,11 @@ package org.jivesoftware.smack.parsing;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import org.jivesoftware.smack.TestUtils;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smack.provider.ProviderManager;
+import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.junit.After;
import org.junit.Before;
diff --git a/test-unit/org/jivesoftware/smack/TestUtils.java b/test-unit/org/jivesoftware/smack/test/util/TestUtils.java
similarity index 98%
rename from test-unit/org/jivesoftware/smack/TestUtils.java
rename to test-unit/org/jivesoftware/smack/test/util/TestUtils.java
index 0eae51e35..7d240bb03 100644
--- a/test-unit/org/jivesoftware/smack/TestUtils.java
+++ b/test-unit/org/jivesoftware/smack/test/util/TestUtils.java
@@ -17,7 +17,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.jivesoftware.smack;
+package org.jivesoftware.smack.test.util;
import java.io.IOException;
import java.io.StringReader;
diff --git a/test-unit/org/jivesoftware/smack/util/PacketParserUtilsTest.java b/test-unit/org/jivesoftware/smack/util/PacketParserUtilsTest.java
index e7c25161a..5b4025ba4 100644
--- a/test-unit/org/jivesoftware/smack/util/PacketParserUtilsTest.java
+++ b/test-unit/org/jivesoftware/smack/util/PacketParserUtilsTest.java
@@ -24,10 +24,10 @@ import java.util.TimeZone;
import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Diff;
-import org.jivesoftware.smack.TestUtils;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
+import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smackx.packet.DelayInformation;
import org.junit.Ignore;
import org.junit.Test;
diff --git a/test-unit/org/jivesoftware/smackx/forward/ForwardedTest.java b/test-unit/org/jivesoftware/smackx/forward/ForwardedTest.java
index 6ffe05657..7cea6539f 100644
--- a/test-unit/org/jivesoftware/smackx/forward/ForwardedTest.java
+++ b/test-unit/org/jivesoftware/smackx/forward/ForwardedTest.java
@@ -24,8 +24,8 @@ import java.util.GregorianCalendar;
import java.util.Properties;
import java.util.TimeZone;
-import org.jivesoftware.smack.TestUtils;
import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smackx.packet.DelayInfo;
import org.jivesoftware.smackx.packet.DelayInformation;
import org.jivesoftware.smackx.forward.Forwarded;
diff --git a/test-unit/org/jivesoftware/smackx/ping/PingTest.java b/test-unit/org/jivesoftware/smackx/ping/PingTest.java
index c127f18dc..96b0fba63 100644
--- a/test-unit/org/jivesoftware/smackx/ping/PingTest.java
+++ b/test-unit/org/jivesoftware/smackx/ping/PingTest.java
@@ -16,11 +16,11 @@
package org.jivesoftware.smackx.ping;
import org.jivesoftware.smack.DummyConnection;
-import org.jivesoftware.smack.TestUtils;
import org.jivesoftware.smack.ThreadedDummyConnection;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.ping.packet.Ping;
+import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smackx.packet.DiscoverInfo;
import org.junit.Before;
diff --git a/test-unit/org/jivesoftware/smackx/pubsub/ConfigureFormTest.java b/test-unit/org/jivesoftware/smackx/pubsub/ConfigureFormTest.java
index 784c56f39..68834d6b8 100644
--- a/test-unit/org/jivesoftware/smackx/pubsub/ConfigureFormTest.java
+++ b/test-unit/org/jivesoftware/smackx/pubsub/ConfigureFormTest.java
@@ -22,7 +22,7 @@ package org.jivesoftware.smackx.pubsub;
import static org.junit.Assert.assertEquals;
import org.jivesoftware.smack.SmackConfiguration;
-import org.jivesoftware.smack.ThreadedDummyConnection;
+import org.jivesoftware.smack.ThreadedDummyConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.PacketExtension;
diff --git a/test-unit/org/jivesoftware/smackx/pubsub/ItemValidationTest.java b/test-unit/org/jivesoftware/smackx/pubsub/ItemValidationTest.java
index eb967e5f7..b28b6b9ae 100644
--- a/test-unit/org/jivesoftware/smackx/pubsub/ItemValidationTest.java
+++ b/test-unit/org/jivesoftware/smackx/pubsub/ItemValidationTest.java
@@ -26,10 +26,10 @@ import static org.junit.Assert.assertTrue;
import java.io.Reader;
import java.io.StringReader;
-import org.jivesoftware.smack.TestUtils;
import org.jivesoftware.smack.ThreadedDummyConnection;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
import org.jivesoftware.smackx.pubsub.provider.ItemsProvider;
diff --git a/workgroup/source/org/jivesoftware/smackx/workgroup/WorkgroupProviderInitializer.java b/workgroup/source/org/jivesoftware/smackx/workgroup/WorkgroupProviderInitializer.java
new file mode 100644
index 000000000..a198a32e6
--- /dev/null
+++ b/workgroup/source/org/jivesoftware/smackx/workgroup/WorkgroupProviderInitializer.java
@@ -0,0 +1,11 @@
+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";
+ }
+}