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