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 2007/02/24 22:22:41 UTC

svn commit: r511352 [1/3] - in /incubator/roller/branches/roller_4.0: src/org/apache/roller/business/ src/org/apache/roller/business/jpa/ src/org/apache/roller/pojos/ tests/org/apache/roller/business/ web/WEB-INF/classes/

Author: snoopdave
Date: Sat Feb 24 13:22:40 2007
New Revision: 511352

URL: http://svn.apache.org/viewvc?view=rev&rev=511352
Log:
Straight-to-JPA backend implementation now in place, passing 75 of 78 unit tests (96.15%)

Added:
    incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/
    incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAAutoPingManagerImpl.java
    incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPABookmarkManagerImpl.java
    incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAPingQueueManagerImpl.java
    incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAPingTargetManagerImpl.java
    incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAPropertiesManagerImpl.java
    incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPARefererManagerImpl.java
    incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPARollerImpl.java
    incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAThreadManagerImpl.java
    incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAUserManagerImpl.java
    incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAWeblogManagerImpl.java
Modified:
    incubator/roller/branches/roller_4.0/src/org/apache/roller/business/RollerFactory.java
    incubator/roller/branches/roller_4.0/src/org/apache/roller/pojos/WeblogEntryData.orm.xml
    incubator/roller/branches/roller_4.0/tests/org/apache/roller/business/WeblogEntryTest.java
    incubator/roller/branches/roller_4.0/web/WEB-INF/classes/JPAEMF.properties
    incubator/roller/branches/roller_4.0/web/WEB-INF/classes/roller.properties

Modified: incubator/roller/branches/roller_4.0/src/org/apache/roller/business/RollerFactory.java
URL: http://svn.apache.org/viewvc/incubator/roller/branches/roller_4.0/src/org/apache/roller/business/RollerFactory.java?view=diff&rev=511352&r1=511351&r2=511352
==============================================================================
--- incubator/roller/branches/roller_4.0/src/org/apache/roller/business/RollerFactory.java (original)
+++ incubator/roller/branches/roller_4.0/src/org/apache/roller/business/RollerFactory.java Sat Feb 24 13:22:40 2007
@@ -31,7 +31,8 @@
     private static Log log = LogFactory.getLog(RollerFactory.class);
     
     private static final String DEFAULT_IMPL =
-        "org.apache.roller.business.datamapper.jpa.JPARollerImpl";
+        "org.apache.roller.business.jpa.JPARollerImpl";
+        //"org.apache.roller.business.datamapper.jpa.JPARollerImpl";
         //"org.apache.roller.business.hibernate.HibernateRollerImpl";
     
     private static Roller rollerInstance = null;

Added: incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAAutoPingManagerImpl.java
URL: http://svn.apache.org/viewvc/incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAAutoPingManagerImpl.java?view=auto&rev=511352
==============================================================================
--- incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAAutoPingManagerImpl.java (added)
+++ incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAAutoPingManagerImpl.java Sat Feb 24 13:22:40 2007
@@ -0,0 +1,144 @@
+
+/*
+ * 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.business.jpa;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.roller.RollerException;
+import org.apache.roller.business.RollerFactory;
+import org.apache.roller.business.pings.AutoPingManager;
+import org.apache.roller.business.pings.PingQueueManager;
+import org.apache.roller.config.PingConfig;
+import org.apache.roller.pojos.AutoPingData;
+import org.apache.roller.pojos.PingTargetData;
+import org.apache.roller.pojos.WeblogEntryData;
+import org.apache.roller.pojos.WebsiteData;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.persistence.Query;
+
+/*
+ * JPAAutoPingManagerImpl.java
+ *
+ * Created on May 29, 2006, 11:29 AM
+ *
+ */
+public class JPAAutoPingManagerImpl implements AutoPingManager {
+
+    private JPAPersistenceStrategy strategy;
+    
+    /**
+     * The logger instance for this class.
+     */
+    private static Log logger = LogFactory
+            .getFactory().getInstance(JPAAutoPingManagerImpl.class);
+
+    /**
+     * Creates a new instance of JPAAutoPingManagerImpl
+     */
+    public JPAAutoPingManagerImpl
+            (JPAPersistenceStrategy strategy) {
+        this.strategy = strategy;
+    }
+
+    public AutoPingData getAutoPing(String id) throws RollerException {
+        return (AutoPingData)strategy.load(AutoPingData.class, id);
+    }
+
+    public void saveAutoPing(AutoPingData autoPing) throws RollerException {
+        strategy.store(autoPing);
+    }
+
+    public void removeAutoPing(AutoPingData autoPing) throws RollerException {
+        strategy.remove(autoPing);
+    }
+
+    public void removeAutoPing(PingTargetData pingTarget, WebsiteData website)
+            throws RollerException {
+        Query q = strategy.getNamedQuery("AutoPingData.removeByPingTarget&Website");
+        q.setParameter(1, pingTarget);
+        q.setParameter(2, website);
+        q.executeUpdate();
+    }
+
+    public void removeAutoPings(Collection autopings) 
+            throws RollerException {
+        strategy.removeAll(autopings);
+    }
+
+    public void removeAllAutoPings() 
+            throws RollerException {
+        strategy.removeAll(AutoPingData.class);
+    }
+
+    public void queueApplicableAutoPings(WeblogEntryData changedWeblogEntry)
+            throws RollerException {
+        if (PingConfig.getSuspendPingProcessing()) {
+            if (logger.isDebugEnabled())
+                logger.debug("Ping processing is suspended." +
+                    " No auto pings will be queued.");
+            return;
+        }
+
+        PingQueueManager pingQueueMgr = RollerFactory.getRoller().
+            getPingQueueManager();
+        List applicableAutopings = getApplicableAutoPings(changedWeblogEntry);
+        for (Iterator i = applicableAutopings.iterator(); i.hasNext(); ) {
+            AutoPingData autoPing = (AutoPingData) i.next();
+            pingQueueMgr.addQueueEntry(autoPing);
+        }
+    }
+
+    public List getAutoPingsByWebsite(WebsiteData website)
+            throws RollerException {
+        Query q = strategy.getNamedQuery("AutoPingData.getByWebsite");
+        q.setParameter(1, website);
+        return q.getResultList();
+    }
+
+    public List getAutoPingsByTarget(PingTargetData pingTarget) 
+            throws RollerException {
+        Query q = strategy.getNamedQuery("AutoPingData.getByPingTarget");
+        q.setParameter(1, pingTarget);
+        return q.getResultList();
+    }
+
+    public List getApplicableAutoPings(WeblogEntryData changedWeblogEntry) 
+            throws RollerException {
+        return getAutoPingsByWebsite(changedWeblogEntry.getWebsite());
+        //        return (List)strategy.newQuery(AutoPingData.class, "AutoPingData.getByWebsite")
+        //            .execute(changedWeblogEntry.getWebsite());
+    }
+
+    public List getCategoryRestrictions(AutoPingData autoPing)
+            throws RollerException {
+        return Collections.EMPTY_LIST;
+    }
+
+    public void setCategoryRestrictions
+            (AutoPingData autoPing, Collection newCategories) {
+        // NOT YET IMPLEMENTED
+    }
+
+    public void release() {}
+    
+}

