newAggregateContentUUIDs.add(aggregateNode.getUUID());
AggregateFilter aggregateFilter = loadAggregateFilter(aggregateNode);
for (FeedChannel channel : aggregateFeed.getChannels()) {
if (channel.hasFeed()) {
saveFeedChannel(channel, aggregateFilter, dataNode);
}
}
}
return newAggregateContentUUIDs;
}
/**
* Load a single aggregate content node from the given <code>parentNode</code> with the given
* <code>aggregateName</code>. If no such aggregate could be found, <code>null</code> is returned.
*
* @param parentNode the parentNode to load the node from
* @param aggregateNodeName the name of the aggregate content node to load
* @return the aggregate content node, or <code>null</code> if no such node was found
* @throws IllegalStateException when multiple aggregate content nodes with the same name are found
*/
protected Node loadSingleAggregateNode(Node parentNode, String aggregateNodeName) throws RepositoryException {
// ////////////////
// SHOULD BE MOVE IN NodeUtil
// ///////////////
NodeIterator nodeIterator = parentNode.getNodes(aggregateNodeName);
Collection<Node> aggregateNodes = new ArrayList<Node>();
while (nodeIterator.hasNext()) {
Node currentNode = nodeIterator.nextNode();
if (NodeUtil.isNodeType(currentNode, RSSAggregatorNodeTypes.RSSAggregator.NAME)) {
aggregateNodes.add(currentNode);
}
}
// ////////////////
// END
// ///////////////
int size = aggregateNodes.size();
if (size > 1) {
throw new IllegalStateException(format(
"Expected content node '%s' to have at most 1 child named '%s' of item type '%s', but found %s",
parentNode, aggregateNodeName, RSSAggregatorNodeTypes.RSSAggregator.NAME, size));
}
if (aggregateNodes.isEmpty()) {
return null;
}
return aggregateNodes.iterator().next();
}
/**
* Behaves exactly like {@link NodeUtil#createPath(Node contentNode, String name, String itemType, boolean save)}. This method exists for
* testability.
*
* @param contentNode the contentNode to (create if non-existent and then) get
* @param name the name of the node
* @param itemType the type of the content node
* @return the created content node
* @throws RepositoryException when an exception occurs accessing the Content Repository
*/
protected Node getOrCreateNode(Node contentNode, String name, String itemType) throws RepositoryException {
return NodeUtil.createPath(contentNode, name, itemType, true);
}
/**
* Load the {@link AggregateFilter} for the {@link AggregateFeed} which is represented by the given
* <code>aggregateNode</code>. Only
*
* @param aggregateNode the content node representing the AggregateFeed to load the AggregateFilter for
* @return the aggregate filter
* @throws RepositoryException when an exception occurs accessing the Content Repository
*/
public AggregateFilter loadAggregateFilter(Node aggregateNode) throws RepositoryException {
Node filtersNode = aggregateNode.hasNode("filters") ? aggregateNode.getNode("filters") : null;
if (filtersNode == null) {
return new AggregateFilter(Collections.<FilterPredicate>emptySet());
}
Set<FilterPredicate> filters = new HashSet<FilterPredicate>();
List<Node> filterNodes = NodeUtil.asList(NodeUtil.getNodes(filtersNode, VersionUtil.getNodeTypeName(filtersNode)));
for (Node n : filterNodes) {
FilterPredicate filterPredicate = filterPredicateMapper.map(n);
if (filterPredicate == null) {
continue;
}
filters.add(filterPredicate);
}
return new AggregateFilter(filters);
}
/**
* Save the {@link SyndFeed#getEntries() entries} contained {@link FeedChannel#feed in} the given
* {@link FeedChannel} that pass the given {@link AggregateFilter} in the provided <code>dataNode</code>.
*
* @param dataNode the content node to store the feed content under
* @param feedChannel the feed channel to save
* @param aggregateFilter the aggregate filter to apply to entries in the feed channel
* @throws RepositoryException when an exception occurs accessing the Content Repository
*/
@SuppressWarnings("unchecked")
protected Node saveFeedChannel(FeedChannel feedChannel, AggregateFilter aggregateFilter, Node dataNode) throws RepositoryException {
Node channelNode = recreateFeedChannelNode(feedChannel, dataNode);
List<SyndEntry> entries = feedChannel.getFeed().getEntries();
int size = entries.size();
for (int i = 0; i < size; i++) {
SyndEntry entry = entries.get(i);
String entryName = format("entry-%s", i);
if (aggregateFilter.include(entry)) {
createFeedChannelEntryNode(entry, entryName, channelNode);
}
}
return channelNode;
}
/**
* Recreate the feed channel content node the given feed channel in the Content Repository.
*
* @param dataNode the node to store the feed channel under
* @param feedChannel the feed channel to recreate
* @return the created feed channel content node
* @throws RepositoryException when an exception occurs accessing the Content Repository
*/
protected Node recreateFeedChannelNode(FeedChannel feedChannel, Node dataNode) throws RepositoryException {
String channelName = feedChannel.getName();
if (dataNode.hasNode(channelName)) {
String absPath = dataNode.getNode(channelName).getPath();
dataNode.getSession().removeItem(absPath);
}
Node channelNode = NodeUtil.createPath(dataNode, channelName, NodeTypes.Content.NAME, true);
SyndFeed feed = feedChannel.getFeed();
channelNode.setProperty("description", feed.getDescription()); // 'My Blog'
channelNode.setProperty("link", feed.getLink()); // 'http://domain.com'
channelNode.setProperty("rss", feedChannel.getUrl()); // 'http://domain.com/channel.rss'
channelNode.setProperty("title", !isEmpty(feedChannel.getTitle()) ? feedChannel.getTitle() : feed.getTitle());
channelNode.setProperty("type", feed.getFeedType()); // 'rss_2.0'
return channelNode;
}
/**
* Create a feed channel entry node under the given <code>channelNode</code> with the given <code>nodeName</code>
* for the given <code>entry</code>.
*
* @param entry the feed channel entry to save
* @param nodeName the name of the feed channel entry node to create
* @param channelNode the feed channel content node to create the feed channel entry under
* @throws RepositoryException when an exception occurs accessing the Content Repository
*/
protected Node createFeedChannelEntryNode(SyndEntry entry, String nodeName, Node channelNode) throws RepositoryException {
Node entryNode = NodeUtil.createPath(channelNode, nodeName, NodeTypes.Content.NAME, true);
entryNode.setProperty("author", entry.getAuthor() == null ? "" : entry.getAuthor());
entryNode.setProperty("channelTitle", PropertyUtil.getString(channelNode, "title"));
final SyndContent description = entry.getDescription();
String descriptionString;
if (description != null && StringUtils.isNotBlank(description.getValue())) {
descriptionString = description.getValue();
} else {
descriptionString = getEntryContent(entry);
}
entryNode.setProperty("description", descriptionString);
entryNode.setProperty("content", getEntryContent(entry));
entryNode.setProperty("link", entry.getLink());
Date publishedDate = entry.getPublishedDate();
if (publishedDate == null) {
publishedDate = new Date();
}
entryNode.setProperty("pubDate", publishedDate.getTime());
entryNode.setProperty("title", entry.getTitle());
createCategoriesNode(entry, entryNode);
return entryNode;
}
/**
* Retrieves the main content of an entry.
*
* @param entry Feed entry holding all data.
* @return Entry content as String or empty String if no content is available.
*/
protected String getEntryContent(SyndEntry entry) {
String entryContent = "";
if (entry != null && entry.getContents().size() > 0) {
@SuppressWarnings("unchecked")
final List<SyndContent> contents = entry.getContents();
for (SyndContent content : contents) {
if (StringUtils.equalsIgnoreCase("html", content.getType()) && StringUtils.isNotBlank(content.getType())) {
entryContent = content.getValue();
break;
}
}
}
return entryContent;
}