Вопрос

I am using aSmack with Openfire.

After a lot of research I found that Openfire doesn't support message archiving and installed the plugin OpenArchive.

Now archiving works fine and all the messages are stored fine.

Now in the client side I tried sending an IQ stanza to retrieve the archived chats.

First I added an IQ Provider like below:

pm.addIQProvider("list", "urn:xmpp:archive", new ListIQProvider());

Then I used:

final IQ iq = new IQ()
{

    @Override public String getChildElementXML()
    {

        return "<list  xmlns='urn:xmpp:archive' with='test@customOpenfire.com'><set xmlns='http://jabber.org/protocol/rsm'><max xmlns='http://jabber.org/protocol/rsm'>30</max></set> </list>";

    }
};

iq.setType(IQ.Type.GET);
iq.setPacketID("987654321");

xmppConnection.sendPacket(iq);

It worked fine and I received the response.

<iq id="987654321" to="admin@customOpenfire.com/Smack" type="result">
<list xmlns="urn:xmpp:archive">
    <chat with="test@customOpenfire.com" start="2014-04-06T12:11:28.674Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-03T16:55:59.523Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-05T16:33:03.377Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-02T14:32:10.499Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-06T12:47:52.961Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-03T14:46:24.877Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-06T12:37:14.608Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-03T15:48:46.642Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-02T13:46:07.750Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-04T18:25:57.968Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-03T19:08:45.238Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-04T18:47:19.067Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-04T19:34:27.819Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-02T15:09:13.140Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-03T18:30:36.804Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-05T14:09:34.973Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-04T22:47:54.363Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-02T15:32:44.540Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-03T17:18:37.940Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-03T13:37:15.630Z"/>
    <chat with="test@customOpenfire.com" start="2014-04-04T17:10:39.116Z"/>

    <set xmlns="http://jabber.org/protocol/rsm">
        <first index="0">66</first>
        <last>139</last>
        <count>21</count>
    </set>
</list>
</iq>

Then I wanted to retrieve the actual messages so I sent this IQ stanza:

final IQ iq = new IQ()
{

    @Override public String getChildElementXML()
    {

        return "<retrieve  xmlns='urn:xmpp:archive' with='test@customOpenfire.com'><set xmlns='http://jabber.org/protocol/rsm'><max xmlns='http://jabber.org/protocol/rsm'>30</max></set> </retrieve>";

    }
};

iq.setType(IQ.Type.GET);
iq.setPacketID("987654321");

xmppConnection.sendPacket(iq);

Of course after I created my custom provider and added it like below:

pm.addIQProvider("retrieve", "urn:xmpp:archive", new ChatIQProvider());

And I should have received something like:

<iq xmlns="jabber:client" type="result" id="hgfg" to="admin@customOpenfire.com/7dd0f2fc">
<chat xmlns="urn:xmpp:archive" with="test@customOpenfire.com" start="2014-04-02T13:46:07.750Z">
    <from secs="0" jid="test@customOpenfire.com">
        <body>hello</body>
    </from>
    <to secs="2">
        <body>hey</body>
    </to>
    <from secs="5" jid="test@customOpenfire.com">
        <body>test</body>
    </from>
    <set xmlns="http://jabber.org/protocol/rsm">
        <first index="0">0</first>
        <last>2</last>
        <count>3</count>
    </set>
</chat>

But in my packet listener the result isn't parsed and is treated just like the list stanza is treated if I remove the ListIQProvider().

Here are my custom classes:

ChatIQ:

public class ChatIQ extends IQ {


private String xmlns;
private String with;
private String start;

private List<From> froms;
private Set set;

public ChatIQ()
{
    this.froms = new ArrayList<ChatIQ.From>();
}

public String getXmlns()
{
    return xmlns;
}

public void setXmlns(String xmlns)
{
    this.xmlns = xmlns;
}

public String getWith()
{
    return with;
}

public void setWith(String with)
{
    this.with = with;
}

public String getStart()
{
    return start;
}

public void setStart(String start)
{
    this.start = start;
}



public void addFrom(From from)
 {
    froms.add(from);
 }

 public List<From> getFroms()
 {
    return froms;
 }

public Set getSet()
 {
    return set;
 }

 public void setSet(Set set)
 {
    this.set = set;
 }

 @Override
 public String getChildElementXML()
 {
     StringBuilder builder = new StringBuilder("<chat xmlns=\"urn:xmpp:archive\"");
        builder.append("with=\"").append(with).append("\"");
        builder.append(" start=\"");
        builder.append(start);
        builder.append("\">");
        for(From from : froms)
        {
            builder.append(from.toXml());
        }
        builder.append(set.toXml());
        builder.append("</chat>");
        return builder.toString();
 }

 public static class From 
 {
     private String secs;
     private String jid;

     private Body body;

     public String getSecs()
    {
        return secs;
    }


    public void setSecs(String secs)
    {
        this.secs = secs;
    }


    public String getJid()
    {
        return jid;
    }


