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.ObservationUtil;
37 import info.magnolia.cms.util.QueryUtil;
38 import info.magnolia.context.MgnlContext;
39 import info.magnolia.context.MgnlContext.Op;
40 import info.magnolia.jcr.node2bean.Node2BeanProcessor;
41 import info.magnolia.jcr.util.NodeTypes;
42 import info.magnolia.module.ModuleRegistry;
43 import info.magnolia.module.rssaggregator.generator.FeedGenerator;
44 import info.magnolia.module.rssaggregator.importhandler.RSSFeedFetcher;
45 import info.magnolia.module.scheduler.JobDefinition;
46 import info.magnolia.module.scheduler.SchedulerModule;
47 import info.magnolia.objectfactory.Components;
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
72
73
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
85 private static final String DEFAULT_MONTHS_INCLUDED = "3";
86
87 private Map<String, FeedGenerator> feedGenerators = new HashMap<String, FeedGenerator>();
88
89 private Map<String, String> planetOptions = new HashMap<String, String>();
90
91 private RSSFeedFetcher fetcher;
92
93 private boolean automatedImport;
94
95 private String cron;
96
97 private Map<String, RSSJob> jobs = new Hashtable<String, RSSJob>();
98
99 public RSSAggregator(){
100
101
102 try {
103 mapRSSJobs();
104 } catch (Exception e) {
105 throw new RuntimeException(e);
106 }
107 Iterator<String> it = jobs.keySet().iterator();
108 while(it.hasNext()){
109 RSSJob job = jobs.get(it.next());
110 registerObservation(job);
111 }
112 }
113
114 public static RSSAggregator getInstance() {
115 return (RSSAggregator) ModuleRegistry.Factory.getInstance().getModuleInstance("rssaggregator");
116 }
117
118 public Map<String, FeedGenerator> getFeedGenerators() {
119 return new HashMap<String, FeedGenerator>(feedGenerators);
120 }
121
122 public void setFeedGenerators(HashMap<String, FeedGenerator> feedGenerators) {
123 this.feedGenerators.clear();
124 this.feedGenerators.putAll(feedGenerators);
125 }
126
127 public void addFeedGenerator(String name, FeedGenerator feedGenerator) {
128 this.feedGenerators.put(name, feedGenerator);
129 }
130
131 public int getMonthsIncluded() {
132 if (getPlanetOptions().containsKey(MONTHS_INCLUDED_OPTION)) {
133 String lastMonthOption = getPlanetOptions().get(MONTHS_INCLUDED_OPTION);
134 if (StringUtils.isNumeric(lastMonthOption)) {
135 return Integer.parseInt(lastMonthOption);
136 }
137
138 }
139 return Integer.parseInt(DEFAULT_MONTHS_INCLUDED);
140 }
141
142
143 public Map<String, String> getPlanetOptions() {
144 return planetOptions;
145 }
146
147 public void setPlanetOptions(Map<String, String> planetOptions) {
148 this.planetOptions = planetOptions;
149 }
150
151 public void setFetcher(RSSFeedFetcher fetcher){
152 this.fetcher = fetcher;
153 }
154
155 public RSSFeedFetcher getFetcher(){
156 return this.fetcher;
157 }
158
159 public void setCron(String cron) {
160 this.cron = cron;
161 }
162
163 public String getCron() {
164 return this.cron;
165 }
166
167 public void setAutomatedImport(boolean automatedImport){
168 this.automatedImport = automatedImport;
169 }
170
171 public boolean getAutomatedImport(){
172 return this.automatedImport;
173 }
174
175 private void mapRSSJobs() throws Exception{
176 jobs.clear();
177 MgnlContext.doInSystemContext(new Op<Void, RepositoryException>() {
178 @Override
179 public Void exec() throws RepositoryException {
180 try {
181 Session session = MgnlContext.getInstance().getJCRSession(RSSAggregatorConstants.WORKSPACE);
182 List<Node> rss = new ArrayList<Node>();
183 Node root = session.getRootNode();
184 findAllRSSInFolder(root, rss);
185 Iterator<Node> nodes = rss.iterator();
186
187 Node2BeanProcessor n2b = Components.getComponent(Node2BeanProcessor.class);
188 while(nodes.hasNext()){
189 Node node = nodes.next();
190 RSSJob job = (RSSJob) n2b.toBean(node, RSSJob.class);
191 jobs.put(job.getName(), job);
192 initJob(job);
193 }
194 }catch(Exception e){
195 log.error("RSS jobs initial mapping error: " + e.getMessage());
196 }
197 return null;
198 }
199 });
200 }
201
202 private void findAllRSSInFolder(Node node, Collection<Node> rssCol) throws RepositoryException {
203 NodeIterator nodesIter = node.getNodes();
204 Collection<Node> nodes = new HashSet<Node>();
205 Collection<Node> folders = new HashSet<Node>();
206 while (nodesIter.hasNext()) {
207 Node newNode = (Node) nodesIter.next();
208 if (newNode.isNodeType(RSSAggregatorConstants.NODE_TYPE)) {
209 nodes.add(newNode);
210 } else if (newNode.isNodeType(NodeTypes.Folder.NAME)) {
211 folders.add(newNode);
212 }
213 }
214
215 if (!nodes.isEmpty()) {
216 for (Node rssNode : nodes) {
217 rssCol.add(rssNode);
218 }
219 }
220 if (!folders.isEmpty()) {
221 for (Node folder : folders) {
222 findAllRSSInFolder(folder, rssCol);
223 }
224 }
225 }
226
227 public void mapRSSJob(final String name) throws RepositoryException{
228 if(jobs.containsKey(name)){
229 jobs.remove(name);
230 }
231 MgnlContext.doInSystemContext(new Op<Void, RepositoryException>() {
232 @Override
233 public Void exec() throws RepositoryException {
234 try {
235 String query = "select * from [mgnl:content] as t where contains(t.name, '"+ name + "')";
236 NodeIterator it = QueryUtil.search(RSSAggregatorConstants.WORKSPACE, query, javax.jcr.query.Query.JCR_SQL2, RSSAggregatorConstants.NODE_TYPE);
237 Node node = it.nextNode();
238 Node2BeanProcessor n2b = Components.getComponent(Node2BeanProcessor.class);
239 RSSJob job = (RSSJob) n2b.toBean(node, RSSJob.class);
240 jobs.put(job.getName(), job);
241 initJob(job);
242 }catch(Exception e){
243 throw new RuntimeException(e);
244 }
245 return null;
246 }
247 });
248 }
249
250 private void initJob(RSSJob job) {
251 SchedulerModule scheduler = SchedulerModule.getInstance();
252 if ((job.getAutomatedImport() && job.getOverrideDefault()) || (!job.getOverrideDefault() && this.automatedImport)) {
253 Map<String, Object> params = new HashMap<String, Object>();
254 String cron = this.cron;
255 if(job.getOverrideDefault()){
256 cron = job.getCron();
257 }
258 params.put("name", job.getName());
259 try {
260 scheduler.stopJob(job.getName());
261 scheduler.addJob(new JobDefinition(
262 job.getName(),
263 "rss",
264 "importRss",
265 cron,
266 params));
267 log.info("Added RSS job " + job.getName() + " with cron " + cron);
268 }
269 catch (SchedulerException e) {
270 log.error("can't start scheduled job for rss import[" + job.getName() + "]", e);
271 }
272 }
273 else {
274 try {
275 scheduler.stopJob(job.getName());
276 }
277 catch (SchedulerException e) {
278 log.error("can't delete scheduled job for rss import[" + job.getName() + "]", e);
279 }
280 }
281 }
282
283 public void registerObservation(RSSJob job){
284 String name = job.getName();
285 ObservationUtil.registerChangeListener(RSSAggregatorConstants.WORKSPACE, "/" + name, false, RSSAggregatorConstants.NODE_TYPE, new RSSEventListener(name));
286 }
287
288 public boolean jobExists(String name) {
289 return jobs.containsKey(name);
290 }
291
292 public void removeJob(String key){
293 jobs.remove(key);
294 }
295
296 public RSSJob getJobByName (String name) {
297 return jobs.get(name);
298 }
299
300 public void registerJob (String name, RSSJob job) {
301 jobs.put(name, job);
302 }
303
304 public Map<String, RSSJob> getJobs(){
305 return this.jobs;
306 }
307
308 private class RSSEventListener implements EventListener{
309
310 private String name;
311
312 public RSSEventListener(String name){
313 this.name = name;
314 }
315
316 @Override
317 public void onEvent(EventIterator events) {
318 try{
319 mapRSSJob(name);
320 }catch(Exception e){
321 throw new RuntimeException(e);
322 }
323 }
324 }
325 }