You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@roller.apache.org by cl...@apache.org on 2006/12/05 01:26:21 UTC

svn commit: r482428 [4/6] - in /incubator/roller/trunk/sandbox/jdobackend: src/org/apache/roller/business/datamapper/ src/org/apache/roller/business/jdo/ src/org/apache/roller/business/jpa/ src/org/apache/roller/planet/ src/org/apache/roller/planet/poj...

Modified: incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/business/datamapper/DatamapperWeblogManagerImpl.java
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/business/datamapper/DatamapperWeblogManagerImpl.java?view=diff&rev=482428&r1=482427&r2=482428
==============================================================================
--- incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/business/datamapper/DatamapperWeblogManagerImpl.java (original)
+++ incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/business/datamapper/DatamapperWeblogManagerImpl.java Mon Dec  4 16:26:15 2006
@@ -18,17 +18,37 @@
  */
 package org.apache.roller.business.datamapper;
 
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
+import org.apache.commons.collections.comparators.ReverseComparator;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.roller.RollerException;
-import org.apache.roller.model.WeblogManager;
-import org.apache.roller.pojos.Assoc;
+import org.apache.roller.business.RollerFactory;
+import org.apache.roller.business.WeblogManager;
 import org.apache.roller.pojos.CommentData;
+import org.apache.roller.pojos.RefererData;
+import org.apache.roller.pojos.UserData;
 import org.apache.roller.pojos.WeblogCategoryData;
 import org.apache.roller.pojos.WeblogEntryData;
+import org.apache.roller.pojos.WeblogEntryTagData;
 import org.apache.roller.pojos.WebsiteData;