    public void setJid(String jid)
    {
        this.jid = jid;
    }


    public Body getBody()
    {
        return body;
    }


    public void setBody(Body body)
    {
        this.body = body;
    }


    public String toXml()
    {
        StringBuilder builder = new StringBuilder("<from ");
        builder.append("secs=\"").append(secs).append("\" ");
        builder.append("jid=\"").append(jid).append("\" >");
        builder.append(body.toXml());
        builder.append("</from>");
        return builder.toString();
    }
 }

 public static class Body 
 {
     private String message;

    public Body(String message)
    {
        this.message = message;
    }

    public String getMessage()
    {
        return message;
    }

    public void setMessage(String message)
    {
        this.message = message;
    }

    public Object toXml()
    {
        StringBuilder builder = new StringBuilder("<body>");
        builder.append(message);
        builder.append("</body>");
        return builder.toString();
    }
 }

 public static class Set {
    private int last;
    private int count;
    private int indexAtt;
    private int first;

    public Set()
    {
    }

    public int getLast()
    {
        return last;
    }

    public void setLast(int last)
    {
        this.last = last;
    }

    public int getCount()
    {
        return count;
    }

    public void setCount(int count)
    {
        this.count = count;
    }

    public int getIndexAtt()
    {
        return indexAtt;
    }

    public void setIndexAtt(int indexAtt)
    {
        this.indexAtt = indexAtt;
    }

    public int getFirst()
    {
        return first;
    }

    public void setFirst(int first)
    {
        this.first = first;
    }

    public String toXml()
    {
        StringBuilder builder = new StringBuilder("<set xmlns=\"http://jabber.org/protocol/rsm\">");
        builder.append("<first index=\"").append(indexAtt).append("\">").append(first).append("</first>");
        builder.append("<last>").append(last).append("</last>");
        builder.append("<count>").append(count).append("</count>");
        builder.append("</set>");
        return builder.toString();
    }
 }

}

ChatIQProvider:

public class ChatIQProvider implements IQProvider {

 public ChatIQProvider()
 {
 }

 @Override
 public IQ parseIQ(XmlPullParser parser) throws Exception
 {
    Log.d("CHAT IQ PROVIDER", String.format("Received iq packet, namespace[%s], name[%s]", parser.getNamespace(), parser.getName()));
    ChatIQ iq = new ChatIQ();
    ChatIQ.Set set = new Set();
    boolean done = false;

    From from = new From();
    String secs = "", jid = "";
    while (!done)
    {
        int eventType = parser.next();
        if (eventType == XmlPullParser.START_TAG)
        {
            if (parser.getName().equals("from"))
            {
                secs = parser.getAttributeValue("", "secs");
                jid = parser.getAttributeValue("", "jid");

                from = new From();

                iq.addFrom(from);
            }
            else if(parser.getName().equals("body") && from.getBody()==null)
            {
                ChatIQ.Body body = new Body(parser.nextText());
                from.setBody(body);
            }
            else if (parser.getName().equals("first"))
            {
                int index = parseInt(parser.getAttributeValue("", "index"));
                set.setIndexAtt(index);
                int first = parseInt(parser.nextText());
                set.setFirst(first);
            }
            else if (parser.getName().equals("last"))
            {
                int last = parseInt(parser.nextText());
                set.setLast(last);
            }
            else if (parser.getName().equals("count"))
            {
                int count = parseInt(parser.nextText());
                set.setCount(count);
            }
        }
        else if (eventType == XmlPullParser.END_TAG)
        {
            if (parser.getName().equals("chat"))
            {
                iq.setSet(set);
                done = true;
            }
        }
    }

    return iq;
 }

 private int parseInt(String integer)
 {
    return Integer.parseInt((integer != null ? integer : "0"));
 }
}

My questions are as follow:

  • Why isn't my custom IQProvider recognized?
  • Can I get the received XML and parse it without going through IQproviders?
  • Is there a simpler way to retrieve the archived messages from the server? Knowing that the messages exist and I do receive them in the client side I just can't find a way to get the content and parse it.

Thank you.

Это было полезно?

Решение

I fixed the issue, and I forgot to post the answer here for other people who might have the same problem. Anyway the fix is pretty simple. Instead of:

pm.addIQProvider("retrieve", "urn:xmpp:archive", new ChatIQProvider());

I should've used:

pm.addIQProvider("chat", "urn:xmpp:archive", new ChatIQProvider());

I have to register the name of the child tag of the IQ response and not the request, hence "chat" instead of "retrieve".

Hope this helps, as I could not find any examples on the use of custom IQ in the android smack api.

Since the comment was too long I added it here.

Here you go:

ListIQProvider:

public class ListIQProvider implements IQProvider {

 public ListIQProvider()
 {
 }

