Shot in the dark - Smack API, Android SDK and ClassCastExceptions

I think this is a bit of a long shot but I figured I might as well give this a try.

So I’m using this patched version of the Smack API with the Google Phone Android SDK to try to get a custom client working on that platform. What I keep running into in different places are ClassCastExceptions when the API tries to cast Packets into subclasses.

Here’s an example from the ServiceDiscoveryManager

PacketCollector collector =

connection.createPacketCollector(new PacketIDFilter(disco.getPacketID()));

connection.sendPacket(disco);

// Wait up to 5 seconds for a result.

IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

// Stop queuing results

collector.cancel();

if (result == null) {

Log.i(“TestClient”, “++++ No response from the server.”);

}

if (result.getType() == IQ.Type.ERROR) {

Log.i(“TestClient”,result.getError().toString());

}

Log.i(“TestClient”, “>>>> “” + result.getClass().getName()+”"");

Packet test = (Packet) result;

Log.i(“TestClient”, "Result class loader: " + test.getClass().getClassLoader().toString());

Log.i(“TestClient”, "DI Class loader: " + DiscoverItems.class.getClassLoader().toString());

return (DiscoverItems) test;

the cast of the Packet to DiscoverItems will fail with a ClassCastException. As you can see from the Log statements in this case I’ve even made sure the Class loaders are the same and that the Class is the parent class. I can even successfully cast it to a Packet but not to a sub class.

Any help would be appreciated before I pull my hair out.

Thanks.

One other data point. when I log the class type I keep getting this:

“org.jivesoftware.smack.PacketReader$4”

i can’t really tell why.

Hey Mike,

That usually happens when smackx.jar is missing in the classpath. When this problem happens with custom classes (i.e. classes that you implemented) then it could be a problem of registering your extension as a PacketExtension or IQExtension.

Regards,

– Gato

So after mucking around a bunch I tracked it down to a ProviderManager issue, on this platform the smack.providers isn’t getting either read or the ClassLoader isn’t working. I’m not really a Java guy so I’m not totally up on the way the ClassLoaders work but it doesn’t seem to be working properly on here. I’ve had other similar issues with a couple of Smack classes that use static loaders to attach themselves to the XMPPConnection class.

I eventually got this working by manually configuring the providers in the provider manager. Probably not the best solution but its working for me so far.

Ok so I’ve gotten a few questions about this so I guess I should really answer my own question

From what I can tell each provider has a static function that registers it with the provider manage this usually happens on class load but this isn’t working with Android so none of the providers are getting registered with the provider manager. However you can do that manually. So I have a function like so:

You can call it with configure(ProviderManager.getInstance()) I do this before calling new XMPPConnection()

