Smack Extensions

Hey all,

Starting with tomorrow’‘s daily build, you’‘ll see a new smackx.jar file. This JAR contains Smack standard extensions, which will generally implement various JEP’'s found at:

http://www.jabber.org/jeps/jeplist.php

So far, support for private data (JEP 49) is included (although it’'s not tested yet and may not work for another day or two). Gato will soon check-in support for sharing roster items (JEP 93) and is also working on message events (JEP 22).

The reason we’'re building a lot of this functionality into a seperate JAR is:

  • Not all users will need the functionality.

  • We want to keep the core JAR file as small as possible.

  • The main JAR file will eventually represent the core XMPP standard (once it is finalized) but nothing else. The smackx.jar will contain extended functionality the community has implemented but that other servers and clients may not support.

Regards,

Matt

Hi Matt,

I gave the Private data extension a try (Aug 6 build) using the DefaultPrivateData feature. I added a PrivateDataManager to a connection and it saves PD using DefaultPrivateData ok. When trying to retrieve PD, I would get a ClassCastException when calling getPrivateData on the manager. I determined that I wasn’'t registering an IQProvider for the element and namespace. I used

ProviderManager.addIQProvider(element,namespace,new PrivateDataManager.PrivateDataIQProvider());

(noob question: “PrivateDataManager.PrivateDataIQProvider()” is static, yet the compiler forces me to use “new”. I assume it’'s because the one method “ParseIQ” is not declared static?)

After that, the PrivateDataIQProvider.parseIQ wasn’‘t parsing correctly. Here’‘s patch that works for DefaultPrivateData, no guarantee it doesn’‘t break a custom pd handler (but it shouldn’'t):

public IQ parseIQ(XmlPullParser parser) throws Exception {
            PrivateData privateData = null;
            boolean done = false;
               String elementName = parser.getName();
               String namespace = parser.getNamespace();
               // See if any objects are registered to handle this private data type.
               Object provider = getPrivateDataProvider(elementName, namespace);
               // If there is a registered provider, use it.
               if (provider != null) {
                    if (provider instanceof PrivateDataProvider) {
                         privateData = ((PrivateDataProvider)provider).parsePrivateData(parser);
                    }
                    // Otherwise it''s a JavaBean, so use introspection.
                    else {
                         privateData = parseWithIntrospection(elementName, (Class)provider,
                                   parser);
                    }
               }
               else {
                    DefaultPrivateData data = new DefaultPrivateData(elementName, namespace);
                    boolean finished = false;
                while (!finished) {
                    int event = parser.next();
                    if (event == XmlPullParser.START_TAG) {
                        String name = parser.getName();
                        // If an empty element, set the value with the empty string.
                        if (parser.isEmptyElementTag()) {
                            data.setValue(name,"");
                        }
                        // Otherwise, get the the element text.
                        else {
                            event = parser.next();
                            if (event == XmlPullParser.TEXT) {
                                String value = parser.getText();
                                data.setValue(name, value);
                            }
                        }
                    }
                    else if (event == XmlPullParser.END_TAG) {
                        if (parser.getName().equals(elementName)) {
                            finished = true;
                        }
                    }
                }
                privateData = data;
               }
            IQ result = new PrivateDataResult(privateData);
            return result;
            }

Take care,

John

Never mind the dumb noob question…it’'s a class! (slaps self around a bit with a trout)

John,

Urg, the method name should be addPrivateDataProvider and not addIQProvider. I’'ll fix that. But, you have the right idea – just pass in a new instance of the provider class and that will be used for the parsing.

Regards,

Matt

Hi Matt,

The private data using DefaultPrivateData is still broken in the Aug13 build. When the method is called, the parser is already on the first element so the parser.next() at the beginning of the while loop is throwing it off.

public IQ parseIQ(XmlPullParser parser) throws Exception {
    PrivateData privateData = null;
    boolean done = false;
    while (!done) {
        *int eventType = parser.next();*
        if (eventType == XmlPullParser.START_TAG) {

Is there a reason that that outer while loop needs to stay? I had eliminated it from that previous patch I posted but maybe I’'ve missed something?

Also, you added a presenceChanged to the RosterListener interface (thanks!) but it never gets fired. In Roster.PresencePacketListener.processPacket the check is: entries.contains(key) where entries is an ArrayList of RosterEntries but “key” is a string.

And if you’'re keeping track of stuff that uses Smack, I came across this http://blojsim.sourceforge.net

Take care,

John

John,

Can you try out a more recent build? I’'m pretty sure that I already fixed this build.

Thanks for the blojsim link!

-Matt

Matt,

That was a typo. Both problems are in the Aug23 build.

John

Thanks for the update. I’'ll look into both issues now.

-Matt

I can’'t see anything wrong with the private data – can you paste in some example code that shows the issue? I just tested with the following and everything worked:

PrivateDataManager privManager = new PrivateDataManager(con);
DefaultPrivateData privateData = new DefaultPrivateData("foo", "bar");
privateData.setValue("favoriteColor", "blue");
privateData.setValue("favoriteFood", "pizza");
privManager.setPrivateData(privateData); DefaultPrivateData resultData = (DefaultPrivateData)privManager.getPrivateData("foo", "bar"); System.out.println(resultData.getValue("favoriteColor"));
System.out.println(resultData.getValue("favoriteFood"));

Thanks,

Matt

Ok, it does work

When I referenced the externally built jars, everything worked but when I referenced the project in Eclipse, I couldn’'t even get you example to work. It got that ClassCastException which clued me in that it was something about the IQ provider.

I’'ve been calling

ProviderManager.addIQProvider("notes","http://us.insurgent.javeren/notes",new PrivateDataManager.PrivateDataIQProvider());

which used to work in the older builds. Now the element and namespace have to be “query” and “jabber:iq:private”.

Won’'t this preclude using PrivateDataManager.PrivateDataIQProvider() for some generic items and a custom one for others?

Take care,

Mr. Going Braindead

John,

The reason it wouldn’‘t work through Eclipse is that you didn’‘t have the smack.providers file in the classpath. Most IDE’'s will let you add items to the classpath of the project which would let you compile from inside the IDE.

The PrivateDataIQProvider shouldn’‘t be registered by external classes since it’‘s already registered to handle private data IQ’‘s in the smack.providers file that is inside smackx.jar. Plus, I just don’‘t understand your code – it doesn’‘t make sense since private data uses an established element name and namespace according to its JEP. I’‘m not quite sure what you’'re trying to do…

Regards,

Matt

Matt,

Yea, I had my own smack.providers in the project so it wasn’'t picking up anything from the one you provided. Adding the appropriate entries to my smack.providers also lets it work.

The reason I was doing what I’'m doing is that I was confusing the function of PrivateDataIQProvider with addPrivateDataProvider. The reason for this confusion was that if you go back to the Aug06 build where I started playing with the PD stuff, the only way that it would work was to call ProviderManager.addIQProvider(“notes”,“http://us.insurgent.javeren/notes”,new PrivateDataManager.PrivateDataIQProvider()); and then loadPrivateData(“notes”,“http://us.insurgent.javeren/notes”);

Then when I saw addIQProvider having to be called with query/jabber:iq:private I thought we were going to be limited to a single handler for all private data. Now I see that’'s not the case

Though I bet there are some more gems like this in my code

Take care,

John