AdHocCommandDataProvider getting in the way?

I have a smackx I’m writing for a protocol called Openlink. An example of a packet I’m trying to send/receive is below.

The problem seems to be that I’m trying to register an IQProvider for command#http://jabber.org/protocol/commands but only AdHocCommandDataProvider seems to receive it.

The provider works fine in Smack 3.2.2 so wondering what’s changed in Smack 4.x which breaks this?

Sent:

<iq id='8fR4x-3' to='vmstsp.garyvms.gltd.local' type='set'> <command xmlns='http://jabber.org/protocol/commands' action='execute'
  node='http://xmpp.org/protocol/openlink:01:00:00#get-profiles'>
  <iodata xmlns='urn:xmpp:tmp:io-data' type='input'>
  <in>
  <jid>gary@garyvms.gltd.local</jid>
  </in>
  </iodata> </command>
</iq>

Received:

<iq type="result" id="1deK7-3" from="vmstsp.garyvms.gltd.local" to="gary@garyvms.gltd.local/office"> <command xmlns="http://jabber.org/protocol/commands" sessionid="7905"
  node="http://xmpp.org/protocol/openlink:01:00:00#get-profiles" status="completed">
  <iodata xmlns="urn:xmpp:tmp:io-data" type="output">
  <out>
  <profiles xmlns="http://xmpp.org/protocol/openlink:01:00:00/profiles">
  <profile id="gary_office" device="vmstsp" default="true"
  online="true">
  <actions>
  <action id="ClearConnection" label="ClearConnection" />
  <action id="ClearCall" label="ClearCall" />
  <action id="AddThirdParty" label="AddThirdParty" />
  <action id="RemoveThirdParty" label="RemoveThirdParty" />
  <action id="MuteParty" label="MuteParty" />
  <action id="UnMuteParty" label="UnMuteParty" />
  <action id="StartVoiceDrop" label="StartVoiceDrop" />
  <action id="StopVoiceDrop" label="StopVoiceDrop" />
  <action id="JoinCall" label="JoinCall" />
  <action id="SendDigit" label="SendDigit" />
  </actions>
  </profile>
  <profile id="gary_sip" device="vmstsp" default="false"
  online="true">
  <actions>
  <action id="ClearConnection" label="ClearConnection" />
  <action id="ClearCall" label="ClearCall" />
  <action id="AddThirdParty" label="AddThirdParty" />
  <action id="RemoveThirdParty" label="RemoveThirdParty" />
  <action id="MuteParty" label="MuteParty" />
  <action id="UnMuteParty" label="UnMuteParty" />
  <action id="StartVoiceDrop" label="StartVoiceDrop" />
  <action id="StopVoiceDrop" label="StopVoiceDrop" />
  <action id="JoinCall" label="JoinCall" />
  <action id="SendDigit" label="SendDigit" />
  </actions>
  </profile>
  </profiles>
  </out>
  </iodata> </command>
</iq>

Actually nothing has changed as far as I can remember.

Did you accidentally exchanged sent and received in your post? Why do you need to replace the provider?

Think the order in which IQ Providers are added has changed. I’ve stuck the code in a ConnecitonListener and registering the IQProvider on authenticated - which works for my purposes at least.

Why am I replacing the provider…good question, not 100% sure whether the existing AdhocCommand stuff can be used with the example above unless I’m missing something. I’ll spend some time looking into it but as with projects under deadlines there are casualties along the way, one of which is sometimes figuring out the best practise within the existing framework - especially if you have to wade through source code to understand it.

Incidentally in trying to use AhocCommandManager I was trying to get some info what Manager objects do in Smack. The Manager class itself has no documentation. Is there any info on this?

See:
Manager (smack 4.0.4 API)

And btw. love XmlStringBuilder. Such a neat little utility class - why didn’t anyone (myself included!) think of writing this before?

Think the order in which IQ Providers are added has changed. I’ve stuck the code in a ConnecitonListener and registering the IQProvider on authenticated - which works for my purposes at least.
The latest registered provider should always win (ie. overwrite the existing one). Smack does configure the default/provided provides once the first connection class is initialized. You can prefer class initialization by calling SmackConfiguration.getVersion() and then configure you custom provider. No need to resort to connection listeners. I think this is the cleaner solution. But I’m going to make Smack initialize itself when ProviderManager is invoked, which should make this workaround no longer required in the future.

especially if you have to wade through source code to understand it.
Got the message and fully agree, Smack is lacking documentation. Unfortunately I don’t have the time and resources to change that at the moment. Pull requests welcome

I don’t have written any code in smackx.commands, so I have to read myself into the code too in order to answer you some more questions. But from a short look it appears that the provider and AdHocCommandData could be sufficient for your use case.

Incidentally in trying to use AhocCommandManager I was trying to get some info what Manager objects do in Smack. The Manager class itself has no documentation. Is there any info on this?
Well the ‘Manager’ class is not something the average Smack user should bother with. It’s a container for a weak reference to the connection instance of a Manager.

