Skip to content

Commit 9d6213a

Browse files
committed
Merge pull request #65 from surevine/master
Paging for the recent items feed
2 parents 2334a0d + 8ab38d8 commit 9d6213a

13 files changed

Lines changed: 328 additions & 114 deletions

File tree

src/main/java/org/buddycloud/channelserver/channel/ChannelManagerImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.buddycloud.channelserver.db.NodeStore;
1313
import org.buddycloud.channelserver.db.exception.NodeStoreException;
1414
import org.buddycloud.channelserver.pubsub.affiliation.Affiliations;
15+
import org.buddycloud.channelserver.pubsub.model.GlobalItemID;
1516
import org.buddycloud.channelserver.pubsub.model.NodeAffiliation;
1617
import org.buddycloud.channelserver.pubsub.model.NodeItem;
1718
import org.buddycloud.channelserver.pubsub.model.NodeSubscription;
@@ -391,7 +392,7 @@ public ArrayList<String> getNodeList() throws NodeStoreException {
391392

392393
@Override
393394
public CloseableIterator<NodeItem> getRecentItems(JID user, Date since,
394-
int maxPerNode, int limit, String afterItemId, String node)
395+
int maxPerNode, int limit, GlobalItemID afterItemId, String node)
395396
throws NodeStoreException {
396397
return nodeStore.getRecentItems(user, since, maxPerNode, limit,
397398
afterItemId, node);

src/main/java/org/buddycloud/channelserver/db/NodeStore.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import org.buddycloud.channelserver.db.exception.NodeStoreException;
99
import org.buddycloud.channelserver.pubsub.affiliation.Affiliations;
10+
import org.buddycloud.channelserver.pubsub.model.GlobalItemID;
1011
import org.buddycloud.channelserver.pubsub.model.NodeAffiliation;
1112
import org.buddycloud.channelserver.pubsub.model.NodeItem;
1213
import org.buddycloud.channelserver.pubsub.model.NodeSubscription;
@@ -423,7 +424,7 @@ int getCountNodeThread(String nodeId, String itemId)
423424
* @throws NodeStoreException
424425
*/
425426
CloseableIterator<NodeItem> getRecentItems(JID user, Date since,
426-
int maxPerNode, int limit, String afterItemId, String node) throws NodeStoreException;
427+
int maxPerNode, int limit, GlobalItemID afterItemId, String node) throws NodeStoreException;
427428