-import org.apache.roller.pojos.UserData;
+import org.apache.roller.pojos.HitCountData;
+import org.apache.roller.pojos.StatCount;
+import org.apache.roller.pojos.WeblogEntryTagAggregateData;
+import org.apache.roller.pojos.TagStat;
+import org.apache.roller.pojos.TagStatComparator;
+import org.apache.roller.util.DateUtil;
+import org.apache.roller.util.Utilities;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.Collections;
 
 /*
  * DatamapperWeblogManagerImpl.java
@@ -38,178 +58,385 @@
  */
 public class DatamapperWeblogManagerImpl implements WeblogManager {
 
+    private static Log log = LogFactory.getLog(DatamapperWeblogManagerImpl.class);
+    
     private DatamapperPersistenceStrategy strategy;
 
+    /* inline creation of reverse comparator, anonymous inner class */
+    private Comparator reverseComparator = new ReverseComparator();
+
+    private Comparator tagStatComparator = new TagStatComparator();
+
     public DatamapperWeblogManagerImpl
             (DatamapperPersistenceStrategy strategy) {
+        log.debug("Instantiating Datamapper Weblog Manager");
+
         this.strategy = strategy;
     }
 
     /**
-     * Save weblog entry.
+     * Save weblog category.
      */
-    public void saveWeblogEntry(WeblogEntryData entry) throws RollerException {
-        //To change body of implemented methods use File | Settings | File Templates.
+    public void saveWeblogCategory(WeblogCategoryData cat)
+            throws RollerException {
+        if(this.isDuplicateWeblogCategoryName(cat)) {
+            throw new RollerException("Duplicate category name");
+        }
+        
+        // update weblog last modified date.  date updated by saveWebsite()
+        RollerFactory.getRoller().getUserManager().saveWebsite(cat.getWebsite());
+        
+        this.strategy.store(cat);
     }
 
-    /**
-     * Remove weblog entry.
-     */
-    public void removeWeblogEntry(WeblogEntryData entry)
+    public void moveWeblogCategory(WeblogCategoryData srcCat, WeblogCategoryData destCat)
             throws RollerException {
-        //To change body of implemented methods use File | Settings | File Templates.
+        
+        // TODO: this check should be made before calling this method?
+        if (destCat.descendentOf(srcCat)) {
+            throw new RollerException(
+                    "ERROR cannot move parent category into it's own child");
+        }
+        
+        log.debug("Moving category "+srcCat.getPath()+" under "+destCat.getPath());
+        
+        srcCat.setParent(destCat);
+        if("/".equals(destCat.getPath())) {
+            srcCat.setPath("/"+srcCat.getName());
+        } else {
+            srcCat.setPath(destCat.getPath() + "/" + srcCat.getName());
+        }
+        saveWeblogCategory(srcCat);
+        
+        // the main work to be done for a category move is to update the 
+        // path attribute of the category and all descendent categories
+        updatePathTree(srcCat);
+    }
+    
+    
+    // updates the paths of all descendents of the given category
+    private void updatePathTree(WeblogCategoryData cat) throws RollerException {
+        
+        log.debug("Updating path tree for category "+cat.getPath());
+        
+        WeblogCategoryData childCat = null;
+        Iterator childCats = cat.getWeblogCategories().iterator();
+        while(childCats.hasNext()) {
+            childCat = (WeblogCategoryData) childCats.next();
+            
+            log.debug("OLD child category path was "+childCat.getPath());
+            
+            // update path and save
+            if("/".equals(cat.getPath())) {
+                childCat.setPath("/" + childCat.getName());
+            } else {
+                childCat.setPath(cat.getPath() + "/" + childCat.getName());
+            }
+            saveWeblogCategory(childCat);
+            
+            log.debug("NEW child category path is "+ childCat.getPath());
+            
+            // then make recursive call to update this cats children
+            updatePathTree(childCat);
+        }
     }
-
+    
     /**
-     * Get weblog entry by id.
+     * Remove weblog category.
      */
-    public WeblogEntryData getWeblogEntry(String id) throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public void removeWeblogCategory(WeblogCategoryData cat)
+            throws RollerException {
+        if(cat.retrieveWeblogEntries(true).size() > 0) {
+            throw new RollerException("Cannot remove category with entries");
+        }
+        
+        // remove cat
+        this.strategy.remove(cat);
+        
+        // update website default cats if needed
+        if(cat.getWebsite().getBloggerCategory().equals(cat)) {
+            WeblogCategoryData rootCat = this.getRootWeblogCategory(cat.getWebsite());
+            cat.getWebsite().setBloggerCategory(rootCat);
+            this.strategy.store(cat.getWebsite());
+        }
+        
+        if(cat.getWebsite().getDefaultCategory().equals(cat)) {
+            WeblogCategoryData rootCat = this.getRootWeblogCategory(cat.getWebsite());
+            cat.getWebsite().setDefaultCategory(rootCat);
+            this.strategy.store(cat.getWebsite());
+        }
+        
+        // update weblog last modified date.  date updated by saveWebsite()
+        RollerFactory.getRoller().getUserManager().saveWebsite(cat.getWebsite());
     }
 
     /**
-     * Get weblog entry by anchor.
+     * Recategorize all entries with one category to another.
      */
-    public WeblogEntryData getWeblogEntryByAnchor(WebsiteData website,
-            String anchor) throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public void moveWeblogCategoryContents(WeblogCategoryData srcCat, WeblogCategoryData destCat)
+            throws RollerException {
+                
+        // TODO: this check should be made before calling this method?
+        if (destCat.descendentOf(srcCat)) {
+            throw new RollerException(
+                    "ERROR cannot move parent category into it's own child");
+        }
+        
+        // get all entries in category and subcats
+        List results = srcCat.retrieveWeblogEntries(true);
+        
+        // Loop through entries in src cat, assign them to dest cat
+        Iterator iter = results.iterator();
+        WebsiteData website = destCat.getWebsite();
+        while (iter.hasNext()) {
+            WeblogEntryData entry = (WeblogEntryData) iter.next();
+            entry.setCategory(destCat);
+            entry.setWebsite(website);
+            this.strategy.store(entry);
+        }
+        
+        // Make sure website's default and bloggerapi categories
+        // are valid after the move
+        
+        if (srcCat.getWebsite().getDefaultCategory().getId().equals(srcCat.getId())
+        || srcCat.getWebsite().getDefaultCategory().descendentOf(srcCat)) {
+            srcCat.getWebsite().setDefaultCategory(destCat);
+            this.strategy.store(srcCat.getWebsite());
+        }
+        
+        if (srcCat.getWebsite().getBloggerCategory().getId().equals(srcCat.getId())
+        || srcCat.getWebsite().getBloggerCategory().descendentOf(srcCat)) {
+            srcCat.getWebsite().setBloggerCategory(destCat);
+            this.strategy.store(srcCat.getWebsite());
+        }
     }
 
     /**
-     * Get WeblogEntries by offset/length as list in reverse chronological
-     * order. The range offset and list arguments enable paging through query
-     * results.
-     * @param website Weblog or null to get for all weblogs.
-     * @param user User or null to get for all users.
-     * @param startDate Start date or null for no start date.
-     * @param endDate End date or null for no end date.
-     * @param catName Category path or null for all categories.
-     * @param status Status of DRAFT, PENDING, PUBLISHED or null for all
-     * @param sortBy Sort by either 'pubTime' or 'updateTime' (null for
-     * pubTime)
-     * @param offset Offset into results for paging
-     * @param range Max comments to return (or -1 for no limit)
-     * @return List of WeblogEntryData objects in reverse chrono order.
-     * @throws org.apache.roller.RollerException
+     * Save comment.
      */
-    public List getWeblogEntries(WebsiteData website, UserData user,
-            Date startDate, Date endDate, String catName,
-            String status, String sortBy, String locale,
-            int offset, int range) throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public void saveComment(CommentData comment) throws RollerException {
+        this.strategy.store(comment);
+        
+        // update weblog last modified date.  date updated by saveWebsite()
+        RollerFactory.getRoller().getUserManager().saveWebsite(comment.getWeblogEntry().getWebsite());
     }
 
     /**
-     * Get Weblog Entries grouped by day. This method returns a Map that
-     * contains Lists, each List contains WeblogEntryData objects, and the Lists
-     * are keyed by Date objects.
-     * @param website Weblog or null to get for all weblogs.
-     * @param startDate Start date or null for no start date.
-     * @param endDate End date or null for no end date.
-     * @param catName Category path or null for all categories.
-     * @param status Status of DRAFT, PENDING, PUBLISHED or null for all
-     * @param offset Offset into results for paging
-     * @param range Max comments to return (or -1 for no limit)
-     * @return Map of Lists, keyed by Date, and containing WeblogEntryData.
-     * @throws org.apache.roller.RollerException
+     * Remove comment.
      */
-    public Map getWeblogEntryObjectMap(WebsiteData website,
-            Date startDate, Date endDate, String catName,
-            String status, String locale, int offset,
-            int range) throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public void removeComment(CommentData comment) throws RollerException {
+        this.strategy.remove(comment);
+        
+        // update weblog last modified date.  date updated by saveWebsite()
+        RollerFactory.getRoller().getUserManager().saveWebsite(comment.getWeblogEntry().getWebsite());
     }
 
     /**
-     * Get Weblog Entry date strings grouped by day. This method returns a Map
-     * that contains Lists, each List contains YYYYMMDD date strings objects,
-     * and the Lists are keyed by Date objects.
-     * @param website Weblog or null to get for all weblogs.
-     * @param startDate Start date or null for no start date.
-     * @param endDate End date or null for no end date.
-     * @param catName Category path or null for all categories.
-     * @param status Status of DRAFT, PENDING, PUBLISHED or null for all
-     * @param offset Offset into results for paging
-     * @param range Max comments to return (or -1 for no limit)
-     * @return Map of Lists, keyed by Date, and containing date strings.
-     * @throws org.apache.roller.RollerException
+     * Save weblog entry.
      */
-    public Map getWeblogEntryStringMap(WebsiteData website,
-            Date startDate, Date endDate, String catName,
-            String status, String locale, int offset,
-            int range) throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    // TODO: perhaps the createAnchor() and queuePings() items should go outside this method?
+    public void saveWeblogEntry(WeblogEntryData entry) throws RollerException {
+        if (entry.getAnchor() == null || entry.getAnchor().trim().equals("")) {
+            entry.setAnchor(this.createAnchor(entry));
+        }
+        
+        for(Iterator it = entry.getAddedTags().iterator(); it.hasNext();) {
+            String name = (String) it.next();
+            updateTagCount(name, entry.getWebsite(), 1);
+        }
+        
+        for(Iterator it = entry.getRemovedTags().iterator(); it.hasNext();) {
+            String name = (String) it.next();
+            updateTagCount(name, entry.getWebsite(), -1);
+        }  
+        
+        this.strategy.store(entry);
+        
+        // update weblog last modified date.  date updated by saveWebsite()
+        if(entry.isPublished()) {
+            RollerFactory.getRoller().getUserManager().saveWebsite(entry.getWebsite());
+        }
+        
+        if(entry.isPublished()) {
+            // Queue applicable pings for this update.
+            RollerFactory.getRoller().getAutopingManager().queueApplicableAutoPings(entry);
+        }
     }
 
     /**
-     * Get weblog entries with given category or, optionally, any sub-category
-     * of that category.
-     * @param cat Category
-     * @param subcats True if sub-categories are to be fetched
-     * @return List of weblog entries in category
+     * Remove weblog entry.
      */
-    public List getWeblogEntries(WeblogCategoryData cat, boolean subcats)
+    public void removeWeblogEntry(WeblogEntryData entry)
             throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+        List referers = (List) strategy.newQuery(RefererData.class, "RefererData.getByWeblogEntry").execute(entry);
+        for (Iterator iter = referers.iterator(); iter.hasNext();) {
+            RefererData referer = (RefererData) iter.next();
+            this.strategy.remove(referer);
+        }
+        
+        // remove comments
+        List comments = getComments(
+                null,  // website
+                entry,
+                null,  // search String
+                null,  // startDate
+                null,  // endDate
+                null,  // pending
+                null,  // approved
+                null,  // spam
+                true,  // reverse chrono order (not that it matters)
+                0,     // offset
+                -1);   // no limit
+        Iterator commentsIT = comments.iterator();
+        while (commentsIT.hasNext()) {
+            this.strategy.remove((CommentData) commentsIT.next());
+        }
+        
+        // remove tags aggregates
+        if(entry.getTags() != null) {
+            for(Iterator it = entry.getTags().iterator(); it.hasNext(); ) {
+                WeblogEntryTagData tag = (WeblogEntryTagData) it.next();
+                updateTagCount(tag.getName(), entry.getWebsite(), -1);
+            }
+        }
+        
+        // remove entry
+        this.strategy.remove(entry);
+        
+        // update weblog last modified date.  date updated by saveWebsite()
+        if(entry.isPublished()) {
+            RollerFactory.getRoller().getUserManager().saveWebsite(entry.getWebsite());
+        }
+        
+        // TODO: remove entry from cache mapping
     }
 
-    /**
-     * Get weblog enties ordered by descending number of comments.
-     * @param website Weblog or null to get for all weblogs.
-     * @param startDate Start date or null for no start date.
-     * @param endDate End date or null for no end date.
-     * @param offset Offset into results for paging
-     * @param length Max comments to return (or -1 for no limit)
-     * @return List of WeblogEntryData objects.
-     */
-    public List getMostCommentedWeblogEntries(WebsiteData website,
-            Date startDate, Date endDate, int offset,
-            int length) throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public List getNextPrevEntries(WeblogEntryData current, String catName, 
+                                   String locale, int maxEntries, boolean next)
+            throws RollerException {
+        DatamapperQuery query = null;
+        List results = null;
+        WeblogCategoryData category = null;
+        
+        if (catName != null && !catName.trim().equals("/")) {
+            category = getWeblogCategoryByPath(current.getWebsite(), null, catName);
+        }
+        
+        if (category != null) {
+            if (locale != null) {
+                if (next) {
+                    query = strategy.newQuery(WeblogEntryData.class, 
+                            "WeblogEntryData.getByWebsite&Status&PubTimeGreater&Category&LocaleLikeOrderByPubTimeAsc");
+                    if (maxEntries > -1) query.setRange(0, maxEntries);
+                    results = (List) query.execute(new Object[] {current.getWebsite(), WeblogEntryData.PUBLISHED, current.getPubTime(), category, locale});
+                } else {
+                    query = strategy.newQuery(WeblogEntryData.class, 
+                            "WeblogEntryData.getByWebsite&Status&PubTimeLess&Category&LocaleLikeOrderByPubTimeDesc");
+                    if (maxEntries > -1) query.setRange(0, maxEntries);
+                    results = (List) query.execute(new Object[] {current.getWebsite(), WeblogEntryData.PUBLISHED, current.getPubTime(), category, locale});  
+                }
+            } else {
+                if (next) {
+                    results = (List) strategy.newQuery(WeblogEntryData.class, 
+                            "WeblogEntryData.getByWebsite&Status&PubTimeGreater&CategoryOrderByPubTimeAsc");
+                    if (maxEntries > -1) query.setRange(0, maxEntries);
+                    results = (List) query.execute(new Object[] {current.getWebsite(), WeblogEntryData.PUBLISHED, current.getPubTime(), category});
+                
+                } else {
+                    results = (List) strategy.newQuery(WeblogEntryData.class, 
+                            "WeblogEntryData.getByWebsite&Status&PubTimeLess&CategoryOrderByPubTimeDesc");
+                    if (maxEntries > -1) query.setRange(0, maxEntries);
+                    results = (List) query.execute(new Object[] {current.getWebsite(), WeblogEntryData.PUBLISHED, current.getPubTime(), category});                
+                }
+            }
+        } else {
+            if (locale != null) {
+                if (next) {
+                    query = strategy.newQuery(WeblogEntryData.class, 
+                            "WeblogEntryData.getByWebsite&Status&PubTimeGreater&LocaleLikeOrderByPubTimeAsc");
+                    if (maxEntries > -1) query.setRange(0, maxEntries);
+                    results = (List) query.execute(new Object[] {current.getWebsite(), WeblogEntryData.PUBLISHED, current.getPubTime(), locale});
+                } else {
+                    query = strategy.newQuery(WeblogEntryData.class, 
+                            "WeblogEntryData.getByWebsite&Status&PubTimeLess&LocaleLikeOrderByPubTimeDesc");
+                    if (maxEntries > -1) query.setRange(0, maxEntries);
+                    results = (List) query.execute(new Object[] {current.getWebsite(), WeblogEntryData.PUBLISHED, current.getPubTime(), locale});  
+                }
+            } else {
+                if (next) {
+                    results = (List) strategy.newQuery(WeblogEntryData.class, 
+                            "WeblogEntryData.getByWebsite&Status&PubTimeGreater&OrderByPubTimeAsc");
+                    if (maxEntries > -1) query.setRange(0, maxEntries);
+                    results = (List) query.execute(new Object[] {current.getWebsite(), WeblogEntryData.PUBLISHED, current.getPubTime()});
+                
+                } else {
+                    results = (List) strategy.newQuery(WeblogEntryData.class, 
+                            "WeblogEntryData.getByWebsite&Status&PubTimeLessOrderByPubTimeDesc");
+                    if (maxEntries > -1) query.setRange(0, maxEntries);
+                    results = (List) query.execute(new Object[] {current.getWebsite(), WeblogEntryData.PUBLISHED, current.getPubTime()});                
+                }
+            }
+        }
+        return results; 
     }
-
+    
     /**
-     * Get the WeblogEntry following, chronologically, the current entry.
-     * Restrict by the Category, if named.
-     * @param current The "current" WeblogEntryData
-     * @param catName The value of the requested Category Name
+     * Get top level categories for a website.
+     * @param website Website.
      */
-    public WeblogEntryData getNextEntry(WeblogEntryData current,
-            String catName, String locale) throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public WeblogCategoryData getRootWeblogCategory(WebsiteData website)
+            throws RollerException {
+        if (website == null)
+            throw new RollerException("website is null");
+        
+        return (WeblogCategoryData) strategy.newQuery(WeblogCategoryData.class, 
+                "WeblogCategoryData.getByWebsite&ParentNull").setUnique().execute(website);
     }
 
     /**
-     * Get the WeblogEntry prior to, chronologically, the current entry.
-     * Restrict by the Category, if named.
-     * @param current The "current" WeblogEntryData.
-     * @param catName The value of the requested Category Name.
+     * Get WebLogCategory objects for a website.
      */
-    public WeblogEntryData getPreviousEntry(WeblogEntryData current,
-            String catName, String locale) throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public List getWeblogCategories(WebsiteData website, boolean includeRoot)
+            throws RollerException {
+        if (website == null)
+            throw new RollerException("website is null");
+        
+        if (includeRoot) return getWeblogCategories(website);
+        
+        return (List) strategy.newQuery(WeblogCategoryData.class, 
+                "WeblogCategoryData.getByWebsite&ParentNull").execute(website);
     }
 
     /**
-     * Get entries next after current entry.
-     * @param entry Current entry.
-     * @param catName Only return entries in this category (if not null).
-     * @param maxEntries Maximum number of entries to return.
+     * Get WebLogCategory objects for a website.
      */
-    public List getNextEntries(WeblogEntryData entry, String catName,
-            String locale, int maxEntries) throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public List getWeblogCategories(WebsiteData website)
+            throws RollerException {
+        return (List) strategy.newQuery(WeblogCategoryData.class, 
+                "WeblogCategoryData.getByWebsite").execute(website);
     }
 
     /**
-     * Get entries previous to current entry.
-     * @param entry Current entry.
-     * @param catName Only return entries in this category (if not null).
-     * @param maxEntries Maximum number of entries to return.
+     * Get WeblogEntries by offset/length as list in reverse chronological order.
+     * The range offset and list arguments enable paging through query results.
+     * @param website    Weblog or null to get for all weblogs.
+     * @param user       User or null to get for all users.
+     * @param startDate  Start date or null for no start date.
+     * @param endDate    End date or null for no end date.
+     * @param catName    Category path or null for all categories.
+     * @param status     Status of DRAFT, PENDING, PUBLISHED or null for all
+     * @param sortBy     Sort by either 'pubTime' or 'updateTime' (null for pubTime)
+     * @param offset     Offset into results for paging
+     * @param range     Max comments to return (or -1 for no limit)
+     * @return List of WeblogEntryData objects in reverse chrono order.
+     * @throws RollerException
      */
-    public List getPreviousEntries(WeblogEntryData entry, String catName,
-            String locale, int maxEntries) throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public List getWeblogEntries(WebsiteData website,  UserData user,
+                                 Date startDate, Date endDate,
+                                 String catName, List tags,
+                                 String status, String sortBy, String locale,             
+                                 int offset, int range) throws RollerException {
+        return null; // TODO not implemented
     }
 
     /**
@@ -219,15 +446,29 @@
      */
     public List getWeblogEntriesPinnedToMain(Integer max)
             throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+        DatamapperQuery query = strategy.newQuery(WeblogCategoryData.class, 
+                "WeblogEntryData.getByPinnedToMainOrderByPubTime");
+        if (max != null) {
+            query.setRange(0, max.intValue());
+        }
+        return (List) query.execute(Boolean.TRUE);   
     }
 
     /**
-     * Get time of last update for a weblog specified by username
+     * Get weblog entry by anchor.
      */