Added: incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPABookmarkManagerImpl.java
URL: http://svn.apache.org/viewvc/incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPABookmarkManagerImpl.java?view=auto&rev=511352
==============================================================================
--- incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPABookmarkManagerImpl.java (added)
+++ incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPABookmarkManagerImpl.java Sat Feb 24 13:22:40 2007
@@ -0,0 +1,382 @@
+
+/*
+ * 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.business.jpa;
+
+import java.io.StringReader;
+import java.util.Iterator;
+import java.util.List;
+import javax.persistence.NoResultException;
+import javax.persistence.Query;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.roller.RollerException;
+import org.apache.roller.business.BookmarkManager;
+import org.apache.roller.business.RollerFactory;
+import org.apache.roller.pojos.BookmarkData;
+import org.apache.roller.pojos.FolderData;
+import org.apache.roller.pojos.WebsiteData;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.input.SAXBuilder;
+
+/*
+ * JPABookmarkManagerImpl.java
+ *
+ * Created on May 31, 2006, 3:49 PM
+ *
+ */
+public class JPABookmarkManagerImpl implements BookmarkManager {
+    
+    private JPAPersistenceStrategy strategy;
+    
+    /**
+     * The logger instance for this class.
+     */
+    private static Log log = LogFactory
+            .getFactory().getInstance(JPABookmarkManagerImpl.class);
+
+    /**
+     * Creates a new instance of JPABookmarkManagerImpl
+     */
+    public JPABookmarkManagerImpl 
+            (JPAPersistenceStrategy strategy) {
+        log.debug("Instantiating JPA Bookmark Manager");
+
+        this.strategy = strategy;
+    }
+
+    public void saveBookmark(BookmarkData bookmark) throws RollerException {
+        if (bookmark.getId() == null) { //(!PersistentObjectHelper.isObjectPersistent(bookmark)) {
+            // This is a new object make sure that relationship is set on managed
+            // copy of other side
+            bookmark.getFolder().getBookmarks().add(bookmark);
+        }
+
+        this.strategy.store(bookmark);
+
+        // update weblog last modified date (date is updated by saveWebsite())
+        RollerFactory.getRoller().getUserManager().
+            saveWebsite(bookmark.getWebsite());
+    }
+
+    public BookmarkData getBookmark(String id) throws RollerException {
+        return (BookmarkData) strategy.load(BookmarkData.class, id);
+    }
+
+    public void removeBookmark(BookmarkData bookmark) throws RollerException {
+        //Now remove it from database
+        this.strategy.remove(bookmark);
+        //Remove the bookmark from its parent folder
+        bookmark.getFolder().getBookmarks().remove(bookmark);
+        // update weblog last modified date.  date updated by saveWebsite()
+        RollerFactory.getRoller().getUserManager()
+                .saveWebsite(bookmark.getWebsite());
+    }
+
+    public void saveFolder(FolderData folder) throws RollerException {
+        
+        if(folder.getId() == null && isDuplicateFolderName(folder)) {
+            throw new RollerException("Duplicate folder name");
+        }
+
+        if (folder.getId() == null) { //(!PersistentObjectHelper.isObjectPersistent(folder)) {
+            // Newly added object. If it has a parent,
+            // maintain relationship from both sides
+            FolderData parent = folder.getParent();
+            if(parent != null) {
+                parent.getFolders().add(folder);
+            }
+        }
+
+        this.strategy.store(folder);
+
+        // update weblog last modified date.  date updated by saveWebsite()
+        RollerFactory.getRoller().getUserManager().
+            saveWebsite(folder.getWebsite());
+    }
+
+    public void removeFolder(FolderData folder) throws RollerException {
+        this.strategy.remove(folder);
+        FolderData parent = folder.getParent();
+        if (parent != null) {
+            parent.getFolders().remove(folder);
+        }
+
+        // update weblog last modified date.  date updated by saveWebsite()
+        RollerFactory.getRoller().getUserManager().
+            saveWebsite(folder.getWebsite());
+    }
+    
+    public void moveFolder(FolderData srcFolder, FolderData destFolder)
+            throws RollerException {
+        
+        // TODO: this check should be made before calling this method?
+        if (destFolder.descendentOf(srcFolder)) {
+            throw new RollerException(
+                    "ERROR cannot move parent folder into it's own child");
+        }
+        
+        log.debug("Moving folder " + srcFolder.getPath() + " under " +
+            destFolder.getPath());
+        
+        // Manage relationships
+        FolderData oldParent = srcFolder.getParent();
+        if(oldParent != null) {
+            oldParent.getFolders().add(srcFolder);
+        }
+        srcFolder.setParent(destFolder);
+        destFolder.getFolders().add(srcFolder);
+        
+        if("/".equals(destFolder.getPath())) {
+            srcFolder.setPath("/"+srcFolder.getName());
+        } else {
+            srcFolder.setPath(destFolder.getPath() + "/" + srcFolder.getName());
+        }
+        saveFolder(srcFolder);
+        
+        // the main work to be done for a category move is to update the 
+        // path attribute of the category and all descendent categories
+        updatePathTree(srcFolder);
+    }    
+
+    // updates the paths of all descendents of the given folder
+    private void updatePathTree(FolderData folder) throws RollerException {
+        
+        log.debug("Updating path tree for folder "+folder.getPath());
+        
+        FolderData childFolder = null;
+        Iterator childFolders = folder.getFolders().iterator();
+        while(childFolders.hasNext()) {
+            childFolder = (FolderData) childFolders.next();
+            
+            log.debug("OLD child folder path was "+childFolder.getPath());
+            
+            // update path and save
+            if("/".equals(folder.getPath())) {
+                childFolder.setPath("/" + childFolder.getName());
+            } else {
+                childFolder.setPath(folder.getPath() + "/" + 
+                    childFolder.getName());
+            }
+            saveFolder(childFolder);
+            
+            log.debug("NEW child folder path is "+ childFolder.getPath());
+            
+            // then make recursive call to update this folders children
+            updatePathTree(childFolder);
+        }
+    }
+
+    
+    /**
+     * Retrieve folder and lazy-load it's sub-folders and bookmarks.
+     */
+    public FolderData getFolder(String id) throws RollerException {
+        return (FolderData) strategy.load(FolderData.class, id);
+    }
+
+    
+    public void importBookmarks(
+            WebsiteData website, String folderName, String opml)
+            throws RollerException {
+        String msg = "importBookmarks";
+        try {
+            // Build JDOC document OPML string
+            SAXBuilder builder = new SAXBuilder();
+            StringReader reader = new StringReader( opml );
+            Document doc = builder.build( reader );
+
+            FolderData newFolder = getFolder(website, folderName);
+            if (newFolder == null) {
+                newFolder = new FolderData(
+                        getRootFolder(website), 
+                        folderName, folderName, website);
+                this.strategy.store(newFolder);
+            }
+
+            // Iterate through children of OPML body, importing each
+            Element body = doc.getRootElement().getChild("body");
+            Iterator iter = body.getChildren().iterator();
+            while (iter.hasNext()) {
+                Element elem = (Element)iter.next();
+                importOpmlElement( website, elem, newFolder );
+            }
+
+        } catch (Exception ex) {
+            throw new RollerException(ex);
+        }
+    }
+
+    // convenience method used when importing bookmarks
+    // NOTE: this method does not commit any changes; 
+    // that is done by importBookmarks()
+    private void importOpmlElement(
+            WebsiteData website, Element elem, FolderData parent)
+            throws RollerException {
+        String text = elem.getAttributeValue("text");
+        String title = elem.getAttributeValue("title");
+        String desc = elem.getAttributeValue("description");
+        String url = elem.getAttributeValue("url");
+        //String type = elem.getAttributeValue("type");
+        String xmlUrl = elem.getAttributeValue("xmlUrl");
+        String htmlUrl = elem.getAttributeValue("htmlUrl");
+
+        title =   null!=title ? title : text;
+        desc =    null!=desc ? desc : title;
+        xmlUrl =  null!=xmlUrl ? xmlUrl : url;
+        url =     null!=htmlUrl ? htmlUrl : url;
+
+        if (elem.getChildren().size()==0) {
+            // Leaf element.  Store a bookmark
+            // Currently bookmarks must have at least a name and 
+            // HTML url to be stored. Previous logic was
+            // trying to skip invalid ones, but was letting ones 
+            // with an xml url and no html url through
+            // which could result in a db exception.
+            // TODO: Consider providing error feedback instead of 
+            // silently skipping the invalid bookmarks here.
+            if (null != title && null != url) {
+                BookmarkData bd = new BookmarkData(parent,
+                        title,
+                        desc,
+                        url,
+                        xmlUrl,
+                        new Integer(0),
+                        new Integer(100),
+                        null);
+                parent.addBookmark(bd);
+                // TODO: maybe this should be saving the folder?
+                this.strategy.store(bd);
+            }
+        } else {
+            // Store a folder
+            FolderData fd = new FolderData(
+                    parent,
+                    title,
+                    desc,
+                    parent.getWebsite());
+            this.strategy.store(fd);
+
+            // Import folder's children
+            Iterator iter = elem.getChildren("outline").iterator();
+            while ( iter.hasNext() ) {
+                Element subelem = (Element)iter.next();
+                importOpmlElement( website, subelem, fd  );
+            }
+        }
+    }
+
+
+    public FolderData getFolder(WebsiteData website, String path)
+            throws RollerException {
+
+        if (path == null || path.trim().equals("/")) {
+            return getRootFolder(website);
+        } else {
+            String folderPath = path;
+
+            // all folder paths must begin with a '/'
+            if(!folderPath.startsWith("/")) {
+                folderPath = "/"+folderPath;
+            }
+
+            // now just do simple lookup by path
+            Query query = strategy.getNamedQuery("FolderData.getByWebsite&Path");
+            query.setParameter(1, website);
+            query.setParameter(2, folderPath);
+            try {
+                return (FolderData)query.getSingleResult();
+            } catch (NoResultException e) {
+                return null;
+            }
+        }
+    }
+
+    /**
+     * @see org.apache.roller.model.BookmarkManager#retrieveBookmarks(
+     *      org.apache.roller.pojos.FolderData, boolean)
+     */
+    public List getBookmarks(FolderData folder, boolean subfolders) 
+            throws RollerException {
+        Query query = null;
+        List results = null;
+
+        if(!subfolders) {
+            // if no subfolders then this is an equals query
+            query = strategy.getNamedQuery("BoomarkData.getByFolder");
+            query.setParameter(1, folder);
+            results = query.getResultList();
+        } else {
+            // if we are doing subfolders then do a case sensitive
+            // query using folder path
+            query = strategy.getNamedQuery( 
+                "BoomarkData.getByFolder.pathLike&Folder.website");
+            query.setParameter(1, folder.getPath() + '%');
+            query.setParameter(2, folder.getWebsite());
+            results = query.getResultList();
+        }
+            
+        return results;
+    }
+
+    public FolderData getRootFolder(WebsiteData website)
+            throws RollerException {
+        if (website == null)
+            throw new RollerException("website is null");
+        
+        Query q = strategy.getNamedQuery("FolderData.getByWebsite&ParentNull");
+        q.setParameter(1, website);
+        try {
+            return (FolderData)q.getSingleResult();
+        } catch (NoResultException e) {
+            return null;
+        }
+    }
+
+    public List getAllFolders(WebsiteData website)
+            throws RollerException {
+        if (website == null)
+            throw new RollerException("Website is null");
+        
+        Query q = strategy.getNamedQuery("FolderData.getByWebsite");
+        q.setParameter(1, website);
+        return q.getResultList();
+    }
+
+    
+    /**
+     * make sure the given folder doesn't already exist.
+     */
+    private boolean isDuplicateFolderName(FolderData folder) 
+        throws RollerException {
+
+        // ensure that no sibling categories share the same name
+        FolderData parent = folder.getParent();
+        if (null != parent) {
+            return (getFolder(folder.getWebsite(), folder.getPath()) != null);
+        }
+        
+        return false;
+    }
+
+
+    public void release() {}
+    
+}

