KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > de > nava > informa > utils > FeedManagerEntry


1 //
2
// Informa -- RSS Library for Java
3
// Copyright (c) 2002 by Niko Schmuck
4
//
5
// Niko Schmuck
6
// http://sourceforge.net/projects/informa
7
// mailto:niko_schmuck@users.sourceforge.net
8
//
9
// This library is free software.
10
//
11
// You may redistribute it and/or modify it under the terms of the GNU
12
// Lesser General Public License as published by the Free Software Foundation.
13
//
14
// Version 2.1 of the license should be included with this distribution in
15
// the file LICENSE. If the license is not included with this distribution,
16
// you may find a copy at the FSF web site at 'www.gnu.org' or 'www.fsf.org',
17
// or you may write to the Free Software Foundation, 675 Mass Ave, Cambridge,
18
// MA 02139 USA.
19
//
20
// This library is distributed in the hope that it will be useful,
21
// but WITHOUT ANY WARRANTY; without even the implied waranty of
22
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23
// Lesser General Public License for more details.
24
//
25
// $Id: FeedManagerEntry.java,v 1.16 2004/06/28 19:33:44 niko_schmuck Exp $
26

27 package de.nava.informa.utils;
28
29 import de.nava.informa.core.ChannelBuilderIF;
30 import de.nava.informa.core.ChannelIF;
31 import de.nava.informa.core.FeedIF;
32 import de.nava.informa.core.ParseException;
33 import de.nava.informa.impl.basic.Feed;
34 import de.nava.informa.parsers.FeedParser;
35
36 import java.io.IOException JavaDoc;
37
38 import java.net.URL JavaDoc;
39 import java.net.HttpURLConnection JavaDoc;
40 import java.net.URLConnection JavaDoc;
41
42 import java.util.Date JavaDoc;
43
44 import org.apache.commons.logging.Log;
45 import org.apache.commons.logging.LogFactory;
46
47 /**
48  * Holder class for feeds held in the manager. The purpose of this class is to
49  * store the last time we loaded the feed, and determine if the feed needs to be
50  * reread. Whilst we use the data provided by the feed where possible, if this
51  * is not present defaults will be used.
52  * <p>
53  * Its also important to note that we do oversimply things a bit. We ignore the
54  * updateBase even if specified by the feed.
55  * </p>
56  *
57  * @author Sam Newman
58  * @see FeedManager
59  */