public void configure(ProviderManager pm) {

// Private Data Storage

pm.addIQProvider(“query”,“jabber:iq:private”, new PrivateDataManager.PrivateDataIQProvider());

// Time

try {

pm.addIQProvider(“query”,“jabber:iq:time”, Class.forName(“org.jivesoftware.smackx.packet.Time”));

} catch (ClassNotFoundException e) {

Log.w(“TestClient”, “Can’t load class for org.jivesoftware.smackx.packet.Time”);

}

// Roster Exchange

pm.addExtensionProvider(“x”,“jabber:x:roster”, new RosterExchangeProvider());

// Message Events

pm.addExtensionProvider(“x”,“jabber:x:event”, new MessageEventProvider());

// Chat State

    pm.addExtensionProvider("active","http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());

    pm.addExtensionProvider("composing","http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());

    pm.addExtensionProvider("paused","http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());

    pm.addExtensionProvider("inactive","http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());

    pm.addExtensionProvider("gone","http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());

// XHTML

    pm.addExtensionProvider("html","http://jabber.org/protocol/xhtml-im", new XHTMLExtensionProvider());

// Group Chat Invitations

pm.addExtensionProvider(“x”,“jabber:x:conference”, new GroupChatInvitation.Provider());

// Service Discovery # Items

    pm.addIQProvider("query","http://jabber.org/protocol/disco#items", new DiscoverItemsProvider());

// Service Discovery # Info

    pm.addIQProvider("query","http://jabber.org/protocol/disco#info", new DiscoverInfoProvider());

// Data Forms

pm.addExtensionProvider(“x”,“jabber:x:data”, new DataFormProvider());

// MUC User

    pm.addExtensionProvider("x","http://jabber.org/protocol/muc#user", new MUCUserProvider());

// MUC Admin

    pm.addIQProvider("query","http://jabber.org/protocol/muc#admin", new MUCAdminProvider());

// MUC Owner

    pm.addIQProvider("query","http://jabber.org/protocol/muc#owner", new MUCOwnerProvider());

// Delayed Delivery

pm.addExtensionProvider(“x”,“jabber:x:delay”, new DelayInformationProvider());

// Version

try {

pm.addIQProvider(“query”,“jabber:iq:version”, Class.forName(“org.jivesoftware.smackx.packet.Version”));

} catch (ClassNotFoundException e) {

// Not sure what’s happening here.

}

// VCard

pm.addIQProvider(“vCard”,“vcard-temp”, new VCardProvider());

// Offline Message Requests

    pm.addIQProvider("offline","http://jabber.org/protocol/offline", new OfflineMessageRequest.Provider());

// Offline Message Indicator

    pm.addExtensionProvider("offline","http://jabber.org/protocol/offline", new OfflineMessageInfo.Provider());

// Last Activity

pm.addIQProvider(“query”,“jabber:iq:last”, new LastActivity.Provider());

// User Search

pm.addIQProvider(“query”,“jabber:iq:search”, new UserSearch.Provider());

// SharedGroupsInfo

    pm.addIQProvider("sharedgroup","http://www.jivesoftware.org/protocol/sharedgroup", new SharedGroupsInfo.Provider());

// JEP-33: Extended Stanza Addressing

    pm.addExtensionProvider("addresses","http://jabber.org/protocol/address", new MultipleAddressesProvider());

// FileTransfer

    pm.addIQProvider("si","http://jabber.org/protocol/si", new StreamInitiationProvider());

    pm.addIQProvider("query","http://jabber.org/protocol/bytestreams", new BytestreamsProvider());

    pm.addIQProvider("open","http://jabber.org/protocol/ibb", new IBBProviders.Open());

    pm.addIQProvider("close","http://jabber.org/protocol/ibb", new IBBProviders.Close());

    pm.addExtensionProvider("data","http://jabber.org/protocol/ibb", new IBBProviders.Data());

// Privacy

pm.addIQProvider(“query”,“jabber:iq:privacy”, new PrivacyProvider());

    pm.addIQProvider("command", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider());

    pm.addExtensionProvider("malformed-action", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.MalformedActionError());

    pm.addExtensionProvider("bad-locale", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.BadLocaleError());

    pm.addExtensionProvider("bad-payload", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.BadPayloadError());

    pm.addExtensionProvider("bad-sessionid", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.BadSessionIDError());

    pm.addExtensionProvider("session-expired", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.SessionExpiredError());

}

1 Like

Hey there,

Thanks for your comeback, bitdamaged. Your solution works just fine . However, I could not find anywhere the AdHocCommandDataProvider class.

Oh, and I dont know how the h*** did I miss this thread. I have started another with almost the same title … .

Thanks again, both you and Gato,

Mike

Ah sorry about that the AdHoc Command was actually something I specifically needed and I found somewhere out on the nets! So I’m actually building and patching my own Smack Jar.

If you need it let me know I can fill you in.

Dont think I will. Thanks

Hello, i have a problem with the vCard.load function that does nothing in an Android application but works well with a J2SE project.

To fix that problem, I tried to add the following line before creating the connection : ProviderManager.getInstance().addIQProvider(“vCard”, “vcard-temp”, new VCardProvider());

Since i added this, i have my listener function connectionClosedOnError (from ConnectionListener) which is called just after the first call to vCard.load…

I tried with the standard smack jars and also the ones provided by the bhoost project: same result with and without the code line above.

I faced the same issue and found a fix.

Actually, on Android, smack don’t take into account everything that is part of the smack.jar and smackx.jar meta data (basically, the content of the files "smack-config.xml and smack.provider is ignored) which explain why the various packet providers must be initialized by hand. I guess that the META-INF section is discarded when Android convert the .jar inot its private format.

Then, regarding your precise problem, I manage to fix this by applying the following patch:

Index: source/org/jivesoftware/smackx/provider/VCardProvider.java
===================================================================
--- source/org/jivesoftware/smackx/provider/VCardProvider.java  (revision 10629)
+++ source/org/jivesoftware/smackx/provider/VCardProvider.java  (working copy)
@@ -244,14 +244,16 @@
} private void appendText(StringBuilder result, Node node) {
-            NodeList childNodes = node.getChildNodes();
-            for (int i = 0; i < childNodes.getLength(); i++) {
-                Node nd = childNodes.item(i);
-                String nodeValue = nd.getNodeValue();
-                if (nodeValue != null) {
-                    result.append(nodeValue);
+            if (node.hasChildNodes()) {
+                NodeList childNodes = node.getChildNodes();
+                for (int i = 0; i < childNodes.getLength(); i++) {
+                    Node nd = childNodes.item(i);
+                    String nodeValue = nd.getNodeValue();
+                    if (nodeValue != null) {
+                        result.append(nodeValue);
+                    }
+                    appendText(result, nd);
}
-                appendText(result, nd);
}
}
}

I guess there is an error in the function that is visible only on Android because the org.w3c.dom package is not exactely the same then the one present in smack.jar but I’m not sure, since I’m not a Java expert. So, if somebody has information about this, he is welcome!

Cheers,

Thanks Mike,

The configure() method helps me. I am also getting

java.lang.ClassCastException: org.jivesoftware.smack.PacketReader$4:

when I am trying out the file transfer. For me the error is something to do with StreamInitiation stuff.

[java] at org.jivesoftware.smackx.filetransfer.FileTransferNegotiator.negotiateOutgoingTr ansfer(FileTransferNegotiator.java:401)

Which is for the line after the if()statement:

       if (iqResponse.getType().equals(IQ.Type.RESULT)) {
            StreamInitiation response =(StreamInitiation) siResponse;

I have never thought that it has anything to do with the Android repackaging of Jar. Should have searched the forum with android tag all the time later for android project, I guess.

Was this VCard fix for Android smack file transfer or something else?

Hi,

Could you please tell me if your patch solved your file transfer problem ?

Like others, I can’t send files to android phones, it seams that the asmack filetransferlistener si not working.

It would be very helpful if you could send me your compiled asmack patched version, because I can’t compile the whole source code.

Thanks,

DataSmith

Ryan thanks for posting this, it saved me a lot of hassle.

For the benefit of those who might search for this in the future, I was experiencing NullPointerException (NPE) with MultiUserChat.getRoomInfo returning -1 users from RoomInfo.getOccupantsCount(). Adding these providers fixed it:

pm.addIQProvider(“query”,“http://jabber.org/protocol/disco#info”, new DiscoverInfoProvider());

pm.addExtensionProvider(“x”,“jabber:x:data”, new DataFormProvider());