/**
* Copyright (c) 2011, SOCIETIES Consortium (WATERFORD INSTITUTE OF TECHNOLOGY (TSSG), HERIOT-WATT UNIVERSITY (HWU), SOLUTA.NET
* (SN), GERMAN AEROSPACE CENTRE (Deutsches Zentrum fuer Luft- und Raumfahrt e.V.) (DLR), Zavod za varnostne tehnologije
* informacijske družbe in elektronsko poslovanje (SETCCE), INSTITUTE OF COMMUNICATION AND COMPUTER SYSTEMS (ICCS), LAKE
* COMMUNICATIONS (LAKE), INTEL PERFORMANCE LEARNING SOLUTIONS LTD (INTEL), PORTUGAL TELECOM INOVA��O, SA (PTIN), IBM ISRAEL
* SCIENCE AND TECHNOLOGY LTD (IBM), INSTITUT TELECOM (ITSUD), AMITEC DIACHYTI EFYIA PLIROFORIKI KAI EPIKINONIES ETERIA
* PERIORISMENIS EFTHINIS (AMITEC), TELECOM ITALIA S.p.a.(TI), TRIALOG (TRIALOG), Stiftelsen SINTEF (SINTEF), NEC EUROPE LTD
* (NEC))
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.societies.comm.xmpp.xc.impl;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.convert.Registry;
import org.simpleframework.xml.convert.RegistryStrategy;
import org.simpleframework.xml.core.Persister;
import org.simpleframework.xml.strategy.Strategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.societies.api.comm.xmpp.datatypes.*;
import org.societies.api.comm.xmpp.exceptions.CommunicationException;
import org.societies.api.comm.xmpp.exceptions.XMPPError;
import org.societies.api.comm.xmpp.interfaces.ICommCallback;
import org.societies.api.comm.xmpp.interfaces.IFeatureServer;
import org.societies.api.identity.IIdentity;
import org.societies.api.identity.InvalidFormatException;
import org.societies.simple.basic.URIConverter;
import org.societies.simple.converters.EventItemsConverter;
import org.societies.simple.converters.PubsubItemConverter;
import org.societies.simple.converters.PubsubItemsConverter;
import org.societies.simple.converters.XMLGregorianCalendarConverter;
import org.xmpp.packet.*;
import org.xmpp.packet.IQ.Type;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.InvalidParameterException;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Joao M. Goncalves (PTIN), Miquel Martin (NEC), Alec Leckey (Intel)
* <p/>
* TODO list
* <p/>
* - this jaxb-dom4j conversion code is VERY BAD but i have to
* rush this; the propper solution would be to rewrite whack
* <p/>
* - it is single threaded and can be stuck by the synchronous calls to
* the extensions - should support "send and forget"
* <p/>
* - only supports one extension per namespace - the last to register
* sticks
* <p/>
* - dom4j parsing just sucks to use with jaxb - should cut dom4j out of
* it and have the packed handled in a lighter way
* <p/>
* - only supports one pojo per stanza - according to rfc6120: ok for IQ
* request/result processing; not ok for errors, messages and presence
*/
// TODO review this class
// TODO had to place synchronous blocks because marshallers are not threadsafe
public class CommManagerHelper {
private static final String JABBER_CLIENT = "jabber:client";
private static final String JABBER_SERVER = "jabber:server";
private static Logger LOG = LoggerFactory.getLogger(CommManagerHelper.class);
private SAXReader reader = new SAXReader(); // TODO the sax reader is not threadsafe either so I turned every method where it is used to synchronized :(
private final Map<String, IFeatureServer> featureServers = new HashMap<String, IFeatureServer>();
private final Map<String, ICommCallback> iqCommCallbacks = new HashMap<String, ICommCallback>();
private final Map<String, ICommCallback> nsCommCallbacks = new HashMap<String, ICommCallback>();
private final Map<String, String> nsToPackage = new HashMap<String, String>();
private final Map<String, HostedNode> localToplevelNodes = new HashMap<String, HostedNode>();
private final List<XMPPNode> allToplevelNodes = new ArrayList<XMPPNode>();
private Serializer s;
private ClassLoaderManager clm;
public CommManagerHelper() {
this.clm = new ClassLoaderManager();
Registry registry = new Registry();
Strategy strategy = new RegistryStrategy(registry);
s = new Persister(strategy);
try {
registry.bind(com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl.class, XMLGregorianCalendarConverter.class);
registry.bind(java.net.URI.class, URIConverter.class);
registry.bind(org.jabber.protocol.pubsub.event.Items.class, new EventItemsConverter(s));
registry.bind(org.jabber.protocol.pubsub.Items.class, new PubsubItemsConverter(s));
registry.bind(org.jabber.protocol.pubsub.Item.class, new PubsubItemConverter(s));
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
public String[] getSupportedNamespaces() {
String[] returnArray = new String[featureServers.size()];
return featureServers.keySet().toArray(returnArray);
}
public synchronized IQ handleDiscoItems(IQ iq) {
String node = null;
Attribute nodeAttr = ((Element)(iq.getElement().elements().get(0))).attribute("node");
if (nodeAttr != null)
node = nodeAttr.getText();
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
os.write(XMPPNode.ITEM_QUERY_RESPONSE_OPEN_BYTES);
if (node == null) {
// return top level nodes
for (XMPPNode n : allToplevelNodes)
os.write(n.getItemXmlBytes());
} else {
// return specific nodes
// check if some root-level node matches specified node
HostedNode localNode = localToplevelNodes.get(node);
// if not try to use node hierarchy to find speficied node
if (localNode == null) {
String[] nodePath = node.split("/");
for (int i = 0; i < nodePath.length; i++) {
if (i == 0)
localNode = localToplevelNodes.get(nodePath[i]);
else
localNode = localNode.getLocalChild(nodePath[i]);
if (localNode == null)
break;
}
}
// if found node, write it (and children)
if (localNode != null) {
os.write(localNode.getQueryXmlBytes());
for (XMPPNode n : localNode.getChildren())
os.write(n.getItemXmlBytes());
}
}
os.write(XMPPNode.ITEM_QUERY_RESPONSE_CLOSE_BYTES);
} catch (IOException e) {
LOG.error(e.getMessage());
}
try {
if (os.size() > 0) {
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
// return items
IQ response = new IQ(Type.result, iq.getID());
response.setTo(iq.getFrom());
Document dom4jItems = reader.read(is);
response.getElement().add(dom4jItems.getRootElement());
return response;
}
} catch (DocumentException e) {
LOG.error("DocumentException trying to build XML document from '"+os.toString()+"': "+e.getMessage());
return buildErrorResponse(iq.getFrom(), iq.getID(), e.getMessage(), e);
}
// return empty answer
iq.setTo(iq.getFrom());
iq.setType(Type.result);
iq.setFrom("");
return iq;
}
public void addRootNode(XMPPNode newNode) {
if (newNode instanceof HostedNode)
localToplevelNodes.put(newNode.getNode(), (HostedNode) newNode);
allToplevelNodes.add(newNode);
}
public void removeRootNode(XMPPNode node) {
if (node instanceof HostedNode)
localToplevelNodes.remove(((HostedNode) node).getNode());
allToplevelNodes.remove(node);
}
private Object ifNotNull(Object o, String type, String which)
throws UnavailableException {
if (o == null) {
throw new UnavailableException("Can not process " + type + ": "
+ which);
} else {
return o;
}
}
private String removeFragment(String namespace) {
int cardinalIndex = namespace.indexOf("#");
if (cardinalIndex > 0)
return namespace.substring(0, cardinalIndex);
else
return namespace;
}
private IFeatureServer getFeatureServer(String namespace)
throws UnavailableException {
return (IFeatureServer) ifNotNull(featureServers.get(removeFragment(namespace)),
"namespace", namespace);
}
private ICommCallback getCommCallback(String iqId)
throws UnavailableException {
return (ICommCallback) ifNotNull(iqCommCallbacks.get(iqId),
"IQ id", iqId);
}
private ICommCallback getMessageCommCallback(String namespace)
throws UnavailableException {
return (ICommCallback) ifNotNull(nsCommCallbacks.get(namespace),
"namespace", namespace);
}
/**
* Retrieves a package from a namespace mapping
*
* @throws UnavailableException
*/
private String getPackage(String namespace) throws UnavailableException {
String p = nsToPackage.get(namespace);
if (p == null)
throw new UnavailableException("No namespace registered: " + namespace);
return p;
}
public void dispatchIQResult(IQ iq) {
Element element = getElementAny(iq);
ClassLoader oldCl = null;
try {
ICommCallback callback = getCommCallback(iq.getID());
oldCl = clm.classLoaderMagicTemp(iq.getID());
// payloadless (confirmation) iqs
if (element == null) {
callback.receiveResult(TinderUtils.stanzaFromPacket(iq), null);
return;
}
String ns = element.getNamespace().getURI();
if (ns.equals(XMPPInfo.INFO_NAMESPACE)) {
SimpleEntry<String, XMPPInfo> infoMap = ParsingUtils.parseInfoResult(element);
callback.receiveInfo(TinderUtils.stanzaFromPacket(iq), infoMap.getKey(), infoMap.getValue());
return;
}
if (ns.equals(XMPPNode.ITEM_NAMESPACE)) {
SimpleEntry<String, List<String>> nodeMap = ParsingUtils.parseItemsResult(element);
callback.receiveItems(TinderUtils.stanzaFromPacket(iq), nodeMap.getKey(), nodeMap.getValue());
return;
}
Class<?> c = getClass(ns, element.getName());
Object bean = s.read(c, element.asXML());
callback.receiveResult(TinderUtils.stanzaFromPacket(iq), bean);
} catch (UnavailableException e) {
LOG.error("Unable to find package for namespace", e);
} catch (InvalidFormatException e) {
LOG.error("Unable to convert Tinder Packet into Stanza", e);
} catch (ClassNotFoundException e) {
LOG.error("Unable to create class", e);
} catch (Exception e) {
LOG.error("Unable to serialise Simple element", e);
}
if (oldCl != null)
Thread.currentThread().setContextClassLoader(oldCl);
}
public void dispatchIQError(IQ iq) {
ClassLoader oldCl = null;
try {
ICommCallback callback = getCommCallback(iq.getID());
// http://xmpp.org/rfcs/rfc6120.html#stanzas-error-syntax
Element errorElement = (Element) iq.getElement().elements().get(0);
if (iq.getElement().elements().size() > 1) {
// handle [OPTIONAL to include sender XML here]
// TODO currently ignoring
errorElement = (Element) iq.getElement().elements().get(1);
}
// assumes the stanza error comes first in the error element as described the syntax
String errorElementStr = ((Element) errorElement.elements().get(0)).getName();
StanzaError se = StanzaError.valueOf(errorElementStr.replaceAll("-", "_").toLowerCase());
XMPPError error = new XMPPError(se, null);
// handle optional error fields: text and application error
// [<text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'
// xml:lang='langcode'>
// OPTIONAL descriptive text
// </text>]
// [OPTIONAL application-specific condition element]
oldCl = clm.classLoaderMagicTemp(iq.getID());
if (errorElement.elements().size() > 1)
error = parseApplicationError(se, errorElement.elements());
callback.receiveError(TinderUtils.stanzaFromPacket(iq), error);
} catch (UnavailableException e) {
LOG.error("Unable to find package for namespace", e);
} catch (InvalidFormatException e) {
LOG.error("Unable to convert Tinder Packet into Stanza", e);
} catch (ClassNotFoundException e) {
LOG.error("Unable to find class during Simple serialisation prep", e);
}
if (oldCl != null)
Thread.currentThread().setContextClassLoader(oldCl);
}
private XMPPError parseApplicationError(StanzaError error, List list) throws UnavailableException, ClassNotFoundException {
Object appError = null;
String text = "";
for (Object o : list) {
if (o instanceof Element) {
Element e = (Element) o;
if (e.getNamespaceURI().equals(XMPPError.STANZA_ERROR_NAMESPACE_DECL)) {
if (e.getName().equals("text"))
text = e.getText();
} else {
Class<?> c = getClass(e.getNamespaceURI(), e.getName());
try {
appError = s.read(c, e.asXML());
} catch (Exception e1) {
throw new UnavailableException(e1.getMessage());
}
}
}
}
if (appError == null)
return new XMPPError(error, text);
else
return new XMPPError(error, text, appError);
}
public IQ dispatchIQ(IQ iq) {
Element element = getElementAny(iq);
String namespace = element.getNamespace().getURI();
JID originalFrom = iq.getFrom();
String id = iq.getID();
ClassLoader oldClassloader = null;
try {
IFeatureServer fs = getFeatureServer(namespace);
oldClassloader = clm.classLoaderMagic(fs);
Class<?> c = getClass(namespace, element.getName());
Object bean = s.read(c, element.asXML());
Object responseBean = null;
if (iq.getType().equals(IQ.Type.get))
responseBean = fs.getQuery(TinderUtils.stanzaFromPacket(iq), bean);
if (iq.getType().equals(IQ.Type.set))
responseBean = fs.setQuery(TinderUtils.stanzaFromPacket(iq), bean);
return buildResponseIQ(originalFrom, id, responseBean);
} catch (XMPPError e) {
return buildApplicationErrorResponse(originalFrom, id, e);
} catch (UnavailableException e) {
return buildErrorResponse(originalFrom, id, e.getMessage(), e);
} catch (DocumentException e) {
String message = e.getClass().getName()
+ "Error (un)marshalling the message:" + e.getMessage();
return buildErrorResponse(originalFrom, id, message, e);
} catch (InvalidFormatException e) {
String message = e.getClass().getName()
+ "Error (un)marshalling the message:" + e.getMessage();
return buildErrorResponse(originalFrom, id, message, e);
} catch (ClassNotFoundException e) {
String message = e.getClass().getName() + ": Unable to load class from classloader '"+Thread.currentThread().getContextClassLoader().toString()+"' for serialisation - " + e.getMessage();
return buildErrorResponse(originalFrom, id, message, e);
} catch (Exception e) {
LOG.error("Uncaught exception occurred", e);
String message = e.getClass().getName() + "Unable to serialise Simple element";
return buildErrorResponse(originalFrom, id, message, e);
} finally {
if (oldClassloader != null)
Thread.currentThread().setContextClassLoader(oldClassloader);
}
}
private Class<?> getClass(String namespace, String name) throws ClassNotFoundException, UnavailableException {
String packageStr = getPackage(namespace);
String beanName = name.substring(0, 1).toUpperCase() + name.substring(1);
return Thread.currentThread().getContextClassLoader().loadClass(packageStr + "." + beanName);
}
public void dispatchMessage(Message message) {
Element element = getElementAny(message);
String namespace = element.getNamespace().getURI();
ClassLoader oldCl = null;
try {
try {
ICommCallback cb = getMessageCommCallback(namespace);
oldCl = clm.classLoaderMagic(cb);
} catch (UnavailableException e) {
IFeatureServer fs = getFeatureServer(namespace);
oldCl = clm.classLoaderMagic(fs);
}
Class<?> c = getClass(namespace, element.getName());
Object bean = s.read(c, element.asXML());
try {
ICommCallback cb = getMessageCommCallback(namespace);
cb.receiveMessage(TinderUtils.stanzaFromPacket(message), bean);
} catch (UnavailableException e) {
IFeatureServer fs = getFeatureServer(namespace);
fs.receiveMessage(TinderUtils.stanzaFromPacket(message), bean);
}
} catch (UnavailableException e) {
LOG.error("Unable to find package or feature server for namespace", e);
} catch (InvalidFormatException e) {
LOG.error("Unable to convert Tinder Packet into Stanza", e);
} catch (ClassNotFoundException e) {
String m = e.getClass().getName() + "Error finding class:" + e.getMessage();
LOG.error(m, e);
} catch (Exception e) {
String m = e.getClass().getName() + "Error de-serializing the message:" + e.getMessage();
LOG.error(m, e);
}
if (oldCl != null)
Thread.currentThread().setContextClassLoader(oldCl);
}
public synchronized IQ sendIQ(Stanza stanza, IQ.Type type, Object payload, ICommCallback callback)
throws CommunicationException {
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
s.write(payload, os);
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
Document document = reader.read(is);
IQ iq = TinderUtils.createIQ(stanza, type); // ???
iq.getElement().add(document.getRootElement());
iqCommCallbacks.put(iq.getID(), callback);
clm.addTempClassloader(iq.getID(), payload); // or use the callback?
return iq;
} catch (ClassNotFoundException e) {
throw new CommunicationException("ClassNotFoundException for class " + payload.getClass().toString(), e);
} catch (Exception e) {
throw new CommunicationException("Error sending IQ message", e);
}
}
public synchronized Message sendMessage(Stanza stanza, Message.Type type, Object payload)
throws CommunicationException {
if (payload == null) {
throw new InvalidParameterException("Payload can not be null");
}
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
s.write(payload, os);
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
Document document = reader.read(is);
Message message = TinderUtils.createMessage(stanza, type);
message.getElement().add(document.getRootElement());
return message;
} catch (Exception e) {
throw new CommunicationException("Error sending Message message", e);
}
}
public void register(IFeatureServer fs) throws CommunicationException {
jaxbMapping(fs.getXMLNamespaces(), fs.getJavaPackages());
clm.classloaderRegistry(fs);
for (String ns : fs.getXMLNamespaces()) {
LOG.info("registering FeatureServer for namespace " + ns);
featureServers.put(ns, fs);
}
}
public void register(ICommCallback messageCallback) throws CommunicationException {
jaxbMapping(messageCallback.getXMLNamespaces(), messageCallback.getJavaPackages());
clm.classloaderRegistry(messageCallback);
if (messageCallback.getXMLNamespaces().size() > 0) {
String mainNs = messageCallback.getXMLNamespaces().get(0);
if (mainNs.contains("#")) { // ICommCallback has priority only for the mainNs in case it has a fragment
LOG.info("registering Callback for namespace " + mainNs);
nsCommCallbacks.put(mainNs, messageCallback);
}
}
}
private void jaxbMapping(List<String> namespaces, List<String> packages) throws CommunicationException {
// TODO latest namespace register sticks! no multiple namespace support atm
//assumes a 1:1 mapping between NS and package (for prototyping)
try {
for (int i = 0; i < packages.size(); i++) {
String packageStr = packages.get(i);
String nsStr = namespaces.get(i);
LOG.info("registering namespace-package mapping: " + nsStr + " <-> " + packageStr);
nsToPackage.put(nsStr, packageStr);
}
} catch (Exception ex) {
LOG.error("Error in JAXBMapping adding: " + ex.getMessage());
}
}
/**
* Get the element with the payload out of the XMPP packet.
*/
private Element getElementAny(Packet p) {
if (p instanceof IQ) {
// According to the schema in RCF6121 IQs only have one
// element, unless they have an error
switch (p.getElement().elements().size()) {
case 0:
return null;
default:
return (Element) p.getElement().elements().get(0);
// TODO handle errors
}
} else if (p instanceof Message) {
// according to the schema in RCF6121 messages have an unbounded
// number
// of "subject", "body" or "thread" elements before the any element
// part
Message message = (Message) p;
for (Object o : message.getElement().elements()) {
String ns = ((Element) o).getNamespace().getURI();
if (!(ns.equals(JABBER_CLIENT) || ns.equals(JABBER_SERVER))) {
return (Element) o;
}
}
LOG.warn("Got a Message with no payload element.");
return null;
} else {
LOG.warn("Got Packet type that I could not handle: "
+ p.getClass().getName());
return null;
}
}
private synchronized IQ buildApplicationErrorResponse(JID originalFrom, String id, XMPPError error) {
try {
IQ errorResponse = new IQ(Type.error, id);
errorResponse.setTo(originalFrom);
ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write(error.getStanzaErrorBytes(), 0, error.getStanzaErrorBytes().length);
if (error.getApplicationError() != null) {
s.write(error.getApplicationError(), os);
}
os.write(XMPPError.CLOSE_ERROR_BYTES, 0, XMPPError.CLOSE_ERROR_BYTES.length);
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
Document dom4jError = reader.read(is);
errorResponse.getElement().add(dom4jError.getRootElement());
return errorResponse;
} catch (DocumentException e) {
return buildErrorResponse(originalFrom, id, "DocumentException while building application error", e);
} catch (Exception e) {
return buildErrorResponse(originalFrom, id, "Serializing Exception while building application error", e);
}
}
private IQ buildErrorResponse(JID originalFrom, String id, String message, Exception e) {
LOG.warn("Error occurred:" + message);
IQ errorResponse = new IQ(Type.error, id);
errorResponse.setTo(originalFrom);
// default error
PacketError error = new PacketError(PacketError.Condition.internal_server_error, PacketError.Type.cancel, message);
if (e instanceof UnavailableException) // unrecognized namespace
error = new PacketError(PacketError.Condition.service_unavailable, PacketError.Type.cancel);
errorResponse.getElement().add(error.getElement());
return errorResponse;
}
private synchronized IQ buildResponseIQ(JID originalFrom, String id, Object responseBean)
throws DocumentException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
Document document = null;
if (responseBean != null) {
try {
s.write(responseBean, os);
} catch (Exception e) {
LOG.warn("Error writing responseBean to output stream", e);
// e.printStackTrace();
}
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
document = reader.read(is);
}
IQ responseIq = new IQ(Type.result, id);
responseIq.setTo(originalFrom);
if (document != null)
responseIq.getElement().add(document.getRootElement());
return responseIq;
}
class UnavailableException extends Exception {
private static final long serialVersionUID = -7976036541747605416L;
public UnavailableException(String message) {
super(message);
}
}
public synchronized IQ buildInfoIq(IIdentity entity, String node, ICommCallback callback) throws CommunicationException {
IQ infoIq = new IQ(Type.get);
infoIq.setTo(entity.getJid());
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
os.write(ParsingUtils.getInfoQueryRequestBytes(node));
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
Document document = reader.read(is);
infoIq.getElement().add(document.getRootElement());
} catch (IOException e) {
throw new CommunicationException("Error building disco#info request", e);
} catch (DocumentException e) {
throw new CommunicationException("Error building disco#info request", e);
}
iqCommCallbacks.put(infoIq.getID(), callback);
return infoIq;
}
public synchronized IQ buildItemsIq(IIdentity entity, String node, ICommCallback callback) throws CommunicationException {
IQ itemsIq = new IQ(Type.get);
itemsIq.setTo(entity.getJid());
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
os.write(ParsingUtils.getItemsQueryRequestBytes(node));
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
Document document = reader.read(is);
itemsIq.getElement().add(document.getRootElement());
} catch (IOException e) {
throw new CommunicationException("Error building disco#items request", e);
} catch (DocumentException e) {
throw new CommunicationException("Error building disco#items request", e);
}
iqCommCallbacks.put(itemsIq.getID(), callback);
return itemsIq;
}
}