60 public class FeedManagerEntry {
61
62   private static final long MILLISECONDS_IN_HOUR = 3600000L;
63
64   private static final long MILLISECONDS_IN_DAY = 86400000L;
65
66   private static final long MILLISECONDS_IN_MONTH = 2419200000L;
67
68   /** Over simplificatin here - assuming a non-leap year */
69   private static final long MILLISECONDS_IN_YEAR = 31536000000L;
70
71   /* logger handler */
72   private static Log logger = LogFactory.getLog(FeedManagerEntry.class);
73
74   private String JavaDoc defaultUpdatePeriod;
75
76   private int defaultUpdateFrequency;
77
78   /**
79    * Stores the number of milliseconds since the last update after which the
80    * feed is out of date
81    */

82   private long timeToExpire;
83
84   /** The channel we hold */
85   private FeedIF feed;
86
87   /** The last time we updated a feed */
88   private long lastUpdate;
89
90   /** The URI for the feed */
91   private String JavaDoc feedUri;
92
93   private ChannelBuilderIF channelBuilder;
94
95   /** the wantedTtl for the feed * */
96   private long wantedTtl = -1;
97
98   /** stores the values necessary to make conditionnale GET * */
99   private ConditionalGetValues httpHeaders = new ConditionalGetValues();
100
101   /**
102    * Creates a new FeedManagerEntry object.
103    *
104    * @param feedUri
105    * @param builder
106    * @param defaultUpdatePeriod
107    * @param defaultUpdateFrequency
108    * @throws FeedManagerException
109    */

110   public FeedManagerEntry(String JavaDoc feedUri, ChannelBuilderIF builder,
111       String JavaDoc defaultUpdatePeriod, int defaultUpdateFrequency)
112       throws FeedManagerException {
113     this.feedUri = feedUri;
114     this.channelBuilder = builder;
115     this.defaultUpdatePeriod = defaultUpdatePeriod;
116     this.defaultUpdateFrequency = defaultUpdateFrequency;
117     this.feed = retrieveFeed(feedUri);
118     this.lastUpdate = System.currentTimeMillis();
119   }
120
121   /**
122    * Loads the channel and sets up the time to expire
123    *
124    * @param uri
125    * The location for the rss file
126    * @return The Channel
127    * @throws FeedManagerException
128    * If the feed specified by <code>uri</code> is invalid
129    */

130   private FeedIF retrieveFeed(String JavaDoc uri) throws FeedManagerException {
131     try {
132       URL JavaDoc urlToRetrieve = new URL JavaDoc(uri);
133
134       URLConnection JavaDoc conn = null;
135       try {
136         conn = urlToRetrieve.openConnection();
137
138         if (conn instanceof HttpURLConnection JavaDoc) {
139
140           HttpURLConnection JavaDoc httpConn = (HttpURLConnection JavaDoc) conn;
141
142           httpConn.setInstanceFollowRedirects(true); // not needed, default ?
143

144           // Hack for User-Agent : problem for
145
// http://www.diveintomark.org/xml/rss.xml
146
HttpHeaderUtils.setUserAgent(httpConn, "Informa Java API");
147
148           logger.debug("retr feed at url " + uri + ": ETag"
149               + HttpHeaderUtils.getETagValue(httpConn) + " if-modified :"
150               + HttpHeaderUtils.getLastModified(httpConn));
151
152           // get initial values for cond. GET in updateChannel
153
this.httpHeaders.setETag(HttpHeaderUtils.getETagValue(httpConn));
154           this.httpHeaders.setIfModifiedSince(HttpHeaderUtils
155               .getLastModified(httpConn));
156         }
157       } catch (java.lang.ClassCastException JavaDoc e) {
158         conn = null;
159         logger.warn("problem cast to HttpURLConnection " + uri, e);
160         throw new FeedManagerException(e);
161       } catch (NullPointerException JavaDoc e) {
162         logger.error("problem NPE " + uri + " conn=" + conn, e);
163         conn = null;
164         throw new FeedManagerException(e);
165       }
166
167       ChannelIF channel = null;
168       /*
169        * if ( conn == null ) { channel = FeedParser.parse(getChannelBuilder(),
170        * uri); } else {
171        */

172       channel = FeedParser.parse(getChannelBuilder(), conn.getInputStream());
173       //}
174

175       this.timeToExpire = getTimeToExpire(channel);
176       this.feed = new Feed(channel);
177
178       Date JavaDoc currDate = new Date JavaDoc();
179       this.feed.setLastUpdated(currDate);
180       this.feed.setDateFound(currDate);
181       this.feed.setLocation(urlToRetrieve);
182       logger.info("feed retrieved " + uri);
183
184     } catch (IOException JavaDoc e) {
185       logger.error("IOException " + feedUri + " e=" + e);
186       e.printStackTrace();
187       throw new FeedManagerException(e);
188     } catch (ParseException e) {
189       e.printStackTrace();
190       throw new FeedManagerException(e);
191     }
192
193     return this.feed;
194   }
195
196   /**
197    * Updates the channel associated with this feed use conditional get stuff.
198    * http://fishbowl.pastiche.org/2002/10/21/http_conditional_get_for_rss_hackers
199    *
200    * @throws FeedManagerException
201    */

202   private synchronized void updateChannel() throws FeedManagerException {
203     try {
204       String JavaDoc feedUrl = this.feed.getLocation().toString();
205
206       URL JavaDoc aURL = null;
207       try {
208         aURL = new URL JavaDoc(feedUrl);
209       } catch (java.net.MalformedURLException JavaDoc e) {
210         logger.error("Could not create URL for " + feedUrl);
211       }
212
213       URLConnection JavaDoc conn = null;
214       try {
215         conn = aURL.openConnection();
216
217         if (conn instanceof HttpURLConnection JavaDoc) {
218
219           HttpURLConnection JavaDoc httpConn = (HttpURLConnection JavaDoc) conn;
220
221           httpConn.setInstanceFollowRedirects(true);
222           // Hack for User-Agent : problem for
223
// http://www.diveintomark.org/xml/rss.xml
224
HttpHeaderUtils.setUserAgent(httpConn, "Informa Java API");
225           HttpHeaderUtils.setETagValue(httpConn, this.httpHeaders.getETag());
226           HttpHeaderUtils.setIfModifiedSince(httpConn, this.httpHeaders
227               .getIfModifiedSince());
228           httpConn.connect();
229           if (httpConn.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
230
231             logger.info("cond. GET for feed at url " + feedUrl + ": no change");
232             this.feed.setLastUpdated(new Date JavaDoc());
233             // TODO : add a property in FeedIF interface for lastGet ?
234
this.lastUpdate = System.currentTimeMillis();
235             return;
236           }
237           logger.info("cond. GET for feed at url " + feedUrl + ": changed");
238           logger.debug("feed at url " + feedUrl + " new values : ETag"
239               + HttpHeaderUtils.getETagValue(httpConn) + " if-modified :"
240               + HttpHeaderUtils.getLastModified(httpConn));
241
242           this.httpHeaders.setETag(HttpHeaderUtils.getETagValue(httpConn));
243           this.httpHeaders.setIfModifiedSince(HttpHeaderUtils
244               .getLastModified(httpConn));
245         }
246
247       } catch (java.lang.ClassCastException JavaDoc e) {
248         logger.warn("problem cast to HttpURLConnection (reading from a file?) "
249             + feedUrl, e);
250       }
251
252       ChannelIF channel = null;
253       if (conn == null) {
254         channel = FeedParser.parse(getChannelBuilder(), feedUrl);
255       } else {
256         channel = FeedParser.parse(getChannelBuilder(), conn.getInputStream());
257       }
258
259       this.feed.setChannel(channel);
260       this.feed.setLastUpdated(new Date JavaDoc());
261       this.lastUpdate = System.currentTimeMillis();
262       logger.info("feed updated " + feedUrl);
263     } catch (IOException JavaDoc e) {
264       throw new FeedManagerException(e);
265     } catch (ParseException e) {
266       throw new FeedManagerException(e);
267     }
268   }
269
270   /**
271    * Checks to see if the feed is out of date - if it is the feed is reloaded
272    * from the URI, otherwise the cached version is returned.
273    *
274    * @return The up todate feed
275    * @throws FeedManagerException
276    */

277   public FeedIF getFeed() throws FeedManagerException {
278     if (isOutOfDate()) {
279       updateChannel();
280     }
281     return this.feed;
282   }
283
284   public void setWantedTtl(long ms) {
285     this.wantedTtl = ms;
286     //recalculate the timeToExpire
287
this.timeToExpire = this.getTimeToExpire(this.feed.getChannel());
288   }
289
290   /**
291    * Based on the update period and update frequceny and on the optional
292    * wantedTtl for the feed, calculate how many milliseconds after the
293    * <code>lastUpdate</code> before this feed is considered out of date
294    *
295    * @param channel
296    * @return The number of milliseconds before we can consider the feed invalid
297    * @throws IllegalArgumentException
298    */

299   private long getTimeToExpire(ChannelIF channel) {
300     long temp = (new CacheSettings()).getTtl(channel, this.wantedTtl);
301     return temp;
302   }
303
304   /**
305    * Determines if the feed is out of date.
306    *
307    * @return false if the feed is up to date, else true
308    */

309   private boolean isOutOfDate() {
310     boolean outOfDate = false;
311     logger.info(this + " isOutOfDate " + this.feedUri + "lupdt: " + lastUpdate
312         + ",tte=" + timeToExpire + "<?"
313         + (System.currentTimeMillis() - lastUpdate));
314     if ((lastUpdate + timeToExpire) < System.currentTimeMillis()) {
315       outOfDate = true;
316     }
317     return outOfDate;
318   }
319
320   private ChannelBuilderIF getChannelBuilder() {
321     return channelBuilder;
322   }
323
324 }
Popular Tags