 @Override
 public IQ parseIQ(XmlPullParser parser) throws Exception
 {
    ListIQ iq = new ListIQ();
    ListIQ.Set set = new Set();
    boolean done = false;

    String with = "", start = "";
    while (!done)
    {
        int eventType = parser.next();
        if (eventType == XmlPullParser.START_TAG)
        {
            if (parser.getName().equals("chat"))
            {
                with = parser.getAttributeValue("", "with");
                start = parser.getAttributeValue("", "start");
                iq.addChat(new Chat(with, start));
            }
            else if (parser.getName().equals("first"))
            {
                int index = parseInt(parser.getAttributeValue("", "index"));
                set.setIndexAtt(index);
                int first = parseInt(parser.nextText());
                set.setFirst(first);
            }
            else if (parser.getName().equals("last"))
            {
                int last = parseInt(parser.nextText());
                set.setLast(last);
            }
            else if (parser.getName().equals("count"))
            {
                int count = parseInt(parser.nextText());
                set.setCount(count);
            }
        }
        else if (eventType == XmlPullParser.END_TAG)
        {
            if (parser.getName().equals("list"))
            {
                iq.setSet(set);
                done = true;
            }
        }
    }

    return iq;
 }

 private int parseInt(String integer)
 {
    return Integer.parseInt((integer != null ? integer : "0"));
 }

}

ListIQ:

public class ListIQ extends IQ {

 private List<Chat> chats;

 private Set set;

 public ListIQ()
 {
    this.chats = new ArrayList<ListIQ.Chat>();
 }

 public Set getSet()
 {
    return set;
 }

 public void setSet(Set set)
 {
    this.set = set;
 }

 public void addChat(Chat chat)
 {
    chats.add(chat);
 }

 public List<Chat> getChats()
 {
    return chats;
 }

 @Override
 public String getChildElementXML()
 {
    StringBuilder builder = new StringBuilder("<list xmlns=\"urn:xmpp:archive\">");
    for (Chat chat : chats)
    {
        builder.append(chat.toXml());
    }
    builder.append(set.toXml());
    builder.append("</list>");
    return builder.toString();
 }

 public static class Chat {
    private String with;
    private String start;

    public Chat()
    {
    }

    public Chat(String with, String start)
    {
        this.with = with;
        this.start = start;
    }

    public String getWith()
    {
        return with;
    }

    public void setWith(String with)
    {
        this.with = with;
    }

    public String getStart()
    {
        return start;
    }

    public void setStart(String start)
    {
        this.start = start;
    }

    public String toXml()
    {
        StringBuilder builder = new StringBuilder("<chat with=\"");
        builder.append(with).append("\"");
        builder.append(" start=\"");
        builder.append(start);
        builder.append("\"/>");
        return builder.toString();
    }

 }

 public static class Set {
    private int last;
    private int count;
    private int indexAtt;
    private int first;

    public Set()
    {
    }

    public int getLast()
    {
        return last;
    }

    public void setLast(int last)
    {
        this.last = last;
    }

    public int getCount()
    {
        return count;
    }

    public void setCount(int count)
    {
        this.count = count;
    }

    public int getIndexAtt()
    {
        return indexAtt;
    }

    public void setIndexAtt(int indexAtt)
    {
        this.indexAtt = indexAtt;
    }

    public int getFirst()
    {
        return first;
    }

    public void setFirst(int first)
    {
        this.first = first;
    }

    public String toXml()
    {
        StringBuilder builder = new StringBuilder("<set xmlns=\"http://jabber.org/protocol/rsm\">");
        builder.append("<first index=\"").append(indexAtt).append("\">").append(first).append("</first>");
        builder.append("<last>").append(last).append("</last>");
        builder.append("<count>").append(count).append("</count>");
        builder.append("</set>");
        return builder.toString();
    }
 }

}

I just use this class, to call ServiceProviders.Register_Providers(ProviderManager.getInstance()); right after the Xmpp Connection is connected.

ServiceProviders:

public class ServiceProviders
{
    public static void Register_Providers(ProviderManager pm)
    {
        Log.e("PROVIDER", "START");
        // 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());

        // 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());

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

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

        // archive
        pm.addIQProvider("list", "urn:xmpp:archive", new ListIQProvider());

        pm.addIQProvider("chat", "urn:xmpp:archive", new ChatIQProvider());

    }
}

@HirenPatel

first you need to add the archive plugin to Openfire. Then after the Xmpp connection in the client you must register your providers including the one that helps retrieve the chat messages:

pm.addIQProvider("list", "urn:xmpp:archive", new ListIQProvider());
pm.addIQProvider("chat", "urn:xmpp:archive", new ChatIQProvider());

Then you need to send an IQ stanza like follow:

final IQ iq = new IQ()
{

    @Override public String getChildElementXML()
    {

        return "<retrieve  xmlns='urn:xmpp:archive' with='test@customOpenfire.com'><set xmlns='http://jabber.org/protocol/rsm'><max xmlns='http://jabber.org/protocol/rsm'>30</max></set> </retrieve>";

    }
};

iq.setType(IQ.Type.GET);
iq.setPacketID("987654321");

xmppConnection.sendPacket(iq);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top