001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/web/tags/sakai_2-4-1/news-impl/impl/src/java/org/sakaiproject/news/impl/BasicNewsChannel.java $
003: * $Id: BasicNewsChannel.java 29128 2007-04-18 23:14:31Z ajpoland@iupui.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.news.impl;
021:
022: import java.io.IOException;
023: import java.net.MalformedURLException;
024: import java.net.URL;
025: import java.text.DateFormat;
026: import java.util.Date;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.Vector;
030:
031: import org.apache.commons.logging.Log;
032: import org.apache.commons.logging.LogFactory;
033: import org.sakaiproject.javax.Filter;
034: import org.sakaiproject.news.api.NewsChannel;
035: import org.sakaiproject.news.api.NewsConnectionException;
036: import org.sakaiproject.news.api.NewsFormatException;
037: import org.sakaiproject.news.api.NewsItem;
038: import org.sakaiproject.news.api.NewsItemEnclosure;
039: import org.sakaiproject.util.FormattedText;
040: import org.sakaiproject.util.ResourceLoader;
041: import org.sakaiproject.util.Validator;
042:
043: import com.sun.syndication.feed.synd.SyndEnclosure;
044: import com.sun.syndication.feed.synd.SyndEntry;
045: import com.sun.syndication.feed.synd.SyndFeed;
046: import com.sun.syndication.feed.synd.SyndImage;
047: import com.sun.syndication.fetcher.FeedFetcher;
048: import com.sun.syndication.fetcher.impl.HttpURLFeedFetcher;
049:
050: /***********************************************************************************
051: * NewsChannel implementation
052: **********************************************************************************/
053:
054: public class BasicNewsChannel implements NewsChannel {
055: protected String m_source = null;
056:
057: protected String m_link = null;
058:
059: protected String m_title = null;
060:
061: protected String m_description = null;
062:
063: protected String m_language = null;
064:
065: protected String m_copyright = null;
066:
067: protected String m_pubdate = null;
068:
069: protected String m_lastbuilddate = null;
070:
071: protected String m_imageLink = null;
072:
073: protected String m_imageTitle = null;
074:
075: protected String m_imageUrl = null;
076:
077: protected String m_imageHeight = "31";
078:
079: protected String m_imageWidth = "88";
080:
081: protected String m_imageDescription = null;
082:
083: protected List<NewsItem> m_items = null;
084:
085: /** Our log (commons). */
086: private static Log M_log = LogFactory
087: .getLog(BasicNewsService.class);
088:
089: /**
090: * Construct.
091: *
092: * @param source
093: * The URL from which the feed can be obtained
094: * @exception NewsConnectionException,
095: * for errors making the connection.
096: * @exception NewsFormatException,
097: * for errors in the URL or errors parsing the feed.
098: */
099: public BasicNewsChannel(String source)
100: throws NewsConnectionException, NewsFormatException {
101: if (m_items == null) {
102: m_items = new Vector<NewsItem>();
103: }
104:
105: // get the file, parse it and cache it
106: // throw NewsConnectionException if unable to get file
107: // throw NewsFormatException if file is in wrong format
108: initChannel(source);
109: }
110:
111: // get the file, parse it and cache it
112: // throw NewsConnectionException if unable to get file
113: // throw NewsFormatException if file is in wrong format
114: // updated to use ROME syndication api
115: private void initChannel(String source)
116: throws NewsConnectionException, NewsFormatException {
117: SyndFeed feed = null;
118:
119: ResourceLoader rl = new ResourceLoader("news-impl");
120:
121: try {
122: URL feedUrl = new URL(source);
123: FeedFetcher feedFetcher = new HttpURLFeedFetcher();
124: feed = feedFetcher.retrieveFeed(feedUrl);
125:
126: } catch (MalformedURLException e) {
127: if (M_log.isDebugEnabled())
128: M_log.debug("initChannel(" + source + ") bad url: "
129: + e.getMessage());
130: throw new NewsFormatException("\"" + source + "\" "
131: + rl.getString("is_not_a_valid_url"));
132: } catch (IOException e) {
133: if (M_log.isDebugEnabled())
134: M_log.debug("initChannel(" + source
135: + ") constructor: couldn't connect: "
136: + e.getMessage());
137: throw new NewsConnectionException(rl
138: .getString("unable_to_obtain_news_feed")
139: + " " + source);
140: } catch (Exception e) {
141: M_log.info("initChannel(" + source
142: + ") constructor: couldn't parse: "
143: + e.getMessage());
144: throw new NewsConnectionException(rl
145: .getString("unable_to_interpret")
146: + " " + source);
147: }
148:
149: m_title = feed.getTitle();
150: m_source = source;
151: m_description = feed.getDescription();
152: m_description = Validator.stripAllNewlines(m_description);
153:
154: m_lastbuilddate = "";
155: m_pubdate = "";
156: Date pubdate = feed.getPublishedDate();
157: if (pubdate != null) {
158: m_pubdate = DateFormat.getDateInstance().format(pubdate);
159: m_lastbuilddate = m_pubdate;
160: }
161: m_pubdate = Validator.stripAllNewlines(m_pubdate);
162: m_lastbuilddate = Validator.stripAllNewlines(m_lastbuilddate);
163:
164: m_copyright = feed.getCopyright();
165: m_copyright = Validator.stripAllNewlines(m_copyright);
166:
167: m_language = feed.getLanguage();
168: m_language = Validator.stripAllNewlines(m_language);
169:
170: m_link = feed.getLink();
171: m_link = Validator.stripAllNewlines(m_link);
172:
173: SyndImage image = feed.getImage();
174: if (image != null) {
175: m_imageLink = image.getLink();
176: m_imageLink = Validator.stripAllNewlines(m_imageLink);
177:
178: m_imageTitle = image.getTitle();
179: m_imageTitle = Validator.stripAllNewlines(m_imageTitle);
180:
181: m_imageUrl = image.getUrl();
182: m_imageUrl = Validator.stripAllNewlines(m_imageUrl);
183:
184: m_imageHeight = "";
185:
186: m_imageWidth = "";
187:
188: m_imageDescription = image.getDescription();
189: m_imageDescription = Validator
190: .stripAllNewlines(m_imageDescription);
191:
192: }
193: // others??
194: m_items = new Vector<NewsItem>();
195:
196: List items = feed.getEntries();
197:
198: for (int i = 0; i < items.size(); ++i) {
199: SyndEntry entry = (SyndEntry) items.get(i);
200:
201: String iTitle = entry.getTitle();
202: iTitle = Validator.stripAllNewlines(iTitle);
203:
204: String iDescription = null;
205: try {
206: if (entry.getDescription() != null) {
207: iDescription = FormattedText
208: .processEscapedHtml(entry.getDescription()
209: .getValue());
210: iDescription = Validator
211: .stripAllNewlines(iDescription);
212: }
213: } catch (Exception e) {
214: M_log.warn(e);
215: }
216:
217: String iLink = entry.getLink();
218: iLink = Validator.stripAllNewlines(iLink);
219: String iPubDate = "";
220: Date entrydate = entry.getPublishedDate();
221: if (entrydate != null) {
222: iPubDate = DateFormat.getDateInstance().format(
223: entrydate);
224: }
225:
226: List<NewsItemEnclosure> enclosures = new Vector<NewsItemEnclosure>();
227: List syndEnclosures = entry.getEnclosures();
228:
229: for (int j = 0; j < syndEnclosures.size(); j++) {
230: SyndEnclosure syndEnclosure = (SyndEnclosure) syndEnclosures
231: .get(j);
232:
233: enclosures.add(new BasicNewsItemEnclosure(syndEnclosure
234: .getUrl(), syndEnclosure.getType(),
235: syndEnclosure.getLength()));
236:
237: }
238: iPubDate = Validator.stripAllNewlines(iPubDate);
239: m_items.add(new BasicNewsItem(iTitle, iDescription, iLink,
240: iPubDate, enclosures));
241: }
242: } // initChannel
243:
244: /**
245: * A .
246: *
247: * @return the NewsItem that has the specified id.
248: */
249: public List getNewsitems() {
250: List<NewsItem> rv = new Vector<NewsItem>();
251: rv.addAll(m_items);
252:
253: return rv;
254:
255: } // getNewsitems
256:
257: /**
258: * A .
259: *
260: * @param filter
261: * A filtering object to accept messages, or null if no filtering is desired.
262: * @return a list of NewsItems objects (may be empty).
263: */
264: public List getNewsitems(Filter filter) {
265: List items = new Vector<NewsItem>(m_items);
266: if (filter != null) {
267: List<NewsItem> accepted = new Vector<NewsItem>();
268: Iterator it = items.iterator();
269: while (it.hasNext()) {
270: NewsItem item = (NewsItem) it.next();
271: if (filter.accept(item)) {
272: accepted.add(item);
273: }
274: }
275: items = accepted;
276: }
277: return items;
278: }
279:
280: public String getSource() {
281: return m_source;
282: }
283:
284: public String getLink() {
285: return m_link;
286: }
287:
288: public String getTitle() {
289: return m_title;
290: }
291:
292: public String getDescription() {
293: return m_description;
294: }
295:
296: public String getLanguage() {
297: return m_language;
298: }
299:
300: public String getCopyright() {
301: return m_copyright;
302: }
303:
304: public String getPubdate() {
305: return m_pubdate;
306: }
307:
308: public String getLastbuilddate() {
309: return m_lastbuilddate;
310: }
311:
312: public String getImageUrl() {
313: return m_imageUrl;
314: }
315:
316: public String getImageTitle() {
317: return m_imageTitle;
318: }
319:
320: public String getImageLink() {
321: return m_imageLink;
322: }
323:
324: public String getImageWidth() {
325: return m_imageWidth;
326: }
327:
328: public String getImageHeight() {
329: return m_imageHeight;
330: }
331:
332: public String getImageDescription() {
333: return m_imageDescription;
334: }
335:
336: public void setNewsitems(List items) {
337: m_items = new Vector(items);
338: }
339:
340: public void addNewsitem(NewsItem item) {
341: m_items.add(item);
342: }
343:
344: public void setSource(String source)
345: throws NewsConnectionException, NewsFormatException {
346: m_source = source;
347: initChannel(source);
348: }
349:
350: public void setLink(String link) {
351: m_link = link;
352: }
353:
354: public void setTitle(String title) {
355: m_title = title;
356: }
357:
358: public void setDescription(String description) {
359: m_description = description;
360: }
361:
362: public void setLanguage(String language) {
363: m_language = language;
364: }
365:
366: public void setCopyright(String copyright) {
367: m_copyright = copyright;
368: }
369:
370: public void setPubdate(String pubdate) {
371: m_pubdate = pubdate;
372: }
373:
374: public void setLastbuilddate(String builddate) {
375: m_lastbuilddate = builddate;
376: }
377:
378: public void setImageUrl(String imageUrl) {
379: m_imageUrl = imageUrl;
380: }
381:
382: public void setImageTitle(String imageTitle) {
383: m_imageTitle = imageTitle;
384: }
385:
386: public void setImageLink(String imageLink) {
387: m_imageLink = imageLink;
388: }
389:
390: public void setImageWidth(String imageWidth) {
391: m_imageWidth = imageWidth;
392: }
393:
394: public void setImageHeight(String imageHeight) {
395: m_imageHeight = imageHeight;
396: }
397:
398: public void setImageDescription(String imageDescription) {
399: m_imageDescription = imageDescription;
400: }
401:
402: /**
403: * Checks whether an update is available for the rss news feed.
404: *
405: * @return true if update is available, false otherwise
406: */
407: public boolean isUpdateAvailable() {
408: // %%%%%%%%
409: return true;
410: }
411:
412: /**
413: * Checks the relative ordering of the String url's of two Channels. Same
414: * response pattern as compareTo method for Strings--negative if "this"
415: * object is greater than parameter, zero if the objects are equal, and
416: * positive if "this" object is less than the parameter. The parameter
417: * can be a String reference or a NewsChannel object (otherwise method
418: * throws ClassCastException).
419: *
420: * @return A negative integer if "this" object is greater than parameter,
421: * zero if the objects are equal, and a positive integer if "this" object
422: * is less than the parameter
423: */
424: public int compareTo(Object obj) throws ClassCastException {
425: int rv = 0;
426: if (m_source == null) {
427: if (obj != null) {
428: rv = -1;
429: }
430: } else if (obj == null) {
431: rv = 1;
432: } else if (obj instanceof String) {
433: rv = m_source.compareTo((String) obj);
434: } else {
435: NewsChannel other = (NewsChannel) obj;
436: rv = m_source.compareTo(other.getLink());
437: }
438: return rv;
439: }
440:
441: /**
442: * Checks whether the parameter obj refers to the same channel as "this" channel.
443: * The parameter can be a String URL or a NewsChannel object (otherwise method
444: * throws ClassCastException).
445: *
446: * @return true if the channels are the same, false otherwise
447: */
448: public boolean equals(Object obj) throws ClassCastException {
449: return (compareTo(obj) == 0);
450: }
451:
452: /**
453: * Calculates a hash code for the channel object's URL.
454: *
455: * @return The hash-code for the String URL to the channel.
456: */
457: public int hashCode() {
458: String hval = "";
459: if (m_source != null) {
460: hval = m_source;
461: }
462:
463: return hval.hashCode();
464: }
465:
466: } // BasicNewsChannel
|