1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
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
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
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
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
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
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
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
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",
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
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
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 }