View Javadoc

1   /**
2    * Copyright 2011-2013 The Kuali Foundation Licensed under the Educational Community
3    * License, Version 2.0 (the "License"); you may not use this file except in
4    * compliance with the License. You may obtain a copy of the License at
5    *
6    * http://www.osedu.org/licenses/ECL-2.0
7    *
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11   * License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  package org.kuali.mobility.news.dao;
15  
16  import java.io.InputStreamReader;
17  import java.io.UnsupportedEncodingException;
18  import java.net.MalformedURLException;
19  import java.net.URL;
20  import java.net.URLEncoder;
21  import java.util.ArrayList;
22  import java.util.Date;
23  import java.util.HashMap;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.apache.log4j.Logger;
28  import org.kuali.mobility.news.entity.NewsArticle;
29  import org.kuali.mobility.news.entity.NewsSource;
30  import org.springframework.context.ApplicationContext;
31  import org.springframework.context.ApplicationContextAware;
32  
33  import com.sun.syndication.feed.synd.SyndEntryImpl;
34  import com.sun.syndication.feed.synd.SyndFeed;
35  import com.sun.syndication.io.ParsingFeedException;
36  import com.sun.syndication.io.SyndFeedInput;
37  
38  /**
39   * @author Kuali Mobility Team (mobility.collab@kuali.org)
40   */
41  public class NewsCacheImpl implements NewsCache, ApplicationContextAware {
42  
43  	private static final Logger LOG = Logger.getLogger(NewsCacheImpl.class);
44  	private ApplicationContext applicationContext;
45  	private Map<Long, NewsSource> newsSources;
46  
47  	public NewsCacheImpl() {
48  		newsSources = new HashMap<Long, NewsSource>();
49  	}
50  
51  	/**
52  	 * Update the cache for a single NewsSource. Called when a NewsSource is
53  	 * saved.
54  	 *
55  	 * @param source the NewsSource that was saved
56  	 */
57  	public void updateCache(NewsSource source) {
58  		if (source.isActive()) {
59  			LOG.debug("NewsSource " + source.getId() + " is active & will be refreshed.");
60  			if (!getNewsSources().containsKey(source.getId())) {
61  				getNewsSources().put(source.getId(), source);
62  			}
63  			updateSource(source);
64  		} else {
65  			LOG.debug("NewsSource is inactive & being removed.");
66  			getNewsSources().remove(source.getId());
67  		}
68  	}
69  
70  	/**
71  	 * Does the actual work of updating a news feed and its articles
72  	 *
73  	 * @param feed the NewsFeed to update
74  	 * @param source the NewsSource that defines the feed to update
75  	 */
76  	@SuppressWarnings("unchecked")
77  	public void updateSource(NewsSource source) {
78  		if (source == null) {
79  			LOG.error("Not updating, source is null.");
80  			return;
81  		} else if (source.getUrl() == null) {
82  			LOG.debug("Not updating source due to no URL for source " + source.getId());
83  			source.setTitle(source.getName());
84  			return;
85  		}
86  
87  		URL feedUrl = null;
88  		try {
89  			feedUrl = new URL(source.getUrl());
90  		} catch (MalformedURLException e) {
91  			LOG.error("Bad feed url: " + source.getUrl(), e);
92  		}
93  		SyndFeedInput input = new SyndFeedInput();
94  		SyndFeed syndFeed = null;
95  		try {
96  			syndFeed = input.build(new InputStreamReader(feedUrl.openStream()));
97  			if (syndFeed != null) {
98  				LOG.debug("Feed data retrieved, populating articles for: " + syndFeed.getTitle());
99  				source.setTitle(syndFeed.getTitle());
100 				source.setAuthor(syndFeed.getAuthor());
101 				source.setDescription(syndFeed.getDescription());
102 
103 				List<NewsArticle> articles = new ArrayList<NewsArticle>();
104 				for (SyndEntryImpl entry : (List<SyndEntryImpl>) syndFeed.getEntries()) {
105 					LOG.debug("Processing article: " + entry.getTitle());
106 					NewsArticle article = (NewsArticle) getApplicationContext().getBean("newsArticle");
107 
108 					article.setTitle(entry.getTitle());
109 					try {
110 						article.setDescription(entry.getDescription().getValue());
111 					} catch (NullPointerException npe) {
112 						LOG.error("Description for " + entry.getTitle() + " is null.");
113 						article.setDescription(null);
114 					}
115 					article.setLink(entry.getLink());
116 					try {
117 						article.setPublishDate(new Date(entry.getPublishedDate().getTime()));
118 					} catch (Exception e) {
119 						LOG.error("Error creating timestamp for article: " + entry.getTitle());
120 						LOG.error(e.getLocalizedMessage());
121 					}
122 					article.setSourceId(source.getId());
123 					try {
124 						article.setArticleId(URLEncoder.encode(entry.getUri(), "UTF-8"));
125 					} catch (UnsupportedEncodingException e) {
126 						article.setArticleId(entry.getUri());
127 					}
128 
129 					articles.add(article);
130 				}
131 				source.setArticles(articles);
132 			} else {
133 				source.setTitle(source.getName());
134 			}
135 		} catch(ParsingFeedException pfe ) {
136 			LOG.error("Error parsing feed: " + source.getName());
137 			LOG.error( pfe.getLocalizedMessage(), pfe );
138 		} catch (Exception e) {
139 			LOG.error("Error reading feed: " + source.getName(), e);
140 		}
141 
142 	}
143 
144 	public ApplicationContext getApplicationContext() {
145 		return applicationContext;
146 	}
147 
148 	public void setApplicationContext(ApplicationContext applicationContext) {
149 		this.applicationContext = applicationContext;
150 	}
151 
152 	public Map<Long, NewsSource> getNewsSources() {
153 		return newsSources;
154 	}
155 
156 	public void setNewsSources(Map<Long, NewsSource> newsSources) {
157 		this.newsSources = newsSources;
158 	}
159 }