-    public Date getWeblogLastPublishTime(WebsiteData website)
-            throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public WeblogEntryData getWeblogEntryByAnchor(WebsiteData website,
+            String anchor) throws RollerException {
+        if (website == null)
+            throw new RollerException("Website is null");
+        
+        if (anchor == null)
+            throw new RollerException("Anchor is null");
+        
+        // TODO Impl entryAnchorToIdMap CACHE
+        
+        return (WeblogEntryData)strategy.newQuery(WeblogEntryData.class, "getByAnchor")
+            .execute(anchor);
     }
 
     /**
@@ -238,86 +479,190 @@
      */
     public Date getWeblogLastPublishTime(WebsiteData website, String catName)
             throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
-    }
+        WeblogCategoryData cat = null;
 
-    /**
-     * Save weblog category.
-     */
-    public void saveWeblogCategory(WeblogCategoryData cat)
-            throws RollerException {
-        //To change body of implemented methods use File | Settings | File Templates.
+        if (catName != null && website != null) {
+            cat = getWeblogCategoryByPath(website, null, catName);
+        }
+    
+        List list = null;
+        if (website != null) {
+            if (cat != null) {
+                  list = (List) strategy.newQuery(WeblogEntryData.class, 
+                          "WeblogEntryData.getByStatus&PubTimeLessEqual&Category&WebsiteOrderByPubTimeDesc").setRange(0, 1)
+                          .execute(new Object[] {WeblogEntryData.PUBLISHED, new Date(), cat, website});
+            } else {
+                list = (List) strategy.newQuery(WeblogEntryData.class, 
+                        "WeblogEntryData.getByStatus&PubTimeLessEqual&WebsiteOrderByPubTimeDesc").setRange(0, 1)
+                        .execute(new Object[] {WeblogEntryData.PUBLISHED, new Date(), website});
+            }
+        } else {
+            // cat must also be null
+            list = (List) strategy.newQuery(WeblogEntryData.class, 
+                    "WeblogEntryData.getByStatus&PubTimeLessEqualOrderByPubTimeDesc").setRange(0, 1)
+                    .execute(new Object[] {WeblogEntryData.PUBLISHED, new Date()});
+        }
+        if (list.size() > 0) {
+            return ((WeblogEntryData)list.get(0)).getPubTime();
+        } else {
+            return null;
+        }
     }
 
     /**
-     * Remove weblog category.
+     * Get weblog entries with given category or, optionally, any sub-category
+     * of that category.
+     * @param cat Category
+     * @param subcats True if sub-categories are to be fetched
+     * @return List of weblog entries in category
      */
-    public void removeWeblogCategory(WeblogCategoryData cat)
+    // TODO: this method should be removed and it's functionality moved to getWeblogEntries()
+    public List getWeblogEntries(WeblogCategoryData cat, boolean subcats)
             throws RollerException {
-        //To change body of implemented methods use File | Settings | File Templates.
+        List results = null;
+        
+        if (!subcats) {
+            results = (List) strategy.newQuery(WeblogEntryData.class, "WeblogEntryData.getByCategory").execute(cat);
+        } else {
+            results = (List) strategy.newQuery(WeblogEntryData.class, 
+                    "WeblogEntryData.getByCategory.pathLike&Website").execute(new Object[] {cat, cat.getPath(), cat.getWebsite()});  
+        }
+        
+        return results;
     }
 
     /**
-     * Get category by id.
+     * Create unique anchor for weblog entry.
      */
-    public WeblogCategoryData getWeblogCategory(String id)
-            throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public String createAnchor(WeblogEntryData entry) throws RollerException {
+        // Check for uniqueness of anchor
+        String base = entry.createAnchorBase();
+        String name = base;
+        int count = 0;
+            
+        while (true) {
+            if (count > 0) {
+                name = base + count;
+            }
+                
+            List results = (List) strategy.newQuery(WeblogEntryData.class, 
+                "WeblogEntryData.getByWeblogEntry&Anchor").execute(new Object[] {entry, name});
+                
+            if (results.size() < 1) {
+                break;
+            } else {
+                count++;
+            }
+        }
+        return name;
     }
 
     /**
-     * Recategorize all entries with one category to another.
+     * Check for duplicate category name.
      */
-    public void moveWeblogCategoryContents(String srcId, String destId)
+    public boolean isDuplicateWeblogCategoryName(WeblogCategoryData cat)
             throws RollerException {
-        //To change body of implemented methods use File | Settings | File Templates.
+
+        // ensure that no sibling categories share the same name
+        WeblogCategoryData parent = cat.getParent();
+        if (null != parent) {
+            return (getWeblogCategoryByPath(cat.getWebsite(), cat.getPath()) != null);
+        }
+        
+        return false;
     }
 
     /**
-     * Get top level categories for a website.
-     * @param website Website.
+     * Check if weblog category is in use.
      */
