View Javadoc
1   /**
2    * This file Copyright (c) 2013-2016 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.generator;
35  
36  import static info.magnolia.module.rssaggregator.RSSAggregator.*;
37  import static java.lang.String.format;
38  
39  import info.magnolia.cms.beans.config.ServerConfiguration;
40  import info.magnolia.jcr.util.NodeTypes;
41  import info.magnolia.jcr.util.PropertyUtil;
42  import info.magnolia.jcr.util.SessionUtil;
43  import info.magnolia.module.rssaggregator.RSSAggregatorConstants;
44  import info.magnolia.module.rssaggregator.util.ContentMapper;
45  import info.magnolia.module.rssaggregator.util.MagnoliaTemplate;
46  import info.magnolia.objectfactory.Components;
47  
48  import java.util.Date;
49  import java.util.List;
50  
51  import javax.jcr.Node;
52  import javax.jcr.RepositoryException;
53  
54  import org.apache.commons.lang3.StringUtils;
55  import org.slf4j.Logger;
56  import org.slf4j.LoggerFactory;
57  
58  import com.rometools.rome.feed.synd.SyndContent;
59  import com.rometools.rome.feed.synd.SyndContentImpl;
60  import com.rometools.rome.feed.synd.SyndEntry;
61  import com.rometools.rome.feed.synd.SyndEntryImpl;
62  import com.rometools.rome.feed.synd.SyndFeed;
63  import com.rometools.rome.feed.synd.SyndLink;
64  import com.rometools.rome.feed.synd.SyndLinkImpl;
65  
66  /**
67   * Generates a {@link com.rometools.rome.feed.synd.SyndFeed} based on aggregate planet feeds defined via the RSS Aggregator Module.
68   */
69  public class PlanetFeedGenerator extends AbstractSyndFeedGenerator implements Cloneable {
70  
71      private static final ContentMapper<SyndEntry> MAPPER = new FeedEntryMapper();
72  
73      private static final Logger log = LoggerFactory.getLogger(PlanetFeedGenerator.class);
74  
75      private static final String FEED_TYPE_RSS = "rss_2.0";
76      private static final String FEED_TYPE_ATOM = "atom_1.0";
77  
78      private static final String DEFAULT_DESCRIPTION = "Magnolia Planet feed post content";
79  
80      private MagnoliaTemplate magnoliaTemplate;
81  
82      private String feedPath;
83      private static String feedType;
84  
85      public PlanetFeedGenerator() {
86          this.magnoliaTemplate = new MagnoliaTemplate();
87      }
88  
89      public void setFeedPath(String feedPath) {
90          this.feedPath = feedPath;
91      }
92  
93      @Override
94      public Feed generate() throws FeedGenerationException {
95          feedType = getFeedType();
96  
97          try {
98              SyndFeed syndFeed = newSyndFeed();
99              syndFeed.setFeedType(feedType);
100             setFeedInfo(syndFeed);
101             syndFeed.setEntries(loadFeedEntries());
102 
103             String xml = syndFeedToXml(syndFeed);
104 
105             return new Feed(xml, DEFAULT_CONTENT_TYPE, DEFAULT_ENCODING);
106         } catch (Exception e) {
107             String message = format("Failed to generate Feed using generator '%s'", getClass().getName());
108             log.error(message, e);
109             throw new FeedGenerationException(message, e);
110         }
111     }
112 
113     @Override
114     public List<SyndEntry> loadFeedEntries() {
115         String entriesQuery = format("/jcr:root%s/planetData[1]/*/* order by @pubDate descending", feedPath);
116         return magnoliaTemplate.xpathQueryForList(RSSAggregatorConstants.WORKSPACE, entriesQuery, NodeTypes.Content.NAME, MAPPER);
117     }
118 
119     @Override
120     @SuppressWarnings(value = "unchecked")
121     public void setFeedInfo(SyndFeed feed) {
122         // common attributes
123         Node feedDescr = SessionUtil.getNode(RSSAggregatorConstants.WORKSPACE, feedPath);
124         feed.setTitle(StringUtils.defaultIfEmpty(PropertyUtil.getString(feedDescr, "title"), ""));
125         feed.setDescription(StringUtils.defaultIfEmpty(PropertyUtil.getString(feedDescr, "description"), DEFAULT_DESCRIPTION));
126         String link = Components.getComponent(ServerConfiguration.class).getDefaultBaseUrl();
127 
128         // ATOM feed
129         if (FEED_TYPE_ATOM.equals(feedType)) {
130             if (!StringUtils.endsWith(link, "/")) {
131                 link += "/";
132             }
133             feed.setUri(link);  // generates the ID, should end with an "/"
134             feed.setPublishedDate(new Date());
135 
136             SyndLink selfLink = new SyndLinkImpl();
137             selfLink.setRel("self");
138             selfLink.setHref(link);
139             selfLink.setType("application/atom+xml");
140             feed.getLinks().add(selfLink);
141         } else {
142             // RSS feed
143             feed.setLink(link);
144         }
145     }
146 
147     @Override
148     public Object clone() throws CloneNotSupportedException {
149         return super.clone();
150     }
151 
152     /**
153      * Determine the feed type.
154      *
155      * @return ATOM or RSS feed type
156      */
157     protected String getFeedType() {
158         String feedType = "";
159 
160         if (StringUtils.endsWith(this.feedPath, "/rss")) {
161             this.feedPath = StringUtils.substringBeforeLast(this.feedPath, "/rss");
162             feedType = FEED_TYPE_RSS;
163         } else {
164             if (StringUtils.endsWith(this.feedPath, "/atom")) {
165                 this.feedPath = StringUtils.substringBeforeLast(this.feedPath, "/atom");
166                 feedType = FEED_TYPE_ATOM;
167             }
168         }
169         if (StringUtils.isBlank(feedType)) {
170             feedType = DEFAULT_FEEDTYPE;
171         }
172         return feedType;
173     }
174 
175     private static class FeedEntryMapper implements ContentMapper<SyndEntry> {
176 
177         @Override
178         public SyndEntry map(Node content) throws RepositoryException {
179 
180             SyndEntry entry = new SyndEntryImpl();
181 
182             // common attributes
183             entry.setTitle(PropertyUtil.getString(content, "title"));
184             entry.setLink(PropertyUtil.getString(content, "link"));
185             entry.setAuthor(PropertyUtil.getString(content, "author"));
186             if (content.hasProperty("pubDate")) {
187                 entry.setPublishedDate(PropertyUtil.getDate(content, "pubDate").getTime());
188             } else {
189                 entry.setPublishedDate(new Date());
190             }
191             SyndContent description = new SyndContentImpl();
192 
193             description.setType("text/html");
194             description.setValue(PropertyUtil.getString(content, "description"));
195             entry.setDescription(description);
196 
197             // ATOM feed
198             if (FEED_TYPE_ATOM.equals(feedType)) {
199                 entry.setUri(PropertyUtil.getString(content, "link")); // ID property of an entry
200             }
201 
202             // if categories are needed, they should be integrated here
203             return entry;
204         }
205     }
206 }