428429
/**
429430
* Get count of recent items for a user

src/main/java/org/buddycloud/channelserver/db/jdbc/JDBCNodeStore.java

Lines changed: 54 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.text.SimpleDateFormat;
1010
import java.util.ArrayDeque;
1111
import java.util.ArrayList;
12+
import java.util.Collections;
1213
import java.util.Date;
1314
import java.util.Deque;
1415
import java.util.HashMap;
@@ -26,6 +27,7 @@
2627
import org.buddycloud.channelserver.db.exception.ItemNotFoundException;
2728
import org.buddycloud.channelserver.db.exception.NodeStoreException;
2829
import org.buddycloud.channelserver.pubsub.affiliation.Affiliations;
30+
import org.buddycloud.channelserver.pubsub.model.GlobalItemID;
2931
import org.buddycloud.channelserver.pubsub.model.NodeAffiliation;
3032
import org.buddycloud.channelserver.pubsub.model.NodeItem;
3133
import org.buddycloud.channelserver.pubsub.model.NodeSubscription;
@@ -941,12 +943,12 @@ public CloseableIterator<NodeItem> getFirehose(int limit,
941943
PreparedStatement stmt = null;
942944
Date beforeDate = null;
943945

944-
if (afterItemId != null) {
945-
beforeDate = getNodeItemById(afterItemId).getUpdated();
946-
} else {
946+
// if (afterItemId != null) {
947+
// beforeDate = getNodeItemById(afterItemId).getUpdated();
948+
// } else {
947949
beforeDate = new Date();
948950
afterItemId = "";
949-
}
951+
// }
950952

951953
if (limit < 0) {
952954
throw new IllegalArgumentException(
@@ -1096,20 +1098,13 @@ public int getCountRecentItems(JID user, Date since, int maxPerNode,
10961098

10971099
@Override
10981100
public CloseableIterator<NodeItem> getRecentItems(JID user, Date since,
1099-
int maxPerNode, int limit, String afterItemId, String node)
1101+
int maxPerNode, int limit, GlobalItemID afterItemId, String node)
11001102
throws NodeStoreException {
11011103

11021104
PreparedStatement stmt = null;
11031105

11041106
try {
1105-
if (null != afterItemId) {
1106-
NodeItem item = getNodeItemById(afterItemId);
1107-
if ((null != item) && (true == item.getUpdated().before(since))) {
1108-
1109-
since = item.getUpdated();
1110-
}
1111-
}
1112-
1107+
11131108
if (null == node)
11141109
node = "/posts";
11151110
if (-1 == limit)
@@ -1129,10 +1124,7 @@ public CloseableIterator<NodeItem> getRecentItems(JID user, Date since,
11291124
Subscriptions.subscribed))
11301125
continue;
11311126
if (false == subscription
1132-
.getNodeId()
1133-
.substring(
1134-
subscription.getNodeId().length()
1135-
- node.length()).equals(node))
1127+
.getNodeId().endsWith(node))
11361128
continue;
11371129
queryParts.add(dialect.selectRecentItemParts().replace(
11381130
"%counter%", String.valueOf(counter)));
@@ -1141,10 +1133,32 @@ public CloseableIterator<NodeItem> getRecentItems(JID user, Date since,
11411133
parameters.add(maxPerNode);
11421134
++counter;
11431135
}
1136+
1137+
String sql = "SELECT * FROM ("
1138+
+ StringUtils.join(queryParts, " UNION ALL ")
1139+
+ ") AS recentItemsQuery";
1140+
1141+
if(afterItemId != null) {
1142+
NodeItem afterItem = getNodeItem(afterItemId.getNodeID(), afterItemId.getItemID());
1143+
1144+
if(afterItem != null) {
1145+
sql += " WHERE \"updated\" < ? OR" +
1146+
" ( \"updated\" = ? AND" +
1147+
" ( \"node\" > ? OR" +
1148+
" ( \"node\" = ? AND \"id\" > ? ) ) )";
1149+
1150+
parameters.add(new Timestamp(afterItem.getUpdated().getTime()));
1151+
parameters.add(new Timestamp(afterItem.getUpdated().getTime()));
1152+
parameters.add(afterItem.getNodeId());
1153+
parameters.add(afterItem.getNodeId());
1154+
parameters.add(afterItem.getId());
1155+
}
1156+
}
1157+
1158+
sql += " ORDER BY \"updated\" DESC, \"node\" ASC, \"id\" ASC LIMIT ?;";
1159+
11441160
stmt = conn
1145-
.prepareStatement("SELECT * FROM ("
1146-
+ StringUtils.join(queryParts, " UNION ALL ")
1147-
+ ") AS recentItemsQuery ORDER BY \"updated\" DESC LIMIT ?;");
1161+
.prepareStatement(sql.toString());
11481162
int index = 1;
11491163
for (Object parameter : parameters) {
11501164
stmt.setObject(index, parameter);
@@ -1169,28 +1183,6 @@ public CloseableIterator<NodeItem> getRecentItems(JID user, Date since,
11691183
}
11701184
}
11711185

1172-
public NodeItem getNodeItemById(String nodeItemId)
1173-
throws NodeStoreException {
1174-
PreparedStatement stmt = null;
1175-
1176-
try {
1177-
stmt = conn.prepareStatement(dialect.selectSingleItem());
1178-
stmt.setString(1, nodeItemId);
1179-
1180-
java.sql.ResultSet rs = stmt.executeQuery();
1181-
1182-
if (rs.next()) {
1183-
return new NodeItemImpl(rs.getString(1), rs.getString(2),
1184-
rs.getTimestamp(3), rs.getString(4), rs.getString(5));
1185-
}
1186-
return null;
1187-
} catch (SQLException e) {
1188-
throw new NodeStoreException(e);
1189-
} finally {
1190-
close(stmt); // Will implicitly close the resultset if required
1191-
}
1192-
}
1193-
11941186
@Override
11951187
public CloseableIterator<NodeItem> getNodeItems(String nodeId)
11961188
throws NodeStoreException {
@@ -1206,7 +1198,7 @@ public ClosableIteratorImpl<NodeItem> getNodeItemReplies(String nodeId,
12061198
try {
12071199
Date since = new Date(0);
12081200
if (null != afterItemId)
1209-
since = getNodeItemById(afterItemId).getUpdated();
1201+
since = getNodeItem(nodeId, afterItemId).getUpdated();
12101202

12111203
String query = dialect.selectItemReplies();
12121204
if (-1 != limit) {
@@ -1269,7 +1261,7 @@ public ClosableIteratorImpl<NodeItem> getNodeItemThread(String nodeId,
12691261
try {
12701262
Date since = new Date(0);
12711263
if (null != afterItemId)
1272-
since = getNodeItemById(afterItemId).getUpdated();
1264+
since = getNodeItem(nodeId, afterItemId).getUpdated();
12731265

12741266
String query = dialect.selectItemThread();
12751267
if (-1 != limit) {
@@ -1383,12 +1375,25 @@ public int countNodeItems(String nodeId) throws NodeStoreException {
13831375
@Override
13841376
public NodeItem getNodeItem(String nodeId, String nodeItemId)
13851377
throws NodeStoreException {
1386-
NodeItem item = getNodeItemById(nodeItemId);
1387-
if (null != item) {
1388-
if (true == item.getNodeId().equals(nodeId))
1389-
return item;
1378+
PreparedStatement stmt = null;
1379+
1380+
try {
1381+
stmt = conn.prepareStatement(dialect.selectSingleItem());
1382+
stmt.setString(1, nodeId);
1383+
stmt.setString(2, nodeItemId);
1384+
1385+
java.sql.ResultSet rs = stmt.executeQuery();
1386+
1387+
if (rs.next()) {
1388+
return new NodeItemImpl(rs.getString(1), rs.getString(2),
1389+
rs.getTimestamp(3), rs.getString(4), rs.getString(5));
1390+
}
1391+
return null;
1392+
} catch (SQLException e) {
1393+
throw new NodeStoreException(e);
1394+
} finally {
1395+
close(stmt); // Will implicitly close the resultset if required
13901396
}
1391-
return null;
13921397
}
13931398

13941399
@Override

src/main/java/org/buddycloud/channelserver/db/jdbc/dialect/Sql92NodeStoreDialect.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public class Sql92NodeStoreDialect implements NodeStoreSQLDialect {
109109
private static final String NODE_EXISTS = "SELECT \"node\" FROM \"nodes\" WHERE \"node\" = ?";
110110

111111
private static final String SELECT_SINGLE_ITEM = "SELECT \"node\", \"id\", \"updated\", \"xml\", \"in_reply_to\""
112-
+ " FROM \"items\" WHERE \"id\" = ?";
112+
+ " FROM \"items\" WHERE \"node\" = ? AND \"id\" = ?";
113113

114114
private static final String SELECT_ITEMS_FOR_NODE = "SELECT \"node\", \"id\", \"updated\", \"xml\", \"in_reply_to\""
115115
+ " FROM \"items\" WHERE \"node\" = ? ORDER BY \"updated\" DESC, \"id\" ASC";

src/main/java/org/buddycloud/channelserver/packetprocessor/iq/namespace/pubsub/get/RecentItemsGet.java

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,19 @@
1313
import org.buddycloud.channelserver.db.CloseableIterator;
1414
import org.buddycloud.channelserver.db.exception.NodeStoreException;
1515
import org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.JabberPubsub;
16-
import org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.PubSubElementProcessor;
1716
import org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.PubSubElementProcessorAbstract;
18-
import org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.PubSubGet;
19-
import org.buddycloud.channelserver.pubsub.model.NodeAffiliation;
17+
import org.buddycloud.channelserver.pubsub.model.GlobalItemID;
2018
import org.buddycloud.channelserver.pubsub.model.NodeItem;
21-
import org.buddycloud.channelserver.pubsub.model.NodeSubscription;
22-
import org.buddycloud.channelserver.queue.FederatedQueueManager;
19+
import org.buddycloud.channelserver.pubsub.model.impl.GlobalItemIDImpl;
2320
import org.dom4j.DocumentException;
2421
import org.dom4j.Element;
2522
import org.dom4j.io.SAXReader;
2623
import org.xmpp.packet.IQ;
2724
import org.xmpp.packet.JID;
2825
import org.xmpp.packet.Packet;
2926
import org.xmpp.packet.PacketError;
30-
import org.xmpp.resultsetmanagement.ResultSet;
27+
import org.xmpp.packet.PacketError.Condition;
28+
import org.xmpp.packet.PacketError.Type;
3129

3230
public class RecentItemsGet extends PubSubElementProcessorAbstract {
3331

@@ -43,7 +41,7 @@ public class RecentItemsGet extends PubSubElementProcessorAbstract {
4341
// RSM details
4442
private String firstItemId = null;
4543
private String lastItemId = null;
46-
private String afterItemId = null;
44+
private GlobalItemID afterItemId = null;
4745
private int maxResults = -1;
4846

4947
private static final Logger logger = Logger.getLogger(RecentItemsGet.class);
@@ -99,15 +97,20 @@ public void process(Element elm, JID actorJID, IQ reqIQ, Element rsm)
9997
}
10098

10199
private void parseRsmElement() {
102-
Element rsmElement = pubsub.element("set");
103-
if (null == rsmElement)
100+
if (null == resultSetManagement)
104101
return;
105102
Element max;
106103
Element after;
107-
if (null != (max = rsmElement.element("max")))
104+
if (null != (max = resultSetManagement.element("max")))
108105
maxResults = Integer.parseInt(max.getTextTrim());
109-
if (null != (after = rsmElement.element("after")))
110-
afterItemId = after.getTextTrim();
106+
if (null != (after = resultSetManagement.element("after"))) {
107+
try {
108+
afterItemId = GlobalItemIDImpl.fromString(after.getTextTrim());
109+
} catch(IllegalArgumentException e) {
110+
createExtendedErrorReply(Type.modify, Condition.bad_request, "Could not parse the 'after' id: " + after);
111+
return;
112+
}
113+
}
111114
}
112115

113116
private void addRsmElement() throws NodeStoreException {
@@ -139,11 +142,21 @@ private void addRecentItems() throws NodeStoreException {
139142
try {
140143
entry = xmlReader.read(new StringReader(item.getPayload()))
141144
.getRootElement();
145+
146+
Element entryIdEl = entry.element("id");
147+
148+
String itemId = item.getId();
149+
150+
if(entryIdEl != null) {
151+
itemId = entryIdEl.getTextTrim();
152+
}
153+
142154
itemElement = itemsElement.addElement("item");
143155
itemElement.addAttribute("id", item.getId());
156+
144157
if (null == firstItemId)
145-
firstItemId = item.getId();
146-
lastItemId = item.getId();
158+
firstItemId = itemId;
159+
lastItemId = itemId;
147160
itemElement.add(entry);
148161
} catch (DocumentException e) {
149162
logger.error("Error parsing a node entry, ignoring. "

src/main/java/org/buddycloud/channelserver/packetprocessor/iq/namespace/pubsub/get/items/UserItemsGet.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,15 +183,17 @@ private void getItems() throws Exception {
183183
Element after = resultSetManagement.element("after");
184184
if (after != null) {
185185
try {
186+
// Try and parse it as a global item id
186187
GlobalItemID afterGlobalItemID = GlobalItemIDImpl.fromString(after.getTextTrim());
187188
afterItemId = afterGlobalItemID.getItemID();
188189

190+
// Check it's for the correct node
189191
if(!afterGlobalItemID.getNodeID().equals(node)) {
190192
createExtendedErrorReply(Type.modify, Condition.item_not_found, "RSM 'after' specifies an unexpected NodeID: " + afterGlobalItemID.getNodeID());
191193
}
192194
} catch(IllegalArgumentException e) {
193-
createExtendedErrorReply(Type.modify, Condition.bad_request, "Could not parse the 'after' id: " + after);
194-
return;
195+
// If the after isn't a valid 'tag:...' then it might just be a straight ItemID
196+
afterItemId = after.getTextTrim();
195197
}
196198
}
197199
}

src/main/java/org/buddycloud/channelserver/packetprocessor/iq/namespace/pubsub/set/SubscribeSet.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11

22
package org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.set;
33

4-
import java.util.ArrayList;
54
import java.util.Collection;
65
import java.util.HashMap;
76
import java.util.HashSet;
@@ -67,7 +66,7 @@ public void process(Element elm, JID actorJID, IQ reqIQ, Element rsm)
6766
}
6867

6968
subscribingJid = request.getFrom();
70-
boolean isLocalNode = true;
69+
7170
boolean isLocalSubscriber = false;
7271

7372
if (actorJID != null) {

src/main/java/org/buddycloud/channelserver/pubsub/model/impl/GlobalItemIDImpl.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,48 @@ public static GlobalItemID fromString(final String str) {
8080

8181
return new GlobalItemIDImpl(service, matcher.group(2), matcher.group(3));
8282
}
83+
84+
/**
85+
*
86+
*/
87+
public static GlobalItemID fromBuddycloudNode(final String bcNodeId, final String itemId) {
88+
return null;
89+
}
90+
91+
@Override
92+
public int hashCode() {
93+
final int prime = 31;
94+
int result = 1;
95+
result = prime * result + ((itemID == null) ? 0 : itemID.hashCode());
96+
result = prime * result + ((nodeID == null) ? 0 : nodeID.hashCode());
97+
result = prime * result + ((service == null) ? 0 : service.hashCode());
98+
return result;
99+
}
100+
101+
@Override
102+
public boolean equals(Object obj) {
103+
if (this == obj)
104+
return true;
105+
if (obj == null)
106+
return false;
107+
if (!(obj instanceof GlobalItemID))
108+
return false;
109+
GlobalItemID other = (GlobalItemID) obj;
110+
if (itemID == null) {
111+
if (other.getItemID() != null)
112+
return false;
113+
} else if (!itemID.equals(other.getItemID()))
114+
return false;
115+
if (nodeID == null) {
116+
if (other.getNodeID() != null)
117+
return false;
118+
} else if (!nodeID.equals(other.getNodeID()))
119+
return false;
120+
if (service == null) {
121+
if (other.getService() != null)
122+
return false;
123+
} else if (!service.equals(other.getService()))
124+
return false;
125+
return true;
126+
}
83127
}

0 commit comments

Comments
 (0)