package org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.get; import java.io.StringReader; import java.util.Date; import java.util.concurrent.BlockingQueue; import org.apache.log4j.Logger; import org.buddycloud.channelserver.Configuration; import org.buddycloud.channelserver.channel.ChannelManager; import org.buddycloud.channelserver.channel.Conf; import org.buddycloud.channelserver.db.CloseableIterator; import org.buddycloud.channelserver.db.exception.NodeStoreException; import org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.JabberPubsub; import org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.PubSubElementProcessorAbstract; import org.buddycloud.channelserver.pubsub.model.GlobalItemID; import org.buddycloud.channelserver.pubsub.model.NodeItem; import org.buddycloud.channelserver.pubsub.model.impl.GlobalItemIDImpl; import org.buddycloud.channelserver.utils.XMLConstants; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.xmpp.packet.IQ; import org.xmpp.packet.JID; import org.xmpp.packet.Packet; import org.xmpp.packet.PacketError; import org.xmpp.packet.PacketError.Condition; import org.xmpp.packet.PacketError.Type; public class RecentItemsGet extends PubSubElementProcessorAbstract { private static final Logger LOGGER = Logger.getLogger(RecentItemsGet.class); private static final String NODE_SUFFIX = "/posts"; private Date maxAge; private Integer maxItems; private Element pubsub; private SAXReader xmlReader; // RSM details private GlobalItemID firstItemId = null; private GlobalItemID lastItemId = null; private GlobalItemID afterItemId = null; private int maxResults = -1; private boolean parentOnly = false; public RecentItemsGet(BlockingQueue<Packet> outQueue, ChannelManager channelManager) { setChannelManager(channelManager); setOutQueue(outQueue); xmlReader = new SAXReader(); acceptedElementName = XMLConstants.RECENT_ITEMS_ELEM; } @Override public void process(Element elm, JID actorJID, IQ reqIQ, Element rsm) throws Exception { response = IQ.createResultIQ(reqIQ); request = reqIQ; actor = actorJID; node = elm.attributeValue(XMLConstants.NODE_ATTR); resultSetManagement = rsm; if (null == actor) { actor = request.getFrom(); } if (!isValidStanza()) { outQueue.put(response); return; } if (!Configuration.getInstance().isLocalJID(request.getFrom())) { response.getElement().addAttribute(XMLConstants.REMOTE_SERVER_DISCOVER_ATTR, Boolean.FALSE.toString()); } pubsub = response.getElement().addElement(XMLConstants.PUBSUB_ELEM, JabberPubsub.NAMESPACE_URI); try { parseRsmElement(); addRecentItems(); addRsmElement(); outQueue.put(response); } catch (NodeStoreException e) { LOGGER.error(e); response.getElement().remove(pubsub); setErrorCondition(PacketError.Type.wait, PacketError.Condition.internal_server_error); } outQueue.put(response); } @Override protected boolean parseRsmElement() { if (null == resultSetManagement) { return true; } Element max = null; Element after = null; if (null != (max = resultSetManagement.element("max"))) { maxResults = Integer.parseInt(max.getTextTrim()); } if (null != (after = resultSetManagement.element("after"))) { try { afterItemId = GlobalItemIDImpl.fromBuddycloudString(after.getTextTrim()); } catch (IllegalArgumentException e) { LOGGER.error(e); createExtendedErrorReply(Type.modify, Condition.bad_request, "Could not parse the 'after' id: " + after); return false; } } return true; } @Override protected void addRsmElement() throws NodeStoreException { if (null == firstItemId) { return; } Element rsm = pubsub.addElement("set", NS_RSM); rsm.addElement("first", NS_RSM).setText(firstItemId.toString()); rsm.addElement("last", NS_RSM).setText(lastItemId.toString()); rsm.addElement("count", NS_RSM).setText(String.valueOf(channelManager.getCountRecentItems(actor, maxAge, maxItems, NODE_SUFFIX, parentOnly))); } @Override protected void addRecentItems() throws NodeStoreException { CloseableIterator<NodeItem> items = channelManager.getRecentItems(actor, maxAge, maxItems, maxResults, afterItemId, NODE_SUFFIX, parentOnly); String lastNodeId = ""; Element itemsElement = null; while (items.hasNext()) { NodeItem item = items.next(); if (!item.getNodeId().equals(lastNodeId)) { itemsElement = pubsub.addElement(XMLConstants.ITEMS_ELEM); itemsElement.addAttribute(XMLConstants.NODE_ATTR, item.getNodeId()); lastNodeId = item.getNodeId(); } try { Element entry = xmlReader.read(new StringReader(item.getPayload())).getRootElement(); Element itemElement = itemsElement.addElement(XMLConstants.ITEM_ELEM); itemElement.addAttribute(XMLConstants.ID_ATTR, item.getId()); if (null == firstItemId) { firstItemId = new GlobalItemIDImpl(null, item.getNodeId(), item.getId()); } lastItemId = new GlobalItemIDImpl(null, item.getNodeId(), item.getId()); itemElement.add(entry); } catch (DocumentException e) { LOGGER.error("Error parsing a node entry, ignoring. " + item.getId()); } } } @Override protected boolean isValidStanza() { boolean valid = false; String failureReason = null; Element recentItems = request.getChildElement().element(acceptedElementName); try { String max = recentItems.attributeValue(XMLConstants.MAX_ATTR); if (null != max) { maxItems = Integer.parseInt(max); String since = recentItems.attributeValue(XMLConstants.SINCE_ATTR); if (null != since) { maxAge = Conf.parseDate(since); valid = true; } else { failureReason = XMLConstants.SINCE_REQUIRED_ELEM; } } else { failureReason = XMLConstants.MAX_REQUIRED_ELEM; } String parentOnlyAttribute = recentItems.attributeValue(XMLConstants.PARENT_ONLY_ATTR); if ((null != parentOnlyAttribute) && ((Boolean.TRUE.toString().equals(parentOnlyAttribute)) || ("1".equals(parentOnlyAttribute)))) { parentOnly = true; } } catch (NumberFormatException e) { failureReason = XMLConstants.INVALID_MAX_VALUE_PROVIDED_ELEM; LOGGER.error(e); } catch (IllegalArgumentException e) { failureReason = XMLConstants.INVALID_SINCE_VALUE_PROVIDED_ELEM; LOGGER.error(e); } if (!valid) { createExtendedErrorReply(PacketError.Type.modify, PacketError.Condition.bad_request, failureReason); } return valid; } }