Clover icon

magnolia-module-rssaggregator 2.6.2

  1. Project Clover database Wed Feb 13 2019 12:15:44 CET
  2. Package info.magnolia.module.rssaggregator.command

File LaunchSingleRSSCommand.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart2.png
60% of files have more coverage

Code metrics

40
117
15
1
301
224
40
0.34
7.8
15
2.67
2.3% of code in this file is excluded from these metrics.

Classes

Class Line # Actions
LaunchSingleRSSCommand 82 117 2.3% 40 147
0.1453488314.5%
 

Contributing tests

This file is covered by 2 tests. .

Source view

1    /**
2    * This file Copyright (c) 2014-2018 Magnolia International
3    * Ltd. (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10    * This file is distributed in the hope that it will be
11    * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12    * implied warranty of MERCHANTABILITY or FITNESS FOR A
13    * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14    * Redistribution, except as permitted by whichever of the GPL
15    * or MNA you select, is prohibited.
16    *
17    * 1. For the GPL license (GPL), you can redistribute and/or
18    * modify this file under the terms of the GNU General
19    * Public License, Version 3, as published by the Free Software
20    * Foundation. You should have received a copy of the GNU
21    * General Public License, Version 3 along with this program;
22    * if not, write to the Free Software Foundation, Inc., 51
23    * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24    *
25    * 2. For the Magnolia Network Agreement (MNA), this file
26    * and the accompanying materials are made available under the
27    * terms of the MNA which accompanies this distribution, and
28    * is available at http://www.magnolia-cms.com/mna.html
29    *
30    * Any modifications to this file must keep this entire header
31    * intact.
32    *
33    */
34    package info.magnolia.module.rssaggregator.command;
35   
36    import static java.lang.String.*;
37    import static org.apache.commons.lang3.StringUtils.isEmpty;
38   
39    import info.magnolia.commands.MgnlCommand;
40    import info.magnolia.context.Context;
41    import info.magnolia.context.MgnlContext;
42    import info.magnolia.jcr.util.NodeTypes;
43    import info.magnolia.jcr.util.NodeUtil;
44    import info.magnolia.jcr.util.PropertyUtil;
45    import info.magnolia.module.ModuleRegistry;
46    import info.magnolia.module.rssaggregator.RSSAggregatorConstants;
47    import info.magnolia.module.rssaggregator.RSSAggregatorNodeTypes;
48    import info.magnolia.module.rssaggregator.RSSJob;
49    import info.magnolia.module.rssaggregator.importhandler.AggregateFeed;
50    import info.magnolia.module.rssaggregator.importhandler.AggregateFeedContentMapper;
51    import info.magnolia.module.rssaggregator.importhandler.AggregateFilter;
52    import info.magnolia.module.rssaggregator.importhandler.FeedChannel;
53    import info.magnolia.module.rssaggregator.importhandler.FilterPredicate;
54    import info.magnolia.module.rssaggregator.importhandler.FilterPredicateContentMapper;
55    import info.magnolia.module.scheduler.SchedulerModule;
56    import info.magnolia.objectfactory.Components;
57   
58    import java.util.ArrayList;
59    import java.util.Collection;
60    import java.util.Collections;
61    import java.util.Date;
62    import java.util.HashSet;
63    import java.util.LinkedHashSet;
64    import java.util.List;
65    import java.util.Set;
66   
67    import javax.jcr.Node;
68    import javax.jcr.NodeIterator;
69    import javax.jcr.RepositoryException;
70   
71    import org.apache.commons.lang3.StringUtils;
72    import org.quartz.SchedulerException;
73   
74    import com.rometools.rome.feed.synd.SyndCategory;
75    import com.rometools.rome.feed.synd.SyndContent;
76    import com.rometools.rome.feed.synd.SyndEntry;
77    import com.rometools.rome.feed.synd.SyndFeed;
78   
79    /**
80    * Starts an update on specific RSS aggregator.
81    */
 