Added: incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAPingQueueManagerImpl.java
URL: http://svn.apache.org/viewvc/incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAPingQueueManagerImpl.java?view=auto&rev=511352
==============================================================================
--- incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAPingQueueManagerImpl.java (added)
+++ incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAPingQueueManagerImpl.java Sat Feb 24 13:22:40 2007
@@ -0,0 +1,115 @@
+
+/*
+ * 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.business.jpa;
+
+import java.sql.Timestamp;
+import java.util.List;
+import javax.persistence.Query;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.roller.RollerException;
+
+import org.apache.roller.business.pings.PingQueueManager;
+import org.apache.roller.pojos.AutoPingData;
+import org.apache.roller.pojos.PingQueueEntryData;
+
+/*
+ * JPAPingQueueManagerImpl.java
+ *
+ * Created on May 28, 2006, 4:11 PM
+ *
+ */
+public class JPAPingQueueManagerImpl implements PingQueueManager {
+
+    private static Log log = LogFactory.getLog(
+        JPAPingQueueManagerImpl.class);
+
+    /** The strategy for this manager. */
+    private JPAPersistenceStrategy strategy;
+
+    /**
+     * Creates a new instance of JPAPingQueueManagerImpl
+     */
+    public JPAPingQueueManagerImpl(
+            JPAPersistenceStrategy strategy) {
+        this.strategy =  strategy;
+    }
+
+    public PingQueueEntryData getQueueEntry(String id) 
+            throws RollerException {
+        return (PingQueueEntryData)strategy.load(
+            PingQueueEntryData.class, id);
+    }
+
+    public void saveQueueEntry(PingQueueEntryData pingQueueEntry) 
+            throws RollerException {
+        log.debug("Storing ping queue entry: " + pingQueueEntry);
+        strategy.store(pingQueueEntry);
+    }
+
+    public void removeQueueEntry(PingQueueEntryData pingQueueEntry) 
+            throws RollerException {
+        log.debug("Removing ping queue entry: " + pingQueueEntry);
+        strategy.remove(pingQueueEntry);
+    }
+
+    
+    public void addQueueEntry(AutoPingData autoPing) throws RollerException {
+        log.debug("Creating new ping queue entry for auto ping configuration: " 
+            + autoPing);
+        
+        // First check if there is an existing ping queue entry 
+        // for the same target and website
+        if (isAlreadyQueued(autoPing)) {
+            log.debug("A ping queue entry is already present" +
+                " for this ping target and website: " + autoPing);
+            return;
+        }
+
+        Timestamp now = new Timestamp(System.currentTimeMillis());
+        PingQueueEntryData pingQueueEntry =
+                new PingQueueEntryData(
+                    null, now, autoPing.getPingTarget(), 
+                    autoPing.getWebsite(), 0);
+        this.saveQueueEntry(pingQueueEntry);
+    }
+
+    public List getAllQueueEntries() 
+            throws RollerException {
+        return (List)strategy.getNamedQuery(
+                "PingQueueEntryData.getAllOrderByEntryTime").getResultList();
+    }
+
+    // private helper to determine if an has already been queued 
+    // for the same website and ping target.
+    private boolean isAlreadyQueued(AutoPingData autoPing) 
+        throws RollerException {
+        // first, determine if an entry already exists
+        Query q = strategy.getNamedQuery("PingQueueEntryData.getByPingTarget&Website");
+        q.setParameter(1, autoPing.getPingTarget());
+        q.setParameter(2, autoPing.getWebsite());
+        return q.getResultList().size() > 0;
+    }
+
+    public void release() {}
+    
+
+}

Added: incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAPingTargetManagerImpl.java
URL: http://svn.apache.org/viewvc/incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAPingTargetManagerImpl.java?view=auto&rev=511352
==============================================================================
--- incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAPingTargetManagerImpl.java (added)
+++ incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAPingTargetManagerImpl.java Sat Feb 24 13:22:40 2007
@@ -0,0 +1,184 @@
+
+/*
+ * 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.business.jpa;
+
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.List;
+import java.util.Iterator;
+import javax.persistence.Query;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.roller.RollerException;
+
+import org.apache.roller.business.pings.PingTargetManager;
+import org.apache.roller.pojos.AutoPingData;
+import org.apache.roller.pojos.PingTargetData;
+import org.apache.roller.pojos.WebsiteData;
+
+/*
+ * JPAPingTargetManagerImpl.java
+ *
+ * Created on May 29, 2006, 2:24 PM
+ *
+ */
+public class JPAPingTargetManagerImpl implements PingTargetManager {
+    
+    /** The logger instance for this class. */
+    private static Log log = LogFactory.getLog(
+        JPAPingTargetManagerImpl.class);
+
+    private JPAPersistenceStrategy strategy;
+    
+    public JPAPingTargetManagerImpl(
+            JPAPersistenceStrategy strategy) {
+        this.strategy = strategy;
+    }
+
+    public void removePingTarget(PingTargetData pingTarget) 
+            throws RollerException {
+        // remove contents and then target
+        this.removePingTargetContents(pingTarget);
+        this.strategy.remove(pingTarget);
+    }
+
+    /**
+     * Convenience method which removes any queued pings or auto pings that
+     * reference the given ping target.
+     */
+    private void removePingTargetContents(PingTargetData ping) 
+            throws RollerException {
+        // Remove the website's ping queue entries
+        Query q = strategy.getNamedQuery("PingQueueEntryData.removeByPingTarget");
+        q.setParameter(1, ping);
+        q.executeUpdate();
+        
+        // Remove the website's auto ping configurations
+        q = strategy.getNamedQuery("AutoPingData.removeByPingTarget");
+        q.setParameter(1, ping);
+        q.executeUpdate();
+    }
+
+    public void removeAllCustomPingTargets()
+            throws RollerException {
+        Query q = strategy.getNamedQuery(
+            "PingTargetData.removeByWebsiteNotNull");
+        q.executeUpdate();
+    }
+
+    public void savePingTarget(PingTargetData pingTarget)
+            throws RollerException {
+        strategy.store(pingTarget);
+    }
+
+    public PingTargetData getPingTarget(String id)
+            throws RollerException {
+        return (PingTargetData)strategy.load(PingTargetData.class, id);
+    }
+
+    public boolean isNameUnique(PingTargetData pingTarget) 
+            throws RollerException {
+        String name = pingTarget.getName();
+        if (name == null || name.trim().length() == 0) return false;
+        
+        String id = pingTarget.getId();
+        
+        // Determine the set of "brother" targets (custom or common) 
+        // among which this name should be unique.
+        List brotherTargets = null;
+        WebsiteData website = pingTarget.getWebsite();
+        if (website == null) {
+            brotherTargets = getCommonPingTargets();
+        } else {
+            brotherTargets = getCustomPingTargets(website);
+        }
+        
+        // Within that set of targets, fail if there is a target 
+        // with the same name and that target doesn't
+        // have the same id.
+        for (Iterator i = brotherTargets.iterator(); i.hasNext();) {
+            PingTargetData brother = (PingTargetData) i.next();
+            // Fail if it has the same name but not the same id.
+            if (brother.getName().equals(name) && 
+                (id == null || !brother.getId().equals(id))) {
+                return false;
+            }
+        }
+        // No conflict found
+        return true;
+    }
+
+    
+    public boolean isUrlWellFormed(PingTargetData pingTarget) 
+            throws RollerException {
+        String url = pingTarget.getPingUrl();
+        if (url == null || url.trim().length() == 0) return false;
+        try {
+            URL parsedUrl = new URL(url);
+            // OK.  If we get here, it parses ok.  Now just check 
+            // that the protocol is http and there is a host portion.
+            boolean isHttp = parsedUrl.getProtocol().equals("http");
+            boolean hasHost = (parsedUrl.getHost() != null) && 
+                (parsedUrl.getHost().trim().length() > 0);
+            return isHttp && hasHost;
+        } catch (MalformedURLException e) {
+            return false;
+        }
+    }
+
+    
+    public boolean isHostnameKnown(PingTargetData pingTarget) 
+            throws RollerException {
+        String url = pingTarget.getPingUrl();
+        if (url == null || url.trim().length() == 0) return false;
+        try {
+            URL parsedUrl = new URL(url);
+            String host = parsedUrl.getHost();
+            if (host == null || host.trim().length() == 0) return false;
+            InetAddress addr = InetAddress.getByName(host);
+            return true;
+        } catch (MalformedURLException e) {
+            return false;
+        } catch (UnknownHostException e) {
+            return false;
+        }
+    }
+
+    public List getCommonPingTargets()
+            throws RollerException {
+        Query q = strategy.getNamedQuery(
+                "PingTargetData.getByWebsiteNullOrderByName");
+        return q.getResultList();
+    }
+
+    public List getCustomPingTargets(WebsiteData website)
+            throws RollerException {
+        Query q = strategy.getNamedQuery(
+                "PingTargetData.getByWebsiteOrderByName");
+        q.setParameter(1, website);
+        return q.getResultList();
+    }
+
+    public void release() {}
+    
+}

