From 28a747f7f3c6d100f9e2a4e3b24817a8e04ac83f Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 24 Jan 2023 13:57:44 +0000 Subject: [PATCH] Add method for adding an RSS feed from an input stream. --- .../briar/api/feed/FeedManager.java | 10 ++- .../briarproject/briar/feed/FeedFactory.java | 4 +- .../briar/feed/FeedFactoryImpl.java | 3 +- .../briar/feed/FeedManagerImpl.java | 34 ++++++++-- .../briar/feed/FeedManagerImplTest.java | 64 ++++++++++++++++++- 5 files changed, 103 insertions(+), 12 deletions(-) diff --git a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java index d51c2b001..0778e5901 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java @@ -6,6 +6,7 @@ import org.briarproject.bramble.api.sync.ClientId; import org.briarproject.nullsafety.NotNullByDefault; import java.io.IOException; +import java.io.InputStream; import java.util.List; @NotNullByDefault @@ -22,10 +23,17 @@ public interface FeedManager { int MAJOR_VERSION = 0; /** - * Adds an RSS feed as a new dedicated blog. + * Adds an RSS feed as a new dedicated blog, or updates the existing blog + * if a blog for the feed already exists. */ Feed addFeed(String url) throws DbException, IOException; + /** + * Adds an RSS feed as a new dedicated blog, or updates the existing blog + * if a blog for the feed already exists. + */ + Feed addFeed(InputStream in) throws DbException, IOException; + /** * Removes an RSS feed. */ diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactory.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactory.java index cd9447f72..6abef67eb 100644 --- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactory.java +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactory.java @@ -6,13 +6,15 @@ import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.briar.api.feed.Feed; +import javax.annotation.Nullable; + interface FeedFactory { /** * Create a new feed based on the feed url * and the metadata of an existing {@link SyndFeed}. */ - Feed createFeed(String url, SyndFeed sf); + Feed createFeed(@Nullable String url, SyndFeed sf); /** * Creates a new updated feed, based on the given existing feed, diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java index bd062faf7..430364cfb 100644 --- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java @@ -18,6 +18,7 @@ import org.briarproject.briar.api.blog.BlogFactory; import org.briarproject.briar.api.feed.Feed; import org.briarproject.briar.api.feed.RssProperties; +import javax.annotation.Nullable; import javax.inject.Inject; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; @@ -51,7 +52,7 @@ class FeedFactoryImpl implements FeedFactory { } @Override - public Feed createFeed(String url, SyndFeed sf) { + public Feed createFeed(@Nullable String url, SyndFeed sf) { String title = sf.getTitle(); if (title == null) title = "RSS"; else title = truncateUtf8(title, MAX_AUTHOR_NAME_LENGTH); diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java index 7c0be7856..ac3d1054b 100644 --- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java @@ -56,6 +56,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; +import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; import javax.inject.Inject; @@ -68,6 +69,7 @@ import static java.util.Collections.singletonList; import static java.util.Collections.sort; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.truncateUtf8; @@ -170,7 +172,19 @@ class FeedManagerImpl implements FeedManager, EventListener, OpenDatabaseHook, @Override public Feed addFeed(String url) throws DbException, IOException { // fetch feed to get posts and metadata - SyndFeed sf = fetchSyndFeed(url); + SyndFeed sf = fetchAndCleanFeed(url); + return addFeed(url, sf); + } + + @Override + public Feed addFeed(InputStream in) throws DbException, IOException { + // fetch feed to get posts and metadata + SyndFeed sf = fetchAndCleanFeed(in); + return addFeed(null, sf); + } + + private Feed addFeed(@Nullable String url, SyndFeed sf) throws DbException { + // extract properties from the feed RssProperties properties = new RssProperties(url, sf.getTitle(), sf.getDescription(), sf.getAuthor(), sf.getLink(), sf.getUri()); @@ -323,7 +337,7 @@ class FeedManagerImpl implements FeedManager, EventListener, OpenDatabaseHook, String url = feed.getProperties().getUrl(); if (url == null) continue; // fetch and clean feed - SyndFeed sf = fetchSyndFeed(url); + SyndFeed sf = fetchAndCleanFeed(url); // sort and add new entries long lastEntryTime = postFeedEntries(feed, sf.getEntries()); updatedFeeds.add( @@ -342,11 +356,17 @@ class FeedManagerImpl implements FeedManager, EventListener, OpenDatabaseHook, LOG.info("Done updating RSS feeds"); } - private SyndFeed fetchSyndFeed(String url) throws IOException { - // fetch feed - InputStream stream = getFeedInputStream(url); - SyndFeed sf = getSyndFeed(stream); - stream.close(); + private SyndFeed fetchAndCleanFeed(String url) throws IOException { + return fetchAndCleanFeed(getFeedInputStream(url)); + } + + private SyndFeed fetchAndCleanFeed(InputStream in) throws IOException { + SyndFeed sf; + try { + sf = getSyndFeed(in); + } finally { + tryToClose(in, LOG, WARNING); + } // clean title String title = sf.getTitle(); diff --git a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerImplTest.java b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerImplTest.java index f7d011149..382e486b3 100644 --- a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerImplTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerImplTest.java @@ -28,6 +28,7 @@ import org.briarproject.briar.api.feed.RssProperties; import org.jmock.Expectations; import org.junit.Test; +import java.io.ByteArrayInputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.Executor; @@ -38,6 +39,7 @@ import okhttp3.OkHttpClient; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.singletonList; import static okhttp3.mockwebserver.SocketPolicy.DISCONNECT_DURING_RESPONSE_BODY; import static org.briarproject.bramble.test.TestUtils.getGroup; @@ -46,6 +48,7 @@ import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEEDS; import static org.briarproject.briar.api.feed.FeedManager.CLIENT_ID; import static org.briarproject.briar.api.feed.FeedManager.MAJOR_VERSION; +import static org.hamcrest.Matchers.nullValue; public class FeedManagerImplTest extends BrambleMockTestCase { @@ -183,7 +186,7 @@ public class FeedManagerImplTest extends BrambleMockTestCase { } @Test - public void testAddNewFeed() throws Exception { + public void testAddNewFeedFromUrl() throws Exception { // Fetching and parsing the feed will succeed; there are no entries String feedXml = createRssFeedXml(); @@ -223,7 +226,7 @@ public class FeedManagerImplTest extends BrambleMockTestCase { } @Test - public void testAddExistingFeed() throws Exception { + public void testAddExistingFeedFromUrl() throws Exception { // Fetching and parsing the feed will succeed; there are no entries String feedXml = createRssFeedXml(); @@ -248,6 +251,63 @@ public class FeedManagerImplTest extends BrambleMockTestCase { feedManager.addFeed(url); } + @Test + public void testAddNewFeedFromInputStream() throws Exception { + // Reading and parsing the feed will succeed; there are no entries + String feedXml = createRssFeedXml(); + Feed newFeed = createFeed(null, blog); + + Group existingBlogGroup = getGroup(BlogManager.CLIENT_ID, + BlogManager.MAJOR_VERSION); + Blog existingBlog = new Blog(existingBlogGroup, localAuthor, true); + Feed existingFeed = createFeed(null, existingBlog); + + expectGetFeeds(existingFeed); + + context.checking(new DbExpectations() {{ + // The added feed doesn't match any existing feed + oneOf(feedMatcher).findMatchingFeed(with(any(RssProperties.class)), + with(singletonList(existingFeed))); + will(returnValue(null)); + // Create the new feed + oneOf(feedFactory).createFeed(with(nullValue(String.class)), + with(any(SyndFeed.class))); + will(returnValue(newFeed)); + // Add the new feed to the list of feeds + Transaction txn = new Transaction(null, false); + oneOf(db).transaction(with(false), withDbRunnable(txn)); + oneOf(blogManager).addBlog(txn, blog); + expectGetFeeds(txn, existingFeed); + expectStoreFeeds(txn, existingFeed, newFeed); + }}); + + expectUpdateFeedNoEntries(newFeed); + expectGetAndStoreFeeds(existingFeed, newFeed); + + feedManager.addFeed(new ByteArrayInputStream(feedXml.getBytes(UTF_8))); + } + + @Test + public void testAddExistingFeedFromInputStream() throws Exception { + // Reading and parsing the feed will succeed; there are no entries + String feedXml = createRssFeedXml(); + Feed newFeed = createFeed(null, blog); + + expectGetFeeds(newFeed); + + context.checking(new DbExpectations() {{ + // The added feed matches an existing feed + oneOf(feedMatcher).findMatchingFeed(with(any(RssProperties.class)), + with(singletonList(newFeed))); + will(returnValue(newFeed)); + }}); + + expectUpdateFeedNoEntries(newFeed); + expectGetAndStoreFeeds(newFeed); + + feedManager.addFeed(new ByteArrayInputStream(feedXml.getBytes(UTF_8))); + } + private Feed createFeed(String url, Blog blog) { RssProperties properties = new RssProperties(url, null, null, null, null, null);