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.context.MgnlContext;
38 import info.magnolia.context.MgnlContext.Op;
39 import info.magnolia.jcr.node2bean.Node2BeanProcessor;
40 import info.magnolia.jcr.util.NodeTypes;
41 import info.magnolia.module.ModuleRegistry;
42 import info.magnolia.module.rssaggregator.generator.FeedGenerator;
43 import info.magnolia.module.rssaggregator.importhandler.RSSFeedFetcher;
44 import info.magnolia.module.scheduler.JobDefinition;
45 import info.magnolia.module.scheduler.SchedulerModule;
46 import info.magnolia.objectfactory.Components;
47
48 import java.util.ArrayList;
49 import java.util.Collection;
50 import java.util.HashMap;
51 import java.util.HashSet;
52 import java.util.Hashtable;
53 import java.util.Iterator;
54 import java.util.List;
55 import java.util.Map;
56
57 import javax.jcr.Node;
58 import javax.jcr.NodeIterator;
59 import javax.jcr.RepositoryException;
60 import javax.jcr.Session;
61 import javax.jcr.observation.EventIterator;
62 import javax.jcr.observation.EventListener;
63
64 import org.apache.commons.lang.StringUtils;
65 import org.quartz.SchedulerException;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69
70
71
72
73
74 public class RSSAggregator {
75
76 private static final Logger log = LoggerFactory.getLogger(RSSAggregator.class);
77
78 public static final String DEFAULT_FEEDTYPE = "rss_2.0";
79 public static final String DEFAULT_ENCODING = "UTF-8";
80 public static final String DEFAULT_CONTENT_TYPE = "text/xml";
81
82 private static final String MONTHS_INCLUDED_OPTION = "lastMonthsIncluded";
83
84 private static final String DEFAULT_MONTHS_INCLUDED = "3";
85
86 private Map<String, FeedGenerator> feedGenerators = new HashMap<String, FeedGenerator>();
87
88 private Map<String, String> planetOptions = new HashMap<String, String>();
89
90 private RSSFeedFetcher fetcher;
91
92 private boolean automatedImport;
93
94 private String cron;
95
96 private Map<String, RSSJob> jobs = new Hashtable<String, RSSJob>();
97
98 public RSSAggregator(){
99
100
101 try {
102 mapRSSJobs();
103 } catch (Exception e) {
104 throw new RuntimeException(e);
105 }
106 Iterator<String> it = jobs.keySet().iterator();
107 while(it.hasNext()){
108 RSSJob job = jobs.get(it.next());
109 registerObservation(job);
110 }
111 }
112
113 public static RSSAggregator getInstance() {
114 return (RSSAggregator) ModuleRegistry.Factory.getInstance().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 setFetcher(RSSFeedFetcher fetcher){
150 this.fetcher = fetcher;
151 }
152
153 public RSSFeedFetcher getFetcher(){
154 return this.fetcher;
155 }
156
157 public void setCron(String cron) {
158 this.cron = cron;
159 }
160
161 public String getCron() {
162 return this.cron;
163 }
164
165 public void setAutomatedImport(boolean automatedImport){
166 this.automatedImport = automatedImport;
167 }
168
169 public boolean getAutomatedImport(){
170 return this.automatedImport;
171 }
172
173 private void mapRSSJobs() throws Exception{
174 jobs.clear();
175 MgnlContext.doInSystemContext(new Op<Void, RepositoryException>() {
176 @Override
177 public Void exec() throws RepositoryException {
178 try {
179 Session session = MgnlContext.getInstance().getJCRSession(RSSAggregatorConstants.WORKSPACE);
180 List<Node> rss = new ArrayList<Node>();
181 Node root = session.getRootNode();
182 findAllRSSInFolder(root, rss);
183 Iterator<Node> nodes = rss.iterator();
184
185 Node2BeanProcessor n2b = Components.getComponent(Node2BeanProcessor.class);
186 while(nodes.hasNext()){
187 Node node = nodes.next();
188 RSSJob job = (RSSJob) n2b.toBean(node, RSSJob.class);
189 jobs.put(job.getName(), job);
190 initJob(job);
191 }
192 }catch(Exception e){
193 log.error("RSS jobs initial mapping error: " + e.getMessage());
194 }
195 return null;
196 }
197 });
198 }
199
200 private void findAllRSSInFolder(Node node, Collection<Node> rssCol) throws RepositoryException {
201 NodeIterator nodesIter = node.getNodes();
202 Collection<Node> nodes = new HashSet<Node>();
203 Collection<Node> folders = new HashSet<Node>();
204 while (nodesIter.hasNext()) {
205 Node newNode = (Node) nodesIter.next();
206 if (newNode.isNodeType(RSSAggregatorConstants.NODE_TYPE)) {
207 nodes.add(newNode);
208 } else if (newNode.isNodeType(NodeTypes.Folder.NAME)) {
209 folders.add(newNode);
210 }
211 }
212
213 if (!nodes.isEmpty()) {
214 for (Node rssNode : nodes) {
215 rssCol.add(rssNode);
216 }
217 }
218 if (!folders.isEmpty()) {
219 for (Node folder : folders) {
220 findAllRSSInFolder(folder, rssCol);
221 }
222 }
223 }
224
225 public void mapRSSJob(final String name) throws RepositoryException{
226 if(jobs.containsKey(name)){
227 jobs.remove(name);
228 }
229 MgnlContext.doInSystemContext(new Op<Void, RepositoryException>() {
230 @Override
231 public Void exec() throws RepositoryException {
232 try {
233 Session session = MgnlContext.getInstance().getJCRSession(RSSAggregatorConstants.WORKSPACE);
234 Node root = session.getRootNode().getNode(name);
235 Node2BeanProcessor n2b = Components.getComponent(Node2BeanProcessor.class);
236 RSSJob job = (RSSJob) n2b.toBean(root, RSSJob.class);
237 jobs.put(job.getName(), job);
238 initJob(job);
239 }catch(Exception e){
240 throw new RuntimeException(e);
241 }
242 return null;
243 }
244 });
245 }
246
247 private void initJob(RSSJob job) {
248 SchedulerModule scheduler = SchedulerModule.getInstance();
249 if ((job.getAutomatedImport() == true && job.getOverrideDefault() == true) || (job.getOverrideDefault() == false && this.automatedImport == true)) {
250 Map<String, Object> params = new HashMap<String, Object>();
251 String cron = this.cron;
252 if(job.getOverrideDefault() == true){
253 cron = job.getCron();
254 }
255 params.put("name", job.getName());
256 try {
257 scheduler.stopJob(job.getName());
258 scheduler.addJob(new JobDefinition(
259 job.getName(),
260 "rss",
261 "importRss",
262 cron,
263 params));
264 log.info("Added RSS job " + job.getName() + " with cron " + cron);
265 }
266 catch (SchedulerException e) {
267 log.error("can't start scheduled job for rss import[" + job.getName() + "]", e);
268 }
269 }
270 else {
271 try {
272 scheduler.stopJob(job.getName());
273 }
274 catch (SchedulerException e) {
275 log.error("can't delete scheduled job for rss import[" + job.getName() + "]", e);
276 }
277 }
278 }
279
280 public void registerObservation(RSSJob job){
281 String name = job.getName();
282 ObservationUtil.registerChangeListener(RSSAggregatorConstants.WORKSPACE, "/" + name, false, RSSAggregatorConstants.NODE_TYPE, new RSSEventListener(name));
283 }
284
285 private class RSSEventListener implements EventListener{
286
287 private String name;
288
289 public RSSEventListener(String name){
290 this.name = name;
291 }
292
293 @Override
294 public void onEvent(EventIterator events) {
295 try{
296 mapRSSJob(name);
297 }catch(Exception e){
298 throw new RuntimeException(e);
299 }
300 }
301 }
302 }