View Javadoc

1   /**
2    * This file Copyright (c) 2008-2013 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;
35  
36  import info.magnolia.cms.util.ObservationUtil;
37  import info.magnolia.context.MgnlContext;
38  import info.magnolia.context.MgnlContext.Op;
39  import info.magnolia.jcr.node2bean.Node2BeanProcessor;
40  import info.magnolia.jcr.util.NodeTypes;
41  import info.magnolia.module.ModuleRegistry;
42  import info.magnolia.module.rssaggregator.generator.FeedGenerator;
43  import info.magnolia.module.rssaggregator.importhandler.RSSFeedFetcher;
44  import info.magnolia.module.scheduler.JobDefinition;
45  import info.magnolia.module.scheduler.SchedulerModule;
46  import info.magnolia.objectfactory.Components;
47  import info.magnolia.repository.RepositoryConstants;
48  
49  import java.util.ArrayList;
50  import java.util.Collection;
51  import java.util.HashMap;
52  import java.util.HashSet;
53  import java.util.Hashtable;
54  import java.util.Iterator;
55  import java.util.List;
56  import java.util.Map;
57  
58  import javax.jcr.Node;
59  import javax.jcr.NodeIterator;
60  import javax.jcr.RepositoryException;
61  import javax.jcr.Session;
62  import javax.jcr.observation.EventIterator;
63  import javax.jcr.observation.EventListener;
64  
65  import org.apache.commons.lang.StringUtils;
66  import org.quartz.SchedulerException;
67  import org.slf4j.Logger;
68  import org.slf4j.LoggerFactory;
69  
70  /**
71   * Configuration bean of the rss aggregator module.
72   *
73   * @author had
74   */
75  public class RSSAggregator {
76  
77      private static final Logger log = LoggerFactory.getLogger(RSSAggregator.class);
78  
79      public static final String DEFAULT_FEEDTYPE = "rss_2.0";
80      public static final String DEFAULT_ENCODING = "UTF-8";
81      public static final String DEFAULT_CONTENT_TYPE = "text/xml";
82  
83      private static final String MONTHS_INCLUDED_OPTION = "lastMonthsIncluded";
84      // default number of months to go back for statistics collection
85      private static final String DEFAULT_MONTHS_INCLUDED = "3";
86      public static final String RSS_CONFIG_PATH = "/modules/rssaggregator/config";
87  
88      private Map<String, FeedGenerator> feedGenerators = new HashMap<String, FeedGenerator>();
89  
90      private Map<String, String> planetOptions = new HashMap<String, String>();
91  
92      private RSSFeedFetcher fetcher;
93  
94      private Map<String, RSSJob> jobs = new Hashtable<String, RSSJob>();
95  
96      public RSSAggregator(){
97          init();
98          ObservationUtil.registerChangeListener(RepositoryConstants.CONFIG, RSS_CONFIG_PATH, false, NodeTypes.Content.NAME, new RSSConfigEventListener());
99      }
100 
101     private void init(){
102         try {
103             mapRSSJobs();
104         } catch (Exception e) {
105             throw new RuntimeException(e);
106         }        
107     }
108 
109     /**
110      * @deprecated since 2.2.4, use IoC instead
111      */
112     public static RSSAggregator getInstance() {
113         return (RSSAggregator) Components.getComponent(ModuleRegistry.class).getModuleInstance("rssaggregator");
114     }
115 
116     public Map<String, FeedGenerator> getFeedGenerators() {
117         return new HashMap<String, FeedGenerator>(feedGenerators);
118     }
119 
120     public void setFeedGenerators(HashMap<String, FeedGenerator> feedGenerators) {
121         this.feedGenerators.clear();
122         this.feedGenerators.putAll(feedGenerators);
123     }
124 
125     public void addFeedGenerator(String name, FeedGenerator feedGenerator) {
126         this.feedGenerators.put(name, feedGenerator);
127     }
128 
129     public int getMonthsIncluded() {
130         if (getPlanetOptions().containsKey(MONTHS_INCLUDED_OPTION)) {
131             String lastMonthOption = getPlanetOptions().get(MONTHS_INCLUDED_OPTION);
132             if (StringUtils.isNumeric(lastMonthOption)) {
133                 return Integer.parseInt(lastMonthOption);
134             }
135 
136         }
137         return Integer.parseInt(DEFAULT_MONTHS_INCLUDED);
138     }
139 
140     public Map<String, String> getPlanetOptions() {
141         return planetOptions;
142     }
143 
144     public void setPlanetOptions(Map<String, String> planetOptions) {
145         this.planetOptions = planetOptions;
146     }
147 
148     public void setFetcher(RSSFeedFetcher fetcher){
149         this.fetcher = fetcher;
150     }
151 
152     public RSSFeedFetcher getFetcher(){
153         return this.fetcher;
154     }
155 
156     private String retrieveCron() {
157         try {
158             return MgnlContext.getJCRSession(RepositoryConstants.CONFIG).getNode(RSS_CONFIG_PATH).getProperty("cron").getString();
159         } catch (RepositoryException e) {
160             log.debug("Property cron could not be retrieved from rss module configuration, hourly configuration will be used.");
161         }
162         // If not able to get the property return default one - hourly update
163         return "0 0 0/1 1/1 * ? *";
164     }
165 
166     private boolean retrieveAutomatedImport() {
167         try {
168             return MgnlContext.getJCRSession(RepositoryConstants.CONFIG).getNode(RSS_CONFIG_PATH).getProperty("automatedImport").getBoolean();
169         } catch (RepositoryException e) {
170             log.debug("Property automatedImport could not be retrieved from rss module configuration, it will be disabled by default.");
171         }
172         // If not able to get the property return default one - disabled global scheduled import
173         return false;
174     }
175 
176     private void mapRSSJobs() throws Exception{
177         jobs.clear();
178         MgnlContext.doInSystemContext(new Op<Void, RepositoryException>() {
179             @Override
180             public Void exec() throws RepositoryException {
181                 try {
182                     Session session = MgnlContext.getInstance().getJCRSession(RSSAggregatorConstants.WORKSPACE);
183                     List<Node> rss = new ArrayList<Node>();
184                     Node root = session.getRootNode();
185                     findAllRSSInFolder(root, rss);
186                     Iterator<Node> nodes = rss.iterator();
187 
188                     Node2BeanProcessor n2b = Components.getComponent(Node2BeanProcessor.class);
189                     while(nodes.hasNext()){
190                         Node node = nodes.next();
191                         RSSJob job = (RSSJob) n2b.toBean(node, RSSJob.class);
192                         job.setName(node.getIdentifier());
193                         jobs.put(job.getName(), job);
194                         initJob(job);
195                     }
196                 }catch(Exception e){
197                     log.error("RSS jobs initial mapping error: " + e.getMessage());
198                 }
199                 return null;
200                 }
201             });
202     }
203 
204     private void findAllRSSInFolder(Node node, Collection<Node> rssCol) throws RepositoryException {
205         NodeIterator nodesIter = node.getNodes();
206         Collection<Node> nodes = new HashSet<Node>();
207         Collection<Node> folders = new HashSet<Node>();
208         while (nodesIter.hasNext()) {
209             Node newNode = (Node) nodesIter.next();
210             if (newNode.isNodeType(RSSAggregatorConstants.NODE_TYPE)) {
211                 nodes.add(newNode);
212             } else if (newNode.isNodeType(NodeTypes.Folder.NAME)) {
213                 folders.add(newNode);
214             }
215         }
216 
217         if (!nodes.isEmpty()) {
218             for (Node rssNode : nodes) {
219                 rssCol.add(rssNode);
220             }
221         }
222         if (!folders.isEmpty()) {
223             for (Node folder : folders) {
224                 findAllRSSInFolder(folder, rssCol);
225             }
226         }
227     }
228 
229     public void mapRSSJob(final String name) throws RepositoryException{
230         if(jobs.containsKey(name)){
231             jobs.remove(name);
232         }
233         MgnlContext.doInSystemContext(new Op<Void, RepositoryException>() {
234             @Override
235             public Void exec() throws RepositoryException {
236                 try {
237                     Node node = MgnlContext.getJCRSession(RSSAggregatorConstants.WORKSPACE).getNodeByIdentifier(name);
238                     Node2BeanProcessor n2b = Components.getComponent(Node2BeanProcessor.class);
239                     RSSJob job = (RSSJob) n2b.toBean(node, RSSJob.class);
240                     job.setName(node.getIdentifier());
241                     jobs.put(job.getName(), job);
242                     initJob(job);
243                 } catch(Exception e){
244                     throw new RuntimeException(e);
245                 }
246                 return null;
247                 }
248             });
249     }
250 
251     private void initJob(RSSJob job) {
252         SchedulerModule scheduler = (SchedulerModule) Components.getComponent(ModuleRegistry.class).getModuleInstance("scheduler");
253         if ((job.getAutomatedImport() && job.getOverrideDefault()) || (!job.getOverrideDefault() && retrieveAutomatedImport())) {
254             Map<String, Object> params = new HashMap<String, Object>();
255             String cron = retrieveCron();
256             if(job.getOverrideDefault()){
257                 cron = job.getCron();
258             }
259             params.put("name", job.getName());
260             try {
261                 scheduler.stopJob(job.getName());
262                 scheduler.removeJob(job.getName());
263                 scheduler.addJob(new JobDefinition(
264                         job.getName(),
265                         "rss",
266                         "importRss",//empty command has to be set in config
267                         cron,
268                         params));
269                 log.info("Added RSS job " + job.getName() + " with cron " + cron);
270             }
271             catch (SchedulerException e) {
272                 log.error("can't start scheduled job for rss import[" + job.getName() + "]", e);
273             }
274         }
275         else {
276             try {
277                 scheduler.stopJob(job.getName());
278                 scheduler.removeJob(job.getName());
279             }
280             catch (SchedulerException e) {
281                 log.error("can't delete scheduled job for rss import[" + job.getName() + "]", e);
282             }
283         }
284     }
285 
286     public boolean jobExists(String name) {
287         return jobs.containsKey(name);
288     }
289 
290     public void removeJob(String key){
291         jobs.remove(key);
292     }
293 
294     public RSSJob getJobByName (String name) {
295         return jobs.get(name);
296     }
297 
298     public void registerJob (String name, RSSJob job) {
299         jobs.put(name, job);
300     }
301 
302     public Map<String, RSSJob> getJobs(){
303         return this.jobs;
304     }
305 
306     private class RSSConfigEventListener implements EventListener{
307 
308         @Override
309         public void onEvent(EventIterator events) {
310             init();
311         }
312     }
313 }