Added: incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAPropertiesManagerImpl.java
URL: http://svn.apache.org/viewvc/incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAPropertiesManagerImpl.java?view=auto&rev=511352
==============================================================================
--- incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAPropertiesManagerImpl.java (added)
+++ incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPAPropertiesManagerImpl.java Sat Feb 24 13:22:40 2007
@@ -0,0 +1,312 @@
+
+/*
+ * 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.business.jpa;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.roller.RollerException;
+
+import org.apache.roller.business.PropertiesManager;
+import org.apache.roller.business.Roller;
+import org.apache.roller.business.RollerFactory;
+import org.apache.roller.config.RollerRuntimeConfig;
+import org.apache.roller.config.runtime.ConfigDef;
+import org.apache.roller.config.runtime.DisplayGroup;
+import org.apache.roller.config.runtime.PropertyDef;
+import org.apache.roller.config.runtime.RuntimeConfigDefs;
+import org.apache.roller.pojos.RollerConfigData;
+import org.apache.roller.pojos.RollerPropertyData;
+import org.apache.roller.business.jpa.JPAPersistenceStrategy;
+
+
+/*
+ * JPAPropertiesManagerImpl.java
+ *
+ * Created on May 29, 2006, 2:06 PM
+ *
+ */
+public class JPAPropertiesManagerImpl implements PropertiesManager {
+    
+    /** The logger instance for this class. */
+    private static Log log = LogFactory.getLog(
+        JPAPropertiesManagerImpl.class);
+
+    private JPAPersistenceStrategy strategy;
+    
+    /**
+     * Creates a new instance of JPAPropertiesManagerImpl
+     */
+    public JPAPropertiesManagerImpl (
+            JPAPersistenceStrategy strategy) {
+        log.debug("Instantiating JPA Properties Manager");
+
+        this.strategy = strategy;
+
+        // TODO: and new method initialize(props)
+        init();
+    }
+
+    /**
+     * Retrieve a single property by name.
+     */
+    public RollerPropertyData getProperty(String name) throws RollerException {
+        return (RollerPropertyData) strategy
+            .load(RollerPropertyData.class,name);
+    }
+
+
+    /**
+     * Retrieve all properties.
+     *
+     * Properties are returned in a Map to make them easy to lookup.  The Map
+     * uses the property name as the key and the RollerPropertyData object
+     * as the value.
+     */
+    public Map getProperties() throws RollerException {
+
+        HashMap props = new HashMap();
+        List list = (List) strategy.getNamedQuery("RollerPropertyData.getAll").getResultList();
+        /*
+         * for convenience sake we are going to put the list of props
+         * into a map for users to access it.  The value element of the
+         * hash still needs to be the RollerPropertyData object so that
+         * we can save the elements again after they have been updated
+         */
+        RollerPropertyData prop = null;
+        Iterator it = list.iterator();
+        while(it.hasNext()) {
+            prop = (RollerPropertyData) it.next();
+            props.put(prop.getName(), prop);
+        }
+
+        return props;
+    }
+
+
+    /**
+     * Save a single property.
+     */
+    public void saveProperty(RollerPropertyData property) 
+            throws RollerException {
+        this.strategy.store(property);
+    }
+
+
+    /**
+     * Save all properties.
+     */
+    public void saveProperties(Map properties) throws RollerException {
+
+        // just go through the list and saveProperties each property
+        Iterator props = properties.values().iterator();
+        while (props.hasNext()) {
+            this.strategy.store((RollerPropertyData) props.next());
+        }
+    }
+
+
+    private void init() {
+        Map props = null;
+        try {
+            props = this.getProperties();
+
+            if(props.size() < 1) {
+                // empty props table ... try migrating, then load defaults
+                props = migrateOldRollerConfig(props);
+                props = initializeMissingProps(props);
+            } else {
+                // found existing props ... check for new props
+                props = initializeMissingProps(props);
+            }
+
+            // save our changes
+            this.saveProperties(props);
+        } catch (Exception e) {
+            log.fatal("Failed to initialize runtime configuration properties."+
+                    "Please check that the database has been upgraded!", e);
+            throw new RuntimeException(e);
+        }
+
+    }
+
+
+    /**
+     * Migrate data from the old roller config.
+     * This is called only if the existing runtime properties are empty.
+     */
+    private Map migrateOldRollerConfig(Map props) {
+        // try to get the old config
+        Roller roller = RollerFactory.getRoller();
+        RollerConfigData rollerConfig = null;
+
+        try {
+            rollerConfig = roller.getConfigManager().getRollerConfig();
+        } catch (Exception e) {
+            // We currently treat any exception obtaining the roller config
+            // as if we had not found it.
+            log.error(e);
+        }
+
+        if (rollerConfig != null) {
+            log.info("Found old roller config ... " + 
+                "doing migration to new runtime properties.");
+            // copy over data
+            props.put("site.name",
+                new RollerPropertyData("site.name", 
+                    rollerConfig.getSiteName()));
+            props.put("site.description",
+                new RollerPropertyData("site.description", 
+                    rollerConfig.getSiteDescription()));
+            props.put("site.adminemail",
+                new RollerPropertyData("site.adminemail", 
+                    rollerConfig.getEmailAddress()));
+            props.put("site.absoluteurl",
+                new RollerPropertyData("site.absoluteurl", 
+                    rollerConfig.getAbsoluteURL()));
+            props.put("site.linkbacks.enabled",
+                new RollerPropertyData("site.linkbacks.enabled", 
+                    rollerConfig.getEnableLinkback().toString()));
+            props.put("users.registration.enabled",
+                new RollerPropertyData("users.registration.enabled", 
+                    rollerConfig.getNewUserAllowed().toString()));
+            props.put("users.themes.path",
+                new RollerPropertyData("users.themes.path", 
+                    rollerConfig.getUserThemes()));
+            props.put("users.editor.pages",
+                new RollerPropertyData("users.editor.pages", 
+                    rollerConfig.getEditorPages()));
+            props.put("users.comments.enabled",
+                new RollerPropertyData("users.comments.enabled", "true"));
+            props.put("users.comments.autoformat",
+                new RollerPropertyData("users.comments.autoformat", 
+                    rollerConfig.getAutoformatComments().toString()));
+            props.put("users.comments.escapehtml",
+                new RollerPropertyData("users.comments.escapehtml", 
+                    rollerConfig.getEscapeCommentHtml().toString()));
+            props.put("users.comments.emailnotify",
+                new RollerPropertyData("users.comments.emailnotify", 
+                    rollerConfig.getEmailComments().toString()));
+            props.put("uploads.enabled",
+                new RollerPropertyData("uploads.enabled", 
+                    rollerConfig.getUploadEnabled().toString()));
+            props.put("uploads.types.allowed",
+                new RollerPropertyData("uploads.types.allowed", 
+                    rollerConfig.getUploadAllow()));
+            props.put("uploads.types.forbid",
+                new RollerPropertyData("uploads.types.forbid", 
+                    rollerConfig.getUploadForbid()));
+            props.put("uploads.file.maxsize",
+                new RollerPropertyData("uploads.file.maxsize", 
+                    rollerConfig.getUploadMaxFileMB().toString()));
+            props.put("uploads.dir.maxsize",
+                new RollerPropertyData("uploads.dir.maxsize", 
+                    rollerConfig.getUploadMaxDirMB().toString()));
+            /* no longer part of runtime config
+            props.put("aggregator.enabled",
+                new RollerPropertyData("aggregator.enabled", 
+                    rollerConfig.getEnableAggregator().toString()));
+            props.put("aggregator.cache.enabled",
+                new RollerPropertyData("aggregator.cache.enabled", 
+                    rollerConfig.getRssUseCache().toString()));
+            props.put("aggregator.cache.timeout",
+                new RollerPropertyData("aggregator.cache.timeout", 
+                    rollerConfig.getRssCacheTime().toString()));
+            props.put("debug.memory.enabled",
+                new RollerPropertyData("debug.memory.enabled", 
+                    rollerConfig.getMemDebug().toString()));
+             */
+            props.put("spam.blacklist",
+                new RollerPropertyData("spam.blacklist", 
+                    rollerConfig.getRefererSpamWords()));
+        } else {
+            log.info("Old roller config not found ... " + 
+                "default values will be loaded");
+        }
+
+        return props;
+    }
+
+
+    /**
+     * This method compares the property definitions in the RuntimeConfigDefs
+     * file with the properties in the given Map and initializes any properties
+     * that were not found in the Map.
+     *
+     * If the Map of props is empty/null then we will initialize all properties.
+     **/
+    private Map initializeMissingProps(Map props) {
+
+        if(props == null)
+            props = new HashMap();
+
+        // start by getting our runtimeConfigDefs
+        RuntimeConfigDefs runtimeConfigDefs =
+                RollerRuntimeConfig.getRuntimeConfigDefs();
+
+        // can't do initialization without our config defs
+        if(runtimeConfigDefs == null)
+            return props;
+
+        // iterator through all the definitions and add properties
+        // that are not already in our props map
+        ConfigDef configDef = null;
+        DisplayGroup dGroup = null;
+        PropertyDef propDef = null;
+        Iterator defs = runtimeConfigDefs.getConfigDefs().iterator();
+        while(defs.hasNext()) {
+            configDef = (ConfigDef) defs.next();
+
+            Iterator groups = configDef.getDisplayGroups().iterator();
+            while(groups.hasNext()) {
+                dGroup = (DisplayGroup) groups.next();
+
+                Iterator propdefs = dGroup.getPropertyDefs().iterator();
+                while(propdefs.hasNext()) {
+                    propDef = (PropertyDef) propdefs.next();
+
+                    // do we already have this prop?  if not then add it
+                    if(!props.containsKey(propDef.getName())) {
+                        RollerPropertyData newprop =
+                            new RollerPropertyData(
+                                propDef.getName(), propDef.getDefaultValue());
+
+                        props.put(propDef.getName(), newprop);
+
+                        log.info("Found uninitialized property " +
+                            propDef.getName() +
+                            " ... setting value to [" + 
+                            propDef.getDefaultValue() + "]");
+                    }
+                }
+            }
+        }
+
+        return props;
+    }
+
+
+    public void release() {}
+
+}

