You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@roller.apache.org by sn...@apache.org on 2006/09/14 23:27:39 UTC

svn commit: r443477 [2/2] - in /incubator/roller/trunk/sandbox/planetroller: ./ etc/ nbproject/ nbproject/private/ src/org/apache/roller/tools/ src/org/apache/roller/tools/planet/ web/ web/META-INF/ web/WEB-INF/ web/WEB-INF/lib/

Modified: incubator/roller/trunk/sandbox/planetroller/src/org/apache/roller/tools/PlanetTool.java
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/sandbox/planetroller/src/org/apache/roller/tools/PlanetTool.java?view=diff&rev=443477&r1=443476&r2=443477
==============================================================================
--- incubator/roller/trunk/sandbox/planetroller/src/org/apache/roller/tools/PlanetTool.java (original)
+++ incubator/roller/trunk/sandbox/planetroller/src/org/apache/roller/tools/PlanetTool.java Thu Sep 14 14:27:38 2006
@@ -1,5 +1,41 @@
 /*
- * Copyright 2005 David M Johnson
+* Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  The ASF licenses this file to You
+* under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.roller.tools;
+
+import com.sun.syndication.feed.synd.SyndEntry;
+import com.sun.syndication.feed.synd.SyndFeed;
+import com.sun.syndication.fetcher.FeedFetcher;
+import com.sun.syndication.fetcher.impl.FeedFetcherCache;
+import com.sun.syndication.fetcher.impl.HttpURLFeedFetcher;
+import com.sun.syndication.fetcher.impl.SyndFeedInfo;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.sql.Timestamp;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Comparator;
+import java.util.Date;
+/*
+ * Copyright 2005 Roller project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,21 +49,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Date;
+
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
+import org.apache.commons.lang.StringUtils;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.roller.pojos.PlanetConfigData;
+import org.apache.roller.pojos.PlanetEntryData;
+import org.apache.roller.pojos.PlanetGroupData;
+import org.apache.roller.pojos.PlanetSubscriptionData;
+import org.apache.roller.util.Utilities;
+import org.apache.roller.util.rome.DiskFeedInfoCache;
 import org.apache.velocity.VelocityContext;
 import org.apache.velocity.app.VelocityEngine;
 import org.apache.velocity.texen.Generator;
@@ -36,162 +74,132 @@
 import org.jdom.JDOMException;
 import org.jdom.input.SAXBuilder;
 import org.jdom.xpath.XPath;
-import org.apache.roller.RollerException;
-import org.apache.roller.business.PlanetManagerImpl;
-import org.apache.roller.pojos.PlanetConfigData;
-import org.apache.roller.pojos.PlanetEntryData;
-import org.apache.roller.pojos.PlanetGroupData;
-import org.apache.roller.pojos.PlanetSubscriptionData;
-import org.apache.roller.util.Utilities;
+
 
 
 /**
- * Utility that aggregates multiple newsfeeds using Rome Fetcher and calls 
+ * Utility that aggregates multiple newsfeeds using Rome Fetcher and calls
  * Velocity Texen control template generate files (HTML, RSS, OPML, etc.).
  * Does everything in memory; no database storage is used.
- * <p />
- * Written for Blogs, Wikis, and Feeds in Action and designed for use outside 
- * of Roller.
  *
  * @author David M Johnson
  */
