package com.tanza.rufus.feed; import com.tanza.rufus.core.User; import com.tanza.rufus.db.ArticleDao; import com.sun.syndication.io.SyndFeedInput; import com.sun.syndication.io.XmlReader; import org.apache.commons.collections.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.URL; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * Service responsible for parsing and validating * {@link User} requested RSS feed sources. * * Created by jtanza. */ public class FeedParser { private static final Logger logger = LoggerFactory.getLogger(FeedParser.class); private final ArticleDao articleDao; private final FeedProcessor processor; public FeedParser(ArticleDao articleDao, FeedProcessor processor) { this.articleDao = articleDao; this.processor = processor; } /** * Validates and converts {@param requestFeeds} into * {@link URL}s * * @param user * @param requestFeeds * @return */ public List<FeedResponse> parse(User user, List<String> requestFeeds) { long userId = user.getId(); Set<String> pruned = new HashSet<>(requestFeeds); List<String> existing = articleDao.getSources(userId).stream().map(s -> s.getUrl().toString()).collect(Collectors.toList()); List<FeedResponse> feedFeedResponses = new ArrayList<>(); pruned.forEach((String f) -> { if (existing.contains(f)) { feedFeedResponses.add(FeedResponse.invalid("Already Subscribed to Feed!", f)); } else { FeedResponse response = FeedParser.validate(f); if (response.isValid()) { logger.info("added feed {} for user {}", f, userId); articleDao.addSource(userId, response.getUrl()); } feedFeedResponses.add(response); } }); processor.invalidateCache(userId); //invalidate the user's article cache after having added new sources return feedFeedResponses; } private static FeedResponse validate(String feedRequestUrl) { try { URL url = new URL(feedRequestUrl); SyndFeedInput input = new SyndFeedInput(); input.build(new XmlReader(url)); //ensure request is a valid rss feed return FeedResponse.valid(feedRequestUrl); } catch (Exception e) { logger.debug("could not parse feed request {}, reason {}", feedRequestUrl, e.getMessage()); return FeedResponse.invalid(e.getMessage(), feedRequestUrl); } } public static class FeedResponse { private boolean valid; private String error; private String url; private FeedResponse(boolean valid, String error, String url) { this.valid = valid; this.error = error; this.url = url; } public static FeedResponse valid(String url) { return new FeedResponse(true, null, url); } public static FeedResponse invalid(String error, String url) { return new FeedResponse(false, error, url); } public boolean isValid() { return valid; } public String getError() { return error; } public String getUrl() { return url; } /** * Formats a collection of {@param responses} into a single user facing message * to be used after an {@link User} attempts to add new feed subscriptions. * * @param responses * @return */ public static String formatMessage(List<FeedResponse> responses) { StringBuilder builder = new StringBuilder(); List<FeedResponse> valid = responses.stream().filter(FeedResponse::isValid).collect(Collectors.toList()); if (CollectionUtils.isNotEmpty(valid)) { builder.append("Successfully added : \n"); valid.stream().forEach(r -> builder.append(r.getUrl()).append("\n")); } responses.removeAll(valid); if (CollectionUtils.isNotEmpty(responses)) { builder.append("\nCould not add : \n"); responses.stream().forEach(r -> builder .append(r.getUrl()) .append(" - Reason : ") .append(r.getError()) .append("\n") ); } return builder.toString(); } } }