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