package info.persistent.pushbot;
import com.google.appengine.api.xmpp.JID;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
import info.persistent.pushbot.data.Subscription;
import info.persistent.pushbot.util.Feeds;
import info.persistent.pushbot.util.Persistence;
import info.persistent.pushbot.util.Xmpp;
import org.apache.commons.lang3.StringEscapeUtils;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import javax.jdo.PersistenceManager;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
public class PushSubscriberServlet extends HttpServlet {
private static final Logger logger =
Logger.getLogger(PushSubscriberServlet.class.getName());
private static final int MAX_ENTRIES_TO_DISPLAY = 3;
/** Subscription verifications arrive via GETs */
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
resp.setStatus(200);
resp.setContentType("text/plain");
resp.getOutputStream().print(req.getParameter("hub.challenge"));
resp.getOutputStream().flush();
JID user = new JID(req.getPathInfo().substring(1));
if (req.getParameter("hub.mode").equals("subscribe")) {
Xmpp.sendMessage(user, "Subscribed to " + req.getParameter("hub.topic"));
} else if (req.getParameter("hub.mode").equals("unsubscribe")) {
Xmpp.sendMessage(
user, "Unsubscribed from " + req.getParameter("hub.topic"));
}
}
/** Actual notifications arrive via POSTs */
@SuppressWarnings("unchecked")
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
resp.setStatus(204);
SyndFeed feed = Feeds.parseFeed(req.getInputStream());
if (feed == null) {
return;
}
List<SyndEntry> entries = feed.getEntries();
if (entries.isEmpty()) {
return;
}
JID user = new JID(req.getPathInfo().substring(1));
// TODO(mihaip): this is potentially incorrect if a feed gets redirected and
// its self URL changes since the time the subscription was created
List<URL> feedUrls = Feeds.getLinkUrl(feed, Feeds.SELF_RELATION);
if (!feedUrls.isEmpty()) {
URL feedUrl = feedUrls.get(0);
List<Subscription> subscriptions =
Subscription.getSubscriptionsForUserAndFeedUrl(user, feedUrl);
if (!subscriptions.isEmpty()) {
final Subscription subscription = subscriptions.get(0);
Set<String> seenEntryIds = subscription.getSeenEntryIds();
List<String> newEntryIds = Lists.newArrayList();
List<SyndEntry> filteredEntries = Lists.newArrayList();
for (SyndEntry entry : entries) {
String entryId = Feeds.getEntryId(entry);
if (seenEntryIds.contains(entryId)) {
logger.info("Filtering out already seen entry from " + feedUrl);
continue;
}
filteredEntries.add(entry);
newEntryIds.add(entryId);
}
if (!filteredEntries.isEmpty()) {
subscription.addSeenEntryIds(newEntryIds);
Persistence.withManager(new Persistence.Closure() {
@Override public void run(PersistenceManager manager) {
manager.makePersistent(subscription);
}
});
} else {
return;
}
entries = filteredEntries;
} else {
logger.warning("Got notification for feed without subscription " + feedUrl);
}
}
// If subscribing to a previously unseen URL, the hub might report a bunch
// of entries as new, so we sort them by published date and only show the
// first few
Collections.sort(entries, new Comparator<SyndEntry>() {
@Override public int compare(SyndEntry o1, SyndEntry o2) {
if (o1.getPublishedDate() == null) {
return 1;
}
if (o2.getPublishedDate() == null) {
return -1;
}
return o2.getPublishedDate().compareTo(o1.getPublishedDate());
}
});
List<SyndEntry> displayEntries;
if (entries.size() > MAX_ENTRIES_TO_DISPLAY) {
displayEntries = entries.subList(0, MAX_ENTRIES_TO_DISPLAY);
} else {
displayEntries = entries;
}
StringBuilder message = new StringBuilder("Update from ")
.append(StringEscapeUtils.unescapeHtml4(feed.getTitle())).append(":");
for (SyndEntry displayEntry : displayEntries) {
String title = displayEntry.getTitle();
if (Strings.isNullOrEmpty(title)) {
title = "(title unknown)";
} else {
title = StringEscapeUtils.unescapeHtml4(title);
}
String link = displayEntry.getLink();
if (Strings.isNullOrEmpty(link)) {
link = "<no link>";
}
message.append("\n ").append(title).append(": ").append(link);
}
if (displayEntries.size() != entries.size()) {
message.append("\n (and ")
.append(entries.size() - displayEntries.size()).append(" more)");
}
Xmpp.sendMessage(user, message.toString());
}
}