author | dadubrow |
Wed, 16 Dec 2009 10:28:34 -0600 | |
changeset 688 | ae5ff180a61d |
parent 104 | 4ca9a303c257 |
permissions | -rw-r--r-- |
2 | 1 |
/* |
2 |
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 |
* All rights reserved. |
|
4 |
* This component and the accompanying materials are made available |
|
5 |
* under the terms of the License "Eclipse Public License v1.0" |
|
6 |
* which accompanies this distribution, and is available |
|
7 |
* at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 |
* |
|
9 |
* Initial Contributors: |
|
10 |
* Nokia Corporation - initial contribution. |
|
11 |
* |
|
12 |
* Contributors: |
|
13 |
* |
|
14 |
* Description: |
|
15 |
* |
|
16 |
*/ |
|
17 |
||
18 |
package com.nokia.carbide.cpp.internal.news.reader.feed; |
|
19 |
||
20 |
import java.io.File; |
|
21 |
import java.io.FileInputStream; |
|
22 |
import java.io.InputStream; |
|
23 |
import java.net.URL; |
|
24 |
import java.util.ArrayList; |
|
25 |
import java.util.Iterator; |
|
26 |
import java.util.List; |
|
27 |
import java.util.Properties; |
|
28 |
import java.util.Timer; |
|
29 |
import java.util.TimerTask; |
|
30 |
||
31 |
import org.eclipse.core.runtime.IPath; |
|
32 |
import org.eclipse.core.runtime.Path; |
|
33 |
import org.eclipse.core.runtime.Platform; |
|
34 |
import org.eclipse.jface.preference.IPreferenceStore; |
|
35 |
import org.eclipse.osgi.service.datalocation.Location; |
|
36 |
||
37 |
import com.nokia.carbide.cpp.internal.news.reader.CarbideNewsReaderPlugin; |
|
38 |
import com.nokia.carbide.cpp.internal.news.reader.gen.FeedCache.FeedCacheManager; |
|
39 |
import com.nokia.carbide.cpp.internal.news.reader.gen.FeedInfo.FeedInfoConstants; |
|
40 |
import com.nokia.carbide.cpp.internal.news.reader.gen.FeedInfo.FeedInfoManager; |
|
41 |
import com.nokia.carbide.cpp.internal.news.reader.gen.FeedInfo.FeedType; |
|
42 |
import com.nokia.carbide.cpp.internal.news.reader.ui.NewsPreferenceConstants; |
|
43 |
import com.sun.syndication.feed.synd.SyndFeed; |
|
44 |
import com.sun.syndication.fetcher.FeedFetcher; |
|
45 |
import com.sun.syndication.fetcher.impl.FeedFetcherCache; |
|
46 |
import com.sun.syndication.fetcher.impl.HashMapFeedInfoCache; |
|
47 |
||
48 |
/** |
|
49 |
* A class to manage feeds for the Carbide.c++ news reader. |
|
50 |
* |
|
51 |
*/ |
|
52 |
public class FeedManager { |
|
53 |
||
54 |
// public data |
|
55 |
public static final String FEED_CACHE_FILE_NAME = "carbide.c++.news.xml"; |
|
56 |
public static final String PROPERTIES_FILE_LOCATION = "configuration/server.properties"; |
|
57 |
||
58 |
// private data |
|
59 |
private FeedCacheManager feedCacheManager; |
|
60 |
private FeedInfoManager feedListingManager; |
|
61 |
private List<CarbideSyndFeed> newsFeeds; |
|
62 |
private CarbideSyndFeed resourceFeed; |
|
63 |
private Timer updateTimer; |
|
64 |
||
65 |
/** |
|
66 |
* Private class to handle scheduled feeds updates. |
|
67 |
* |
|
68 |
*/ |
|
69 |
private class UpdateTimerTask extends TimerTask { |
|
70 |
/* |
|
71 |
* (non-Javadoc) |
|
72 |
* @see java.util.TimerTask#run() |
|
73 |
*/ |
|
74 |
public void run(){ |
|
75 |
CarbideNewsReaderPlugin.updateFeeds(); |
|
76 |
} |
|
77 |
} |
|
78 |
||
79 |
/** |
|
80 |
* The constructor. |
|
81 |
*/ |
|
82 |
public FeedManager() { |
|
83 |
feedCacheManager = new FeedCacheManager(); |
|
84 |
feedListingManager = new FeedInfoManager(); |
|
85 |
newsFeeds = new ArrayList<CarbideSyndFeed>(); |
|
86 |
} |
|
87 |
||
88 |
public boolean isCacheEmpty() { |
|
89 |
return feedCacheManager.isCacheEmpty(); |
|
90 |
} |
|
91 |
||
92 |
/** |
|
93 |
* Load feeds listed in the news feeds listing file. |
|
94 |
*/ |
|
95 |
@SuppressWarnings("static-access") |
|
96 |
public void loadFeeds() { |
|
97 |
try { |
|
104
4ca9a303c257
Removed remote connections from UI thread during Carbide startup; fix for Bug 8917.
stechong
parents:
99
diff
changeset
|
98 |
if (!loadFeedListing(false)) { |
2 | 99 |
return; |
100 |
} |
|
101 |
||
102 |
if (feedCacheManager.isCacheEmpty()) { |
|
103 |
loadFeedCache(); |
|
104 |
} |
|
105 |
clearFeeds(); |
|
106 |
updateCache(); |
|
107 |
feedCacheManager.fireFeedCacheChanged(feedCacheManager.isCacheUpdated()); |
|
108 |
feedCacheManager.setCacheUpdated(false); |
|
109 |
} catch (Exception e) { |
|
110 |
CarbideNewsReaderPlugin.log(e); |
|
111 |
} |
|
112 |
} |
|
113 |
||
114 |
/** |
|
115 |
* Retrieve all the currently available news feeds. |
|
116 |
* @return list of feed objects |
|
117 |
*/ |
|
118 |
public List<CarbideSyndFeed> getNewsFeeds() { |
|
119 |
validateFeeds(); |
|
120 |
return newsFeeds; |
|
121 |
} |
|
122 |
||
123 |
/** |
|
124 |
* Retrieve a property string from the news reader properties file. |
|
125 |
* @param key - key used to identify the property string |
|
126 |
* @return property string if found; null otherwise |
|
127 |
*/ |
|
128 |
public String getProperty(String key) { |
|
129 |
try { |
|
130 |
Location installLocation = Platform.getInstallLocation(); |
|
131 |
if (installLocation == null) { |
|
132 |
return null; |
|
133 |
} |
|
134 |
||
135 |
URL url = installLocation.getURL(); |
|
136 |
if (url == null) { |
|
137 |
return null; |
|
138 |
} |
|
139 |
||
140 |
IPath path = new Path(url.getPath()); |
|
141 |
path = path.append(PROPERTIES_FILE_LOCATION); |
|
142 |
File file = path.toFile(); |
|
143 |
InputStream is = new FileInputStream(file); |
|
144 |
Properties properties = new Properties(); |
|
145 |
properties.load(is); |
|
146 |
is.close(); |
|
147 |
String result = (String)properties.get(key); |
|
148 |
return result; |
|
149 |
} catch (Exception e) { |
|
150 |
CarbideNewsReaderPlugin.log(e); |
|
151 |
} |
|
152 |
||
153 |
return null; |
|
154 |
} |
|
155 |
||
156 |
/** |
|
157 |
* Retrieve the resource feed. |
|
158 |
* @return resource feed object |
|
159 |
*/ |
|
160 |
public CarbideSyndFeed getResourceFeed() { |
|
161 |
validateFeeds(); |
|
162 |
return resourceFeed; |
|
163 |
} |
|
164 |
||
165 |
public List<CarbideSyndFeed> getSubscribedNewsFeeds() { |
|
166 |
validateFeeds(); |
|
167 |
List<CarbideSyndFeed> subscribedFeeds = new ArrayList<CarbideSyndFeed>(); |
|
168 |
for (Iterator<CarbideSyndFeed> iterator = newsFeeds.iterator(); iterator.hasNext();) { |
|
169 |
CarbideSyndFeed feed = iterator.next(); |
|
170 |
if (feed.isSubscribed()) { |
|
171 |
subscribedFeeds.add(feed); |
|
172 |
} |
|
173 |
} |
|
174 |
return subscribedFeeds; |
|
175 |
} |
|
176 |
||
177 |
/** |
|
178 |
* Return the total number of unread entries from subscribed news feeds. |
|
179 |
* @return total number of unread entries from subscribed news feeds |
|
180 |
*/ |
|
181 |
public int getUnreadEntriesCount() { |
|
182 |
int count = 0; |
|
183 |
for (Iterator<CarbideSyndFeed> iterator = newsFeeds.iterator(); iterator.hasNext();) { |
|
184 |
CarbideSyndFeed feed = iterator.next(); |
|
185 |
if (feed.isSubscribed()) { |
|
186 |
count += feed.getUnreadEntries().size(); |
|
187 |
} |
|
188 |
} |
|
189 |
return count; |
|
190 |
} |
|
191 |
||
192 |
/** |
|
99 | 193 |
* Search for a feed entry by name and then mark it as read. |
194 |
* @param entries - feed entries to be checked |
|
195 |
* @param entryTitle - title of the entry to be marked as read |
|
2 | 196 |
*/ |
99 | 197 |
public void markEntryAsRead(List<CarbideSyndEntry> entries, String entryTitle) { |
2 | 198 |
if (entries == null || entryTitle == null) { |
199 |
return; |
|
200 |
} |
|
201 |
||
202 |
for (Iterator<CarbideSyndEntry> iterator = entries.iterator(); iterator.hasNext();) { |
|
203 |
CarbideSyndEntry entry = iterator.next(); |
|
204 |
String title = entry.getTitle(); |
|
205 |
title = title.replaceAll("\n", ""); |
|
99 | 206 |
if (title.equals(entryTitle) && !entry.isRead()) { |
2 | 207 |
entry.setRead(true); |
208 |
break; |
|
209 |
} |
|
210 |
} |
|
211 |
||
212 |
} |
|
213 |
||
214 |
/** |
|
215 |
* Reset the update timer. |
|
216 |
*/ |
|
217 |
public void resetUpdateTimer() { |
|
218 |
if (updateTimer == null) { |
|
219 |
updateTimer = new Timer(); |
|
220 |
} |
|
221 |
long updateInterval = getUpdateInterval(); |
|
222 |
UpdateTimerTask updateTask = new UpdateTimerTask(); |
|
223 |
updateTimer.schedule(updateTask, updateInterval); |
|
224 |
} |
|
225 |
||
226 |
/** |
|
227 |
* Save relevant feed information to local cache. |
|
228 |
*/ |
|
229 |
public void saveFeeds() { |
|
230 |
feedCacheManager.clearFeedCache(); |
|
231 |
for (Iterator<CarbideSyndFeed> iterator = newsFeeds.iterator(); iterator.hasNext();) { |
|
232 |
CarbideSyndFeed feed = iterator.next(); |
|
233 |
feedCacheManager.saveFeedToCache(feed); |
|
234 |
} |
|
235 |
feedCacheManager.saveFeedToCache(resourceFeed); |
|
236 |
saveFeedCache(); |
|
237 |
} |
|
238 |
||
239 |
/** |
|
240 |
* Perform any necessary actions when the total number of unread entries from |
|
241 |
* subscribed news feeds has changed. |
|
242 |
*/ |
|
243 |
@SuppressWarnings("static-access") |
|
244 |
public void unreadEntriesCountChanged() { |
|
245 |
feedCacheManager.fireFeedCacheChanged(false); |
|
246 |
} |
|
247 |
||
248 |
/** |
|
249 |
* Update all news and resource feeds. |
|
250 |
*/ |
|
251 |
@SuppressWarnings("static-access") |
|
252 |
public void updateFeeds() { |
|
253 |
try { |
|
104
4ca9a303c257
Removed remote connections from UI thread during Carbide startup; fix for Bug 8917.
stechong
parents:
99
diff
changeset
|
254 |
if (!loadFeedListing(false)) { |
2 | 255 |
return; |
256 |
} |
|
257 |
||
258 |
clearFeeds(); |
|
259 |
updateCache(); |
|
260 |
feedCacheManager.fireFeedCacheChanged(feedCacheManager.isCacheUpdated()); |
|
261 |
feedCacheManager.setCacheUpdated(false); |
|
262 |
} catch (Exception e) { |
|
263 |
CarbideNewsReaderPlugin.log(e); |
|
264 |
} |
|
265 |
} |
|
266 |
||
267 |
/** |
|
268 |
* Add a news feed. The news reader can have multiple news feeds. |
|
269 |
* @param feed - incoming news feed |
|
270 |
* @return true on success |
|
271 |
*/ |
|
272 |
private boolean addNewsFeed(CarbideSyndFeed feed) { |
|
273 |
if (feed == null) { |
|
274 |
return false; |
|
275 |
} |
|
276 |
||
277 |
if (feed != null ) { |
|
278 |
boolean feedExist = false; |
|
279 |
for (Iterator<CarbideSyndFeed> iterator = newsFeeds.iterator(); iterator.hasNext();) { |
|
280 |
CarbideSyndFeed aFeed = iterator.next(); |
|
281 |
if (aFeed.getTitle().equals(feed.getTitle()) && |
|
282 |
aFeed.getLink().equals(feed.getLink())) { |
|
283 |
int index = newsFeeds.indexOf(aFeed); |
|
284 |
newsFeeds.set(index, feed); |
|
285 |
feedExist = true; |
|
286 |
} |
|
287 |
} |
|
288 |
||
289 |
if (!feedExist) { |
|
290 |
feed.setType(FeedInfoConstants.FEED_TYPE_NEWS); |
|
291 |
newsFeeds.add(feed); |
|
292 |
} |
|
293 |
return true; |
|
294 |
} |
|
295 |
return false; |
|
296 |
} |
|
297 |
||
298 |
/** |
|
299 |
* Add a resource feed. The news reader can only have one resource feed. |
|
300 |
* @param feed - incoming resource feed |
|
301 |
* @return true on success |
|
302 |
*/ |
|
303 |
private boolean addResourceFeed(CarbideSyndFeed feed) { |
|
304 |
if (feed == null) { |
|
305 |
return false; |
|
306 |
} |
|
307 |
||
308 |
resourceFeed = feed; |
|
309 |
if (resourceFeed != null) { |
|
310 |
resourceFeed.setType(FeedInfoConstants.FEED_TYPE_RESOURCE); |
|
311 |
return true; |
|
312 |
} |
|
313 |
return false; |
|
314 |
} |
|
315 |
||
316 |
/** |
|
317 |
* Clear all news and resource feeds. |
|
318 |
*/ |
|
319 |
private void clearFeeds() { |
|
320 |
newsFeeds.clear(); |
|
321 |
resourceFeed = null; |
|
322 |
} |
|
323 |
||
324 |
/** |
|
325 |
* Feed feed content from its server. |
|
326 |
* @param feedUrl - URL of the feed |
|
327 |
* @return feed object on success; null otherwise |
|
328 |
*/ |
|
329 |
private CarbideSyndFeed fetchFeed(URL feedUrl) throws Exception { |
|
330 |
if (feedUrl == null) { |
|
331 |
return null; |
|
332 |
} |
|
333 |
||
334 |
FeedFetcherCache feedInfoCache = HashMapFeedInfoCache.getInstance(); |
|
83
6c6d4b0c9171
Specifies acceptable content types in connection request header; fix for Bug 8775.
stechong
parents:
81
diff
changeset
|
335 |
FeedFetcher fetcher = new CarbideFeedFetcher(feedInfoCache); |
2 | 336 |
SyndFeed sFeed = fetcher.retrieveFeed(feedUrl); |
337 |
CarbideSyndFeed feed = new CarbideSyndFeed(sFeed); |
|
338 |
if (feed != null) { |
|
339 |
feed.setSubscribed(true); |
|
340 |
feed.setLink(feedUrl.toString()); |
|
341 |
return feed; |
|
342 |
} |
|
343 |
return null; |
|
344 |
} |
|
345 |
||
346 |
/** |
|
347 |
* Get the default local feeds cache file. |
|
348 |
* @return default local feeds cache |
|
349 |
*/ |
|
350 |
private File getDefaultFeedCacheFile() { |
|
351 |
String location = System.getProperty("user.home") + File.separator + |
|
352 |
FEED_CACHE_FILE_NAME; |
|
353 |
File file = new File(location); |
|
354 |
return file; |
|
355 |
} |
|
356 |
||
357 |
/** |
|
358 |
* Get the update interval from news reader preferences. |
|
359 |
* @return update interval |
|
360 |
*/ |
|
361 |
private long getUpdateInterval() { |
|
362 |
IPreferenceStore store = CarbideNewsReaderPlugin.getPrefsStore(); |
|
363 |
long interval = store.getLong(NewsPreferenceConstants.UPDATE_INTERVAL) * 1000 * 60 * 60; |
|
364 |
return interval; |
|
365 |
} |
|
366 |
||
367 |
/** |
|
368 |
* Load feed information from local cache. |
|
369 |
*/ |
|
370 |
private void loadFeedCache() throws Exception { |
|
371 |
File file = getDefaultFeedCacheFile(); |
|
372 |
URL url = file.toURL(); |
|
373 |
if (file.exists()) { |
|
374 |
if (url != null) { |
|
375 |
feedCacheManager.loadCacheFromFile(url); |
|
376 |
} |
|
377 |
} |
|
378 |
else { |
|
379 |
file.createNewFile(); |
|
380 |
feedCacheManager.createDefaultFeedCache(); |
|
381 |
feedCacheManager.saveCacheToFile(url); |
|
382 |
} |
|
383 |
} |
|
384 |
||
385 |
/** |
|
104
4ca9a303c257
Removed remote connections from UI thread during Carbide startup; fix for Bug 8917.
stechong
parents:
99
diff
changeset
|
386 |
* Load feed information from feed listing file. |
4ca9a303c257
Removed remote connections from UI thread during Carbide startup; fix for Bug 8917.
stechong
parents:
99
diff
changeset
|
387 |
* @param useLocal - use local copy of feed listing file? |
2 | 388 |
* @return true on success; false otherwise |
389 |
*/ |
|
104
4ca9a303c257
Removed remote connections from UI thread during Carbide startup; fix for Bug 8917.
stechong
parents:
99
diff
changeset
|
390 |
private boolean loadFeedListing(boolean useLocalCopy) throws Exception { |
4ca9a303c257
Removed remote connections from UI thread during Carbide startup; fix for Bug 8917.
stechong
parents:
99
diff
changeset
|
391 |
URL url = feedListingManager.getFeedInfoFileURL(useLocalCopy); |
2 | 392 |
if (url != null) { |
393 |
return feedListingManager.loadFeedInfo(url); |
|
394 |
} |
|
395 |
return false; |
|
396 |
} |
|
397 |
||
398 |
/** |
|
399 |
* Save feed information to local cache. |
|
400 |
*/ |
|
401 |
private void saveFeedCache() { |
|
402 |
try { |
|
403 |
File file = getDefaultFeedCacheFile(); |
|
404 |
URL url = file.toURL(); |
|
405 |
if (file.exists()) { |
|
406 |
if (url != null) { |
|
407 |
feedCacheManager.saveCacheToFile(url); |
|
408 |
} |
|
409 |
} |
|
410 |
else { |
|
411 |
file.createNewFile(); |
|
412 |
feedCacheManager.saveCacheToFile(url); |
|
413 |
} |
|
414 |
} catch (Exception e) { |
|
415 |
CarbideNewsReaderPlugin.log(e); |
|
416 |
} |
|
417 |
} |
|
418 |
||
419 |
/** |
|
420 |
* Update local cache of feeds. |
|
421 |
*/ |
|
422 |
private void updateCache() throws Exception { |
|
423 |
List<FeedType> feedInfoList = feedListingManager.getFeedInfo().getFeeds().getFeed(); |
|
424 |
for (Iterator<FeedType> iterator = feedInfoList.iterator(); iterator.hasNext();) { |
|
425 |
FeedType feedInfo = iterator.next(); |
|
426 |
URL feedUrl = new URL(feedInfo.getUrl()); |
|
427 |
if (feedUrl != null) { |
|
428 |
CarbideSyndFeed feed = fetchFeed(feedUrl); |
|
429 |
if (feed != null) { |
|
430 |
if (feedCacheManager.isFeedInCache(feed)) { |
|
431 |
feedCacheManager.syncFeedWithCache(feed); |
|
432 |
} |
|
433 |
else { |
|
434 |
feed.setIsNew(true); |
|
435 |
} |
|
436 |
feedCacheManager.saveFeedToCache(feed); |
|
437 |
||
438 |
if (FeedInfoManager.isNewsFeed(feedInfo)) { |
|
439 |
if (!addNewsFeed(feed)) { |
|
440 |
break; |
|
441 |
} |
|
442 |
} |
|
443 |
else |
|
444 |
if (FeedInfoManager.isResourceFeed(feedInfo)) { |
|
445 |
if (!addResourceFeed(feed)) { |
|
446 |
break; |
|
447 |
} |
|
448 |
} |
|
449 |
} |
|
450 |
} |
|
451 |
} |
|
452 |
// reset the update timer after each cache update |
|
453 |
resetUpdateTimer(); |
|
454 |
} |
|
455 |
||
456 |
/** |
|
457 |
* Check news and resource feeds to make sure they are valid. |
|
458 |
* Reload feeds from cache if necessary. |
|
459 |
*/ |
|
460 |
private void validateFeeds() { |
|
81 | 461 |
if ((newsFeeds == null || newsFeeds.size() == 0) && resourceFeed == null) { |
2 | 462 |
try { |
104
4ca9a303c257
Removed remote connections from UI thread during Carbide startup; fix for Bug 8917.
stechong
parents:
99
diff
changeset
|
463 |
if (!loadFeedListing(true)) { |
2 | 464 |
return; |
465 |
} |
|
466 |
||
467 |
loadFeedCache(); |
|
468 |
List<FeedType> feedInfoList = feedListingManager.getFeedInfo().getFeeds().getFeed(); |
|
469 |
for (Iterator<FeedType> iterator = feedInfoList.iterator(); iterator.hasNext();) { |
|
470 |
FeedType feedInfo = iterator.next(); |
|
471 |
URL feedUrl = new URL(feedInfo.getUrl()); |
|
472 |
CarbideSyndFeed feed = feedCacheManager.loadFeedFromCache(feedUrl); |
|
473 |
if (feed != null) { |
|
474 |
if (FeedInfoManager.isNewsFeed(feedInfo)) { |
|
475 |
if (!addNewsFeed(feed)) { |
|
476 |
break; |
|
477 |
} |
|
478 |
} |
|
479 |
else |
|
480 |
if (FeedInfoManager.isResourceFeed(feedInfo)) { |
|
481 |
if (!addResourceFeed(feed)) { |
|
482 |
break; |
|
483 |
} |
|
484 |
} |
|
485 |
} |
|
486 |
} |
|
487 |
} catch (Exception e) { |
|
488 |
CarbideNewsReaderPlugin.log(e); |
|
489 |
} |
|
490 |
} |
|
491 |
} |
|
492 |
||
493 |
} |