Jan 18, 2008 1:16 PM
Shot in the dark - Smack API, Android SDK and ClassCastExceptions.
-
Like (0)
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());
}
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
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());
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,
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