82    public class LaunchSingleRSSCommand extends MgnlCommand {
83   
84    private FilterPredicateContentMapper filterPredicateMapper;
85   
86    private RSSJob job;
87   
 
88  0 toggle public LaunchSingleRSSCommand() {
89    }
90   
 
91  2 toggle public LaunchSingleRSSCommand(RSSJob job) {
92  2 this.job = job;
93    }
94   
 
95  2 toggle private void init() {
96  2 setFilterPredicateContentMapper(new FilterPredicateContentMapper());
97    }
98   
 
99  2 toggle @Override
100    public boolean execute(Context context) throws Exception {
101  2 init();
102  2 Set<String> newContentUUIDs = new LinkedHashSet<String>();
103  2 Node rss = getRSSNodeByFeedName(job.getName());
104  2 if (rss != null) {
105  0 Set<AggregateFeed> fetchedAggregateFeeds = job.getFetcher().fetchAggregateFeeds(getFeeds(rss));
106  0 Set<String> newAggregateContentUUIDs = saveAggregates(fetchedAggregateFeeds, rss);
107  0 newContentUUIDs.addAll(newAggregateContentUUIDs);
108  0 rss.getSession().save();
109    }
110  2 return false;
111    }
112   
 
113  2 toggle private Node getRSSNodeByFeedName(String name) throws RepositoryException {
114  2 try {
115  2 return MgnlContext.getJCRSession(RSSAggregatorConstants.WORKSPACE).getNodeByIdentifier(name);
116    } catch (RepositoryException e) {
117  2 try {
118  2 log.debug("RSS with name " + name + "doesn't exist anymore");
119  2 SchedulerModule scheduler = (SchedulerModule) Components.getComponent(ModuleRegistry.class).getModuleInstance("scheduler");
120  2 scheduler.stopJob(name);
121  2 scheduler.removeJob(name);
122    } catch (SchedulerException ex) {
123    // If the node does not exist we try to remove it from scheduler to prevent future launches
124    }
125    }
126  2 return null;
127    }
128   
 
129  0 toggle public Set<AggregateFeed> getFeeds(Node node) throws RepositoryException {
130  0 AggregateFeedContentMapper aggregateFeedMapper = new AggregateFeedContentMapper();
131  0 Set<AggregateFeed> aggregateFeeds = new HashSet<AggregateFeed>();
132  0 AggregateFeed aggregateFeed = aggregateFeedMapper.map(node);
133  0 aggregateFeeds.add(aggregateFeed);
134  0 return aggregateFeeds;
135    }
136   
 
137  0 toggle protected Set<String> saveAggregates(Set<AggregateFeed> aggregateFeeds, Node parentNode) throws RepositoryException {
138  0 Set<String> newAggregateContentUUIDs = new HashSet<String>();
139  0 for (AggregateFeed aggregateFeed : aggregateFeeds) {
140  0 Node aggregateNode = parentNode;
141  0 Node dataNode = NodeUtil.createPath(aggregateNode, "data", NodeTypes.Content.NAME);
142  0 newAggregateContentUUIDs.add(aggregateNode.getIdentifier());
143  0 AggregateFilter aggregateFilter = loadAggregateFilter(aggregateNode);
144  0 for (FeedChannel channel : aggregateFeed.getChannels()) {
145  0 if (channel.hasFeed()) {
146  0 saveFeedChannel(channel, aggregateFilter, dataNode);
147    }
148    }
149    }
150  0 return newAggregateContentUUIDs;
151    }
152   
 
153  0 toggle protected Node loadSingleAggregateNode(Node parentNode, String aggregateNodeName) throws RepositoryException {
154  0 NodeIterator nodeIterator = parentNode.getNodes(aggregateNodeName);
155  0 Collection<Node> aggregateNodes = new ArrayList<Node>();
156  0 while (nodeIterator.hasNext()) {
157  0 Node currentNode = nodeIterator.nextNode();
158  0 if (NodeUtil.isNodeType(currentNode, RSSAggregatorNodeTypes.RSSAggregator.NAME)) {
159  0 aggregateNodes.add(currentNode);
160    }
161    }
162  0 int size = aggregateNodes.size();
163  0 if (size > 1) {
164  0 throw new IllegalStateException(format(
165    "Expected content node '%s' to have at most 1 child named '%s' of item type '%s', but found %s",
166    parentNode, aggregateNodeName, RSSAggregatorNodeTypes.RSSAggregator.NAME, size));
167    }
168  0 if (aggregateNodes.isEmpty()) {
169  0 return null;
170    }
171  0 return aggregateNodes.iterator().next();
172    }
173   
 
174  0 toggle public AggregateFilter loadAggregateFilter(Node aggregateNode) throws RepositoryException {
175  0 Node filtersNode = aggregateNode.hasNode("filters") ? aggregateNode.getNode("filters") : null;
176  0 if (filtersNode == null) {
177  0 return new AggregateFilter(Collections.<FilterPredicate>emptySet());
178    }
179    /*
180    * order matters here. The elements in the set must be in the same order as they are in JCR boolean b = true; b |= true; b &= false; System.out.println(b); -> false
181    *
182    * b = true; b &= true; b |= false; System.out.println(b); -> true
183    *
184    * See http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.22.2
185    */
186  0 Set<FilterPredicate> filters = new LinkedHashSet<FilterPredicate>();
187   
188  0 List<Node> filterNodes = NodeUtil.asList(NodeUtil.getNodes(filtersNode, NodeTypes.ContentNode.NAME));
189   
190  0 for (Node n : filterNodes) {
191  0 FilterPredicate filterPredicate = filterPredicateMapper.map(n);
192  0 if (filterPredicate == null) {
193  0 continue;
194    }
195  0 filters.add(filterPredicate);
196    }
197  0 return new AggregateFilter(filters);
198    }
199   
 
200  0 toggle protected Node saveFeedChannel(FeedChannel feedChannel, AggregateFilter aggregateFilter, Node dataNode) throws RepositoryException {
201  0 Node channelNode = recreateFeedChannelNode(feedChannel, dataNode);
202  0 List<SyndEntry> entries = feedChannel.getFeed().getEntries();
203  0 int size = entries.size();
204  0 for (int i = 0; i < size; i++) {
205  0 SyndEntry entry = entries.get(i);
206  0 String entryName = format("entry-%s", i);
207  0 if (aggregateFilter.include(entry)) {
208  0 createFeedChannelEntryNode(entry, entryName, channelNode);
209    }
210    }
211  0 return channelNode;
212    }
213   
 
214  0 toggle protected Node recreateFeedChannelNode(FeedChannel feedChannel, Node dataNode) throws RepositoryException {
215  0 String channelName = feedChannel.getName();
216  0 if (dataNode.hasNode(channelName)) {
217  0 String absPath = dataNode.getNode(channelName).getPath();
218  0 dataNode.getSession().removeItem(absPath);
219    }
220  0 Node channelNode = NodeUtil.createPath(dataNode, channelName, NodeTypes.Content.NAME);
221   
222  0 SyndFeed feed = feedChannel.getFeed();
223  0 channelNode.setProperty("description", feed.getDescription()); // 'My Blog'
224  0 channelNode.setProperty("link", feed.getLink()); // 'http://domain.com'
225  0 channelNode.setProperty("rss", feedChannel.getUrl()); // 'http://domain.com/channel.rss'
226  0 channelNode.setProperty("title", !isEmpty(feedChannel.getTitle()) ? feedChannel.getTitle() : feed.getTitle());
227  0 channelNode.setProperty("type", feed.getFeedType()); // 'rss_2.0'
228  0 channelNode.getSession().save();
229  0 return channelNode;
230    }
231   
 
232  0 toggle protected Node createFeedChannelEntryNode(SyndEntry entry, String nodeName, Node channelNode) throws RepositoryException {
233  0 Node entryNode = NodeUtil.createPath(channelNode, nodeName, NodeTypes.Content.NAME);
234  0 entryNode.setProperty("author", entry.getAuthor() == null ? "" : entry.getAuthor());
235  0 entryNode.setProperty("channelTitle", PropertyUtil.getString(channelNode, "title"));
236  0 final SyndContent description = entry.getDescription();
237   
238  0 String descriptionString;
239  0 if (description != null && StringUtils.isNotBlank(description.getValue())) {
240  0 descriptionString = description.getValue();
241    } else {
242  0 descriptionString = getEntryContent(entry);
243    }
244   
245  0 entryNode.setProperty("description", descriptionString);
246  0 entryNode.setProperty("content", getEntryContent(entry));
247  0 entryNode.setProperty("link", entry.getLink());
248  0 Date publishedDate = entry.getPublishedDate();
249  0 if (publishedDate == null) {
250  0 publishedDate = new Date();
251    }
252  0 entryNode.setProperty("pubDate", publishedDate.getTime());
253  0 entryNode.setProperty("title", entry.getTitle());
254   
255  0 createCategoriesNode(entry, entryNode);
256  0 return entryNode;
257    }
258   
 
259  0 toggle protected String getEntryContent(SyndEntry entry) {
260  0 String entryContent = "";
261   
262  0 if (entry != null && entry.getContents().size() > 0) {
263  0 final List<SyndContent> contents = entry.getContents();
264  0 for (SyndContent content : contents) {
265  0 if (StringUtils.equalsIgnoreCase("html", content.getType()) && StringUtils.isNotBlank(content.getType())) {
266  0 entryContent = content.getValue();
267  0 break;
268    }
269    }
270    }
271  0 return entryContent;
272    }
273   
 
274  0 toggle protected Node createCategoriesNode(SyndEntry entry, Node entryNode) throws RepositoryException {
275  0 Node categoriesNode = NodeUtil.createPath(entryNode, "categories", NodeTypes.Content.NAME);
276  0 List<SyndCategory> categories = entry.getCategories();
277  0 for (int i = 0; i < categories.size(); i++) {
278  0 SyndCategory category = categories.get(i);
279  0 String categoryIndex = valueOf(i);
280  0 String categoryName = category.getName();
281  0 categoriesNode.setProperty(categoryIndex, categoryName);
282    }
283  0 return categoriesNode;
284    }
285   
 
286  2 toggle protected FilterPredicateContentMapper setFilterPredicateContentMapper(FilterPredicateContentMapper filterPredicateMapper) {
287  2 if (filterPredicateMapper == null) {
288  0 throw new IllegalArgumentException("'filterPredicateContentMapper' must not be null");
289    }
290  2 this.filterPredicateMapper = filterPredicateMapper;
291  2 return this.filterPredicateMapper;
292    }
293   
 
294    toggle public void setJob(RSSJob job) {
295    this.job = job;
296    }
297   
 
298    toggle public RSSJob getJob() {
299    return this.job;
300    }
301    }