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

svn commit: r398712 [14/32] - in /incubator/roller/trunk/src/org/apache: ./ roller/ roller/business/ roller/business/hibernate/ roller/business/referrers/ roller/business/runnable/ roller/business/search/ roller/business/search/operations/ roller/busin...

Added: incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/BookmarksAction.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/BookmarksAction.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/BookmarksAction.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/BookmarksAction.java Mon May  1 15:23:02 2006
@@ -0,0 +1,381 @@
+/*
+* 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.
+*/
+/*
+ * Created on Oct 21, 2003
+ */
+package org.apache.roller.presentation.bookmarks.actions;
+
+import java.text.MessageFormat;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.action.ActionMessage;
+import org.apache.struts.action.ActionMessages;
+import org.apache.struts.actions.DispatchAction;
+import org.apache.roller.RollerException;
+import org.apache.roller.model.BookmarkManager;
+import org.apache.roller.model.Roller;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.pojos.BookmarkData;
+import org.apache.roller.pojos.FolderData;
+import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.presentation.BasePageModel;
+import org.apache.roller.presentation.RollerRequest;
+import org.apache.roller.presentation.RollerSession;
+import org.apache.roller.presentation.bookmarks.formbeans.BookmarksForm;
+import org.apache.roller.presentation.cache.CacheManager;
+
+/**
+ * Actions that are initiated from the BookmarksForm.
+ *
+ * @struts.action name="bookmarksForm" path="/editor/bookmarks" parameter="method"
+ * @struts.action-forward name="BookmarksForm" path=".BookmarksForm"
+ *
+ * @author Dave Johnson
+ */
+public class BookmarksAction extends DispatchAction
+{
+    private static Log mLogger =
+        LogFactory.getFactory().getInstance(BookmarksAction.class);
+
+    /**
+     * Present the BookmarksForm loaded with folder specified by request.
+     * @param mapping Action mapping.
+     * @param actionForm Form bean.
+     * @param request Request.
+     * @param response Response.
+     * @return Forward to BookmarksForm or access-denied.
+     * @throws RollerException
+     */
+    public ActionForward selectFolder(
+        ActionMapping       mapping,
+        ActionForm          actionForm,
+        HttpServletRequest  request,
+        HttpServletResponse response)
+        throws RollerException
+    {
+        BookmarksPageModel pageModel = new BookmarksPageModel(
+            request, response, mapping, (BookmarksForm)actionForm);
+        if (RollerSession.getRollerSession(request).isUserAuthorizedToAuthor(
+                pageModel.getFolder().getWebsite()))
+        {
+            request.setAttribute("model", pageModel);
+            return mapping.findForward("BookmarksForm");
+        }
+        else
+        {
+            return mapping.findForward("access-denied");
+        }
+    }
+
+    /**
+     * Delete folders and bookmarks indicated by BookmarksForm bean.
+     * @param mapping Action mapping.
+     * @param actionForm Form bean.
+     * @param request Request.
+     * @param response Response.
+     * @return Forward to BookmarksForm or access-denied.
+     * @throws RollerException
+     */
+    public ActionForward deleteSelected(
+        ActionMapping       mapping,
+        ActionForm          actionForm,
+        HttpServletRequest  request,
+        HttpServletResponse response)
+        throws RollerException
+    {
+        Roller roller = RollerFactory.getRoller();
+        BookmarksPageModel pageModel = new BookmarksPageModel(
+            request, response, mapping, (BookmarksForm)actionForm);
+        WebsiteData website = pageModel.getFolder().getWebsite();
+        if (RollerSession.getRollerSession(request).isUserAuthorizedToAuthor(website))
+        {
+            BookmarkManager bmgr = roller.getBookmarkManager();
+            BookmarksForm form = (BookmarksForm)actionForm;
+
+            mLogger.debug("Deleting folders and bookmarks.");
+
+            String folders[] = form.getSelectedFolders();
+            if (null != folders)
+            {
+                for (int i = 0; i < folders.length; i++)
+                {
+                    FolderData fd = bmgr.getFolder(folders[i]);
+                    bmgr.removeFolder(fd); // removes child folders and bookmarks too
+                }
+            }
+            
+            BookmarkData bookmark = null;
+            String bookmarks[] = form.getSelectedBookmarks();
+            if (null != bookmarks)
+            {
+                for (int j = 0; j < bookmarks.length; j++)
+                {
+                    bookmark = bmgr.getBookmark(bookmarks[j]);
+                    bmgr.removeBookmark(bookmark);
+                }
+            }
+            RollerFactory.getRoller().flush();
+                
+            CacheManager.invalidate(website);
+
+            // recreate model now that folder  is deleted
+            pageModel = new BookmarksPageModel(
+                request, response, mapping, (BookmarksForm)actionForm);
+            request.setAttribute("model", pageModel);
+            return mapping.findForward("BookmarksForm");
+        }
+        else
+        {
+            return mapping.findForward("access-denied");
+        }
+    }
+
+    /**
+     * Move folders and bookmarks indicated by BookmarksForm bean.
+     * @param mapping Action mapping.
+     * @param actionForm  Form bean.
+     * @param request Request.
+     * @param response Response.
+     * @return Forward to BookmarksForm or access-denied.
+     * @throws RollerException
+     */
+    public ActionForward moveSelected(
+        ActionMapping       mapping,
+        ActionForm          actionForm,
+        HttpServletRequest  request,
+        HttpServletResponse response)
+        throws RollerException
+    {
+        ActionMessages messages = new ActionMessages();
+        ActionForward forward = mapping.findForward("BookmarksForm");
+        Roller roller = RollerFactory.getRoller();
+        BookmarksPageModel pageModel = new BookmarksPageModel(
+            request, response, mapping, (BookmarksForm)actionForm);
+        request.setAttribute("model", pageModel);
+        WebsiteData website = pageModel.getFolder().getWebsite();
+
+        if (RollerSession.getRollerSession(request).isUserAuthorizedToAuthor(website))
+        {
+            try 
+            {
+                BookmarkManager bmgr = roller.getBookmarkManager();
+                BookmarksForm form = (BookmarksForm)actionForm;
+    
+                mLogger.debug("Moving folders and bookmarks to folder, id="
+                    + form.getMoveToFolderId());
+    
+                // Move subfolders to new folder.
+                String folders[] = form.getSelectedFolders();
+                FolderData parent = bmgr.getFolder(form.getMoveToFolderId());
+                if (null != folders)
+                {
+                    for (int i = 0; i < folders.length; i++)
+                    {
+                        FolderData fd = bmgr.getFolder(folders[i]);
+    
+                        // Don't move folder into itself.
+                        if (    !fd.getId().equals(parent.getId())
+                             && !parent.descendentOf(fd))
+                        {
+                            fd.setParent(parent);
+                            bmgr.saveFolder(fd);
+                        }
+                        else 
+                        {
+                            messages.add(null, new ActionMessage(
+                                "bookmarksForm.warn.notMoving",fd.getName()));
+                        }
+                    }
+                }
+    
+                // Move bookmarks.
+                String bookmarks[] = form.getSelectedBookmarks();
+                if (null != bookmarks)
+                {
+                    for (int j = 0; j < bookmarks.length; j++)
+                    {
+                        // maybe we should be using folder.addBookmark()?
+                        BookmarkData bd = bmgr.getBookmark(bookmarks[j]);
+                        bd.setFolder(parent);
+                        bmgr.saveBookmark(bd);
+                    }
+                }
+                RollerFactory.getRoller().flush();
+
+                CacheManager.invalidate(website);
+                
+                saveMessages(request, messages);
+                
+                // recreate model now that folder is altered
+                pageModel = new BookmarksPageModel(
+                        request, response, mapping, (BookmarksForm)actionForm);
+                request.setAttribute("model", pageModel);
+            }
+            catch (RollerException e)
+            {
+                ActionErrors errors = new ActionErrors();
+                errors.add(null, new ActionError("bookmarksForm.error.move"));
+                saveErrors(request, errors);       
+            }
+        }
+        else
+        {
+            forward = mapping.findForward("access-denied");
+        }
+        return forward;
+    }
+
+    private static final class FolderPathComparator implements Comparator
+    {
+        public int compare(Object o1, Object o2) {
+            FolderData f1 = (FolderData)o1;
+            FolderData f2 = (FolderData)o2;
+            int res = 0;
+            try
+            {
+                res = f1.getPath().compareTo(f2.getPath());
+            }
+            catch (RollerException e)
+            {
+                mLogger.error("ERROR: sorting folders");
+            }
+            return res;
+        }
+    }
+    
+    public class BookmarksPageModel extends BasePageModel
+    {
+        private List folderPath = null;
+        private TreeSet allFolders = null;
+        private FolderData folder = null;   
+        
+        public BookmarksPageModel(
+                HttpServletRequest request,
+                HttpServletResponse response,
+                ActionMapping mapping,
+                BookmarksForm form) throws RollerException
+        {
+            super("",  request, response, mapping);
+            
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            RollerSession rollerSession = RollerSession.getRollerSession(request);
+            BookmarkManager bmgr = RollerFactory.getRoller().getBookmarkManager();
+
+            allFolders = new TreeSet(new FolderPathComparator());
+
+            // Find folderid wherever it may be
+            String folderId = (String)
+                request.getAttribute(RollerRequest.FOLDERID_KEY);
+            if (null == folderId)
+            {
+                folderId = request.getParameter(RollerRequest.FOLDERID_KEY);
+            }
+            if (null == folderId)
+            {
+                folderId = form.getFolderId();
+            }
+
+            if (null == folderId || folderId.equals("null"))
+            {
+                website = rreq.getWebsite();
+                folder = bmgr.getRootFolder(website);
+                folderId = folder.getId();
+            }
+            else
+            {
+                folder = bmgr.getFolder(folderId);
+                website = folder.getWebsite();
+            }
+            form.setFolderId(folder.getId());
+
+            if (null != folder.getParent())
+            {
+                folderPath = new LinkedList();
+                folderPath.add(0, folder);
+                FolderData parent = folder.getParent();
+                while (parent != null)
+                {
+                    folderPath.add(0, parent);
+                    parent = parent.getParent();
+                }
+                request.setAttribute(
+                    RollerRequest.PARENTID_KEY, folder.getParent().getId());
+            }
+
+            // Build list of all folders, except for current one, sorted by path.
+            Iterator iter = bmgr.getAllFolders(website).iterator();
+            while (iter.hasNext())
+            {
+                FolderData fd = (FolderData) iter.next();
+                if (!fd.getId().equals(folderId))
+                {
+                    allFolders.add(fd);
+                }
+            }
+            if (allFolders.size() > 0) {
+                request.setAttribute("allFolders", allFolders); // for Struts tags
+            } else {
+                allFolders = null;
+            }
+            request.setAttribute("folder", folder); // for Struts tags          
+        }
+        
+        public String getTitle()
+        {
+            if (folderPath == null || folderPath.isEmpty()) 
+            {
+                return bundle.getString("bookmarksForm.rootTitle");
+            }
+            else 
+            {
+                return MessageFormat.format(
+                    bundle.getString("bookmarksForm.folderTitle"),
+                    new Object[] {folder.getName()});
+            }
+        }
+        public List getFolderPath() 
+        {
+            return folderPath;
+        }
+        public Set getAllFolders() 
+        {
+            return allFolders;
+        }
+        public FolderData getFolder()
+        {
+            return folder;
+        }
+                
+
+    }
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/FolderEditAction.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/FolderEditAction.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/FolderEditAction.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/FolderEditAction.java Mon May  1 15:23:02 2006
@@ -0,0 +1,105 @@
+/*
+* 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.
+*/
+/*
+ * Created on Oct 21, 2003
+ */
+package org.apache.roller.presentation.bookmarks.actions;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.roller.model.BookmarkManager;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.pojos.FolderData;
+import org.apache.roller.presentation.BasePageModel;
+import org.apache.roller.presentation.RollerRequest;
+import org.apache.roller.presentation.bookmarks.formbeans.FolderFormEx;
+
+/**
+ * @struts.action path="/editor/folderEdit" name="folderFormEx" validate="false"
+ * @struts.action-forward name="FolderForm" path=".FolderForm"
+ * 
+ * @author Dave Johnson
+ */
+public class FolderEditAction extends Action
+{
+    public ActionForward execute(
+        ActionMapping       mapping,
+        ActionForm          actionForm,
+        HttpServletRequest  request,
+        HttpServletResponse response)
+        throws Exception
+    {
+        RollerRequest rreq = RollerRequest.getRollerRequest(request);
+        BookmarkManager bmgr = RollerFactory.getRoller().getBookmarkManager();
+        FolderFormEx form = (FolderFormEx)actionForm;
+        
+        FolderData parentFolder = null;
+        if (null!=rreq.getFolder() && null==request.getParameter("correct")) 
+        {
+            // If request specifies folder and we are not correcting an 
+            // already submitted form then load that folder into the form.
+            request.setAttribute("state","edit"); 
+
+            FolderData fd = rreq.getFolder();
+            form.copyFrom(fd, request.getLocale());
+            parentFolder = fd.getParent();
+            
+            BasePageModel pageModel = new BasePageModel(
+                "folderForm.add.title", request, response, mapping);
+            pageModel.setWebsite(parentFolder.getWebsite());
+            request.setAttribute("model", pageModel);
+        }
+        else if (null != request.getParameter("correct"))
+        {
+            // We are correcting a previously submtted form.
+            request.setAttribute("state","correcting"); 
+            
+            String parentId = request.getParameter(RollerRequest.PARENTID_KEY);
+            parentFolder = bmgr.getFolder(parentId);
+            
+            BasePageModel pageModel = new BasePageModel(
+                "folderForm.correct.title", request, response, mapping);
+            pageModel.setWebsite(parentFolder.getWebsite());
+            request.setAttribute("model", pageModel);
+        }
+        else
+        {
+            // We are adding a new bookmark
+            request.setAttribute("state","add");
+            
+            String parentId = request.getParameter(RollerRequest.PARENTID_KEY);
+            parentFolder = bmgr.getFolder(parentId);
+            
+            BasePageModel pageModel = new BasePageModel(
+                "folderForm.add.title", request, response, mapping);
+            pageModel.setWebsite(parentFolder.getWebsite());
+            request.setAttribute("model", pageModel);
+        }
+        
+        request.setAttribute(RollerRequest.PARENTID_KEY, parentFolder.getId());
+        request.setAttribute("parentFolder", parentFolder);
+        
+        return mapping.findForward("FolderForm");
+    }
+
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/FolderSaveAction.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/FolderSaveAction.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/FolderSaveAction.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/FolderSaveAction.java Mon May  1 15:23:02 2006
@@ -0,0 +1,105 @@
+/*
+* 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.
+*/
+/*
+ * Created on Oct 21, 2003
+ */
+package org.apache.roller.presentation.bookmarks.actions;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.roller.model.BookmarkManager;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.pojos.FolderData;
+import org.apache.roller.pojos.PermissionsData;
+import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.presentation.RollerRequest;
+import org.apache.roller.presentation.RollerSession;
+import org.apache.roller.presentation.bookmarks.formbeans.FolderFormEx;
+import org.apache.roller.presentation.cache.CacheManager;
+
+/**
+ * @struts.action path="/editor/folderSave" name="folderFormEx" 
+ *     validate="true" input="/editor/folderEdit.do"
+ * @struts.action-forward name="Bookmarks" path="/editor/bookmarks.do?method=selectFolder"
+ * 
+ * @author Dave Johnson
+ */
+public class FolderSaveAction extends Action
+{
+    public ActionForward execute(
+        ActionMapping       mapping,
+        ActionForm          actionForm,
+        HttpServletRequest  request,
+        HttpServletResponse response)
+        throws Exception
+    {
+        ActionForward forward = mapping.findForward("Bookmarks");
+        FolderFormEx form = (FolderFormEx)actionForm;
+        RollerRequest rreq = RollerRequest.getRollerRequest(request);
+        RollerSession rses = RollerSession.getRollerSession(request);
+        BookmarkManager bmgr = RollerFactory.getRoller().getBookmarkManager();
+        WebsiteData website = null;
+                
+        FolderData fd = null;
+        if (null != form.getId() && !form.getId().trim().equals("")) 
+        {
+            fd = bmgr.getFolder(form.getId());
+            website = fd.getWebsite();
+        }
+        else 
+        {
+            fd = new FolderData();
+            String parentId = request.getParameter(RollerRequest.PARENTID_KEY);
+            FolderData parent = bmgr.getFolder(parentId);
+            website = parent.getWebsite();
+            fd.setParent(parent);
+            fd.setWebsite(website);
+        }
+        
+        if (fd.getWebsite().hasUserPermissions(
+                rses.getAuthenticatedUser(), PermissionsData.AUTHOR))
+        {
+            // Copy form values to object
+            form.copyTo(fd, request.getLocale());
+            bmgr.saveFolder(fd);
+            RollerFactory.getRoller().flush();
+            
+            CacheManager.invalidate(fd);
+        }
+        else
+        {
+            ActionErrors errors = new ActionErrors();
+            errors.add(null, new ActionError("error.permissions.deniedSave"));
+            saveErrors(request, errors);
+            forward = mapping.findForward("access-denied");
+        }         
+        if (null != fd.getParent()) 
+        {
+            request.setAttribute(
+               RollerRequest.FOLDERID_KEY, fd.getParent().getId());
+        }         
+        return forward;
+    }
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/ImportBookmarksFormAction.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/ImportBookmarksFormAction.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/ImportBookmarksFormAction.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/ImportBookmarksFormAction.java Mon May  1 15:23:02 2006
@@ -0,0 +1,198 @@
+/*
+* 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.presentation.bookmarks.actions;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.upload.FormFile;
+import org.apache.roller.model.BookmarkManager;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.presentation.RollerRequest;
+import org.apache.roller.presentation.bookmarks.formbeans.FolderFormEx;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import org.apache.struts.action.ActionMessage;
+import org.apache.struts.action.ActionMessages;
+import org.apache.roller.RollerException;
+import org.apache.roller.model.Roller;
+import org.apache.roller.pojos.FolderData;
+import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.presentation.BasePageModel;
+import org.apache.roller.presentation.RollerSession;
+import org.apache.roller.presentation.cache.CacheManager;
+
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * @struts.action name="folderFormEx" path="/editor/importBookmarks"
+ *  scope="request" input=".import" validate="false"
+ *
+ * @struts.action-forward name="importBookmarks.page" path=".import"
+ *
+ * TODO Should import into folder with same name as imported file
+ */
+public final class ImportBookmarksFormAction extends Action {
+    
+    private static final String HANDLE = "opmlupload.website.handle";    
+    private static Log mLogger =
+            LogFactory.getFactory().getInstance(RollerRequest.class);
+    
+    /**
+     * Request to import bookmarks
+     */
+    public ActionForward execute(
+            ActionMapping 		mapping,
+            ActionForm 			actionForm,
+            HttpServletRequest 	request,
+            HttpServletResponse response)
+            throws Exception {
+        
+        ActionErrors errors = new ActionErrors();
+        FolderFormEx theForm = (FolderFormEx)actionForm;
+        ActionForward fwd = mapping.findForward("importBookmarks.page");
+        
+        RollerRequest rreq = RollerRequest.getRollerRequest(request);
+        RollerSession rses = RollerSession.getRollerSession(request);        
+        BookmarkManager bm = RollerFactory.getRoller().getBookmarkManager();
+        
+        BasePageModel pageModel = new BasePageModel(
+            "bookmarksImport.title", request, response, mapping);
+        request.setAttribute("model", pageModel);
+
+        WebsiteData website = getWebsite(request);
+        pageModel.setWebsite(website);
+        
+        // if user authorized and a file is being uploaded
+        if (rses.isUserAuthorizedToAuthor(website) && theForm.getBookmarksFile() != null) {
+            
+            // this line is here for when the input page is upload-utf8.jsp,
+            // it sets the correct character encoding for the response
+            String encoding = request.getCharacterEncoding();
+            if ((encoding != null) && (encoding.equalsIgnoreCase("utf-8"))) {
+                response.setContentType("text/html; charset=utf-8");
+            }            
+            boolean writeFile = false;
+            
+            //retrieve the file representation
+            FormFile file = theForm.getBookmarksFile();
+            String data = null;
+            InputStream stream = null;
+            try {
+               
+                //retrieve the file data
+                ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                stream = file.getInputStream();
+                if (!writeFile) {
+                    //only write files out that are less than 1MB
+                    if (file.getFileSize() < (4*1024000)) {
+                        
+                        byte[] buffer = new byte[8192];
+                        int bytesRead = 0;
+                        while ((bytesRead=stream.read(buffer,0,8192)) != -1) {
+                            baos.write(buffer, 0, bytesRead);
+                        }
+                        data = new String(baos.toByteArray());
+                        
+                        SimpleDateFormat formatter =
+                                new SimpleDateFormat("yyyyMMddHHmmss");
+                        Date now = new Date();
+                        String folderName = "imported-" + formatter.format(now);
+                        
+                        // Use Roller BookmarkManager to import bookmarks
+
+                        bm.importBookmarks(website, folderName, data);  
+                        RollerFactory.getRoller().flush();
+                        CacheManager.invalidate(website);
+                        
+                        ActionMessages messages = new ActionMessages();
+                        messages.add(ActionMessages.GLOBAL_MESSAGE,
+                           new ActionMessage("bookmarksImport.imported", folderName));
+                        saveMessages(request, messages);
+                    } 
+                    else {
+                        data = "The file is greater than 4MB, "
+                                +" and has not been written to stream."
+                                +" File Size: "+file.getFileSize()+" bytes. "
+                                +" This is a limitation of this particular "
+                                +" web application, hard-coded in "
+                                +" org.apache.struts.webapp.upload.UploadAction";
+                        errors.add(ActionErrors.GLOBAL_ERROR,
+                           new ActionError("bookmarksImport.error",data));
+                    }
+                }
+                
+            } 
+            catch (Exception e) {
+                errors.add(ActionErrors.GLOBAL_ERROR,
+                     new ActionError("bookmarksImport.error",e.toString()));
+                saveErrors(request,errors);
+                mLogger.error("ERROR: importing bookmarks",e);
+            } 
+            finally {
+                if ( stream!=null ) {
+                    try { stream.close(); } 
+                    catch (Exception e) { mLogger.error("Closing stream",e); };
+                }
+            }            
+            //destroy the temporary file created
+            file.destroy();
+        }
+        else if (!rses.isUserAuthorizedToAuthor(website)) {
+            fwd = mapping.findForward("access-denied");
+        }        
+        return fwd;
+    }
+    
+    /**
+     * Other actions can get the website handle from request params, but
+     * request params don't come accross in a file-upload post so we have to
+     * stash the website handle in the session.
+     */
+    public static WebsiteData getWebsite(HttpServletRequest request) throws RollerException {
+        RollerRequest rreq = RollerRequest.getRollerRequest(request);
+        WebsiteData website = rreq.getWebsite();
+        String folderid = request.getParameter(RollerRequest.FOLDERID_KEY);
+        if (website == null && folderid != null) { 
+            BookmarkManager bm = RollerFactory.getRoller().getBookmarkManager();
+            FolderData folder = bm.getFolder(folderid);     
+            website = folder.getWebsite();
+        }           
+        if (website != null) {
+            request.getSession().setAttribute(HANDLE, website.getHandle());
+        } 
+        else {
+            String handle = (String)request.getSession().getAttribute(HANDLE);
+            Roller roller = RollerFactory.getRoller();
+            website = roller.getUserManager().getWebsiteByHandle(handle);
+        }
+        return website;
+    }
+}
+

Added: incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/package.html
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/package.html?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/package.html (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/actions/package.html Mon May  1 15:23:02 2006
@@ -0,0 +1,27 @@
+<!--
+  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.
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+  <title></title>
+</head>
+<body>
+Struts actions for bookmarks and folders.
+
+</body>
+</html>

Added: incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/formbeans/BookmarkFormEx.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/formbeans/BookmarkFormEx.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/formbeans/BookmarkFormEx.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/formbeans/BookmarkFormEx.java Mon May  1 15:23:02 2006
@@ -0,0 +1,77 @@
+/*
+* 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.
+*/
+/*
+ * Created on Apr 8, 2003
+ */
+package org.apache.roller.presentation.bookmarks.formbeans;
+
+import org.apache.roller.RollerException;
+import org.apache.roller.pojos.BookmarkData;
+import org.apache.roller.presentation.forms.BookmarkForm;
+
+/**
+ * Extends the BookmarkForm so that additional properties may be added.
+ * These properties are not persistent and are only needed for the UI.
+ *
+ * @struts.form name="bookmarkFormEx"
+ */
+public class BookmarkFormEx extends BookmarkForm
+{
+    private String mFolderId = null;
+
+    /**
+     *
+     */
+    public BookmarkFormEx()
+    {
+        super();
+    }
+
+    /**
+     * @param dataHolder
+     */
+    public BookmarkFormEx(BookmarkData dataHolder, java.util.Locale locale) throws RollerException
+    {
+        copyFrom(dataHolder, locale);
+    }
+
+    /**
+     * @return
+     */
+    public String getFolderId()
+    {
+        return mFolderId;
+    }
+
+    /**
+     * @param string
+     */
+    public void setFolderId(String string)
+    {
+        mFolderId = string;
+    }
+
+    /**
+     * @see org.apache.roller.presentation.forms.BookmarkForm#setData(org.apache.roller.pojos.BookmarkData)
+     */
+    public void copyFrom(BookmarkData dataHolder, java.util.Locale locale) throws RollerException
+    {
+        super.copyFrom(dataHolder, locale);
+        mFolderId = dataHolder.getFolder().getId();
+    }
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/formbeans/BookmarksForm.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/formbeans/BookmarksForm.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/formbeans/BookmarksForm.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/formbeans/BookmarksForm.java Mon May  1 15:23:02 2006
@@ -0,0 +1,86 @@
+/*
+* 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.
+*/
+/*
+ * Created on Oct 21, 2003
+ */
+package org.apache.roller.presentation.bookmarks.formbeans;
+
+import org.apache.struts.action.ActionForm;
+
+/**
+ * @struts.form name="bookmarksForm"
+ */ 
+public class BookmarksForm extends ActionForm
+{
+    private String folderId = null; 
+    private boolean moveContents = false; 
+    private String moveToFolderId = null; 
+    private String[] selectedBookmarks = null;
+    private String[] selectedFolders = null;
+    
+    public String getFolderId()
+    {
+        return folderId;
+    }
+
+    public void setFolderId(String folderId)
+    {
+        this.folderId = folderId;
+    }
+
+    public boolean isMoveContents()
+    {
+        return moveContents;
+    }
+
+    public void setMoveContents(boolean moveContents)
+    {
+        this.moveContents = moveContents;
+    }
+
+    public String getMoveToFolderId()
+    {
+        return moveToFolderId;
+    }
+
+    public void setMoveToFolderId(String moveToFolderId)
+    {
+        this.moveToFolderId = moveToFolderId;
+    }
+
+    public String[] getSelectedBookmarks()
+    {
+        return selectedBookmarks;
+    }
+
+    public void setSelectedBookmarks(String[] selectedBookmarks)
+    {
+        this.selectedBookmarks = selectedBookmarks;
+    }
+
+    public String[] getSelectedFolders()
+    {
+        return selectedFolders;
+    }
+
+    public void setSelectedFolders(String[] selectedFolders)
+    {
+        this.selectedFolders = selectedFolders;
+    }
+
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/formbeans/FolderFormEx.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/formbeans/FolderFormEx.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/formbeans/FolderFormEx.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/formbeans/FolderFormEx.java Mon May  1 15:23:02 2006
@@ -0,0 +1,105 @@
+/*
+* 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.presentation.bookmarks.formbeans;
+
+import org.apache.struts.upload.FormFile;
+import org.apache.roller.RollerException;
+import org.apache.roller.pojos.FolderData;
+import org.apache.roller.presentation.forms.FolderForm;
+
+
+/**
+ * Extends the FolderForm so that additional properties may be added.
+ * These properties are not persistent and are only needed for the UI.
+ *  
+ * @struts.form name="folderFormEx"
+ */ 
+public class FolderFormEx extends FolderForm
+{
+	private boolean mMoveContents = false; 
+	private String mMoveToFolderId = null; 
+    private String[] mSelectedBookmarks = null;
+    private String[] mSelectedFolders = null;
+    private transient FormFile mBookmarksFile = null;
+
+	public FolderFormEx()
+	{
+		super();
+	}
+
+	public FolderFormEx(FolderData folderData, java.util.Locale locale) throws RollerException
+	{
+		super(folderData, locale);
+	}
+
+    public String getShortenedDesc() 
+    {
+        if ( getDescription().length() > 20 )
+        {
+            return getDescription().substring(0,19)+"...";
+        }
+        return getDescription();
+    }
+
+    public void setShortenedDesc( String desc )
+    {
+        // readonly
+    }
+
+    //------------------------------------------------- Property bookmarksFile 
+
+    /** Bookmark file to be imported */
+    public void setBookmarksFile(FormFile file) { mBookmarksFile = file; }
+
+    /** Bookmark file to be imported */
+    public FormFile getBookmarksFile() { return mBookmarksFile; }
+
+    //-------------------------------------------------- Property moveContents
+
+	/** If true then contents should be moved when this folder is removed */
+	public boolean getMoveContents() { return mMoveContents; }
+
+	/** If true then contents should be moved when this folder is removed */
+	public void setMoveContents( boolean flag ) { mMoveContents = flag;}
+
+    //------------------------------------------------ Property moveToFolderId
+
+	/** Folder where contents should be moved if this folder is removed */ 
+	public String getMoveToFolderId() { return mMoveToFolderId; }
+
+	/** Folder where contents should be moved if this folder is removed */ 
+	public void setMoveToFolderId( String id ) { mMoveToFolderId = id;}
+
+    //--------------------------------------------- Property selectedBookmarks 
+
+    /** Get selected bookmarks */
+    public String[] getSelectedBookmarks() { return mSelectedBookmarks; }
+
+    /** Set selected bookmarks */
+    public void setSelectedBookmarks( String[] b ) { mSelectedBookmarks = b; }
+
+    //--------------------------------------------- Property selectedBookmarks 
+
+    /** Get selected folders */
+    public String[] getSelectedFolders() { return mSelectedFolders; }
+
+    /** Set selected bookmarks */
+    public void setSelectedFolders( String[] f ) { mSelectedFolders = f; }
+}
+

Added: incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/formbeans/package.html
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/formbeans/package.html?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/formbeans/package.html (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/bookmarks/formbeans/package.html Mon May  1 15:23:02 2006
@@ -0,0 +1,27 @@
+<!--
+  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.
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+  <title></title>
+</head>
+<body>
+Extended form beans needed for bookmarks and folders.
+
+</body>
+</html>

Added: incubator/roller/trunk/src/org/apache/roller/presentation/cache/Cache.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/cache/Cache.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/cache/Cache.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/cache/Cache.java Mon May  1 15:23:02 2006
@@ -0,0 +1,78 @@
+/*
+* 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.
+*/
+/*
+ * RollerCache.java
+ *
+ * Created on September 18, 2005, 10:59 AM
+ */
+
+package org.apache.roller.presentation.cache;
+
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Base interface representing a presentation cache in Roller.
+ *
+ * @author Allen Gilliland
+ */
+public interface Cache {
+    
+    /**
+     * put an item in the cache.
+     */
+    public void put(String key, Object value);
+    
+    
+    /**
+     * get an item from the cache.
+     */
+    public Object get(String key);
+    
+    
+    /**
+     * remove an item from the cache.
+     */
+    public void remove(String key);
+    
+    
+    /**
+     * remove a set of items from the cache.
+     */
+    public void remove(Set keys);
+    
+    
+    /**
+     * clear the entire cache.
+     */
+    public void clear();
+    
+    
+    /**
+     * get a list of keys used in the cache.
+     */
+    public Set keySet();
+    
+    
+    /**
+     * get cache stats.
+     */
+    public Map stats();
+    
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/cache/CacheFactory.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/cache/CacheFactory.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/cache/CacheFactory.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/cache/CacheFactory.java Mon May  1 15:23:02 2006
@@ -0,0 +1,42 @@
+/*
+* 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.
+*/
+/*
+ * CacheFactory.java
+ *
+ * Created on October 26, 2005, 3:25 PM
+ */
+
+package org.apache.roller.presentation.cache;
+
+import java.util.Map;
+
+
+/**
+ * An interface representing a cache factory.  Implementors of this interface
+ * are responsible for providing a method to construct cache implementations.
+ *
+ * In Roller you switch between various caching options by choosing a different
+ * cache factory before starting up the application.
+ *
+ * @author Allen Gilliland
+ */
+public interface CacheFactory {
+    
+    public Cache constructCache(Map properties);
+    
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/cache/CacheHandler.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/cache/CacheHandler.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/cache/CacheHandler.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/cache/CacheHandler.java Mon May  1 15:23:02 2006
@@ -0,0 +1,72 @@
+/*
+* 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.
+*/
+/*
+ * CacheHandler.java
+ *
+ * Created on November 5, 2005, 7:33 PM
+ */
+
+package org.apache.roller.presentation.cache;
+
+import java.util.Map;
+import org.apache.roller.pojos.BookmarkData;
+import org.apache.roller.pojos.CommentData;
+import org.apache.roller.pojos.FolderData;
+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.WeblogTemplate;
+import org.apache.roller.pojos.WebsiteData;
+
+
+/**
+ * A class which utilizes a cache.
+ *
+ * The primary purpose of this interface is to force cache handlers to implement
+ * the set of invalidate() methods which server as notifications of changed
+ * objects.  Various caches can determine for themselves how to deal with changes
+ * to each type of object.
+ *
+ * @author Allen Gilliland
+ */
+public interface CacheHandler {
+    
+    public void invalidate(WeblogEntryData entry);
+    
+    public void invalidate(WebsiteData website);
+    
+    public void invalidate(BookmarkData bookmark);
+    
+    public void invalidate(FolderData folder);
+
+    public void invalidate(CommentData comment);
+
+    public void invalidate(RefererData referer);
+
+    public void invalidate(UserData user);
+
+    public void invalidate(WeblogCategoryData category);
+
+    public void invalidate(WeblogTemplate template);
+    
+    public void clear();
+    
+    public Map getStats();
+    
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/cache/CacheManager.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/cache/CacheManager.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/cache/CacheManager.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/cache/CacheManager.java Mon May  1 15:23:02 2006
@@ -0,0 +1,462 @@
+/*
+* 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.
+*/
+/*
+ * CacheManager.java
+ *
+ * Created on September 30, 2005, 4:28 PM
+ */
+
+package org.apache.roller.presentation.cache;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.roller.business.runnable.ContinuousWorkerThread;
+import org.apache.roller.business.runnable.Job;
+import org.apache.roller.config.RollerConfig;
+import org.apache.roller.pojos.BookmarkData;
+import org.apache.roller.pojos.CommentData;
+import org.apache.roller.pojos.FolderData;
+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.WeblogTemplate;
+import org.apache.roller.pojos.WebsiteData;
+
+
+/**
+ * A governing class for Roller cache objects.
+ *
+ * The purpose of the CacheManager is to provide a level of abstraction between
+ * classes that use a cache and the implementations of a cache.  This allows
+ * us to create easily pluggable cache implementations.
+ * 
+ * The other purpose is to provide a single interface for interacting with all
+ * Roller caches at the same time.  This is beneficial because as data
+ * changes in the system we often need to notify all caches that some part of
+ * their cached data needs to be invalidated, and the CacheManager makes that
+ * process easier.
+ *
+ * @author Allen Gilliland
+ */
+public class CacheManager {
+    
+    private static Log mLogger = LogFactory.getLog(CacheManager.class);
+    
+    private static final String DEFAULT_FACTORY = 
+            "org.apache.roller.presentation.cache.ExpiringLRUCacheFactoryImpl";
+    
+    // a reference to the cache factory in use
+    private static CacheFactory mCacheFactory = null;
+    
+    // maintain a cache of the last expired time for each weblog
+    private static Cache lastExpiredCache = null;
+    
+    // a list of all cache handlers who have obtained a cache
+    private static Set cacheHandlers = new HashSet();
+    
+    private static ContinuousWorkerThread futureInvalidationsThread = null;
+    
+    
+    static {
+        // lookup what cache factory we want to use
+        String classname = RollerConfig.getProperty("cache.defaultFactory");
+        
+        // use reflection to instantiate our factory class
+        try {
+            Class factoryClass = Class.forName(classname);
+            mCacheFactory = (CacheFactory) factoryClass.newInstance();
+        } catch(ClassCastException cce) {
+            mLogger.error("It appears that your factory does not implement "+
+                    "the CacheFactory interface",cce);
+        } catch(Exception e) {
+            mLogger.error("Unable to instantiate cache factory ["+classname+"]"+
+                    " falling back on default", e);
+        }
+        
+        if(mCacheFactory == null) try {
+            // hmm ... failed to load the specified cache factory
+            // lets try our default
+            Class factoryClass = Class.forName(DEFAULT_FACTORY);
+            mCacheFactory = (CacheFactory) factoryClass.newInstance();
+        } catch(Exception e) {
+            mLogger.fatal("Failed to instantiate a cache factory", e);
+        }
+        
+        mLogger.info("Cache Manager Initialized.");
+        mLogger.info("Default cache factory = "+mCacheFactory.getClass().getName());
+        
+        
+        // setup our cache for expiration dates
+        // TODO: this really should not be something that is cached here
+        //       a better approach would be to add a weblog.lastChanged field
+        //       and track this along with the WebsiteData object
+        String lastExpCacheFactory = RollerConfig.getProperty("cache.lastExpired.factory");
+        Map lastExpProps = new HashMap();
+        if(lastExpCacheFactory != null) {
+            lastExpProps.put("factory", lastExpCacheFactory);
+        }
+        lastExpiredCache = CacheManager.constructCache(null, lastExpProps);
+        
+        
+        // add custom handlers
+        String customHandlers = RollerConfig.getProperty("cache.customHandlers");
+        if(customHandlers != null && customHandlers.trim().length() > 0) {
+            
+            String[] cHandlers = customHandlers.split(",");
+            for(int i=0; i < cHandlers.length; i++) {
+                // use reflection to instantiate the handler class
+                try {
+                    Class handlerClass = Class.forName(cHandlers[i]);
+                    CacheHandler customHandler = 
+                            (CacheHandler) handlerClass.newInstance();
+                    
+                    cacheHandlers.add(customHandler);
+                } catch(ClassCastException cce) {
+                    mLogger.error("It appears that your handler does not implement "+
+                            "the CacheHandler interface",cce);
+                } catch(Exception e) {
+                    mLogger.error("Unable to instantiate cache handler ["+cHandlers[i]+"]", e);
+                }
+            }
+        }
+        
+        // determine future invalidations peering window
+        Integer peerTime = new Integer(5);
+        String peerTimeString = RollerConfig.getProperty("cache.futureInvalidations.peerTime");
+        try {
+            peerTime = new Integer(peerTimeString);
+        } catch(NumberFormatException nfe) {
+            // bad input from config file, default already set
+        }
+        
+        // thread time is always 10 secs less than peer time to make sure
+        // there is a little overlap so we don't miss any entries
+        // this means every XX seconds we peer XX + 10 seconds into the future
+        int threadTime = (peerTime.intValue() * 60 * 1000) - (10 * 1000);
+        
+        // start up future invalidations job, running continuously
+        futureInvalidationsThread = new ContinuousWorkerThread("future invalidations thread", threadTime);
+        Job futureInvalidationsJob = new FuturePostingsInvalidationJob();
+        
+        // inputs
+        Map inputs = new HashMap();
+        inputs.put("peerTime", peerTime);
+        futureInvalidationsJob.input(inputs);
+        
+        // set job and start it
+        futureInvalidationsThread.setJob(futureInvalidationsJob);
+        futureInvalidationsThread.start();
+    }
+    
+    
+    // a non-instantiable class
+    private CacheManager() {}
+    
+    
+    /**
+     * Ask the CacheManager to construct a cache.
+     *
+     * Normally the CacheManager will use whatever CacheFactory has been
+     * chosen for the system via the cache.defaultFactory property.
+     * However, it is possible to override the use of the default factory by
+     * supplying a "factory" property to this method.  The value should
+     * be the full classname for the factory you want to use for constructing
+     * the cache.
+     *
+     * example:
+     *   factory -> org.apache.roller.presentation.cache.LRUCacheFactoryImpl
+     *
+     * This allows Roller admins the ability to choose a caching strategy to
+     * use for the whole system, but override it in certain places where they
+     * see fit.  It also allows users to write their own caching modifications
+     * and have them used only by specific caches.
+     */
+    public static Cache constructCache(CacheHandler handler, Map properties) {
+        
+        mLogger.debug("Constructing new cache with props "+properties);
+        
+        Cache cache = null;
+        
+        if(properties != null && properties.containsKey("factory")) {
+            // someone wants a custom cache instance
+            String classname = (String) properties.get("factory");
+            
+            try {
+                // use reflection to instantiate the factory class
+                Class factoryClass = Class.forName(classname);
+                CacheFactory factory = (CacheFactory) factoryClass.newInstance();
+                
+                // now ask for a new cache
+                cache = factory.constructCache(properties);
+            } catch(ClassCastException cce) {
+                mLogger.error("It appears that your factory ["+classname+
+                        "] does not implement the CacheFactory interface",cce);
+            } catch(Exception e) {
+                mLogger.error("Unable to instantiate cache factory ["+classname+
+                        "] falling back on default", e);
+            }
+        }
+        
+        if(cache == null) {
+            // ask our default cache factory for a new cache instance
+            cache = mCacheFactory.constructCache(properties);
+        }
+        
+        // register the handler for this new cache
+        if(handler != null) {
+            cacheHandlers.add(handler);
+        }
+        
+        return cache;
+    }
+    
+    
+    /**
+     * Register a CacheHandler to listen for object invalidations.
+     *
+     * This is here so that it's possible to to add classes which would respond
+     * to object invalidations without necessarily having to create a cache.
+     *
+     * An example would be a handler designed to notify other machines in a 
+     * cluster when an object has been invalidated, or possibly the search
+     * index management classes are interested in knowing when objects are
+     * invalidated.
+     */
+    public static void registerHandler(CacheHandler handler) {
+        
+        mLogger.debug("Registering handler "+handler);
+        
+        if(handler != null) {
+            cacheHandlers.add(handler);
+        }
+    }
+    
+    
+    public static void invalidate(WeblogEntryData entry) {
+        
+        mLogger.debug("invalidating entry = "+entry.getAnchor());
+        
+        setLastExpiredDate(entry.getWebsite().getHandle());
+        
+        Iterator handlers = cacheHandlers.iterator();
+        while(handlers.hasNext()) {
+            ((CacheHandler) handlers.next()).invalidate(entry);
+        }
+    }
+    
+    
+    public static void invalidate(WebsiteData website) {
+        
+        mLogger.debug("invalidating website = "+website.getHandle());
+        
+        setLastExpiredDate(website.getHandle());
+        
+        Iterator handlers = cacheHandlers.iterator();
+        while(handlers.hasNext()) {
+            ((CacheHandler) handlers.next()).invalidate(website);
+        }
+    }
+    
+    
+    public static void invalidate(BookmarkData bookmark) {
+        
+        mLogger.debug("invalidating bookmark = "+bookmark.getId());
+        
+        setLastExpiredDate(bookmark.getWebsite().getHandle());
+        
+        Iterator handlers = cacheHandlers.iterator();
+        while(handlers.hasNext()) {
+            ((CacheHandler) handlers.next()).invalidate(bookmark);
+        }
+    }
+    
+    
+    public static void invalidate(FolderData folder) {
+        
+        mLogger.debug("invalidating folder = "+folder.getId());
+        
+        setLastExpiredDate(folder.getWebsite().getHandle());
+        
+        Iterator handlers = cacheHandlers.iterator();
+        while(handlers.hasNext()) {
+            ((CacheHandler) handlers.next()).invalidate(folder);
+        }
+    }
+    
+    
+    public static void invalidate(CommentData comment) {
+        
+        mLogger.debug("invalidating comment = "+comment.getId());
+        
+        setLastExpiredDate(comment.getWeblogEntry().getWebsite().getHandle());
+        
+        Iterator handlers = cacheHandlers.iterator();
+        while(handlers.hasNext()) {
+            ((CacheHandler) handlers.next()).invalidate(comment);
+        }
+    }
+    
+    
+    public static void invalidate(RefererData referer) {
+        
+        mLogger.debug("invalidating referer = "+referer.getId());
+        
+        // NOTE: Invalidating an entire website for each referer is not
+        //       good for our caching.  This may need reevaluation later.
+        //lastExpiredCache.put(referer.getWebsite().getHandle(), new Date());
+        
+        Iterator handlers = cacheHandlers.iterator();
+        while(handlers.hasNext()) {
+            ((CacheHandler) handlers.next()).invalidate(referer);
+        }
+    }
+    
+    
+    public static void invalidate(UserData user) {
+        
+        mLogger.debug("invalidating user = "+user.getUserName());
+        
+        Iterator handlers = cacheHandlers.iterator();
+        while(handlers.hasNext()) {
+            ((CacheHandler) handlers.next()).invalidate(user);
+        }
+    }
+    
+    
+    public static void invalidate(WeblogCategoryData category) {
+        
+        mLogger.debug("invalidating category = "+category.getId());
+        
+        setLastExpiredDate(category.getWebsite().getHandle());
+        
+        Iterator handlers = cacheHandlers.iterator();
+        while(handlers.hasNext()) {
+            ((CacheHandler) handlers.next()).invalidate(category);
+        }
+    }
+    
+    
+    public static void invalidate(WeblogTemplate template) {
+        
+        mLogger.debug("invalidating template = "+template.getId());
+        
+        setLastExpiredDate(template.getWebsite().getHandle());
+        
+        Iterator handlers = cacheHandlers.iterator();
+        while(handlers.hasNext()) {
+            ((CacheHandler) handlers.next()).invalidate(template);
+        }
+    }
+
+    
+    /**
+     * Flush the entire cache system.
+     */
+    public static void clear() {
+        
+        // update all expired dates
+        lastExpiredCache.clear();
+        
+        // loop through all handlers and trigger a clear
+        CacheHandler handler = null;
+        Iterator handlers = cacheHandlers.iterator();
+        while(handlers.hasNext()) {
+            handler = (CacheHandler) handlers.next();
+            
+            handler.clear();
+        }
+    }
+    
+    
+    /**
+     * Flush a single cache handler.
+     */
+    public static void clear(String handlerClass) {
+        
+        // update all expired dates
+        lastExpiredCache.clear();
+        
+        // loop through all handlers to find the one we want
+        CacheHandler handler = null;
+        Iterator handlers = cacheHandlers.iterator();
+        while(handlers.hasNext()) {
+            handler = (CacheHandler) handlers.next();
+            
+            if(handler.getClass().getName().equals(handlerClass)) {
+                handler.clear();
+            }
+        }
+    }
+    
+    
+    public static void setLastExpiredDate(String weblogHandle) {
+        lastExpiredCache.put("lastExpired:"+weblogHandle, new Date());
+    }
+    
+    
+    /**
+     * Get the date of the last time the specified weblog was invalidated.
+     */
+    public static Date getLastExpiredDate(String weblogHandle) {
+        return (Date) lastExpiredCache.get("lastExpired:"+weblogHandle);
+    }
+    
+    
+    /**
+     * Compile stats from all registered handlers.
+     *
+     * This is basically a hacky version of instrumentation which is being
+     * thrown in because we don't have a full instrumentation strategy yet.
+     * This is here with the full expectation that it will be replaced by
+     * something a bit more elaborate, like JMX.
+     */
+    public static Map getStats() {
+        
+        Map allStats = new HashMap();
+        
+        CacheHandler handler = null;
+        Iterator handlers = cacheHandlers.iterator();
+        while(handlers.hasNext()) {
+            handler = (CacheHandler) handlers.next();
+            
+            allStats.put(handler.getClass().getName(), handler.getStats());
+        }
+        
+        return allStats;
+    }
+    
+    
+    /**
+     * Place to do any cleanup tasks for cache system.
+     */
+    public static void shutdown() {
+        
+        // stop our future invalidations thread
+        if(futureInvalidationsThread != null) {
+            futureInvalidationsThread.interrupt();
+        }
+    }
+    
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/cache/ExpiringCacheEntry.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/cache/ExpiringCacheEntry.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/cache/ExpiringCacheEntry.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/cache/ExpiringCacheEntry.java Mon May  1 15:23:02 2006
@@ -0,0 +1,90 @@
+/*
+* 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.
+*/
+/*
+ * ExpiringCacheEntry.java
+ *
+ * Created on November 4, 2005, 12:10 PM
+ */
+
+package org.apache.roller.presentation.cache;
+
+import java.io.Serializable;
+
+
+/**
+ * A cache entry that expires.
+ *
+ * We use this class to wrap objects being cached and associate a timestamp
+ * and timeout period with them so we can know when they expire.
+ *
+ * @author Allen Gilliland
+ */
+public class ExpiringCacheEntry implements Serializable {
+    
+    private Object value;
+    private long timeCached = -1;
+    private long timeout = 0;
+    
+    
+    public ExpiringCacheEntry(Object value, long timeout) {
+        this.value = value;
+        
+        // make sure that we don't support negative values
+        if(timeout > 0) {
+            this.timeout = timeout;
+        }
+        
+        this.timeCached = System.currentTimeMillis();
+    }
+    
+    
+    public long getTimeCached() {
+        return this.timeCached;
+    }
+    
+    
+    public long getTimeout() {
+        return this.timeout;
+    }
+    
+    
+    /**
+     * Retrieve the value of this cache entry.
+     *
+     * If the value has expired then we return null.
+     */
+    public Object getValue() {
+        if(this.hasExpired()) {
+            return null;
+        } else {
+            return this.value;
+        }
+    }
+    
+    
+    /**
+     * Determine if this cache entry has expired.
+     */
+    public boolean hasExpired() {
+        
+        long now = System.currentTimeMillis();
+        
+        return ((this.timeCached + this.timeout) < now);
+    }
+    
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/cache/ExpiringLRUCacheFactoryImpl.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/cache/ExpiringLRUCacheFactoryImpl.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/cache/ExpiringLRUCacheFactoryImpl.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/cache/ExpiringLRUCacheFactoryImpl.java Mon May  1 15:23:02 2006
@@ -0,0 +1,73 @@
+/*
+* 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.
+*/
+/*
+ * LRUCacheFactoryImpl.java
+ *
+ * Created on October 26, 2005, 3:33 PM
+ */
+
+package org.apache.roller.presentation.cache;
+
+import java.util.Map;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Roller Expiring LRU cache factory.
+ *
+ * @author Allen Gilliland
+ */
+public class ExpiringLRUCacheFactoryImpl implements CacheFactory {
+    
+    private static Log mLogger = 
+            LogFactory.getLog(ExpiringLRUCacheFactoryImpl.class);
+    
+    
+    // protected so only the CacheManager can instantiate us
+    protected ExpiringLRUCacheFactoryImpl() {}
+    
+    
+    /**
+     * Construct a new instance of a Roller Expiring LRUCache.
+     */
+    public Cache constructCache(Map properties) {
+        
+        int size = 100;
+        long timeout = 15 * 60;
+        
+        try {
+            size = Integer.parseInt((String) properties.get("size"));
+        } catch(Exception e) {
+            // ignored
+        }
+        
+        try {
+            timeout = Long.parseLong((String) properties.get("timeout"));
+        } catch(Exception e) {
+            // ignored
+        }
+        
+        Cache cache = new ExpiringLRUCacheImpl(size, timeout);
+        
+        mLogger.debug("new cache constructed. size="+size+", timeout="+timeout);
+        
+        return cache;
+    }
+    
+}
\ No newline at end of file

Added: incubator/roller/trunk/src/org/apache/roller/presentation/cache/ExpiringLRUCacheImpl.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/cache/ExpiringLRUCacheImpl.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/cache/ExpiringLRUCacheImpl.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/cache/ExpiringLRUCacheImpl.java Mon May  1 15:23:02 2006
@@ -0,0 +1,102 @@
+/*
+* 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.
+*/
+/*
+ * ExpiringLRUCacheImpl.java
+ *
+ * Created on November 6, 2005, 10:33 AM
+ */
+
+package org.apache.roller.presentation.cache;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * An LRU cache where entries expire after a given timeout period.
+ *
+ * @author Allen Gilliland
+ */
+public class ExpiringLRUCacheImpl extends LRUCacheImpl {
+    
+    private static Log mLogger = LogFactory.getLog(ExpiringLRUCacheImpl.class);
+    
+    private long timeout = 0;
+    
+    
+    protected ExpiringLRUCacheImpl() {
+        
+        super();
+        this.timeout = 60 * 60 * 1000;
+    }
+    
+    
+    protected ExpiringLRUCacheImpl(int maxsize, long timeout) {
+        
+        super(maxsize);
+        
+        // timeout is specified in seconds; only positive values allowed
+        if(timeout > 0) {
+            this.timeout = timeout * 1000;
+        }
+    }
+    
+    
+    /**
+     * Store an entry in the cache.
+     *
+     * We wrap the cached object in our ExpiringCacheEntry object so that we
+     * can track when the entry has expired.
+     */
+    public synchronized void put(String key, Object value) {
+        
+        ExpiringCacheEntry entry = new ExpiringCacheEntry(value, this.timeout);
+        super.put(key, entry);
+    }
+    
+    
+    /**
+     * Retrieve an entry from the cache.
+     *
+     * This LRU cache supports timeouts, so if the cached object has expired
+     * then we return null, just as if the entry wasn't found.
+     */
+    public Object get(String key) {
+        
+        Object value = null;
+        ExpiringCacheEntry entry = null;
+        
+        synchronized(this) {
+            entry = (ExpiringCacheEntry) super.get(key);
+        }
+        
+        if (entry != null) {
+            
+            value = entry.getValue();
+            
+            // if the value is null then that means this entry expired
+            if (value == null) {
+                mLogger.debug("entry expired ["+key+"]");
+                super.remove(key);
+            }
+        }
+        
+        return value;
+    }
+    
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/cache/FuturePostingsInvalidationJob.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/cache/FuturePostingsInvalidationJob.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/cache/FuturePostingsInvalidationJob.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/cache/FuturePostingsInvalidationJob.java Mon May  1 15:23:02 2006
@@ -0,0 +1,135 @@
+/*
+* 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.
+*/
+/*
+ * FuturePostingsInvalidationJob.java
+ *
+ * Created on February 14, 2006, 5:48 PM
+ */
+
+package org.apache.roller.presentation.cache;
+
+import java.util.Calendar;
+import java.util.Date;
+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.business.runnable.Job;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.model.WeblogManager;
+import org.apache.roller.pojos.WeblogEntryData;
+
+
+/**
+ * Trigger cache invalidations for entries published into the future.
+ *
+ * This job is meant to be run at timed intervals, it will not do any
+ * good to run this job only a single time.
+ *
+ * We do things in a somewhat counterintuitive manner, but it makes things
+ * easier on us from an operational point of view.  We start by looking up
+ * all entries published between now and some time XX mins in the future.  We
+ * then save that list and when XX mins has passed we invalidate the list and
+ * query for entries published in the next XX mins.
+ *
+ * Basically we are building a short term list of future published entries
+ * and expiring them once our wait period is over.  This prevents us from
+ * having to somehow determine which entries published in the last XX mins
+ * had previously been published into the future.
+ *
+ * @author Allen Gilliland
+ */
+public class FuturePostingsInvalidationJob implements Job {
+    
+    private static Log mLogger = LogFactory.getLog(FuturePostingsInvalidationJob.class);
+    
+    // inputs from the user
+    private Map inputs = null;
+    
+    // the list of entries we expire at the start of the next run
+    private List nextExpirations = null;
+    
+    // how far into the future we will look ahead, in minutes
+    int peerTime = 5;
+    
+    public void execute() {
+        
+        mLogger.debug("starting");
+        
+        // notify the cache manager of an invalidation
+        if(nextExpirations != null) {
+            WeblogEntryData entry = null;
+            Iterator entries = nextExpirations.iterator();
+            while(entries.hasNext()) {
+                entry = (WeblogEntryData) entries.next();
+                
+                mLogger.debug("expiring "+entry.getAnchor());
+                
+                CacheManager.invalidate(entry);
+            }
+        }
+        
+        // look for postings from current time to current time plus XX mins
+        List expiringEntries = null;
+        try {
+            WeblogManager mgr = RollerFactory.getRoller().getWeblogManager();
+            
+            // current time
+            Date start = new Date();
+            
+            // XX mins in the future
+            Calendar cal = Calendar.getInstance();
+            cal.setTime(start);
+            cal.add(Calendar.MINUTE, this.peerTime);
+            Date end = cal.getTime();
+            
+            mLogger.debug("looking up entries between "+start+" and "+end);
+            
+            // get all published entries between start and end date
+            expiringEntries = mgr.getWeblogEntries(null, start, end, null, 
+                    null, WeblogEntryData.PUBLISHED, null);
+            
+            this.nextExpirations = expiringEntries;
+            
+        } catch(Exception e) {
+            mLogger.error(e);
+        }
+        
+        mLogger.debug("finished");
+    }
+    
+    
+    public Map output() {
+       return null; 
+    }
+    
+    
+    public void input(Map input) {
+        this.inputs = input;
+        
+        // extract peer time if possible
+        Integer pTime = (Integer) this.inputs.get("peerTime");
+        if(pTime != null) {
+            this.peerTime = pTime.intValue();
+        }
+        
+        mLogger.info("Peeking "+this.peerTime+" minutes into the future each pass");
+    }
+    
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/cache/LRUCacheFactoryImpl.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/cache/LRUCacheFactoryImpl.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/cache/LRUCacheFactoryImpl.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/cache/LRUCacheFactoryImpl.java Mon May  1 15:23:02 2006
@@ -0,0 +1,65 @@
+/*
+* 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.
+*/
+/*
+ * LRUCacheFactoryImpl.java
+ *
+ * Created on November 6, 2005, 10:48 AM
+ */
+
+package org.apache.roller.presentation.cache;
+
+import java.util.Map;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Roller LRU Cache factory.
+ *
+ * @author Allen Gilliland
+ */
+public class LRUCacheFactoryImpl implements CacheFactory {
+    
+    private static Log mLogger = LogFactory.getLog(LRUCacheFactoryImpl.class);
+    
+    
+    // protected so that only the CacheManager can instantiate us
+    protected LRUCacheFactoryImpl() {}
+    
+    
+    /**
+     * Construct a new instance of a Roller LRUCache.
+     */
+    public Cache constructCache(Map properties) {
+        
+        int size = 100;
+        
+        try {
+            size = Integer.parseInt((String) properties.get("size"));
+        } catch(Exception e) {
+            // ignored
+        }
+        
+        Cache cache = new LRUCacheImpl(size);
+        
+        mLogger.debug("new cache constructed. size="+size);
+        
+        return cache;
+    }
+    
+}