View Javadoc

1   /**
2    * This file Copyright (c) 2008-2013 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.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   * Configuration bean of the rss aggregator module.
71   *
72   * @author had
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      // default number of months to go back for statistics collection
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          // start observation for every rss on startup of module and add new one on save of new RSS
100         // check for node move - should re-register
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",//empty command has to be set in config
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 }