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
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
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
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
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
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
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
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",
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
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
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 }