Skip to content

Commit

Permalink
Merging #79.
Browse files Browse the repository at this point in the history
  • Loading branch information
abmargb committed Nov 11, 2013
1 parent e885104 commit ef36929
Show file tree
Hide file tree
Showing 18 changed files with 1,899 additions and 4 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<groupId>org.buddycloud</groupId>
<artifactId>channelserver</artifactId>
<packaging>jar</packaging>
<version>0.12.0</version>
<version>0.12.0-search</version>
<name>Buddycloud Java Server</name>
<scm>
<url>https://github.com/buddycloud/buddycloud-server-java</url>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package org.buddycloud.channelserver.channel;

import java.util.List;

import org.buddycloud.channelserver.db.CloseableIterator;
import org.buddycloud.channelserver.db.NodeStore;
import org.buddycloud.channelserver.db.exception.NodeStoreException;
import org.buddycloud.channelserver.pubsub.affiliation.Affiliations;
import org.buddycloud.channelserver.pubsub.model.NodeItem;
import org.xmpp.packet.JID;

public interface ChannelManager extends NodeStore {
Expand Down Expand Up @@ -47,9 +51,25 @@ public interface ChannelManager extends NodeStore {

/**
* Gets the default affiliation for a node
* @return
*
* @return
*
* @throws NodeStoreException
*/
Affiliations getDefaultNodeAffiliation(String nodeId) throws NodeStoreException;
Affiliations getDefaultNodeAffiliation(String nodeId)
throws NodeStoreException;

/**
* Searches for the provided content or author, or both, across nodes
* the searcher has access to
*
* @param searcher
* @param content
* @param author
* @param page
* @param rpp
* @return
*/
CloseableIterator<NodeItem> performSearch(JID searcher, List content, JID author, int page,
int rpp) throws NodeStoreException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;

Expand Down Expand Up @@ -444,6 +445,12 @@ public Affiliations getDefaultNodeAffiliation(String nodeId)
return Affiliations.member;
}

@Override
public CloseableIterator<NodeItem> performSearch(JID searcher,
List content, JID author, int page, int rpp) throws NodeStoreException {
return nodeStore.performSearch(searcher, content, author, page, rpp);
}

@Override
public ResultSet<NodeItem> getUserItems(JID userJid) throws NodeStoreException {
return nodeStore.getUserItems(userJid);
Expand Down Expand Up @@ -473,5 +480,6 @@ public ResultSet<NodeThread> getNodeThreads(String node, String afterId,
@Override
public int countNodeThreads(String node) throws NodeStoreException {
return nodeStore.countNodeThreads(node);
}
}

}
15 changes: 15 additions & 0 deletions src/main/java/org/buddycloud/channelserver/db/NodeStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.buddycloud.channelserver.db.exception.NodeStoreException;
Expand Down Expand Up @@ -558,6 +559,20 @@ void deleteNodeItemById(String nodeId, String nodeItemId)
*/
ArrayList<String> getNodeList() throws NodeStoreException;

/**
* Search subscribed nodes for content
*
* @param searcher JID of user performing the search
* @param content Keywords upon which to search
* @param author JID of the content author
* @param page Page number of results (>= 1)
* @param rpp Results per page (>= 1)
* @return
* @throws NodeStoreException
*/
CloseableIterator<NodeItem> performSearch(JID searcher, List content,
JID author, int page, int rpp) throws NodeStoreException;

/**
* Retrieves a list of items from public channels "firehose"
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

Expand Down Expand Up @@ -1532,6 +1533,66 @@ public void purgeNodeItems(String nodeId) throws NodeStoreException {
close(stmt); // Will implicitly close the resultset if required
}
}

@Override
public CloseableIterator<NodeItem> performSearch(JID searcher,
List content, JID author, int page, int rpp) throws NodeStoreException {
PreparedStatement stmt = null;
try {
String sql = "SELECT * FROM \"items\" " +
"LEFT JOIN \"subscriptions\" ON \"items\".\"node\" = \"subscriptions\".\"node\" " +
"WHERE " +
"\"subscriptions\".\"user\" = ? " +
"AND \"subscriptions\".\"subscription\" = 'subscribed' " +
"AND RIGHT(\"items\".\"node\", 6) = '/posts' " +
" $searchParameters " +
"ORDER BY \"items\".\"updated\" DESC " +
"LIMIT ? OFFSET ?;";
ArrayList<String> parameterValues = new ArrayList<String>();
parameterValues.add(searcher.toBareJID());
String searchParameters = "";

for (String term : (List<String>) content) {
searchParameters += "AND (\"items\".\"xml\" LIKE ?) ";
parameterValues.add("%<content%>%" + term + "%</content>%");
}

if (null != author) {
searchParameters += "AND (\"items\".\"xml\" LIKE ?)";
parameterValues.add("%<name>" + author.toBareJID() + "</name>%");
}
stmt = conn.prepareStatement(sql.replace("$searchParameters", searchParameters));

int counter = 0;
for (String value : parameterValues) {
++counter;
stmt.setString(counter, value);
}
stmt.setInt(parameterValues.size() + 1, rpp);
stmt.setInt(parameterValues.size() + 2, (page - 1) * rpp);

java.sql.ResultSet rs = stmt.executeQuery();

stmt = null; // Prevent the finally block from closing the
// statement

return new ResultSetIterator<NodeItem>(rs,
new ResultSetIterator.RowConverter<NodeItem>() {
@Override
public NodeItem convertRow(java.sql.ResultSet rs)
throws SQLException {
return new NodeItemImpl(rs.getString(1),
rs.getString(2), rs.getTimestamp(3),
rs.getString(4), rs.getString(5));
}
});
} catch (SQLException e) {
e.printStackTrace();
throw new NodeStoreException(e);
} finally {
close(stmt); // Will implicitly close the resultset if required
}
}

@Override
public ArrayList<String> getNodeList() throws NodeStoreException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.buddycloud.channelserver.packetprocessor.iq.namespace.mam.MessageArchiveManagement;
import org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.JabberPubsub;
import org.buddycloud.channelserver.packetprocessor.iq.namespace.register.JabberRegister;
import org.buddycloud.channelserver.packetprocessor.iq.namespace.search.Search;
import org.buddycloud.channelserver.queue.FederatedQueueManager;
import org.buddycloud.channelserver.queue.UnknownFederatedPacketException;
import org.xmpp.packet.IQ;
Expand Down Expand Up @@ -49,6 +50,8 @@ public IQProcessor(BlockingQueue<Packet> outQueue, Configuration conf,
processorsPerNamespace.put(JabberPubsub.NS_PUBSUB_OWNER, ps);
processorsPerNamespace.put(MessageArchiveManagement.NAMESPACE_MAM,
new MessageArchiveManagement(outQueue, channelManager));
processorsPerNamespace.put(Search.NAMESPACE_URI,
new Search(outQueue, channelManager));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ private void sendServerDiscoInfo()

query.addElement("feature").addAttribute("var",
"http://jabber.org/protocol/disco#info");

query.addElement("feature").addAttribute("var",
"http://jabber.org/protocol/disco#items");

query.addElement("feature").addAttribute("var",
"jabber:iq:search");

outQueue.put(result);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.buddycloud.channelserver.packetprocessor.iq.namespace.search;

import java.util.Collection;
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.exception.NodeStoreException;
import org.buddycloud.channelserver.packetprocessor.PacketProcessor;
import org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.JabberPubsub;
import org.buddycloud.channelserver.pubsub.accessmodel.AccessModels;
import org.buddycloud.channelserver.pubsub.model.impl.NodeSubscriptionImpl;
import org.buddycloud.channelserver.pubsub.subscription.Subscriptions;
import org.dom4j.Element;
import org.dom4j.QName;
import org.xmpp.packet.IQ;
import org.xmpp.packet.IQ.Type;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
import org.xmpp.packet.PacketError;

public class Search implements PacketProcessor<IQ> {

public static final String ELEMENT_NAME = "query";
private static final Logger logger = Logger.getLogger(Search.class);

public static final String NAMESPACE_URI = "jabber:iq:search";

private final BlockingQueue<Packet> outQueue;
private final ChannelManager channelManager;
private IQ request;

private SearchGet searchGet;
private SearchSet searchSet;

public Search(BlockingQueue<Packet> outQueue,
ChannelManager channelManager) {
this.outQueue = outQueue;
this.channelManager = channelManager;

this.searchGet = new SearchGet(outQueue, channelManager);
this.searchSet = new SearchSet(outQueue, channelManager);
}

@Override
public void process(IQ reqIQ) throws Exception {
request = reqIQ;
if (request.getType().equals(Type.get)) {
logger.trace("Using search processor: SearchGet");
this.searchGet.process(request);
return;
} else if (request.getType().equals(Type.set)) {
logger.trace("Using search processor: SearchSet");
this.searchSet.process(request);
return;
}
IQ response = IQ.createResultIQ(request);
response.setType(IQ.Type.error);
PacketError error = new PacketError(
PacketError.Condition.bad_request,
PacketError.Type.modify
);
response.setError(error);
outQueue.put(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package org.buddycloud.channelserver.packetprocessor.iq.namespace.search;

import java.util.concurrent.BlockingQueue;

import org.buddycloud.channelserver.channel.ChannelManager;
import org.buddycloud.channelserver.packetprocessor.PacketProcessor;
import org.dom4j.Element;
import org.xmpp.forms.DataForm;
import org.xmpp.packet.IQ;
import org.xmpp.packet.Packet;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.PacketError.Type;

public class SearchGet implements PacketProcessor<IQ> {

public static final String INSTRUCTIONS = "Search for content/hashtags/mentions";

public static final String TITLE = "Please populate one or more of the following fields";
public static final String CONTENT_FIELD_LABEL = "Content search";
public static final String AUTHOR_FIELD_LABEL = "Author";
public static final String RPP_FIELD_LABEL = "Results per page";
public static final String PAGE_FIELD_LABEL = "Page";

private ChannelManager channelManager;
private BlockingQueue<Packet> outQueue;
private IQ response;

private Element x;

public SearchGet(BlockingQueue<Packet> outQueue,
ChannelManager channelManager) {
this.channelManager = channelManager;
this.outQueue = outQueue;
}

@Override
public void process(IQ request) throws Exception {
response = IQ.createResultIQ(request);

if (false == channelManager.isLocalJID(request.getFrom())) {
sendErrorResponse(PacketError.Type.cancel,
PacketError.Condition.not_allowed);
return;
}

Element query = response.getElement().addElement("query");
query.addAttribute("xmlns", Search.NAMESPACE_URI);
query.addElement("instructions").addText(INSTRUCTIONS);
x = query.addElement("x");
addFields();
outQueue.put(response);
}

private void addFields() {
x.addAttribute("xmlns", DataForm.NAMESPACE);
x.addElement("title").addText(TITLE);
x.addElement("instructions").addText(INSTRUCTIONS);

Element formType = x.addElement("field");
formType.addAttribute("type", "hidden");
formType.addAttribute("var", "FORM_TYPE");
formType.addElement("value").addText(Search.NAMESPACE_URI);

Element content = x.addElement("field");
content.addAttribute("type", "text-multi");
content.addAttribute("var", "content");
content.addAttribute("label", CONTENT_FIELD_LABEL);

Element author = x.addElement("field");
author.addAttribute("type", "jid-single");
author.addAttribute("var", "author");
author.addAttribute("label", AUTHOR_FIELD_LABEL);

Element rpp = x.addElement("field");
rpp.addAttribute("type", "fixed");
rpp.addAttribute("var", "rpp");
rpp.addAttribute("label", RPP_FIELD_LABEL);

Element page = x.addElement("field");
page.addAttribute("type", "fixed");
page.addAttribute("var", "page");
page.addAttribute("label", PAGE_FIELD_LABEL);
}

private void sendErrorResponse(PacketError.Type type,
PacketError.Condition condition) throws InterruptedException {
response.setType(IQ.Type.error);
PacketError error = new PacketError(PacketError.Condition.not_allowed,
PacketError.Type.cancel);
response.setError(error);
outQueue.put(response);
}

}
Loading

0 comments on commit ef36929

Please sign in to comment.