Thanks, there exists actually a similar library that Smack uses for XML construction in its unit tests, but I decided against my normal behavior and not re-use existing code for various reasons:

  1. Since Smack targets Android and JVM you never know if a jar will actually run on all platforms and it’s usually not easy to change 3rd party code to make it compatible again.

  2. Smack follows the philosophy of being minimalistic, e.g. to not depend on to many 3rd party libraries

  3. XmlStringBuilder provides some convenience methods for typical XMPP XML cases (e.g. optIntAttribute).

  4. It uses LazyStringBuilder (but can be changed to strict)

  5. It’s a simple one file library, no real need to relay on other (bigger) libraries

Will definitely look through the code base and see if I can submit some pull requests.

That said, I’ve been using Smack since 2007 and getting a little tired of writing XmlPullParser PacketExtensions.

For example, here’s one for the most simple XML element in the Openlink spec:

public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
        ProfilesExtension profiles = new ProfilesExtension();
        boolean done = false;
        while (!done) {
            int eventType = parser.next();
            logger.debug(eventType + " " + parser.getName());
            if (eventType == XmlPullParser.START_TAG) {
                if ("profile".equals(parser.getName())) {
                    Profile profile = new Profile();
                    profile.setId(parser.getAttributeValue(null, "id"));
                    profile.setDevice(parser.getAttributeValue(null, "device"));
                    profile.setLabel(parser.getAttributeValue(null, "label"));
                    profile.setDefaultProfile(new Boolean(parser.getAttributeValue(null, "default")));
                    profile.setOnline(new Boolean(parser.getAttributeValue(null, "online")));
                    profiles.getProfiles().add(profile);
                    logger.debug("Added profile " + " " + profile.getId());
                    while (!(eventType == XmlPullParser.END_TAG && "profile".equals(parser.getName()))) {
                        logger.debug(eventType == XmlPullParser.END_TAG && "profile".equals(parser.getName()));
                        if (eventType == XmlPullParser.START_TAG && "actions".equals(parser.getName())) {
                            while (!(eventType == XmlPullParser.END_TAG && "actions".equals(parser.getName()))) {
                                if (eventType == XmlPullParser.START_TAG && "action".equals(parser.getName())) {
                                    Action action = new Action();
                                    action.setId(parser.getAttributeValue(null, "id"));
                                    action.setLabel(parser.getAttributeValue(null, "label"));
                                    profile.getActions().put(action.getId(), action);
                                    logger.debug("Added action " + " " + action.getId());
                                }
                                eventType = parser.next();
                            }
                        }
                        eventType = parser.next();
                    }
                }
            } else if (eventType == XmlPullParser.END_TAG) {
                if ("profiles".equals(parser.getName())) {
                    done = true;
                }
            }
        }
        return profiles;
    }

JAXB is drastically needed to help with this kind of thing.

Flow - not to offend, but have you any experience with the Babbler XMPP Client lib?

sco0ter / Babbler / source / — Bitbucket

It uses JAXB and seems fairly complete even though it was only started this year.

No. And I’m not offended, no worries.

But sco0ter is csh here on the forums, I’m sure he will answer your questions.

But JAXB is no option for Smack for various reasons (that already got discussed here before, see https://igniterealtime.jiveon.com/message/233807#233807 ). In short:

  • It’s not available on Android, and Smack design philosophy is to try to have as less dependencies as possible

  • It’s more heavyweight compared to XPP (and I also think it’s not as resource efficient as XPP), so I don’t consider is suitable for resource constraint platforms

but even if I’m wrong and it would become available on Android and benchmarks would show that it has a reasonable performance/resource consumption, switching to JAXB would mean to rewrite every single provider in Smack.

I’m also don’t think that XPP is hard to use, but I’m biased here obviously. It’s also correct that it’s a little bit harder to write a correct parser for XML with XPP. For example showed parser does not consider the parsing depth when determining the end tag (and so do many other parsers in Smack, but I’m currently in the progress of changing that).

Uh and it appears to be possible to use JAXB with Smack: Using JAXB with SMACK as IQ parser

But don’t ask me on the details

Good points.

Lack of Android support for JAXB and performance are two issues which we’ll have to investigate before moving from Smack.

That said on the server side at least we’ve built a number of plugins which use Spring’s JAXBMarshaller and performance is excellent so I think it’s a change Openfire can make if needs be.

You mention that the parser I quoted above does not consider parsing depth. I spent some time looking at the various providers in the smackx lib, do you have any you could point me towards as best of breed?

thanks!

Yeah saw that, only issue might be that the class they use to go from JAXB to XPP is GPL licensed so wondering if that might create a problem since our customers prefer MIT/Apache licensed code, otherwise looks promising!

Smack/MUCUserProvider.java at master · igniterealtime/Smack · GitHub

Basically the condition to determine if you are at the end tag of a element should not be

parser.getEventType == END_TAG && parser.getName().equals(“foo”)

but

parser.getEventType == END_TAG && parser.getDepth() == startTagDepth

otherwise you could end up considering to be at the end of a tag when it comes to nested elements with the same name. This has hit Smack with the introduction of carbons, where a message can be contained within a message.