Added: incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPARefererManagerImpl.java
URL: http://svn.apache.org/viewvc/incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPARefererManagerImpl.java?view=auto&rev=511352
==============================================================================
--- incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPARefererManagerImpl.java (added)
+++ incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPARefererManagerImpl.java Sat Feb 24 13:22:40 2007
@@ -0,0 +1,683 @@
+
+/*
+ * 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.business.jpa;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Collections;
+import java.util.Comparator;
+import javax.persistence.Query;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.lang.StringUtils;
+
+import org.apache.roller.RollerException;
+import org.apache.roller.business.Roller;
+import org.apache.roller.business.RollerFactory;
+import org.apache.roller.business.UserManager;
+import org.apache.roller.business.WeblogManager;
+import org.apache.roller.business.referrers.RefererManager;
+import org.apache.roller.config.RollerRuntimeConfig;
+import org.apache.roller.pojos.RefererData;
+import org.apache.roller.pojos.StatCount;
+import org.apache.roller.pojos.WeblogEntryData;
+import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.pojos.StatCountCountComparator;
+import org.apache.roller.util.LinkbackExtractor;
+import org.apache.roller.util.Utilities;
+
+/*
+ * JPARefererManagerImpl.java
+ *
+ * Created on May 31, 2006, 4:06 PM
+ *
+ */
+public class JPARefererManagerImpl implements RefererManager {
+
+    private static Log log = LogFactory.getLog(
+        JPARefererManagerImpl.class);
+
+    protected static final String DAYHITS = "dayHits";
+    protected static final String TOTALHITS = "totalHits";
+    
+    private static final Comparator statCountCountReverseComparator = 
+            Collections.reverseOrder(StatCountCountComparator.getInstance());
+    
+    /** The strategy for this manager. */
+    protected JPAPersistenceStrategy strategy;
+
+    /**
+     * Creates a new instance of JPARefererManagerImpl
+     */
+    public JPARefererManagerImpl(JPAPersistenceStrategy strategy) {
+        log.debug("Instantiating JPA Referer Manager");
+
+        this.strategy = strategy;
+    }
+
+    public void saveReferer(RefererData referer) throws RollerException {
+        strategy.store(referer);
+    }
+
+    public void removeReferer(RefererData referer) throws RollerException {
+        strategy.remove(referer);
+    }
+
+    /**
+     * Clear referrer dayhits and remove referrers without excerpts.
+     */
+    public void clearReferrers() throws RollerException {
+        clearDayHits();
+        Query q = strategy.getNamedQuery("RefererData.removeByNullOrEmptyExcerpt");
+        q.executeUpdate();
+    }
+
+    /**
+     * Clear referrer dayhits and remove referrers without excerpts.
+     */
+    public void clearReferrers(WebsiteData website) throws RollerException {
+        clearDayHitsByWebsite(website);
+        Query q = strategy.getNamedQuery("RefererData.removeByNullOrEmptyExcerpt&Website");
+        q.setParameter(1, website);
+        q.executeUpdate();
+    }
+
+    /**
+     * Apply ignoreWord/spam filters to all referers in system.
+     */
+    public void applyRefererFilters() throws RollerException {
+        String spamwords = RollerRuntimeConfig.getProperty("spam.blacklist");
+        String[] blacklist = StringUtils.split(
+                StringUtils.deleteWhitespace(spamwords),",");
+        if (blacklist.length == 0) return;
+        List referers = getBlackListedReferer(blacklist);
+        for (Iterator iterator = referers.iterator(); iterator.hasNext();) {
+            RefererData referer= (RefererData) iterator.next();
+            this.strategy.remove(referer);
+        }
+    }
+
+    /**
+     * Apply ignoreWord/spam filters to all referers in website.
+     */
+    public void applyRefererFilters(WebsiteData website)
+            throws RollerException {
+        if (null == website) throw new RollerException("website is null");
+        if (null == website.getBlacklist()) return;
+        
+        String[] blacklist = StringUtils.split(
+                StringUtils.deleteWhitespace(website.getBlacklist()),",");
+        if (blacklist.length == 0) return;
+        List referers = getBlackListedReferer(website, blacklist);
+        for (Iterator iterator = referers.iterator(); iterator.hasNext();) {
+            RefererData referer= (RefererData) iterator.next();
+            this.strategy.remove(referer);
+        }
+    }
+
+    protected List getExistingReferers(WebsiteData website, String dateString,
+            String permalink) throws RollerException {
+
+        Query q = strategy.getNamedQuery( 
+            "RefererData.getByWebsite&DateString&RefererPermalink");
+        q.setParameter(1, website);
+        q.setParameter(2, dateString);
+        q.setParameter(3, permalink);
+        return q.getResultList();
+    }
+
+    protected List getMatchingReferers(WebsiteData website, String requestUrl,
+            String refererUrl) throws RollerException {
+
+        Query q = strategy.getNamedQuery( 
+            "RefererData.getByWebsite&RequestUrl&RefererUrl");
+        q.setParameter(1, website);
+        q.setParameter(2, requestUrl);
+        q.setParameter(3, refererUrl);
+        return q.getResultList();
+    }
+
+    /**
+     * Returns hot weblogs as StatCount objects, in descending order by today's
+     * hits.
+     * @param sinceDays Restrict to last X days (or -1 for all)
+     * @param offset Offset into results (for paging)
+     * @param length Maximum number of results to return (for paging)
+     * @return List of StatCount objects.
+     */
+    public List getHotWeblogs(int sinceDays, int offset, int length)
+            throws RollerException {
+        
+        String msg = "Getting hot weblogs";
+        List results = new ArrayList();
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(new Date());
+        cal.add(Calendar.DATE, -1 * sinceDays);
+        Date startDate = cal.getTime();
+             
+        if (length == -1) {
+            length = Integer.MAX_VALUE - offset;
+        }
+
+        Query q = strategy.getNamedQuery( 
+            "RefererData.getHotWeblogsByWebsite.enabled&Website.active&Website.lastModifiedGreater");
+        
+        if (offset != 0 || length != -1) {
+            q.setFirstResult(offset);
+            q.setMaxResults(length);
+        }
+        q.setParameter(1, Boolean.TRUE);
+        q.setParameter(2, Boolean.TRUE);
+        q.setParameter(3, startDate);
+        List queryResults = (List)q.getResultList();
+        for (Iterator it = queryResults.iterator(); it.hasNext(); ) {
+            Object[] row = (Object[])it.next();
+            Integer hits = (Integer)row[0];
+            String websiteId = (String)row[1];
+            String websiteName = (String)row[2];
+            String websiteHandle = (String)row[3];
+            results.add(new StatCount(
+                websiteId,
+                websiteHandle,
+                websiteName,
+                "statCount.weblogDayHits",
+                hits.longValue()));              
+        }
+        // Original query ordered by desc hits.
+        // JPA QL doesn't allow queries to be ordered by agregates; do it in memory
+        Collections.sort(results, statCountCountReverseComparator);
+
+        return results;
+    }
+
+    protected int getHits(WebsiteData website, String type) 
+            throws RollerException {
+        int hits = -1;
+        if (log.isDebugEnabled()) {
+            log.debug("getHits: " + website.getName());
+        }
+        //TODO: JPAPort. This query retrieves both SUM(r.dayHits), SUM(r.totalHits)
+        //The method only comsumes one of them. We can optimize the logic to retrieve only the 
+        //requied SUM
+        Query query = strategy.getNamedQuery(
+            "RefererData.getHitsByWebsite.enabled&Website.id");
+        query.setParameter(1, Boolean.TRUE);
+        query.setParameter(2, website.getId());
+        List results = query.getResultList();
+
+        Object[] resultsArray = (Object[]) results.get(0);
+        
+        if (resultsArray.length > 0 && type.equals(DAYHITS)) {
+            if ( resultsArray[0] != null ) {
+                hits = ((Long) resultsArray[0]).intValue();
+            }
+        } else if ( resultsArray.length > 0 ) {
+            if ( resultsArray[0] != null ) {
+                hits = ((Long) resultsArray[1]).intValue();
+            }
+        } else {
+            hits = 0;
+        }
+        
+        return hits;
+    }
+
+    /**
+     * Get all referers for specified weblog.
+     * @param weblog
+     * @return List of type RefererData
+     */
+    public List getReferers(WebsiteData weblog) throws RollerException {
+        Query q = strategy.getNamedQuery(
+            "RefererData.getByWebsiteOrderByTotalHitsDesc");
+        q.setParameter(1, weblog);
+        return q.getResultList();
+    }
+
+    /**
+     * Get all referers for specified user that were made today.
+     * @param website Web site.
+     * @return List of type RefererData
+     */
+    public List getTodaysReferers(WebsiteData website) throws RollerException {
+        Query q = strategy.getNamedQuery(
+            "RefererData.getByWebsite&DayHitsGreaterZeroOrderByDayHitsDesc");
+        q.setParameter(1, website);
+        return q.getResultList();
+    }
+
+    /**
+     * Get referers for a specified date.
+     * @param website Web site.
+     * @param date YYYYMMDD format of day's date.
+     * @return List of type RefererData.
+     * @throws org.apache.roller.RollerException
+     */
+    public List getReferersToDate(WebsiteData website, String date)
+            throws RollerException {
+
+        if (website==null )
+            throw new RollerException("website is null");
+        
+        if (date==null )
+            throw new RollerException("Date is null");
+        
+        Query q = strategy.getNamedQuery(
+            "RefererData.getByWebsite&DateString&DuplicateOrderByTotalHitsDesc");
+        q.setParameter(1, website);
+        q.setParameter(2, date);
+        q.setParameter(3, Boolean.FALSE);
+        return q.getResultList();
+    }
+
+    /**
+     * Get referers that refer to a specific weblog entry.
+     * @param entryid Weblog entry ID
+     * @return List of RefererData objects.
+     * @throws org.apache.roller.RollerException
+     */
+    public List getReferersToEntry(String entryid) throws RollerException {
+        if (null == entryid)
+            throw new RollerException("entryid is null");
+        //TODO: DataMapperPort: Change calling code to pass WebLogEntryData instead of id
+        // we should change calling code to pass instance of WeblogEntryData instead
+        // of extracting and passing id. Once that is done, change the code below to
+        // skip the load (Please note that the load below will always find the enty in cache)
+        Query q = strategy.getNamedQuery(
+            "RefererData.getByWeblogEntry&TitleNotNull&ExcerptNotNullOrderByTotalHitsDesc");
+        q.setParameter(1, strategy.load(WeblogEntryData.class, entryid));
+        return q.getResultList();
+    }
+
+    /**
+     * Query for collection of referers.
+     */
+    protected List getReferersToWebsite(WebsiteData website, String refererUrl)
+            throws RollerException {
+        Query q = strategy.getNamedQuery( 
+            "RefererData.getByWebsite&RefererUrl");
+        q.setParameter(1, website);
+        q.setParameter(2, refererUrl);
+        return q.getResultList();
+    }
+
+    /**
+     * Query for collection of referers.
+     */
+    protected List getReferersWithSameTitle(WebsiteData website,
+                                            String requestUrl,
+                                            String title,
+                                            String excerpt)
+            throws RollerException {
+        Query q = strategy.getNamedQuery( 
+            "RefererData.getByWebsite&RequestURL&TitleOrExcerpt");
+        q.setParameter(1, website);
+        q.setParameter(2, requestUrl);
+        q.setParameter(3, title);
+        q.setParameter(4, excerpt);
+        return q.getResultList();
+    }
+
+    /**
+     * Get user's day hits
+     */
+    public int getDayHits(WebsiteData website) throws RollerException {
+        return getHits(website, DAYHITS);
+    }
+
+    /**
+     * Get user's all-time total hits
+     */
+    public int getTotalHits(WebsiteData website) throws RollerException {
+        return getHits(website, TOTALHITS);
+    }
+
+    /**
+     * Retrieve referer by id.
+     */
+    public RefererData getReferer(String id) throws RollerException {
+        return (RefererData)strategy.load(RefererData.class, id);
+    }
+
+    /**
+     * Process an incoming referer.
+     */
+    public void processReferrer(String requestUrl, String referrerUrl,
+            String weblogHandle, String entryAnchor, String dateString) {
+        log.debug("processing referrer ["+referrerUrl+
+                "] accessing ["+requestUrl+"]");
+
+        if (weblogHandle == null)
+            return;
+
+        String selfSiteFragment = "/"+weblogHandle;
+        WebsiteData weblog = null;
+        WeblogEntryData entry = null;
+
+        // lookup the weblog now
+        try {
+            UserManager userMgr = RollerFactory.getRoller().getUserManager();
+            weblog = userMgr.getWebsiteByHandle(weblogHandle);
+            if (weblog == null) return;
+
+            // now lookup weblog entry if possible
+            if (entryAnchor != null) {
+                WeblogManager weblogMgr = RollerFactory.getRoller().
+                    getWeblogManager();
+                entry = weblogMgr.getWeblogEntryByAnchor(weblog, entryAnchor);
+            }
+        } catch (RollerException re) {
+            // problem looking up website, gotta bail
+            log.error("Error looking up website object", re);
+            return;
+        }
+
+        try {
+            List matchRef = null;
+
+            // try to find existing RefererData for referrerUrl
+            if (referrerUrl == null || referrerUrl.trim().length() < 8) {
+                referrerUrl = "direct";
+
+                // Get referer specified by referer URL of direct
+                matchRef = getReferersToWebsite(weblog, referrerUrl);
+            } else {
+                referrerUrl = Utilities.stripJsessionId(referrerUrl);
+
+                // Query for referer with same referer and request URLs
+                matchRef = getMatchingReferers(weblog, requestUrl, referrerUrl);
+
+                // If referer was not found, try adding or leaving off 'www'
+                if ( matchRef.size() == 0 ) {
+                    String secondTryUrl = null;
+                    if ( referrerUrl.startsWith("http://www") ) {
+                        secondTryUrl = "http://"+referrerUrl.substring(11);
+                    } else {
+                        secondTryUrl = "http://www"+referrerUrl.substring(7);
+                    }
+
+                    matchRef = getMatchingReferers(weblog, requestUrl, 
+                        secondTryUrl);
+                    if ( matchRef.size() == 1 ) {
+                        referrerUrl = secondTryUrl;
+                    }
+                }
+            }
+
+            if (matchRef.size() == 1) {
+                // Referer was found in database, so bump up hit count
+                RefererData ref = (RefererData)matchRef.get(0);
+
+                ref.setDayHits(new Integer(ref.getDayHits().intValue() + 1));
+                ref.setTotalHits(new Integer(ref.getTotalHits().intValue() + 1));
+
+                log.debug("Incrementing hit count on existing referer: " +
+                    referrerUrl);
+
+                saveReferer(ref);
+
+            } else if (matchRef.size() == 0) {
+
+                // Referer was not found in database, so new Referer object
+                Integer one = new Integer(1);
+                RefererData ref =
+                        new RefererData(
+                        null,
+                        weblog,
+                        entry,
+                        dateString,
+                        referrerUrl,
+                        null,
+                        requestUrl,
+                        null,
+                        "", // Read comment above regarding Derby bug
+                        Boolean.FALSE,
+                        Boolean.FALSE,
+                        one,
+                        one);
+
+                if (log.isDebugEnabled()) {
+                    log.debug("newReferer="+ref.getRefererUrl());
+                }
+
+                String refurl = ref.getRefererUrl();
+
+                // If not a direct or search engine then search for linkback
+                boolean doLinkbackExtraction =
+                    RollerRuntimeConfig.getBooleanProperty(
+                        "site.linkbacks.enabled");
+                if (doLinkbackExtraction
+                        && entry != null
+                        && !refurl.equals("direct")
+                        && !refurl.startsWith("http://google")
+                        && !refurl.startsWith("http://www.google")
+                        && !refurl.startsWith("http://search.netscape")
+                        && !refurl.startsWith("http://www.blinkpro")
+                        && !refurl.startsWith("http://search.msn")
+                        && !refurl.startsWith("http://search.yahoo")
+                        && !refurl.startsWith("http://uk.search.yahoo")
+                        && !refurl.startsWith("http://www.javablogs.com")
+                        && !refurl.startsWith("http://www.teoma")
+                        ) {
+                    // Launch thread to extract referer linkback
+
+                    try {
+                        Roller mRoller = RollerFactory.getRoller();
+                        mRoller.getThreadManager().executeInBackground(
+                                new LinkbackExtractorRunnable(ref));
+                    } catch (InterruptedException e) {
+                        log.warn("Interrupted during linkback extraction",e);
+                    }
+                } else {
+                    saveReferer(ref);
+                }
+            }
+        } catch (RollerException pe) {
+            log.error(pe);
+        } catch (NullPointerException npe) {
+            log.error(npe);
+        }
+    }
+
+    /**
+     * Use LinkbackExtractor to parse title and excerpt from referer
+     */
+    class LinkbackExtractorRunnable implements Runnable {
+
+        private RefererData mReferer = null;
+
+        public LinkbackExtractorRunnable( RefererData referer) {
+            mReferer = referer;
+        }
+
+        public void run() {
+
+            try {
+                LinkbackExtractor lb = new LinkbackExtractor(
+                        mReferer.getRefererUrl(),mReferer.getRequestUrl());
+
+                if ( lb.getTitle()!=null && lb.getExcerpt()!=null ) {
+                    mReferer.setTitle(lb.getTitle());
+                    mReferer.setExcerpt(lb.getExcerpt());
+
+                    if ( lb.getPermalink() != null ) {
+                        // The presence of a permalink indicates that this
+                        // linkback was parsed out of an RSS feed and is
+                        // presumed to be a good linkback.
+
+                        mReferer.setRefererPermalink(lb.getPermalink());
+
+                        // See if this request/permalink is in the DB
+                        List matchRef = getExistingReferers(
+                                mReferer.getWebsite(),
+                                mReferer.getDateString(),
+                                mReferer.getRefererPermalink());
+
+                        // If it is the first, then set it to be visible
+                        if ( matchRef.size() == 0 ) {
+                            mReferer.setVisible(Boolean.TRUE);
+                        } else {
+                            // We can't throw away duplicates or we will
+                            // end up reparsing them everytime a hit comes
+                            // in from one of them, but we can mark them
+                            // as duplicates.
+                            mReferer.setDuplicate(Boolean.TRUE);
+                        }
+
+                        saveReferer(mReferer);
+
+                    }
+
+                    else {
+                        // Store the new referer
+                        saveReferer(mReferer);
+
+                        // Hacky Referer URL weighting kludge:
+                        //
+                        // If there are multple referers to a request URL,
+                        // then we want to pick the best one. The others
+                        // are marked as duplicates. To do this we use a
+                        // weight. The weight formula is:
+                        //
+                        // w = URL length + (100 if URL contains anchor)
+
+                        // LOOP: find the referer with the highest weight
+                        Boolean visible = Boolean.FALSE;
+                        List refs= getReferersWithSameTitle(
+                                mReferer.getWebsite(),
+                                mReferer.getRequestUrl(),
+                                lb.getTitle(),
+                                lb.getExcerpt());
+                        RefererData chosen = null;
+                        int maxweight = 0;
+                        for (Iterator rdItr = refs.iterator();rdItr.hasNext();) {
+                            RefererData referer = (RefererData) rdItr.next();
+
+                            int weight = referer.getRefererUrl().length();
+                            if (referer.getRefererUrl().indexOf('#') != -1) {
+                                weight += 100;
+                            }
+
+                            if ( weight > maxweight ) {
+                                chosen = referer;
+                                maxweight = weight;
+                            }
+
+                            if (referer.getVisible().booleanValue()) {
+                                // If any are visible then chosen
+                                // replacement must be visible as well.
+                                visible = Boolean.TRUE;
+                            }
+
+                        }
+
+                        // LOOP: to mark all of the lower weight ones
+                        // as duplicates
+                        for (Iterator rdItr = refs.iterator();rdItr.hasNext();) {
+                            RefererData referer = (RefererData) rdItr.next();
+
+                            if (referer != chosen) {
+                                referer.setDuplicate(Boolean.TRUE);
+                            } else {
+                                referer.setDuplicate(Boolean.FALSE);
+                                referer.setVisible(visible);
+                            }
+                            saveReferer(referer);
+                        }
+
+
+                    }
+                } else {
+                    // It is not a linkback, but store it anyway
+                    saveReferer(mReferer);
+
+                    log.info("No excerpt found at refering URL "
+                            + mReferer.getRefererUrl());
+                }
+            } catch (Exception e) {
+                log.error("Processing linkback",e);
+            } finally {
+                strategy.release();
+            }
+
+        }
+
+    }
+    
+    /**
+     * Release all resources held by manager.
+     */
+    public void release() {}
+    
+    protected void clearDayHits() throws RollerException {
+        Query query = strategy.getNamedQuery("RefererData.clearDayHits");
+        query.executeUpdate();
+    }
+
+    protected void clearDayHitsByWebsite(WebsiteData website) throws RollerException {
+        Query query = strategy.getNamedQuery("RefererData.clearDayHitsByWebsite");
+        query.setParameter(1, website);
+        query.executeUpdate();
+    }
+
+    protected List getBlackListedReferer(String[] blacklist) throws
+            RollerException {
+        StringBuffer queryString = getQueryStringForBlackList(blacklist);
+        Query query = strategy.getDynamicQuery(queryString.toString());
+        return (List) query.getResultList();
+    }
+
+    protected List getBlackListedReferer(WebsiteData website, String[] blacklist) 
+            throws RollerException {
+        StringBuffer queryString = getQueryStringForBlackList(blacklist);
+        queryString.append(" AND r.website = ?1 ");
+        Query query = strategy.getDynamicQuery(queryString.toString());
+        query.setParameter(1, website);
+        return query.getResultList();
+    }
+
+    /**
+     * Generates a JPQL query of form
+     * SELECT r FROM RefererData r WHERE
+     *     ( refererUrl like %blacklist[1] ..... OR refererUrl like %blacklist[n])
+     * @param blacklist
+     * @return
+     */
+    private StringBuffer getQueryStringForBlackList(String[] blacklist) {
+        assert blacklist.length > 0;
+        StringBuffer queryString = new StringBuffer("SELECT r FROM RefererData r WHERE (");
+        //Search for any matching entry from blacklist[]
+        final String OR = " OR ";
+        for (int i = 0; i < blacklist.length; i++) {
+            String ignoreWord = blacklist[i];
+            //TODO: DataMapper port: original code use "like ignore case" as follows
+            // or.add(Expression.ilike("refererUrl","%"+ignoreWord+"%"));
+            // There is no equivalent for it in JPA
+            queryString.append("r.refererUrl like '%").append(ignoreWord.trim()).append("%'").append(OR);
+        }
+        // Get rid of last OR
+        queryString.delete(queryString.length() - OR.length(), queryString.length());
+        queryString.append(" ) ");
+        return queryString;
+    }
+}