-    public WeblogCategoryData getRootWeblogCategory(WebsiteData website)
+    public boolean isWeblogCategoryInUse(WeblogCategoryData cat)
             throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+        int entryCount = ((List) strategy.newQuery(WeblogEntryData.class, "").execute(cat)).size();
+            
+        if (entryCount > 0) {
+            return true;
+        }
+            
+        Iterator cats = cat.getWeblogCategories().iterator();
+        while (cats.hasNext()) {
+            WeblogCategoryData childCat = (WeblogCategoryData)cats.next();
+            if (childCat.isInUse()) {
+                return true;
+            }
+        }
+            
+        if (cat.getWebsite().getBloggerCategory().equals(cat)) {
+            return true;
+        }
+            
+        if (cat.getWebsite().getDefaultCategory().equals(cat)) {
+            return true;
+        }
+            
+        return false;
     }
 
     /**
-     * Get category specified by website and categoryPath.
-     * @param website Website of WeblogCategory.
-     * @param categoryPath Path of WeblogCategory, relative to category root.
+     * Generic comments query method.
+     * @param website Website or null for all comments on site
+     * @param entry Entry or null to include all comments
+     * @param startDate Start date or null for no restriction
+     * @param endDate End date or null for no restriction
+     * @param pending Pending flag value or null for no restriction
+     * @param pending Approved flag value or null for no restriction
+     * @param reverseChrono True for results in reverse chrono order
+     * @param spam Spam flag value or null for no restriction
+     * @param offset Offset into results for paging
+     * @param length Max comments to return (or -1 for no limit)
      */
-    public WeblogCategoryData getWeblogCategoryByPath(WebsiteData website,
-            String categoryPath) throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public List getComments(WebsiteData website, WeblogEntryData entry,
+            String searchString, Date startDate,
+            Date endDate, Boolean pending,
+            Boolean approved, Boolean spam,
+            boolean reverseChrono, int offset,
+            int length) throws RollerException {
+        return null;  // TODO not implemented
     }
 
-    /**
-     * Get sub-category by path relative to specified category.
-     * @param category Root of path or null to start at top of category tree.
-     * @param path Path of category to be located.
-     * @param website Website of categories.
-     * @return Category specified by path or null if not found.
-     */
-    public WeblogCategoryData getWeblogCategoryByPath(WebsiteData wd,
-            WeblogCategoryData category, String string) throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public int removeMatchingComments(
+            WebsiteData     website, 
+            WeblogEntryData entry, 
+            String  searchString, 
+            Date    startDate, 
+            Date    endDate, 
+            Boolean pending, 
+            Boolean approved, 
+            Boolean spam) throws RollerException {
+        List comments = getComments( 
+            website, entry, searchString, startDate, endDate, 
+            pending, approved, spam, true, 0, -1);
+        int count = 0;
+        for (Iterator it = comments.iterator(); it.hasNext();) {
+            CommentData comment = (CommentData) it.next();
+            removeComment(comment);
+            count++;
+        }
+        return count;
+        // TODO use Bulk Delete
     }
-
+    
     /**
-     * Get WebLogCategory objects for a website.
+     * Get category by id.
      */
-    public List getWeblogCategories(WebsiteData website)
+    public WeblogCategoryData getWeblogCategory(String id)
             throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+        return (WeblogCategoryData) this.strategy.load(
+                WeblogCategoryData.class, id);
     }
 
     /**
-     * Get WebLogCategory objects for a website.
+     * Get category specified by website and categoryPath.
+     * @param website Website of WeblogCategory.
+     * @param categoryPath Path of WeblogCategory, relative to category root.
      */
-    public List getWeblogCategories(WebsiteData website, boolean includeRoot)
-            throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public WeblogCategoryData getWeblogCategoryByPath(WebsiteData website,
+            String categoryPath) throws RollerException {
+        return getWeblogCategoryByPath(website, null, categoryPath);
     }
 
     /**
@@ -327,127 +672,689 @@
      * @return Forward slash separated path string.
      */
     public String getPath(WeblogCategoryData category) throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+        if (null == category.getParent()) {
+            return "/";
+        } else {
+            String parentPath = getPath(category.getParent());
+            parentPath = "/".equals(parentPath) ? "" : parentPath;
+            return parentPath + "/" + category.getName();
+        }
     }
 
     /**
-     * Get parent association for a category.
+     * Get sub-category by path relative to specified category.
+     * @param category Root of path or null to start at top of category tree.
+     * @param path Path of category to be located.
+     * @param website Website of categories.
+     * @return Category specified by path or null if not found.
      */
-    public Assoc getWeblogCategoryParentAssoc(WeblogCategoryData data)
-            throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public WeblogCategoryData getWeblogCategoryByPath(WebsiteData website,
+            WeblogCategoryData category, String path) throws RollerException {
+        final Iterator cats;
+        final String[] pathArray = Utilities.stringToStringArray(path, "/");
+        
+        if (category == null && (null == path || "".equals(path.trim()))) {
+            throw new RollerException("Bad arguments.");
+        }
+        
+        if (path.trim().equals("/")) {
+            return getRootWeblogCategory(website);
+        } else if (category == null || path.trim().startsWith("/")) {
+            cats = getRootWeblogCategory(website).getWeblogCategories().iterator();
+        } else {
+            cats = category.getWeblogCategories().iterator();
+        }
+        
+        while (cats.hasNext()) {
+            WeblogCategoryData possibleMatch = (WeblogCategoryData)cats.next();
+            if (possibleMatch.getName().equals(pathArray[0])) {
+                if (pathArray.length == 1) {
+                    return possibleMatch;
+                } else {
+                    String[] subpath = new String[pathArray.length - 1];
+                    System.arraycopy(pathArray, 1, subpath, 0, subpath.length);
+                    
+                    String pathString= Utilities.stringArrayToString(subpath,"/");
+                    return getWeblogCategoryByPath(website, possibleMatch, pathString);
+                }
+            }
+        }
+        
+        // The category did not match and neither did any sub-categories
+        return null;
     }
 
     /**
-     * Get child associations for a category.
+     * Get comment by id.
      */
-    public List getWeblogCategoryChildAssocs(WeblogCategoryData data)
-            throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public CommentData getComment(String id) throws RollerException {
+        return (CommentData) this.strategy.load(CommentData.class, id);
     }
 
     /**
-     * Get all descendent associations for a category.
+     * Get weblog entry by id.
      */
-    public List getAllWeblogCategoryDecscendentAssocs(WeblogCategoryData data)
-            throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public WeblogEntryData getWeblogEntry(String id) throws RollerException {
+        return (WeblogEntryData)strategy.load(WeblogEntryData.class, id);
     }
 
     /**
-     * Get all ancestor associates for a category.
+     * Get time of last update for a weblog specified by username
      */
-    public List getWeblogCategoryAncestorAssocs(WeblogCategoryData data)
+    public Date getWeblogLastPublishTime(WebsiteData website)
             throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
-    }
-
-    /**
-     * Save comment.
-     */
-    public void saveComment(CommentData comment) throws RollerException {
-        //To change body of implemented methods use File | Settings | File Templates.
+        return getWeblogLastPublishTime(website, null);
     }
 
     /**
-     * Remove comment.
+     * Get Weblog Entries grouped by day. This method returns a Map that
+     * contains Lists, each List contains WeblogEntryData objects, and the Lists
+     * are keyed by Date objects.
+     * @param website Weblog or null to get for all weblogs.
+     * @param startDate Start date or null for no start date.
+     * @param endDate End date or null for no end date.
+     * @param catName Category path or null for all categories.
+     * @param status Status of DRAFT, PENDING, PUBLISHED or null for all
+     * @param offset Offset into results for paging
+     * @param range Max comments to return (or -1 for no limit)
+     * @return Map of Lists, keyed by Date, and containing WeblogEntryData.
+     * @throws org.apache.roller.RollerException
      */
