Today, i am trying to send an IQ message from a XMPP client to other in my project: Manage device behind NAT using TR069 combined with XMPP Chat Solutions.

TR069 XMPP NAT Traversal

But i get an error response from Server as following:

<error code='503'
       type='cancel'>
<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>

Reason we got this error because we are using custom XMPP IQ message. And we must implement an IQProvider to parse correctly our custom iq message.

I suppose our custom message looks like this:

<iq type="get" from="master@vndemy.com/Smack" to="vunb@vndemy.com/Smack">
    <connectionRequest xmlns="urn:broadband-forum-org:cwmp:xmppConnReq-1-0">
        <username>test</username>
        <password>test</password>
    </connectionRequest>
</iq>

Then we will create two java classes name IQConnectionRequest and IQConnectionRequestProvider:

IQConnectionRequest.java

package vn.vnpttech.ssdc.nms.mediation.stbacs.packet;

import org.jivesoftware.smack.packet.IQ;

/**
 * IQConnectionRequest
 *
 * @author Vunb
 * @date Jun 5, 2015
 * @update Jun 5, 2015
 */
public class IQConnectionRequest extends IQ {

    protected String username;
    protected String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String getChildElementXML() {
        String query
                = "<connectionRequest xmlns=\"urn:broadband-forum-org:cwmp:xmppConnReq-1-0\">"
                + "<username>%s</username>"
                + "<password>%s</password>"
                + "</connectionRequest>";
        return String.format(query, username, password);
    }

    /**
     * Create new IQ Connection Request
     *
     * @param username
     * @param password
     */
    public IQConnectionRequest(String username, String password) {
        this.username = username;
        this.password = password;
        this.setType(Type.GET);
    }

}

IQConnectionRequestProvider.java

package vn.vnpttech.ssdc.nms.mediation.stbacs.packet;

import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.provider.IQProvider;
import org.xmlpull.v1.XmlPullParser;

/**
 * IQConnectionRequestProvider
 *
 * @author Vunb
 * @date Jun 23, 2015
 * @update Jun 23, 2015
 */
public class IQConnectionRequestProvider implements IQProvider {

    public IQ parseIQ(XmlPullParser parser) throws Exception {

        String username = "";
        String password = "";

        // Start parsing loop
        int eventType = parser.next();
        outerloop:
        while (eventType != XmlPullParser.END_DOCUMENT) {
            switch (eventType) {
                case XmlPullParser.START_TAG:
                    String elementName = parser.getName();
                    if ("username".equals(elementName)) {
                        username = parser.nextText();
                    } else if ("password".equals(elementName)) {
                        password = parser.nextText();
                    }
                    break;
                case XmlPullParser.END_TAG:
                    if (parser.getDepth() == 3) {
                        break outerloop;
                    }
                    break;
            }
            eventType = parser.next();
        }
        return new IQConnectionRequest(username, password);
    }

}

Then, at sending side, our code try to send custom XMPP IQ by using Smack 4.1.1 API:

    public void sendConnectionRequest(final String serialNumber, final String cpeUsername, final String cpePassword) throws XMPPException {
        String from = String.format("%s@%s/Smack", username, server);
        String to = String.format("%s@%s/Smack", serialNumber, server);

        IQConnectionRequest iq = new IQConnectionRequest(cpeUsername, cpePassword);
        iq.setFrom(from);
        iq.setTo(to);

        // Note: XMPPConnection already initialized
        connection.sendPacket(iq);
    }

At receiving side, we add a PacketListener to XMPPConnection to listen IQConnectionRequest message:

        // Add packet Listener
        PacketFilter getFilter = new IQTypeFilter(IQ.Type.GET);
        connection.addPacketListener(new PacketListener() {
            public void processPacket(Packet packet) {

                String from = packet.getFrom();
                String to = packet.getTo();

                final IQ request = new IQ() {
                    @Override
                    public String getChildElementXML() {
                        return childElement;
                    }
                };
                request.setFrom(from);
                request.setTo(to);
                
                final IQ result = IQ.createResultIQ(request);
                logger.debug("Reply: " + result.toXML());
                connection.sendPacket(result);

            }
        }, getFilter);

Finally, very important: Register our custom IQ Provider to Smack IQ Parser! Put it into somewhere in program that you like.

    static {
        ProviderManager.getInstance()
                .addIQProvider("connectionRequest",
                        "urn:broadband-forum-org:cwmp:xmppConnReq-1-0",
                        new IQConnectionRequestProvider());
    }

Or by creating a smack.providers file in the META-INF directory of your JAR file. The file is an XML document that contains one or more iqProvider entries, as in the following example:

<?xml version="1.0"?>
<smackProviders>
<iqProvider>
    <elementName>connectionRequest</elementName>
    <namespace>urn:broadband-forum-org:cwmp:xmppConnReq-1-0</namespace>
    <className>vn.vnpttech.ssdc.nms.mediation.stbacs.packet.IQConnectionRequest</className>
</iqProvider>
</smackProviders>

Done! Do it yourself.

Wish you success !

Reference Links

About The Author

  • Hello World

    Bài viết rất hay, có demo trên PHP không ạ ?