001: //
002: // // Informa -- RSS Library for Java
003: // Copyright (c) 2002 by Niko Schmuck
004: //
005: // Niko Schmuck
006: // http://sourceforge.net/projects/informa
007: // mailto:niko_schmuck@users.sourceforge.net
008: //
009: // This library is free software.
010: //
011: // You may redistribute it and/or modify it under the terms of the GNU
012: // Lesser General Public License as published by the Free Software Foundation.
013: //
014: // Version 2.1 of the license should be included with this distribution in
015: // the file LICENSE. If the license is not included with this distribution,
016: // you may find a copy at the FSF web site at 'www.gnu.org' or 'www.fsf.org',
017: // or you may write to the Free Software Foundation, 675 Mass Ave, Cambridge,
018: // MA 02139 USA.
019: //
020: // This library is distributed in the hope that it will be useful,
021: // but WITHOUT ANY WARRANTY; without even the implied waranty of
022: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
023: // Lesser General Public License for more details.
024:
025: // $Id: FeedManager.java,v 1.12 2006/12/04 23:43:27 italobb Exp $
026:
027: package de.nava.informa.utils;
028:
029: import java.io.IOException;
030: import java.util.ArrayList;
031: import java.util.Collection;
032: import java.util.HashMap;
033: import java.util.Iterator;
034: import java.util.Map;
035:
036: import de.nava.informa.core.ChannelBuilderIF;
037: import de.nava.informa.core.ChannelUpdatePeriod;
038: import de.nava.informa.core.FeedIF;
039: import de.nava.informa.core.ParseException;
040: import de.nava.informa.parsers.OPMLParser;
041:
042: /**
043: * A class used to manage feeds. Feeds are parsed and stored using the
044: * <code>addFeed</code> method. Subsequent requests for the same feed URI
045: * (using either <code>addFeed</code> or <code>getFeed</code>) will return
046: * a cached copy, unless the feed is due to be refreshed direct from the source.
047: * <br>
048: * The time before a feed is considered out of date will be read from the feed
049: * if possible, failing that a default UpdatePeriod and UpdateFrequency are
050: * used. <br>
051: *
052: * @author Sam Newman
053: * @version $Id: FeedManager.java,v 1.12 2006/12/04 23:43:27 italobb Exp $
054: */
055: public class FeedManager {
056:
057: /** Default channel builder used if no explicit channel builder was set */
058: private static final ChannelBuilderIF DEFAULT_BUILDER = new de.nava.informa.impl.basic.ChannelBuilder();
059:
060: private ChannelBuilderIF channelBuilder;
061:
062: private ChannelUpdatePeriod defaultUpdatePeriod;
063:
064: private int defaultUpdateFrequency;
065:
066: /** Internal store of FeedEntry's, keyed by feed uri */
067: private Map<String, FeedManagerEntry> feeds;
068:
069: /** cache settings variable */
070: private CacheSettingsIF cacheSettings = new CacheSettings();
071:
072: /** feed Daemon */
073: private FeedRefreshDaemon refreshDaemon = new FeedRefreshDaemon();
074:
075: /**
076: * Creates a new FeedManager object.
077: */
078: public FeedManager() {
079: feeds = new HashMap<String, FeedManagerEntry>();
080: this .defaultUpdatePeriod = ChannelUpdatePeriod.UPDATE_DAILY;
081: this .defaultUpdateFrequency = 1;
082: setCacheSettings(defaultUpdatePeriod, defaultUpdateFrequency);
083: }
084:
085: /**
086: * Creates a new FeedManager object.
087: *
088: * @param defaultUpdatePeriod
089: * @param defaultUpdateFrequency
090: */
091: public FeedManager(ChannelUpdatePeriod defaultUpdatePeriod,
092: int defaultUpdateFrequency) {
093: feeds = new HashMap<String, FeedManagerEntry>();
094: this .defaultUpdatePeriod = defaultUpdatePeriod;
095: this .defaultUpdateFrequency = defaultUpdateFrequency;
096: setCacheSettings(defaultUpdatePeriod, defaultUpdateFrequency);
097: }
098:
099: /**
100: * initialiation of the cacheSettings private variable
101: */
102: private void setCacheSettings(ChannelUpdatePeriod updatePeriod,
103: int updateFrequency) {
104: // TODO refactoring constants declarations
105: long MILLISECONDS_IN_HOUR = 3600000L;
106: long MILLISECONDS_IN_DAY = 86400000L;
107: long MILLISECONDS_IN_MONTH = 2419200000L;
108: long MILLISECONDS_IN_YEAR = 31536000000L;
109:
110: long msInPeriod = 0L;
111:
112: if (updatePeriod.equals(ChannelUpdatePeriod.UPDATE_HOURLY)) {
113: msInPeriod = MILLISECONDS_IN_HOUR;
114: } else if (updatePeriod
115: .equals(ChannelUpdatePeriod.UPDATE_DAILY)) {
116: msInPeriod = MILLISECONDS_IN_DAY;
117: } else if (updatePeriod
118: .equals(ChannelUpdatePeriod.UPDATE_MONTHLY)) {
119: msInPeriod = MILLISECONDS_IN_MONTH;
120: } else if (updatePeriod
121: .equals(ChannelUpdatePeriod.UPDATE_YEARLY)) {
122: msInPeriod = MILLISECONDS_IN_YEAR;
123: } else {
124: throw new IllegalArgumentException("updatePeriod "
125: + updatePeriod + " is not valid");
126: }
127: this .cacheSettings.setDefaultTtl(msInPeriod / updateFrequency);
128: }
129:
130: /**
131: * Loads a feed given the metadata information contained in the given FeedIF
132: * object
133: *
134: * @param feed
135: * @return FeedIF object
136: * @throws FeedManagerException
137: * DOCUMENT ME!
138: */
139: public FeedIF addFeed(FeedIF feed) throws FeedManagerException {
140: return addFeed(feed.getLocation().toString());
141: }
142:
143: /**
144: * Loads a feed given the metadata information contained in the given FeedIF
145: * object
146: *
147: * @param feed
148: * @param ttl
149: * in minutes
150: * @return FeedIF object
151: * @throws FeedManagerException
152: * DOCUMENT ME!
153: */
154: public FeedIF addFeed(FeedIF feed, int ttl)
155: throws FeedManagerException {
156: return addFeed(feed.getLocation().toString(), ttl);
157: }
158:
159: /**
160: * Attempts to load the feeds specified in the given OPML file into the
161: * manager
162: *
163: * @param opmlFeedUri
164: * An OPML file containing a list of feeds
165: * @return A list of FeedIF files representing the feeds added
166: * @throws FeedManagerException
167: * DOCUMENT ME!
168: */
169: public Collection<FeedIF> addFeeds(String opmlFeedUri)
170: throws FeedManagerException {
171: Collection<FeedIF> retFeeds = null;
172:
173: try {
174: Collection feedsColl = OPMLParser.parse(opmlFeedUri);
175: retFeeds = new ArrayList<FeedIF>();
176:
177: for (Iterator iter = feedsColl.iterator(); iter.hasNext();) {
178: FeedIF element = (FeedIF) iter.next();
179: retFeeds.add(addFeed(element));
180: }
181: } catch (IOException e) {
182: throw new FeedManagerException(e);
183: } catch (ParseException e) {
184: throw new FeedManagerException(e);
185: }
186:
187: return retFeeds;
188: }
189:
190: /**
191: * Attempts to load the feeds specified in the given OPML file into the
192: * manager
193: *
194: * @param opmlFeedUri
195: * An OPML file containing a list of feeds
196: * @param ttl
197: * a ttl for all feeds (in minutes)
198: * @return A list of FeedIF files representing the feeds added
199: * @throws FeedManagerException
200: * DOCUMENT ME!
201: */
202: public Collection<FeedIF> addFeeds(String opmlFeedUri, int ttl)
203: throws FeedManagerException {
204: Collection<FeedIF> retFeeds = null;
205:
206: try {
207: Collection feedsColl = OPMLParser.parse(opmlFeedUri);
208: retFeeds = new ArrayList<FeedIF>();
209:
210: for (Iterator iter = feedsColl.iterator(); iter.hasNext();) {
211: FeedIF element = (FeedIF) iter.next();
212: retFeeds.add(addFeed(element, ttl));
213: }
214: } catch (IOException e) {
215: throw new FeedManagerException(e);
216: } catch (ParseException e) {
217: throw new FeedManagerException(e);
218: }
219:
220: return retFeeds;
221: }
222:
223: /**
224: * Sets the channel builder used when reading the news feeds.
225: *
226: * @param chBuilder
227: */
228: public void setChannelBuilder(ChannelBuilderIF chBuilder) {
229: channelBuilder = chBuilder;
230: }
231:
232: /**
233: * Gets the channel builder used by the feed entries.
234: *
235: * @return ChannelBuilderIF
236: */
237: public ChannelBuilderIF getChannelBuilder() {
238: if (channelBuilder == null) {
239: return DEFAULT_BUILDER;
240: } else {
241: return channelBuilder;
242: }
243: }
244:
245: /**
246: * Determines if the manager is handling the feed
247: *
248: * @param feedUri
249: * The URI for the feed
250: * @return true if the feed is managed, else false
251: */
252: public boolean hasFeed(String feedUri) {
253: return feeds.containsKey(feedUri);
254: }
255:
256: /**
257: * Adds the specified feed to the manager. If the feed is already managed
258: * nothing happens
259: *
260: * @param feedUri
261: * The URI of the feed to add
262: * @return The feed being managed
263: * @throws FeedManagerException
264: * If the feed specified is invalid
265: */
266: public FeedIF addFeed(String feedUri) throws FeedManagerException {
267: if (!hasFeed(feedUri)) {
268: FeedManagerEntry fme = new FeedManagerEntry(feedUri,
269: getChannelBuilder(), defaultUpdatePeriod,
270: defaultUpdateFrequency);
271: feeds.put(feedUri, fme);
272: refreshDaemon.addFeed(fme);
273: }
274:
275: return getFeed(feedUri);
276: }
277:
278: /**
279: * Adds the specified feed to the manager with a specific Ttl. The ttl
280: * specified may superseded the global ttl (deducted from
281: * <code>updatePeriod</code> and <code>updateFrequency</code>), depending
282: * on the feed type. This behavior is implemented in the differents
283: * <code>CacheSettingsIF</code> implementations provided.
284: *
285: * @see de.nava.informa.utils.CacheSettingsIF
286: * @param feedUri
287: * The URI of the feed to add
288: * @param wantedTtlmins
289: * The ttl wanted for this feed (in minutes)
290: * @return The feed being managed
291: * @throws FeedManagerException
292: * If the feed specified is invalid
293: */
294: public FeedIF addFeed(String feedUri, int wantedTtlmins)
295: throws FeedManagerException {
296: if (!hasFeed(feedUri)) {
297: FeedManagerEntry FMEntry = new FeedManagerEntry(feedUri,
298: getChannelBuilder(), defaultUpdatePeriod,
299: defaultUpdateFrequency);
300:
301: if (wantedTtlmins > 0) {
302: FMEntry.setWantedTtl(wantedTtlmins * (60 * 1000));
303: }
304: feeds.put(feedUri, FMEntry);
305: refreshDaemon.addFeed(FMEntry);
306: } else {
307: // TODO : what behavior when the ttl is different ?
308: }
309:
310: return getFeed(feedUri);
311: }
312:
313: /**
314: * Removes the specified feed from the manager
315: *
316: * @param feedUri
317: * The URI for the feed to remove
318: */
319: public void removeFeed(String feedUri) {
320: feeds.remove(feedUri);
321: }
322:
323: /**
324: * Retrieves the feed being managed. Note that null will be returned if the
325: * feed is not being managed. If you want to add a feed and return it in the
326: * same method, use <code>addFeed</code> instead.
327: *
328: * @param feedUri
329: * The feed to return
330: * @return The feed being managed, or null if the feed is not present
331: * @throws FeedManagerException
332: * If the feed specified is invalid
333: */
334: public FeedIF getFeed(String feedUri) throws FeedManagerException {
335: FeedIF feed = null;
336:
337: if (hasFeed(feedUri)) {
338: FeedManagerEntry entry = feeds.get(feedUri);
339: feed = entry.getFeed();
340: }
341:
342: return feed;
343: }
344:
345: }
|