-    public void removeComment(CommentData comment) throws RollerException {
-        //To change body of implemented methods use File | Settings | File Templates.
+    public Map getWeblogEntryObjectMap(WebsiteData website,
+                                       Date startDate, Date endDate, String catName, List tags,
+                                       String status, String locale, int offset,
+                                       int range) throws RollerException {
+        return getWeblogEntryMap(
+            website,
+            startDate,
+            endDate,
+            catName,
+            tags,    
+            status,
+            false,
+            locale,
+            offset,
+            range);
     }
 
     /**
-     * Get comment by id.
+     * Get Weblog Entry date strings grouped by day. This method returns a Map
+     * that contains Lists, each List contains YYYYMMDD date strings objects,
+     * and the Lists are keyed by Date objects.
+     * @param website Weblog or null to get for all weblogs.
+     * @param startDate Start date or null for no start date.
+     * @param endDate End date or null for no end date.
+     * @param catName Category path or null for all categories.
+     * @param status Status of DRAFT, PENDING, PUBLISHED or null for all
+     * @param offset Offset into results for paging
+     * @param range Max comments to return (or -1 for no limit)
+     * @return Map of Lists, keyed by Date, and containing date strings.
+     * @throws org.apache.roller.RollerException
      */
-    public CommentData getComment(String id) throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public Map getWeblogEntryStringMap(WebsiteData website,
+            Date startDate, Date endDate, String catName, List tags,
+            String status, String locale, int offset,
+            int range) throws RollerException {
+        return getWeblogEntryMap(
+            website,
+            startDate,
+            endDate,
+            catName,
+            tags,    
+            status,
+            true,
+            locale,
+            offset,
+            range);
+    }
+
+    private Map getWeblogEntryMap(
+            WebsiteData website,
+            Date    startDate,
+            Date    endDate,
+            String  catName,
+            List    tags,
+            String  status,
+            boolean stringsOnly,
+            String  locale,
+            int     offset,
+            int     length) throws RollerException {
+        
+        TreeMap map = new TreeMap(reverseComparator);
+        
+        List entries = getWeblogEntries(
+            website,                 
+            null,
+            startDate,
+            endDate,
+            catName,
+            tags,
+            status,
+            null,
+            locale,          
+            offset,
+            length);
+        
+        Calendar cal = Calendar.getInstance();
+        if (website != null) {
+            cal.setTimeZone(website.getTimeZoneInstance());
+        }
+        
+        SimpleDateFormat formatter = DateUtil.get8charDateFormat();
+        for (Iterator wbItr = entries.iterator(); wbItr.hasNext();) {
+            WeblogEntryData entry = (WeblogEntryData) wbItr.next();
+            Date sDate = DateUtil.getNoonOfDay(entry.getPubTime(), cal);
+            if (stringsOnly) {
+                if (map.get(sDate) == null)
+                    map.put(sDate, formatter.format(sDate));
+            } else {
+                List dayEntries = (List) map.get(sDate);
+                if (dayEntries == null) {
+                    dayEntries = new ArrayList();
+                    map.put(sDate, dayEntries);
+                }
+                dayEntries.add(entry);
+            }
+        }
+        return map;
     }
-
+    
     /**
-     * Generic comments query method.
-     * @param website Website or null for all comments on site
-     * @param entry Entry or null to include all comments
-     * @param startDate Start date or null for no restriction
-     * @param endDate End date or null for no restriction
-     * @param pending Pending flag value or null for no restriction
-     * @param pending Approved flag value or null for no restriction
-     * @param reverseChrono True for results in reverse chrono order
-     * @param spam Spam flag value or null for no restriction
+     * Get weblog enties ordered by descending number of comments.
+     * @param website Weblog or null to get for all weblogs.
+     * @param startDate Start date or null for no start date.
+     * @param endDate End date or null for no end date.
      * @param offset Offset into results for paging
      * @param length Max comments to return (or -1 for no limit)
+     * @return List of WeblogEntryData objects.
      */
-    public List getComments(WebsiteData website, WeblogEntryData entry,
-            String searchString, Date startDate,
-            Date endDate, Boolean pending,
-            Boolean approved, Boolean spam,
-            boolean reverseChrono, int offset,
+    public List getMostCommentedWeblogEntries(WebsiteData website,
+            Date startDate, Date endDate, int offset,
             int length) throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+        DatamapperQuery query = null;
+        List queryResults = null;
+        boolean setRange = offset != 0 || length != -1;
+
+        if (length == -1) {
+            length = Integer.MAX_VALUE - offset;
+        }
+
+        if (endDate == null) endDate = new Date();
+
+        if (website != null) {
+            if (startDate != null) {
+                query = strategy.newQuery(CommentData.class, "CommentData.getMostCommentedWeblogEntryByWebsite&EndDate&StartDate");
+                if (setRange) query.setRange(offset, offset + length);
+                queryResults = (List) query.execute(new Object[] {website, endDate, startDate});
+            } else {
+                query = strategy.newQuery(CommentData.class, "CommentData.getMostCommentedWeblogEntryByWebsite&EndDate");
+                if (setRange) query.setRange(offset, offset + length);
+                queryResults = (List) query.execute(new Object[] {website, endDate});            
+            }
+        } else {
+            if (startDate != null) {
+                query = strategy.newQuery(CommentData.class, "CommentData.getMostCommentedWeblogEntryByEndDate&StartDate");
+                if (setRange) query.setRange(offset, offset + length);
+                queryResults = (List) query.execute(new Object[] {endDate, startDate});
+            } else {
+                query = strategy.newQuery(CommentData.class, "CommentData.getMostCommentedWeblogEntryByEndDate");
+                if (setRange) query.setRange(offset, offset + length);
+                queryResults = (List) query.execute(endDate);            
+            }
+        }
+        
+        List results = new ArrayList();
+        for (Iterator iter = queryResults.iterator(); iter.hasNext();) {
+            Object[] row = (Object[]) iter.next();
+            results.add(new StatCount(
+                (String)row[1],
+                (String)row[2],
+                (String)row[3],
+                "statCount.weblogEntryCommentCountType",
+                new Long(((Integer)row[0]).intValue()).longValue()));
+        }
+        // TODO Collections.sort(results, StatCount.getComparator());
+        Collections.reverse(results);
+        return results;
     }
 
     /**
-     * Create unique anchor for weblog entry.
+     * Get entries next after current entry.
+     * @param current Current entry.
+     * @param catName Only return entries in this category (if not null).
+     * @param maxEntries Maximum number of entries to return.
      */
-    public String createAnchor(WeblogEntryData data) throws RollerException {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public List getNextEntries(WeblogEntryData current, String catName,
+            String locale, int maxEntries) throws RollerException {
+
+        return getNextPrevEntries(current, catName, locale, maxEntries, true);
     }
 
     /**
-     * Check for duplicate category name.
+     * Get entries previous to current entry.
+     * @param current Current entry.
+     * @param catName Only return entries in this category (if not null).
+     * @param maxEntries Maximum number of entries to return.
      */
-    public boolean isDuplicateWeblogCategoryName(WeblogCategoryData data)
-            throws RollerException {
-        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    public List getPreviousEntries(WeblogEntryData current, String catName,
+            String locale, int maxEntries) throws RollerException {
+
+        return getNextPrevEntries(current, catName, locale, maxEntries, false);
     }
 
     /**
-     * Check if weblog category is in use.
+     * Get the WeblogEntry following, chronologically, the current entry.
+     * Restrict by the Category, if named.
+     * @param current The "current" WeblogEntryData
+     * @param catName The value of the requested Category Name
      */
-    public boolean isWeblogCategoryInUse(WeblogCategoryData data)
-            throws RollerException {
-        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    public WeblogEntryData getNextEntry(WeblogEntryData current,
+            String catName, String locale) throws RollerException {
+        WeblogEntryData entry = null;
+        List entryList = getNextEntries(current, catName, locale, 1);
+        if (entryList != null && entryList.size() > 0) {
+            entry = (WeblogEntryData)entryList.get(0);
+        }
+        return entry;
     }
 
     /**
-     * Returns true if ancestor is truly an ancestor of child.
+     * Get the WeblogEntry prior to, chronologically, the current entry.
+     * Restrict by the Category, if named.
+     * @param current The "current" WeblogEntryData.
+     * @param catName The value of the requested Category Name.
      */
-    public boolean isDescendentOf(WeblogCategoryData child,
-            WeblogCategoryData ancestor) throws RollerException {
-        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    public WeblogEntryData getPreviousEntry(WeblogEntryData current,
+            String catName, String locale) throws RollerException {
+        WeblogEntryData entry = null;
+        List entryList = getPreviousEntries(current, catName, locale, 1);
+        if (entryList != null && entryList.size() > 0) {
+            entry = (WeblogEntryData)entryList.get(0);
+        }
+        return entry;
     }
 
     /**
+     * Release all resources held by manager.
+     */
+    public void release() {}
+
+    /**
      * Apply comment default settings from website to all of website's entries.
      */
     public void applyCommentDefaultsToEntries(WebsiteData website)
             throws RollerException {
-        //To change body of implemented methods use File | Settings | File Templates.
+        if (log.isDebugEnabled()) {
+            log.debug("applyCommentDefaults");
+        }       
+//        String updateString = "update WeblogEntryData set "
+//            +"allowComments=:allowed, commentDays=:days, "
+//            +"pubTime=pubTime, updateTime=updateTime " // ensure timestamps are NOT reset
+//            +"where website=:site";
+//        Query update = session.createQuery(updateString);
+//        update.setParameter("allowed", website.getDefaultAllowComments());
+//        update.setParameter("days", new Integer(website.getDefaultCommentDays()));
+//        update.setParameter("site", website);
+//        update.executeUpdate();            
+        // TODO not implemented
     }
 
-    /**
-     * Release all resources held by manager.
+    /* (non-Javadoc)
+     * @see org.apache.roller.business.WeblogManager#getPopularTags(org.apache.roller.pojos.WebsiteData, java.util.Date, int)
      */
-    public void release() {
-        //To change body of implemented methods use File | Settings | File Templates.
+    public List getPopularTags(WebsiteData website, Date startDate, int limit)
+            throws RollerException {
+        DatamapperQuery query = null;
+        List queryResults = null;
+        
+        if (website != null) {
+            if (startDate != null) {
+                query = strategy.newQuery(WeblogEntryTagAggregateData.class, "WeblogEntryTagAggregateData.getPopularTagsByWebsite&StartDate");
+                if (limit > 0) query.setRange(0, limit);
+                queryResults = (List) query.execute(new Object[] {website, startDate});
+            } else {
+                query = strategy.newQuery(WeblogEntryTagAggregateData.class, "WeblogEntryTagAggregateData.getPopularTagsByWebsite");
+                if (limit > 0) query.setRange(0, limit);
+                queryResults = (List) query.execute(startDate);                
+            }
+        } else {
+            if (startDate != null) {
+                query = strategy.newQuery(WeblogEntryTagAggregateData.class, "WeblogEntryTagAggregateData.getPopularTagsByWebsiteNull&StartDate");
+                if (limit > 0) query.setRange(0, limit);
+                queryResults = (List) query.execute(startDate);
+            } else {
+                query = strategy.newQuery(WeblogEntryTagAggregateData.class, "WeblogEntryTagAggregateData.getPopularTagsByWebsiteNull");
+                if (limit > 0) query.setRange(0, limit);
+                queryResults = (List) query.execute();                
+            }
+        }
+
+        // TODO queryString.append("order by sum(total) desc");  ???
+        
+        double min = Integer.MAX_VALUE;
+        double max = Integer.MIN_VALUE;
+
+        List results = new ArrayList(limit);
+            
+        for (Iterator iter = queryResults.iterator(); iter.hasNext();) {
+            Object[] row = (Object[]) iter.next();
+            TagStat t = new TagStat();
+            t.setName((String) row[0]);
+            t.setCount(((Integer) row[1]).intValue());                
+                
+            min = Math.min(min, t.getCount());
+            max = Math.max(max, t.getCount());                
+            results.add(t);
+        }
+            
+        min = Math.log(1+min);
+        max = Math.log(1+max);
+
+        double range = Math.max(.01, max - min) * 1.0001;
+            
+        for (Iterator iter = results.iterator(); iter.hasNext(); ) {
+            TagStat t = (TagStat) iter.next();
+            t.setIntensity((int) (1 + Math.floor(5 * (Math.log(1+t.getCount()) - min) / range)));
+        }            
+
+        // sort results by name, because query had to sort by total
+        Collections.sort(results, tagStatComparator);
+            
+        return results;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.roller.business.WeblogManager#getTags(org.apache.roller.pojos.WebsiteData,
+     *      java.lang.String, java.lang.String, int)
+     */
+    public List getTags(WebsiteData website, String sortBy, String startsWith, int limit) throws RollerException {    
+        DatamapperQuery query = null;
+        List queryResults = null;
+        boolean sortByName = sortBy == null || !sortBy.equals("count");
+        
+        if (website != null) {
+            if (startsWith != null) {
+                if (sortByName) {
+                    query = strategy.newQuery(WeblogEntryTagAggregateData.class, "WeblogEntryTagAggregateData.getTagsByWebsite&NameStartsWithOrderByName");
+                    if (limit > 0) query.setRange(0, limit);
+                    queryResults = (List) query.execute(new Object[] {website, startsWith});
+                } else {
+                    query = strategy.newQuery(WeblogEntryTagAggregateData.class, "WeblogEntryTagAggregateData.getTagsByWebsite&NameStartsWith");
+                    if (limit > 0) query.setRange(0, limit);
+                    queryResults = (List) query.execute(new Object[] {website, startsWith});
+                }
+            } else {
+                if (sortByName) {
+                    query = strategy.newQuery(WeblogEntryTagAggregateData.class, "WeblogEntryTagAggregateData.getTagsByWebsiteOrderByName");
+                    if (limit > 0) query.setRange(0, limit);
+                    queryResults = (List) query.execute(startsWith);   
+                } else {
+                    query = strategy.newQuery(WeblogEntryTagAggregateData.class, "WeblogEntryTagAggregateData.getTagsByWebsite");
+                    if (limit > 0) query.setRange(0, limit);
+                    queryResults = (List) query.execute(startsWith);   
+                }
+            }
+        } else {
+            if (startsWith != null) {
+                if (sortByName) {
+                    query = strategy.newQuery(WeblogEntryTagAggregateData.class, "WeblogEntryTagAggregateData.getTagsByWebsiteNull&NameStartsWithOrderByName");
+                    if (limit > 0) query.setRange(0, limit);
+                    queryResults = (List) query.execute(startsWith);
+                } else {
+                    query = strategy.newQuery(WeblogEntryTagAggregateData.class, "WeblogEntryTagAggregateData.getTagsByWebsiteNull&NameStartsWith");
+                    if (limit > 0) query.setRange(0, limit);
+                    queryResults = (List) query.execute(startsWith);
+                }
+            } else {
+                if (sortByName) {
+                    query = strategy.newQuery(WeblogEntryTagAggregateData.class, "WeblogEntryTagAggregateData.getTagsByWebsiteNullOrderByName");
+                    if (limit > 0) query.setRange(0, limit);
+                    queryResults = (List) query.execute();
+                } else {
+                    query = strategy.newQuery(WeblogEntryTagAggregateData.class, "WeblogEntryTagAggregateData.getTagsByWebsiteNull");
+                    if (limit > 0) query.setRange(0, limit);
+                    queryResults = (List) query.execute();
+                }
+            }
+        }
+        
+        List results = new ArrayList();
+        for (Iterator iter = queryResults.iterator(); iter.hasNext();) {
+            Object[] row = (Object[]) iter.next();
+            TagStat ce = new TagStat();
+            ce.setName((String) row[0]);
+            ce.setCount(((Integer) row[1]).intValue());
+            results.add(ce);
+        }
+        
+        if (!sortByName) {
+            Collections.sort(results, tagStatComparator);
+        }
+        
+        return results;
+    }
+
+    public boolean getTagComboExists(List tags, WebsiteData weblog) {
+        boolean comboExists = false;
+        
+//        StringBuffer queryString = new StringBuffer();
+//        queryString.append("select distinct name ");
+//        queryString.append("from WeblogEntryTagAggregateData ");
+//        queryString.append("where name in ( :tags ) ");
+//            
+//        // are we checking a specific weblog, or site-wide?
+//        if (weblog != null)
+//            queryString.append("and weblog.id = '" + weblog.getId() + "' ");
+//        else
+//            queryString.append("and weblog is null ");
+//            
+//        Query query = session.createQuery(queryString.toString());
+//        query.setParameterList("tags", tags);
+//            
+//        List results = query.list();
+//        comboExists = (results != null && results.size() == tags.size());
+        
+        return comboExists; // TODO not implemented
+    }
+    
+    public void updateTagCount(String name, WebsiteData website, int amount) throws RollerException {
+        if(amount == 0) {
+            throw new RollerException("Tag increment amount cannot be zero.");
+        }
+        
+        if(website == null) {
+            throw new RollerException("Website cannot be NULL.");
+        }
+
+//        Junction conjunction = Expression.conjunction();
+//        conjunction.add(Expression.eq("name", name));
+//        conjunction.add(Expression.eq("weblog", website));
+//
+//        // The reason why add order lastUsed desc is to make sure we keep picking the most recent
+//        // one in the case where we have multiple rows (clustered environment)
+//        // eventually that second entry will have a very low total (most likely 1) and
+//        // won't matter
+//        
+//        Criteria criteria = session.createCriteria(WeblogEntryTagAggregateData.class)
+//            .add(conjunction).addOrder(Order.desc("lastUsed")).setMaxResults(1);
+//        
+//        WeblogEntryTagAggregateData weblogTagData = (WeblogEntryTagAggregateData) criteria.uniqueResult();
+//
+//        conjunction = Expression.conjunction();
+//        conjunction.add(Restrictions.eq("name", name));
+//        conjunction.add(Restrictions.isNull("weblog"));
+//        
+//        criteria = session.createCriteria(WeblogEntryTagAggregateData.class)
+//            .add(conjunction).addOrder(Order.desc("lastUsed")).setMaxResults(1);
+//    
+//        WeblogEntryTagAggregateData siteTagData = (WeblogEntryTagAggregateData) criteria.uniqueResult();
+//        
+//        Timestamp lastUsed = new Timestamp((new Date()).getTime());
+//        
+//        // create it only if we are going to need it.
+//        if(weblogTagData == null && amount > 0) {
+//            weblogTagData = new WeblogEntryTagAggregateData(null, website, name, amount);
+//            weblogTagData.setLastUsed(lastUsed);
+//            session.save(weblogTagData);
+//        } else if(weblogTagData != null) {
+//            session.createQuery("update WeblogEntryTagAggregateData set total = total + ?, lastUsed = current_timestamp() where name = ? and weblog = ?")
+//            .setInteger(0, amount)
+//            .setString(1, weblogTagData.getName())
+//            .setParameter(2, website)
+//            .executeUpdate();
+//        }
+//        
+//        // create it only if we are going to need it.        
+//        if(siteTagData == null && amount > 0) {
+//            siteTagData = new WeblogEntryTagAggregateData(null, null, name, amount);
+//            siteTagData.setLastUsed(lastUsed);
+//            session.save(siteTagData);
+//        } else if(siteTagData != null) {
+//            session.createQuery("update WeblogEntryTagAggregateData set total = total + ?, lastUsed = current_timestamp() where name = ? and weblog is null")
+//            .setInteger(0, amount)
+//            .setString(1, siteTagData.getName())
+//            .executeUpdate();            
+//        }       
+        
+        // delete all bad counts
+        strategy.newRemoveQuery(WeblogEntryTagAggregateData.class, "WeblogEntryTagAggregateData.removeByTotalLessEqual").removeAll(new Integer(0));
+        // TODO not implemented
+    }
+
+
+    public HitCountData getHitCount(String id) throws RollerException {
+
+        if(id == null) {
+            throw new RollerException("Id field cannot be NULL.");
+        }
+
+        return (HitCountData) strategy.load(HitCountData.class, id);
+    }
+    
+    public HitCountData getHitCountByWeblog(WebsiteData weblog) 
+        throws RollerException {
+        
+        return (HitCountData) strategy.newQuery(HitCountData.class, 
+                "HitCountData.getByWeblog").setUnique().execute(weblog);
+    }
+    
+    
+    public List getHotWeblogs(int sinceDays, int offset, int length) 
+        throws RollerException {
+        
+        // figure out start date
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(new Date());
+        cal.add(Calendar.DATE, -1 * sinceDays);
+        Date startDate = cal.getTime();
+        
+        DatamapperQuery query = strategy.newQuery(HitCountData.class, 
+                "HitCountData.getByWeblogEnabledTrueAndActiveTrue&DailyHitsGreaterZero&WeblogLastModifiedGreaterOrderByDailyHitsDesc");
+
+        if (length != -1) {
+            query.setRange(offset, length);
+        }
+        
+        return (List) query.execute(startDate);
+    }
+    
+    
+    public void saveHitCount(HitCountData hitCount) throws RollerException {
+        this.strategy.store(hitCount);
+    }
+    
+    
+    public void removeHitCount(HitCountData hitCount) throws RollerException {
+        this.strategy.remove(hitCount);
+    }
+    
+    
+    public void incrementHitCount(WebsiteData weblog, int amount)
+        throws RollerException {
+
+        if(amount == 0) {
+            throw new RollerException("Tag increment amount cannot be zero.");
+        }
+        
+        if(weblog == null) {
+            throw new RollerException("Weblog cannot be NULL.");
+        }
+
+        HitCountData hitCount = (HitCountData) strategy.newQuery(HitCountData.class, 
+                "HitCountData.getByWeblog").setUnique().execute(weblog);
+        
+        // create it if it doesn't exist
+        if(hitCount == null && amount > 0) {
+            hitCount = new HitCountData();
+            hitCount.setWeblog(weblog);
+            hitCount.setDailyHits(amount);
+            strategy.store(hitCount);
+        } else if(hitCount != null) {
+            hitCount.setDailyHits(hitCount.getDailyHits() + amount);
+            strategy.store(hitCount);
+        }
+    }
+    
+    
+    public void resetAllHitCounts() throws RollerException {
+        
+//            session.createQuery("update HitCountData set dailyHits = 0").executeUpdate();
+
+        // TODO not implemented
+    }
+    
+    
+    public void resetHitCount(WebsiteData weblog) throws RollerException {
+
+        HitCountData hitCount = (HitCountData) strategy.newQuery(HitCountData.class, 
+                "HitCountData.getByWeblog").setUnique().execute(weblog);
+
+        hitCount.setDailyHits(0);
+        strategy.store(hitCount);
+    }
+
+    /**
+     * Get site-wide comment count 
+     */
+    public long getCommentCount() throws RollerException {
+        List results = (List) strategy.newQuery(CommentData.class, "CommentData.getCountAllDistinct").execute();
+        
+        return ((Integer)results.get(0)).intValue();
+    }
+
+
+    /**
+     * Get weblog comment count 
+     */
+    public long getCommentCount(WebsiteData website) throws RollerException {
+        List results = (List) strategy.newQuery(CommentData.class, 
+                "CommentData.getCountDistinctByWebsite").execute(website);
+        
+        return ((Integer)results.get(0)).intValue();
+    }
+
+
+    /**
+     * Get site-wide entry count 
+     */
+    public long getEntryCount() throws RollerException {
+        List results = (List) strategy.newQuery(WeblogEntryData.class, 
+                "WeblogEntryData.getCountDistinctByStatus").execute("PUBLISHED");
+        
+        return ((Integer)results.get(0)).intValue();
+    }
+
+
+    /**
+     * Get weblog entry count 
+     */
+    public long getEntryCount(WebsiteData website) throws RollerException {
+        List results = (List) strategy.newQuery(WeblogEntryData.class, 
+                "WeblogEntryData.getCountDistinctByStatus&Website").execute(new Object[] {"PUBLISHED", website});
+        
+        return ((Integer)results.get(0)).intValue();
     }
+    
 }

Modified: incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/business/jdo/JDORollerImpl.java
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/business/jdo/JDORollerImpl.java?view=diff&rev=482428&r1=482427&r2=482428
==============================================================================
--- incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/business/jdo/JDORollerImpl.java (original)
+++ incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/business/jdo/JDORollerImpl.java Mon Dec  4 16:26:15 2006
@@ -22,7 +22,7 @@
 import org.apache.commons.logging.LogFactory;
 import org.apache.roller.RollerException;
 import org.apache.roller.business.datamapper.DatamapperRollerImpl;
-import org.apache.roller.model.Roller;
+import org.apache.roller.business.Roller;
 
 
 /**

Modified: incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/business/jpa/JPAPersistenceStrategy.java
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/business/jpa/JPAPersistenceStrategy.java?view=diff&rev=482428&r1=482427&r2=482428
==============================================================================
--- incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/business/jpa/JPAPersistenceStrategy.java (original)
+++ incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/business/jpa/JPAPersistenceStrategy.java Mon Dec  4 16:26:15 2006
@@ -38,11 +38,13 @@
 import javax.persistence.EntityManagerFactory;
 import javax.persistence.EntityManager;
 import javax.persistence.Persistence;
+import javax.persistence.PersistenceException;
 
 /**
  * JPAPersistenceStrategy is responsible for the lowest-level interaction with
  * the JPA API.
  */
+// TODO handle PersistenceExceptions!
 public class JPAPersistenceStrategy implements DatamapperPersistenceStrategy {
 
     /**
@@ -77,11 +79,15 @@
      * @throws org.apache.roller.RollerException on any error
      */
     public void flush() throws RollerException {
-        EntityManager em = getEntityManager(false);
-        if (isTransactionActive(em)) {
-            em.getTransaction().commit();
+        try {
+            EntityManager em = getEntityManager(false);
+            if (isTransactionActive(em)) {
+                em.getTransaction().commit();
+            }
+            em.close();
+        } catch (PersistenceException pe) {
+            throw new RollerException(pe);
         }
-        em.close();
     }
 
     /**
@@ -150,6 +156,9 @@
      */
     public void removeAll(Class clazz) throws RollerException {
         //TODO: Think how this would be implemented
+        //One possible solution is to use bulk delete using nammed queries
+        //The name would be generated using clazz.
+        //We will need to make sure that the named query is defined for each clazz
         DatamapperRemoveQuery rq = newRemoveQuery(clazz, null);
         rq.removeAll();
     }

Modified: incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/business/jpa/JPARollerImpl.java
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/business/jpa/JPARollerImpl.java?view=diff&rev=482428&r1=482427&r2=482428
==============================================================================
--- incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/business/jpa/JPARollerImpl.java (original)
+++ incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/business/jpa/JPARollerImpl.java Mon Dec  4 16:26:15 2006
@@ -22,8 +22,7 @@
 import org.apache.commons.logging.LogFactory;
 import org.apache.roller.RollerException;
 import org.apache.roller.business.datamapper.DatamapperRollerImpl;
-import org.apache.roller.business.jpa.JPAPersistenceStrategy;
-import org.apache.roller.model.Roller;
+import org.apache.roller.business.Roller;
 
 
 /**
@@ -57,5 +56,4 @@
         logger.debug("Instantiating JPARollerImpl");
         return new JPARollerImpl();
     }
-    
 }

Added: incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/planet/pojos/PlanetConfigData.orm.xml
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/planet/pojos/PlanetConfigData.orm.xml?view=auto&rev=482428
==============================================================================
--- incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/planet/pojos/PlanetConfigData.orm.xml (added)
+++ incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/planet/pojos/PlanetConfigData.orm.xml Mon Dec  4 16:26:15 2006
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entity-mappings version="1.0" xmlns="http://java.sun.com/xml/ns/persistence/orm"
+                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
+ orm_1_0.xsd">
+    <description>Persistence Metadata for Roller</description>
+    <persistence-unit-metadata>
+        <persistence-unit-defaults>
+            <access>PROPERTY</access>
+        </persistence-unit-defaults>
+    </persistence-unit-metadata>
+    <package>org.apache.roller.pojos</package>
+    <entity metadata-complete="true" name=""
+            class="org.apache.roller.planet.pojos.PlanetConfigData">
+        <table name="rag_config"/>
+        <named-query name="PlanetConfigData.getAll">
+            <query>SELECT p FROM PlanetConfigData p</query>
+        </named-query>
+        <attributes>
+            <id name="id">
+                <column name="id"/>
+            </id>
+            <basic name="groupPage">
+                <column name="group_page" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="mainPage">
+                <column name="main_page" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="proxyHost">
+                <column name="proxy_host" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="proxyPort">
+                <column name="proxy_port" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="siteURL">
+                <column name="site_url" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="adminEmail">
+                <column name="admin_email" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="adminName">
+                <column name="admin_name" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="outputDir">
+                <column name="output_dir" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="templateDir">
+                <column name="template_dir" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="description">
+                <column name="description" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="title">
+                <column name="title" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="cacheDir">
+                <column name="cache_dir" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <many-to-one name="defaultGroup"
+                         target-entity="org.apache.roller.planet.pojos.PlanetGroupData">
+                <join-column name="default_group_id" insertable="true" updatable="true"
+                             nullable="true"/>
+                <cascade>
+                    <cascade-all/>
+                </cascade>
+            </many-to-one>
+        </attributes>
+    </entity>
+</entity-mappings>

Added: incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/planet/pojos/PlanetEntryData.orm.xml
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/planet/pojos/PlanetEntryData.orm.xml?view=auto&rev=482428
==============================================================================
--- incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/planet/pojos/PlanetEntryData.orm.xml (added)
+++ incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/planet/pojos/PlanetEntryData.orm.xml Mon Dec  4 16:26:15 2006
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entity-mappings version="1.0" xmlns="http://java.sun.com/xml/ns/persistence/orm"
+                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
+ orm_1_0.xsd">
+    <description>Persistence Metadata for Roller</description>
+    <persistence-unit-metadata>
+        <persistence-unit-defaults>
+            <access>PROPERTY</access>
+        </persistence-unit-defaults>
+    </persistence-unit-metadata>
+    <package>org.apache.roller.pojos</package>
+    <entity metadata-complete="true" name="" class="org.apache.roller.planet.pojos.PlanetEntryData">
+        <table name="rag_entry"/>
+
+        <named-query name="PlanetEntryData.getByFeedURL">
+            <query>SELECT p FROM PlanetEntryData p WHERE p.subscription.feedURL = ?1</query>
+        </named-query>
+        <named-query name="PlanetEntryData.getByGroup&amp;EndDateOrderByPubTimeDesc">
+            <query>SELECT p FROM PlanetEntryData p JOIN p.subscription s JOIN s.groups g
+                WHERE g =?1 AND p.pubTime &lt; ?2
+                ORDER BY p.pubTime DESC
+            </query>
+        </named-query>
+        <named-query name="PlanetEntryData.getByGroup&amp;EndDate&amp;StartDateOrderByPubTimeDesc">
+            <query>SELECT p FROM PlanetEntryData p JOIN p.subscription s JOIN s.groups g
+                WHERE g=?1 AND p.pubTime &lt; ?2 AND p.pubTime &gt; ?3
+                ORDER BY p.pubTime DESC
+            </query>
+        </named-query>
+        <named-query
+                name="PlanetEntryData.getByExternalOrInternalGroup&amp;EndDate&amp;StartDateOrderByPubTimeDesc">
+            <query>SELECT p FROM PlanetEntryData p JOIN p.subscription s JOIN s.groups g
+                WHERE (g.handle = 'external' OR g.handle = 'all')
+                AND p.pubTime &lt; ?2 AND p.pubTime &gt; ?3
+                ORDER BY p.pubTime DESC
+            </query>
+        </named-query>
+        <named-query
+                name="PlanetEntryData.getByExternalOrInternalGroup&amp;EndDateOrderByPubTimeDesc">
+            <query>SELECT p FROM PlanetEntryData p JOIN p.subscription s JOIN s.groups g
+                WHERE (g.handle = "external" OR g.handle = "all")
+                AND p.pubTime &lt; ?2
+                ORDER BY p.pubTime DESC
+            </query>
+        </named-query>
+        <attributes>
+            <id name="id">
+                <column name="id"/>
+            </id>
+            <basic name="categoriesString">
+                <column name="categories" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="author">
+                <column name="author" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="text">
+                <column name="content" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="guid">
+                <column name="guid" insertable="true" updatable="true" unique="true"/>
+            </basic>
+            <basic name="handle">
+                <column name="handle" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="pubTime">
+                <column name="published" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="permalink">
+                <column name="permalink" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="title">
+                <column name="title" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="updateTime">
+                <column name="updated" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <many-to-one name="subscription"
+                         target-entity="org.apache.roller.planet.pojos.PlanetSubscriptionData">
+                <join-column name="subscription_id" insertable="true" updatable="true"
+                             nullable="false"/>
+            </many-to-one>
+        </attributes>
+    </entity>
+</entity-mappings>

Added: incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/planet/pojos/PlanetGroupData.orm.xml
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/planet/pojos/PlanetGroupData.orm.xml?view=auto&rev=482428
==============================================================================
--- incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/planet/pojos/PlanetGroupData.orm.xml (added)
+++ incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/planet/pojos/PlanetGroupData.orm.xml Mon Dec  4 16:26:15 2006
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entity-mappings version="1.0" xmlns="http://java.sun.com/xml/ns/persistence/orm"
+                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
+ orm_1_0.xsd">
+    <description>Persistence Metadata for Roller</description>
+    <persistence-unit-metadata>
+        <persistence-unit-defaults>
+            <access>PROPERTY</access>
+        </persistence-unit-defaults>
+    </persistence-unit-metadata>
+    <package>org.apache.roller.pojos</package>
+    <entity metadata-complete="true" name="" class="org.apache.roller.planet.pojos.PlanetGroupData">
+        <table name="rag_group"/>
+        <named-query name="PlanetGroupData.getByHandle">
+            <query>SELECT p FROM PlanetGroupData p WHERE p.handle = ?1</query>
+        </named-query>
+        <named-query name="PlanetGroupData.getAll">
+            <query>SELECT p FROM PlanetGroupData p</query>
+        </named-query>
+        <attributes>
+            <id name="id">
+                <column name="id"/>
+            </id>
+            <basic name="categoryRestriction">
+                <column name="cat_restriction" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="description">
+                <column name="description" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="handle">
+                <column name="handle" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="maxFeedEntries">
+                <column name="max_feed_entries" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="maxPageEntries">
+                <column name="max_page_entries" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="title">
+                <column name="title" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <many-to-many name="subscriptions"
+                          target-entity="org.apache.roller.planet.pojos.PlanetSubscriptionData"
+                          mapped-by="groups"/>
+        </attributes>
+    </entity>
+</entity-mappings>

Added: incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/planet/pojos/PlanetSubscriptionData.orm.xml
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/planet/pojos/PlanetSubscriptionData.orm.xml?view=auto&rev=482428
==============================================================================
--- incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/planet/pojos/PlanetSubscriptionData.orm.xml (added)
+++ incubator/roller/trunk/sandbox/jdobackend/src/org/apache/roller/planet/pojos/PlanetSubscriptionData.orm.xml Mon Dec  4 16:26:15 2006
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entity-mappings version="1.0" xmlns="http://java.sun.com/xml/ns/persistence/orm"
+                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
+ orm_1_0.xsd">
+    <description>Persistence Metadata for Roller</description>
+    <persistence-unit-metadata>
+        <persistence-unit-defaults>
+            <access>PROPERTY</access>
+        </persistence-unit-defaults>
+    </persistence-unit-metadata>
+    <package>org.apache.roller.pojos</package>
+    <entity metadata-complete="true" name=""
+            class="org.apache.roller.planet.pojos.PlanetSubscriptionData">
+        <table name="rag_subscription"/>
+        <named-query name="PlanetSubscriptionData.getAll">
+            <query>SELECT p FROM PlanetSubscriptionData p</query>
+        </named-query>
+        <named-query name="PlanetSubscriptionData.getAllOrderByInboundBlogsDesc">
+            <query>SELECT p FROM PlanetSubscriptionData p ORDER BY p.inboundblogs DESC</query>
+        </named-query>
+        <named-query name="PlanetSubscriptionData.getByGroupHandleOrderByInboundBlogsDesc">
+            <query>SELECT p FROM PlanetSubscriptionData p JOIN p.groups g WHERE g.handle = ?1 ORDER
+                BY p.inboundblogs DESC
+            </query>
+        </named-query>
+        <named-query name="PlanetSubscriptionData.getByFeedURL">
+            <query>SELECT p FROM PlanetSubscriptionData p WHERE p.feedURL = ?1</query>
+        </named-query>
+        <attributes>
+            <id name="id">
+                <column name="id"/>
+            </id>
+            <basic name="feedURL">
+                <column name="feed_url" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="lastUpdated">
+                <column name="last_updated" insertable="true" updatable="true" unique="false"/>
+                <temporal>TIMESTAMP</temporal>
+            </basic>
+            <basic name="siteURL">
+                <column name="site_url" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="title">
+                <column name="title" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="author">
+                <column name="author" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="inboundlinks">
+                <column name="inbound_links" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <basic name="inboundblogs">
+                <column name="inbound_blogs" insertable="true" updatable="true" unique="false"/>
+            </basic>
+            <many-to-many name="groups"
+                          target-entity="org.apache.roller.planet.pojos.PlanetGroupData">
+                <join-table name="rag_group_subscription">
+                    <join-column name="subscription_id"/>
+                    <inverse-join-column name="group_id"/>
+                </join-table>
+            </many-to-many>
+        </attributes>
+    </entity>
+</entity-mappings>