Added: incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPARollerImpl.java
URL: http://svn.apache.org/viewvc/incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPARollerImpl.java?view=auto&rev=511352
==============================================================================
--- incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPARollerImpl.java (added)
+++ incubator/roller/branches/roller_4.0/src/org/apache/roller/business/jpa/JPARollerImpl.java Sat Feb 24 13:22:40 2007
@@ -0,0 +1,259 @@
+
+/*
+ * 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.business.jpa;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.roller.RollerException;
+import org.apache.roller.business.BookmarkManager;
+import org.apache.roller.business.ConfigManager;
+import org.apache.roller.business.PropertiesManager;
+import org.apache.roller.business.Roller;
+import org.apache.roller.business.RollerImpl;
+import org.apache.roller.business.UserManager;
+import org.apache.roller.business.WeblogManager;
+import org.apache.roller.business.datamapper.jpa.JPARefererManager;
+import org.apache.roller.business.runnable.ThreadManager;
+import org.apache.roller.business.pings.AutoPingManager;
+import org.apache.roller.business.pings.PingQueueManager;
+import org.apache.roller.business.pings.PingTargetManager;
+import org.apache.roller.business.referrers.RefererManager;
+
+/**
+ * A JPA specific implementation of the Roller business layer.
+ */
+public class JPARollerImpl extends RollerImpl {
+
+    static final long serialVersionUID = 5256135928578074652L;
+
+    protected static Log logger = LogFactory.getLog(JPARollerImpl.class);
+
+    // our singleton instance
+    private static JPARollerImpl me = null;
+
+    // a persistence utility class
+    protected JPAPersistenceStrategy strategy = null;
+
+    // references to the managers we maintain
+    private BookmarkManager bookmarkManager = null;
+    private PropertiesManager propertiesManager = null;
+    private RefererManager referrerManager = null;
+    private UserManager userManager = null;
+    private WeblogManager weblogManager = null;
+    private PingQueueManager pingQueueManager = null;
+    private AutoPingManager autoPingManager = null;
+    private PingTargetManager pingTargetManager = null;
+    private ThreadManager threadManager = null;    
+    
+    /**
+     * Single constructor.
+     * @throws org.apache.roller.RollerException on any error
+     */
+    protected JPARollerImpl() throws RollerException {
+        // set strategy used by JPA
+        strategy = new JPAPersistenceStrategy("RollerPU");
+    }
+
+    protected UserManager createJPAUserManager(JPAPersistenceStrategy strategy) {
+        return new JPAUserManagerImpl((JPAPersistenceStrategy) strategy);
+    }
+
+    protected WeblogManager createJPAWeblogManager(
+            JPAPersistenceStrategy strategy) {
+        return new JPAWeblogManagerImpl((JPAPersistenceStrategy) strategy);
+    }
+
+    protected ThreadManager createJPAThreadManager(
+            JPAPersistenceStrategy strategy) {
+        return new JPAThreadManagerImpl((JPAPersistenceStrategy) strategy);
+    }
+
+    protected RefererManager createJPARefererManagerImpl(JPAPersistenceStrategy strategy) {
+        return new JPARefererManagerImpl((JPAPersistenceStrategy) strategy);
+    }
+
+    /**
+     * Construct and return the singleton instance of the class.
+     * @throws org.apache.roller.RollerException on any error
+     * @return the singleton
+     */
+    public static Roller instantiate() throws RollerException {
+        logger.debug("Instantiating JPARollerImpl");
+        return new JPARollerImpl();
+    }
+    
+    public void flush() throws RollerException {
+        this.strategy.flush();
+    }
+
+    
+    public void release() {
+
+        // release our own stuff first
+        if (bookmarkManager != null) bookmarkManager.release();
+        if (referrerManager != null) referrerManager.release();
+        if (userManager != null) userManager.release();
+        if (weblogManager != null) weblogManager.release();
+        if (pingTargetManager != null) pingTargetManager.release();
+        if (pingQueueManager != null) pingQueueManager.release();
+        if (autoPingManager != null) autoPingManager.release();
+
+        // tell JPA to close down
+        this.strategy.release();
+
+        // then let parent do its thing
+        super.release();
+    }
+
+    
+    public void shutdown() {
+
+        // do our own shutdown first
+        this.release();
+
+        // then let parent do its thing
+        super.shutdown();
+    }
+
+    /**
+     * @see org.apache.roller.business.Roller#getAutopingManager()
+     */
+    public AutoPingManager getAutopingManager() {
+        if (autoPingManager == null) {
+            autoPingManager = createJPAAutoPingManager(strategy);
+        }
+        return autoPingManager;
+    }
+
+    protected AutoPingManager createJPAAutoPingManager(
+            JPAPersistenceStrategy strategy) {
+        return new JPAAutoPingManagerImpl(strategy);
+    }
+    
+    /**
+     * @see org.apache.roller.business.Roller#getBookmarkManager()
+     */
+    public BookmarkManager getBookmarkManager() {
+        if ( bookmarkManager == null ) {
+            bookmarkManager = createJPABookmarkManager(strategy);
+        }
+        return bookmarkManager;
+    }
+
+    protected BookmarkManager createJPABookmarkManager(
+            JPAPersistenceStrategy strategy) {
+        return new JPABookmarkManagerImpl(strategy);
+    }
+
+    /**
+     * @see org.apache.roller.business.Roller#getPingTargetManager()
+     */
+    public PingQueueManager getPingQueueManager() {
+        if (pingQueueManager == null) {
+            pingQueueManager = createJPAPingQueueManager(strategy);
+        }
+        return pingQueueManager;
+    }
+
+    protected PingQueueManager createJPAPingQueueManager(
+            JPAPersistenceStrategy strategy) {
+        return new JPAPingQueueManagerImpl(strategy);
+    }
+
+    /**
+     * @see org.apache.roller.business.Roller#getPingTargetManager()
+     */
+    public PingTargetManager getPingTargetManager() {
+        if (pingTargetManager == null) {
+            pingTargetManager = createJPAPingTargetManager(strategy);
+        }
+        return pingTargetManager;
+    }
+
+    protected PingTargetManager createJPAPingTargetManager(
+            JPAPersistenceStrategy strategy) {
+        return new JPAPingTargetManagerImpl(strategy);
+    }
+
+    /**
+     * @see org.apache.roller.business.Roller#getPropertiesManager()
+     */
+    public PropertiesManager getPropertiesManager() {
+        if (propertiesManager == null) {
+            propertiesManager = createJPAPropertiesManager(strategy);
+        }
+        return propertiesManager;
+    }
+
+    protected PropertiesManager createJPAPropertiesManager(
+            JPAPersistenceStrategy strategy) {
+        return new JPAPropertiesManagerImpl(strategy);
+    }
+
+    /**
+     * @see org.apache.roller.business.Roller#getRefererManager()
+     */
+    public RefererManager getRefererManager() {
+        if ( referrerManager == null ) {
+            referrerManager = createJPARefererManagerImpl(strategy);
+        }
+        return referrerManager;
+    }
+
+    /**
+     * @see org.apache.roller.business.Roller#getUserManager()
+     */
+    public UserManager getUserManager() {
+        if ( userManager == null ) {
+            userManager = createJPAUserManager(strategy);
+        }
+        return userManager;
+    }
+
+    /**
+     * @see org.apache.roller.business.Roller#getWeblogManager()
+     */
+    public WeblogManager getWeblogManager() {
+        if ( weblogManager == null ) {
+            weblogManager = createJPAWeblogManager(strategy);
+        }
+        return weblogManager;
+    }
+
+    /**
+     * @see org.apache.roller.model.Roller#getThreadManager()
+     */
+    public ThreadManager getThreadManager() {
+        if (threadManager == null) {
+            threadManager = createJPAThreadManager(strategy);
+        }
+        return threadManager;
+    }
+
+    /**
+     * This method is deprecated.
+     * @return null
+     * @see org.apache.roller.business.Roller#getConfigManager()
+     * @deprecated see JIRA issue ROL-1151
+     */
+    public ConfigManager getConfigManager() {
+        throw new RuntimeException("Deprecated method getConfigManager.");
+    }
+    
+}