-public class PlanetTool extends PlanetManagerImpl
-{
-    private static Log logger = 
-        LogFactory.getFactory().getInstance(PlanetTool.class);
-
+public class PlanetTool {
+    private static Log logger =
+            LogFactory.getFactory().getInstance(PlanetTool.class);
+    
     protected PlanetConfigData config = null;
     protected Map subsByURL = new HashMap(); // keys are URL strings
     protected Map groupsByHandle = new HashMap(); // keys are handle strings
     protected Map aggregationsByGroup = new HashMap(); // keys are GroupData objects
     
-    /** 
-     * Construct by reading confuration JDOM Document. 
+    /**
+     * Construct by reading confuration JDOM Document.
      */
-    public PlanetTool(Document doc) throws RollerException
-    {
-        try
-        {
+    public PlanetTool(Document doc) throws Exception {
+        try {
             initFromXML(doc);
-        }
-        catch (JDOMException e)
-        {
-            throw new RollerException("Extracting config from parsed XML", e);
+        } catch (JDOMException e) {
+            throw new Exception("Extracting config from parsed XML", e);
         }
     }
-
+    
     /**
      * Call Texen control template specified by configuration to generate files.
      */
-    public void generatePlanet() throws RollerException
-    {
-        try
-        {
+    public void generatePlanet() throws Exception {
+        try {
             VelocityEngine engine = new VelocityEngine();
             engine.setProperty("resource.loader","file");
             engine.setProperty("file.resource.loader.class",
-                 "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
-            engine.setProperty("file.resource.loader.path", 
-                 getConfiguration().getTemplateDir());
+                    "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
+            engine.setProperty("file.resource.loader.path",
+                    getConfiguration().getTemplateDir());
             engine.init();
             
             VelocityContext context = new VelocityContext();
             context.put("date", new Date());
             context.put("utilities", new Utilities());
             context.put("planet", this);
-
+            
+            File outputDir = new File(getConfiguration().getOutputDir());
+            if (!outputDir.exists()) outputDir.mkdirs();
+            
             Generator generator = Generator.getInstance();
             generator.setVelocityEngine(engine);
             generator.setOutputEncoding("utf-8");
             generator.setInputEncoding("utf-8");
             generator.setOutputPath(getConfiguration().getOutputDir());
             generator.setTemplatePath(getConfiguration().getTemplateDir());
-            generator.parse(config.getMainPage(), context);           
+            generator.parse(config.getMainPage(), context);
             generator.shutdown();
-        }
-        catch (Exception e)
-        {
+        } catch (Exception e) {
             e.printStackTrace();
-            throw new RollerException("Writing planet files",e);
+            throw new Exception("Writing planet files",e);
         }
     }
     
-    public void saveConfiguration(PlanetConfigData config) throws RollerException
-    {
+    public void saveConfiguration(PlanetConfigData config) throws Exception {
         this.config = config;
     }
-
-    public void saveGroup(PlanetGroupData sub) throws RollerException
-    {
+    
+    public void saveGroup(PlanetGroupData sub) throws Exception {
         groupsByHandle.put(sub.getHandle(), sub);
     }
-
-    public void saveSubscription(PlanetSubscriptionData sub) throws RollerException
-    {
-        subsByURL.put(sub.getFeedUrl(), sub);
+    
+    public void saveSubscription(PlanetSubscriptionData sub) throws Exception {
+        subsByURL.put(sub.getFeedURL(), sub);
     }
     
-    public void saveEntry(PlanetEntryData entry) throws RollerException
-    {
+    public void saveEntry(PlanetEntryData entry) throws Exception {
         // no-op
     }
-
-    public PlanetSubscriptionData getSubscription(String feedUrl) 
-        throws RollerException
-    {
+    
+    public PlanetSubscriptionData getSubscription(String feedUrl)
+    throws Exception {
         return (PlanetSubscriptionData)subsByURL.get(feedUrl);
     }
-
-    public PlanetConfigData getConfiguration() throws RollerException
-    {
+    
+    public PlanetConfigData getConfiguration() throws Exception {
         return config;
     }
-
-    public List getGroupHandles() throws RollerException
-    {
+    
+    public List getGroupHandles() throws Exception {
         return new ArrayList(groupsByHandle.keySet());
     }
-
-    public List getGroups() throws RollerException
-    {
+    
+    public List getGroups() throws Exception {
         return new ArrayList(groupsByHandle.values());
     }
-
-    public PlanetGroupData getGroup(String handle) throws RollerException
-    {
+    
+    public PlanetGroupData getGroup(String handle) throws Exception {
         return (PlanetGroupData)groupsByHandle.get(handle);
     }
     
     public List getAggregation(
-            PlanetGroupData group, int maxEntries) throws RollerException
-    {
+            PlanetGroupData group, int maxEntries) throws Exception {
         long startTime = System.currentTimeMillis();
         List aggregation = null;
-        try
-        {
+        try {
             // Get aggregation from cache
             aggregation = (List)aggregationsByGroup.get(group);
-            if (aggregation == null) 
-            {
+            if (aggregation == null) {
                 // No aggregation found in cache, let's create a new one
                 aggregation = new ArrayList();
                 
                 // Comparator to help us create reverse chrono sorted list of entries
-                Comparator entryDateComparator = new EntryDateComparator(); 
+                Comparator entryDateComparator = new EntryDateComparator();
                 
                 // Add all of group's subscription's entries to ordered collection
                 Set sortedEntries = new TreeSet(entryDateComparator);
                 Iterator subs = group.getSubscriptions().iterator();
-                while (subs.hasNext())
-                {
+                while (subs.hasNext()) {
                     PlanetSubscriptionData sub = (PlanetSubscriptionData)subs.next();
                     Iterator candidates = sub.getEntries().iterator();
-                    while (candidates.hasNext())
-                    {
+                    while (candidates.hasNext()) {
                         PlanetEntryData candidate = (PlanetEntryData) candidates.next();
-                        if (group.qualified(candidate))
-                        {
-                            sortedEntries.add(candidate);                        
+                        if (group.qualified(candidate)) {
+                            sortedEntries.add(candidate);
                         }
                     }
                 }
@@ -199,131 +207,111 @@
                 // Throw away all but first maxEntris of our new entry list
                 int count = 0;
                 Iterator entries = sortedEntries.iterator();
-                while (entries.hasNext() && count++ < maxEntries)
-                {
+                while (entries.hasNext() && count++ < maxEntries) {
                     aggregation.add(entries.next());
                 }
                 aggregationsByGroup.put(group, aggregation);
             }
-        }
-        catch (Exception e)
-        {
+        } catch (Exception e) {
             logger.error("ERROR: building aggregation for: "+group.getHandle(), e);
-            throw new RollerException(e);
+            throw new Exception(e);
         }
         long endTime = System.currentTimeMillis();
         logger.info("Generated aggregation in "
                 +((endTime-startTime)/1000.0)+" seconds");
-        return aggregation; 
+        return aggregation;
     }
     
-    public void deleteEntry(PlanetEntryData entry) throws RollerException
-    {
+    public void deleteEntry(PlanetEntryData entry) throws Exception {
         // no-op
     }
-
-    public void deleteGroup(PlanetGroupData group) throws RollerException
-    {
+    
+    public void deleteGroup(PlanetGroupData group) throws Exception {
         // no-op
     }
-
-    public void deleteSubscription(PlanetSubscriptionData group) throws RollerException
-    {
+    
+    public void deleteSubscription(PlanetSubscriptionData group) throws Exception {
         // no-op
     }
     
-    public List getTopSubscriptions(int max) throws RollerException
-    {
-        throw new RuntimeException("Not implemented");
+    public void clearCachedAggregations() {
+        // no-op
     }
     
-    public List getTopSubscriptions(PlanetGroupData group, int max) throws RollerException
-    {
-        throw new RuntimeException("Not implemented");
+    public List getTopSubscriptions(int max) throws Exception {
+        throw new RuntimeException("NOT SUPPORTED");
     }
-
-    public List getTopSubscriptions(PlanetGroupData group, int max) throws RollerException
-    {
-        throw new RuntimeException("Not implemented");
+    
+    public List getTopSubscriptions(PlanetGroupData group, int max) throws Exception {
+        throw new RuntimeException("NOT SUPPORTED");
     }
-
-    public PlanetSubscriptionData getSubscriptionById(String id) 
-        throws RollerException
-    {
+    
+    public PlanetSubscriptionData getSubscriptionById(String id) throws Exception {
         throw new RuntimeException("NOT SUPPORTED");
     }
-
-    public PlanetGroupData getGroupById(String id) 
-        throws RollerException
-    {
+    
+    public PlanetGroupData getGroupById(String id)
+    throws Exception {
         throw new RuntimeException("NOT SUPPORTED");
     }
     
-    public List getAggregation(int maxEntries) throws RollerException
-    {
+    public List getAggregation(int maxEntries) throws Exception {
+        throw new RuntimeException("NOT SUPPORTED");
+    }
+    
+    public Date getLastUpdated() {
+        throw new RuntimeException("NOT SUPPORTED");
+    }
+    
+    public Date getLastUpdated(PlanetGroupData group) {
         throw new RuntimeException("NOT SUPPORTED");
     }
-
     //--------------------------------------------------------------------- console
     
-    public static void main(String[] args) 
-    {       
+    public static void main(String[] args) {
         String success = "Planet complete!";
         String error = null;
         Exception traceWorthy = null;
-        String fileName = "planet-config.xml"; 
-        if (args.length == 1)
-        {
+        String fileName = "planet-config.xml";
+        if (args.length == 1) {
             fileName = args[0];
         }
-        try
-        {
+        try {
             SAXBuilder builder = new SAXBuilder();
             Document doc = builder.build(new FileInputStream(fileName));
-            PlanetTool planet = new PlanetTool(doc);         
+            PlanetTool planet = new PlanetTool(doc);
             planet.refreshEntries();
             planet.generatePlanet();
             System.out.println(success);
             System.exit(0);
-        }
-        catch (FileNotFoundException fnfe)
-        {
+        } catch (FileNotFoundException fnfe) {
             error = "Configuration file ["+fileName+"] not found";
-        }
-        catch (JDOMException jde)
-        {
+        } catch (JDOMException jde) {
             error = "Error parsing configuration file ["+fileName+"]";
             traceWorthy = jde;
-        }
-        catch (IOException ioe)
-        {
+        } catch (IOException ioe) {
             error = "IO error. Using configuration file ["+fileName+"]";
             traceWorthy = ioe;
-        }
-        catch (RollerException re)
-        {
+        } catch (Exception re) {
             error = re.getMessage();
-        }    
-        if (error != null) 
-        {
+        }
+        if (error != null) {
             System.out.println(error);
             if (traceWorthy != null) traceWorthy.printStackTrace();
             System.exit(-1);
         }
     }
     
-    public Iterator getAllSubscriptions()
-    {
+    public Iterator getAllSubscriptions() {
         return subsByURL.values().iterator();
     }
     
     //-------------------------------------------------------------------- privates
-
+    
     /**
      * Load config data from XML using XPath.
      */
-    protected void initFromXML(Document doc) throws RollerException, JDOMException
-    {
+    protected void initFromXML(Document doc) throws Exception, JDOMException {
         Map subsByID = new HashMap();
         Element elem = doc.getRootElement();
         
@@ -333,7 +321,7 @@
         config.setGroupPage(  getString(elem,"/planet-config/group-page"));
         config.setAdminName(  getString(elem,"/planet-config/admin-name"));
         config.setAdminEmail( getString(elem,"/planet-config/admin-email"));
-        config.setSiteUrl(    getString(elem,"/planet-config/site-url"));
+        config.setSiteURL(    getString(elem,"/planet-config/site-url"));
         config.setOutputDir(  getString(elem,"/planet-config/output-dir"));
         config.setTemplateDir(getString(elem,"/planet-config/template-dir"));
         config.setTitle(      getString(elem,"/planet-config/title"));
@@ -341,24 +329,22 @@
         
         XPath subsPath = XPath.newInstance("/planet-config/subscription");
         Iterator subs = subsPath.selectNodes(doc).iterator();
-        while (subs.hasNext()) 
-        {
+        while (subs.hasNext()) {
             Element subElem = (Element)subs.next();
             PlanetSubscriptionData sub = new PlanetSubscriptionData();
             String id = subElem.getAttributeValue("id");
             sub.setTitle(   getString(subElem, "title"));
             sub.setAuthor(  getString(subElem, "author"));
-            sub.setFeedUrl( getString(subElem, "feed-url"));
-            sub.setSiteUrl( getString(subElem, "site-url"));           
-            subsByURL.put(sub.getFeedUrl(), sub);
+            sub.setFeedURL( getString(subElem, "feed-url"));
+            sub.setSiteURL( getString(subElem, "site-url"));
+            subsByURL.put(sub.getFeedURL(), sub);
             subsByID.put(id, sub);
         }
         logger.info("Found "+subsByID.size()+" subscriptions");
-                
+        
         XPath groupsPath = XPath.newInstance("/planet-config/group");
         Iterator groups = groupsPath.selectNodes(doc).iterator();
-        while (groups.hasNext()) 
-        {
+        while (groups.hasNext()) {
             Element groupElem = (Element)groups.next();
             PlanetGroupData group = new PlanetGroupData();
             group.setHandle(groupElem.getAttributeValue("handle"));
@@ -367,36 +353,31 @@
             group.setMaxFeedEntries( getInt(   groupElem, "max-feed-entries"));
             group.setMaxPageEntries( getInt(   groupElem, "max-page-entries"));
             group.setCategoryRestriction(
-                                     getString(groupElem, "category-restriction"));
-
+                    getString(groupElem, "category-restriction"));
+            
             XPath refsPath = XPath.newInstance("subscription-ref");
             Iterator refs = refsPath.selectNodes(groupElem).iterator();
-            while (refs.hasNext())
-            {
+            while (refs.hasNext()) {
                 Element refElem = (Element)refs.next();
                 String includeAll = refElem.getAttributeValue("include-all");
-                if (includeAll != null && includeAll.equals("true"))
-                {
+                if (includeAll != null && includeAll.equals("true")) {
                     //group.getSubscriptions().addAll(subsByID.values());
                     group.addSubscriptions(subsByID.values());
-                }
-                else 
-                {
+                } else {
                     String refid = refElem.getAttributeValue("refid");
                     PlanetSubscriptionData sub = (PlanetSubscriptionData)subsByID.get(refid);
-                    if (sub == null) 
-                    {
-                        throw new RollerException("No such subscription ["+refid+"]");
+                    if (sub == null) {
+                        throw new Exception("No such subscription ["+refid+"]");
                     }
                     //group.getSubscriptions().add(sub);
-                    group.addSubscription(sub); 
+                    group.addSubscription(sub);
                 }
             }
             groupsByHandle.put(group.getHandle(), group);
         }
         logger.info("Found "+groupsByHandle.size()+" groups");
     }
-
+    
     //--------------------------------------------------------------- utilities
     
     protected String getString(Element elem, String path) throws JDOMException {
@@ -411,36 +392,190 @@
         return e!=null ? Integer.parseInt(e.getText()) : 0;
     }
     
-    public class EntryDateComparator implements Comparator
-    {
-        public int compare(Object o1, Object o2)
-        {
+    public class EntryDateComparator implements Comparator {
+        public int compare(Object o1, Object o2) {
             PlanetEntryData e1 = (PlanetEntryData)o1;
             PlanetEntryData e2 = (PlanetEntryData)o2;
-            if (e1.getPublished() != null && e2.getPublished() != null)
-            {
-                return e2.getPublished().compareTo(e1.getPublished());
+            if (e1.getPubTime() != null && e2.getPubTime() != null) {
+                return e2.getPubTime().compareTo(e1.getPubTime());
             }
-            if (e1.getPublished() == null)
-            {
-                logger.warn("Entry missing pubDate in sub: " 
-                        + e1.getSubscription().getFeedUrl());
+            if (e1.getPubTime() == null) {
+                logger.warn("Entry missing pubDate in sub: "
+                        + e1.getSubscription().getFeedURL());
+            }
+            if (e2.getPubTime() == null) {
+                logger.warn("Entry missing pubDate in sub: "
+                        + e2.getSubscription().getFeedURL());
             }
-            if (e2.getPublished() == null)
-            {
-                logger.warn("Entry missing pubDate in sub: " 
-                        + e2.getSubscription().getFeedUrl());
-            }            
             return 0;
-        }            
+        }
     }
-
+    
     /**
      * Total number of subscriptions.
      */
-    public int getSubscriptionCount() throws RollerException 
-    {
+    public int getSubscriptionCount() throws Exception {
         return this.subsByURL.size();
-    };
+    }
+        
+    public void refreshEntries() throws Exception {
+        Date now = new Date();
+        long startTime = System.currentTimeMillis();
+        PlanetConfigData config = getConfiguration();
+        if (config == null || config.getCacheDir() == null) {
+            logger.warn("Planet cache directory not set, aborting refresh");
+            return;
+        }
+        FeedFetcherCache feedInfoCache =
+                new DiskFeedInfoCache(config.getCacheDir());
+        
+        if (config.getProxyHost()!=null && config.getProxyPort() > 0) {
+            System.setProperty("proxySet", "true");
+            System.setProperty("http.proxyHost", config.getProxyHost());
+            System.setProperty("http.proxyPort",
+                    Integer.toString(config.getProxyPort()));
+        }
+        /** a hack to set 15 sec timeouts for java.net.HttpURLConnection */
+        System.setProperty("sun.net.client.defaultConnectTimeout", "15000");
+        System.setProperty("sun.net.client.defaultReadTimeout", "15000");
+        
+        FeedFetcher feedFetcher = new HttpURLFeedFetcher(feedInfoCache);
+        //FeedFetcher feedFetcher = new HttpClientFeedFetcher(feedInfoCache);
+        feedFetcher.setUsingDeltaEncoding(false);
+        feedFetcher.setUserAgent("RollerPlanetAggregator");
+                
+        // Loop through all subscriptions in the system
+        Iterator subs = getAllSubscriptions();
+        while (subs.hasNext()) {
+            long subStartTime = System.currentTimeMillis();
+            
+            // Fetch latest entries for each subscription
+            Set newEntries = null;
+            int count = 0;
+            PlanetSubscriptionData sub = (PlanetSubscriptionData)subs.next();
+            newEntries = getNewEntriesRemote(sub, feedFetcher, feedInfoCache);
+            count = newEntries.size();
+            
+            logger.debug("   Entry count: " + count);
+            if (count > 0) {
+                Iterator entryIter = sub.getEntries().iterator();
+                while (entryIter.hasNext()) {
+                    deleteEntry((PlanetEntryData)entryIter.next());
+                }
+                sub.purgeEntries();
+                sub.addEntries(newEntries);
+            }
+            long subEndTime = System.currentTimeMillis();
+            logger.info("   " + count + " - "
+                    + ((subEndTime-subStartTime)/1000.0)
+                    + " seconds to process (" + count + ") entries of "
+                    + sub.getFeedURL());
+        }
+        // Clear the aggregation cache
+        clearCachedAggregations();
+        
+        long endTime = System.currentTimeMillis();
+        logger.info("--- DONE --- Refreshed entries in "
+                + ((endTime-startTime)/1000.0) + " seconds");
+    }
+    
+    /**
+     * Override this if you have local feeds (i.e. feeds that you don't
+     * have to fetch via HTTP and parse with ROME).
+     */
+    protected Set getNewEntriesLocal(PlanetSubscriptionData sub,
+            FeedFetcher feedFetcher, FeedFetcherCache feedInfoCache)
+            throws Exception {
+        
+        // If you don't override, local feeds will be treated as remote feeds
+        return getNewEntriesRemote(sub, feedFetcher, feedInfoCache);
+    }
+    
+    protected Set getNewEntriesRemote(PlanetSubscriptionData sub,
+            FeedFetcher feedFetcher, FeedFetcherCache feedInfoCache)
+            throws Exception {
+        
+        Set newEntries = new TreeSet();
+        SyndFeed feed = null;
+        URL feedUrl = null;
+        Date lastUpdated = new Date();
+        try {
+            feedUrl = new URL(sub.getFeedURL());
+            logger.debug("Get feed from cache "+sub.getFeedURL());
+            feed = feedFetcher.retrieveFeed(feedUrl);
+            SyndFeedInfo feedInfo = feedInfoCache.getFeedInfo(feedUrl);
+            if (feedInfo.getLastModified() != null) {
+                long lastUpdatedLong =
+                        ((Long)feedInfo.getLastModified()).longValue();
+                if (lastUpdatedLong != 0) {
+                    lastUpdated = new Date(lastUpdatedLong);
+                }
+            }
+            Thread.sleep(100); // be nice
+        } catch (Exception e) {
+            logger.warn("ERROR parsing " + sub.getFeedURL()
+            + " : " + e.getClass().getName() + " : " + e.getMessage());
+            logger.debug(e);
+            return newEntries; // bail out
+        }
+        if (lastUpdated!=null && sub.getLastUpdated()!=null) {
+            Calendar feedCal = Calendar.getInstance();
+            feedCal.setTime(lastUpdated);
+            
+            Calendar subCal = Calendar.getInstance();
+            subCal.setTime(sub.getLastUpdated());
+            
+            if (!feedCal.after(subCal)) {
+                if (logger.isDebugEnabled()) {
+                    String msg = MessageFormat.format(
+                            "   Skipping ({0} / {1})",
+                            new Object[] {
+                        lastUpdated, sub.getLastUpdated()});
+                        logger.debug(msg);
+                }
+                return newEntries; // bail out
+            }
+        }
+        if (feed.getPublishedDate() != null) {
+            sub.setLastUpdated(feed.getPublishedDate());
+            saveSubscription(sub);
+        }
+        
+        // Kludge for Feeds without entry dates: most recent entry is given
+        // feed's last publish date (or yesterday if none exists) and earler
+        // entries are placed at once day intervals before that.
+        Calendar cal = Calendar.getInstance();
+        if (sub.getLastUpdated() != null) {
+            cal.setTime(sub.getLastUpdated());
+        } else {
+            cal.setTime(new Date());
+            cal.add(Calendar.DATE, -1);
+        }
+        
+        // Populate subscription object with new entries
+        Iterator entries = feed.getEntries().iterator();
+        while (entries.hasNext()) {
+            try {
+                SyndEntry romeEntry = (SyndEntry) entries.next();
+                PlanetEntryData entry =
+                        new PlanetEntryData(feed, romeEntry, sub);
+                if (entry.getPubTime() == null) {
+                    logger.debug(
+                            "No published date, assigning fake date for "+feedUrl);
+                    entry.setPubTime(new Timestamp(cal.getTime().getTime()));
+                }
+                if (entry.getPermalink() == null) {
+                    logger.warn("No permalink, rejecting entry from "+feedUrl);
+                } else {
+                    saveEntry(entry);
+                    newEntries.add(entry);
+                }
+                cal.add(Calendar.DATE, -1);
+            } catch (Exception e) {
+                logger.error("ERROR processing subscription entry", e);
+            }
+        }
+        return newEntries;
+    }
 }
 

Added: incubator/roller/trunk/sandbox/planetroller/web/META-INF/context.xml
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/sandbox/planetroller/web/META-INF/context.xml?view=auto&rev=443477
==============================================================================
--- incubator/roller/trunk/sandbox/planetroller/web/META-INF/context.xml (added)
+++ incubator/roller/trunk/sandbox/planetroller/web/META-INF/context.xml Thu Sep 14 14:27:38 2006
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Context path="/planetroller"/>

Added: incubator/roller/trunk/sandbox/planetroller/web/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/sandbox/planetroller/web/WEB-INF/web.xml?view=auto&rev=443477
==============================================================================
--- incubator/roller/trunk/sandbox/planetroller/web/WEB-INF/web.xml (added)
+++ incubator/roller/trunk/sandbox/planetroller/web/WEB-INF/web.xml Thu Sep 14 14:27:38 2006
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<web-app  xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
+    <display-name>Planet Roller</display-name>
+    
+    
+</web-app>

Added: incubator/roller/trunk/sandbox/planetroller/web/index.jsp
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/sandbox/planetroller/web/index.jsp?view=auto&rev=443477
==============================================================================
--- incubator/roller/trunk/sandbox/planetroller/web/index.jsp (added)
+++ incubator/roller/trunk/sandbox/planetroller/web/index.jsp Thu Sep 14 14:27:38 2006
@@ -0,0 +1,38 @@
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+<%--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  The ASF licenses this file to You
+  under the Apache License, Version 2.0 (the "License"); you may not
+  use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.  For additional information regarding
+  copyright in this work, please see the NOTICE file in the top level
+  directory of this distribution.
+--%>
+<%--
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 
+--%>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <title>Coming soon...</title>
+    </head>
+    <body>
+
+    <h1>Planet Roller webapp</h1>
+    
+    
+    </body>
+</html>