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 2005/06/08 18:06:46 UTC
svn commit: r189602 [19/50] - in /incubator/roller/trunk: ./ contrib/
contrib/lib/ contrib/plugins/ contrib/plugins/src/ contrib/plugins/src/org/
contrib/plugins/src/org/roller/ contrib/plugins/src/org/roller/presentation/
contrib/plugins/src/org/roller/presentation/velocity/
contrib/plugins/src/org/roller/presentation/velocity/plugins/
contrib/plugins/src/org/roller/presentation/velocity/plugins/acronyms/
contrib/plugins/src/org/roller/presentation/velocity/plugins/bookmarks/
contrib/plugins/src/org/roller/presentation/velocity/plugins/email/
contrib/plugins/src/org/roller/presentation/velocity/plugins/jspwiki/
contrib/plugins/src/org/roller/presentation/velocity/plugins/radeox/
contrib/plugins/src/org/roller/presentation/velocity/plugins/readmore/
contrib/plugins/src/org/roller/presentation/velocity/plugins/smileys/
contrib/plugins/src/org/roller/presentation/velocity/plugins/textile/ docs/
docs/images/ docs/installguide/ docs/installguide/old/ docs/userguide/
docs/userguide/images/ docs/userguide/old/ metadata/ metadata/database/
metadata/database/hibernate/ metadata/xdoclet/ personal/ personal/eclipse/
personal/testing/ src/ src/org/ src/org/roller/ src/org/roller/business/
src/org/roller/business/hibernate/ src/org/roller/business/utils/
src/org/roller/model/ src/org/roller/pojos/ src/org/roller/presentation/
src/org/roller/presentation/atom/ src/org/roller/presentation/bookmarks/
src/org/roller/presentation/bookmarks/actions/
src/org/roller/presentation/bookmarks/formbeans/
src/org/roller/presentation/bookmarks/tags/
src/org/roller/presentation/filters/ src/org/roller/presentation/forms/
src/org/roller/presentation/newsfeeds/ src/org/roller/presentation/pagecache/
src/org/roller/presentation/pagecache/rollercache/
src/org/roller/presentation/tags/ src/org/roller/presentation/tags/calendar/
src/org/roller/presentation/tags/menu/
src/org/roller/presentation/velocity/ src/org/roller/presentation/weblog/
src/org/roller/presentation/weblog/actions/
src/org/roller/presentation/weblog/formbeans/
src/org/roller/presentation/weblog/search/
src/org/roller/presentation/weblog/search/operations/
src/org/roller/presentation/weblog/tags/ src/org/roller/presentation/website/
src/org/roller/presentation/website/actions/
src/org/roller/presentation/website/formbeans/
src/org/roller/presentation/website/tags/
src/org/roller/presentation/xmlrpc/ src/org/roller/util/ tests/ tests/org/
tests/org/roller/ tests/org/roller/business/ tests/org/roller/model/
tests/org/roller/persistence/ tests/org/roller/presentation/
tests/org/roller/presentation/atom/ tests/org/roller/presentation/bookmarks/
tests/org/roller/presentation/velocity/
tests/org/roller/presentation/velocity/plugins/
tests/org/roller/presentation/velocity/plugins/smileys/
tests/org/roller/presentation/velocity/plugins/textile/
tests/org/roller/presentation/xmlrpc/ tests/org/roller/util/ tools/
tools/buildtime/ tools/buildtime/mockrunner-0.2.6/
tools/buildtime/mockrunner-0.2.6/lib/ tools/buildtime/tomcat-4.1.24/
tools/buildtime/xdoclet-1.2/ tools/buildtime/xdoclet-1.2/lib/
tools/hibernate-2.1/ tools/hibernate-2.1/lib/ tools/lib/
tools/standard-1.0.3/ tools/standard-1.0.3/lib/ tools/standard-1.0.3/tld/
tools/struts-1.1/ tools/struts-1.1/lib/ web/ web/WEB-INF/
web/WEB-INF/classes/ web/WEB-INF/classes/flavors/
web/WEB-INF/classes/themes/ web/bookmarks/ web/images/ web/images/editor/
web/images/midas/ web/images/preview/ web/images/smileys/ web/tags/
web/templates/ web/theme/ web/theme/images/ web/theme/lavender/
web/theme/scripts/ web/theme/scripts/classes/ web/themes/ web/themes/basic/
web/themes/berkley/ web/themes/berkley/images/ web/themes/cheb/
web/themes/cheb/images/ web/themes/cheb/scripts/ web/themes/clean/
web/themes/currency-i18n/ web/themes/currency-i18n/images/
web/themes/currency/ web/themes/currency/images/ web/themes/grey2/
web/themes/moonshine/ web/themes/pacifica/ web/themes/robot/
web/themes/rolling/ web/themes/rolling/images/ web/themes/sotto/
web/themes/sotto/images/ web/themes/sotto/styles/ web/themes/sunsets/
web/themes/sunsets/images/ web/themes/sunsets/scripts/
web/themes/sunsets/styles/ web/themes/werner/ web/themes/x2/
web/themes/x2/images/ web/themes/x2/scripts/ web/themes/x2/styles/
web/weblog/ web/website/
Added: incubator/roller/trunk/src/org/roller/presentation/filters/IfModifiedFilter.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/filters/IfModifiedFilter.java?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/filters/IfModifiedFilter.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/filters/IfModifiedFilter.java Wed Jun 8 09:06:16 2005
@@ -0,0 +1,192 @@
+/*
+ * Created on Apr 19, 2003
+ */
+package org.roller.presentation.filters;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.roller.RollerException;
+import org.roller.model.Roller;
+import org.roller.model.WeblogManager;
+import org.roller.pojos.UserData;
+import org.roller.presentation.RollerContext;
+import org.roller.presentation.RollerRequest;
+import org.roller.util.LRUCache;
+import org.roller.util.LRUCache2;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Map;
+import java.text.SimpleDateFormat;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Entry point filter for Newsfeed Servlets, this filter
+ * Handles If-Modified-Since header using per-user and per-category
+ * last weblog pub time. Returns 304 if requested weblog has not been
+ * modified since. Also, sets Last-Modified on outgoing response.
+ *
+ * @web.filter name="IfModifiedFilter"
+ *
+ * @author David M Johnson
+ */
+public class IfModifiedFilter implements Filter
+{
+ private static Log mLogger =
+ LogFactory.getFactory().getInstance(IfModifiedFilter.class);
+
+ private ServletContext mContext;
+ private FilterConfig mConfig;
+
+ // TODO: make cache configurable
+ private static LRUCache2 mDateCache = new LRUCache2(300, 20000);
+ SimpleDateFormat dateFormatter = new SimpleDateFormat("EEE MMM d HH:mm:ss z yyyy");
+
+ public IfModifiedFilter()
+ {
+ super();
+ }
+
+ /**
+ * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
+ */
+ public void init(FilterConfig filterConfig) throws ServletException
+ {
+ // This method is not multithreaded, so we dont need to sync
+ mContext = filterConfig.getServletContext();
+ }
+
+ /**
+ * @see javax.servlet.Filter#doFilter(
+ * javax.servlet.ServletRequest,
+ * javax.servlet.ServletResponse,
+ * javax.servlet.FilterChain)
+ */
+ public void doFilter(
+ ServletRequest req,
+ ServletResponse res,
+ FilterChain chain)
+ throws IOException, ServletException
+ {
+ HttpServletRequest request = (HttpServletRequest) req;
+ HttpServletResponse response = (HttpServletResponse) res;
+ Roller roller = RollerContext.getRoller( request );
+
+ Date updateTime = null;
+ try
+ {
+ updateTime = getLastPublishedDate(request);
+
+ // RSS context loader needs updateTime, so stash it
+ request.setAttribute("updateTime", updateTime);
+
+ // Check the incoming if-modified-since header
+ Date sinceDate =
+ new Date(request.getDateHeader("If-Modified-Since"));
+
+ if (updateTime != null)
+ {
+ // convert date (JDK 1.5 workaround)
+ String date = dateFormatter.format(updateTime);
+ updateTime = new Date(date);
+ if (updateTime.compareTo(sinceDate) <= 0)
+ {
+ response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+ return;
+ }
+ }
+ mLogger.debug("Not returning 304 for: "+request.getRequestURI());
+ }
+ catch (RollerException e)
+ {
+ // Thrown by getLastPublishedDate if there is a db-type error
+ response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ catch (IllegalArgumentException e)
+ {
+ // Thrown by getDateHeader if not in valid format. This can be
+ // safely ignored, the only consequence is that the NOT MODIFIED
+ // response is not set.
+ }
+
+ // Set outgoing last modified header
+ if (updateTime != null)
+ {
+ response.setDateHeader("Last-Modified", updateTime.getTime());
+ }
+
+ chain.doFilter(request, response);
+ }
+
+ public static Date getLastPublishedDate(HttpServletRequest request)
+ throws RollerException
+ {
+ // Get user name without using heavy RollerRequest URL parser
+ String userName = null;
+ String pathInfo = request.getPathInfo();
+ pathInfo = pathInfo != null ? pathInfo : "";
+ String[] pathInfoArray = StringUtils.split(pathInfo, "/");
+ if (pathInfoArray.length == 1)
+ {
+ userName = pathInfoArray[0];
+ }
+ else if (pathInfoArray.length > 1)
+ {
+ // request is for a specific date or anchor, can't return 304
+ return null;
+ }
+
+ // Get last pub time for specific weblog category requested
+ String catname =
+ request.getParameter(RollerRequest.WEBLOGCATEGORYNAME_KEY);
+
+ // update times are cached to reduce database queries per request
+ StringBuffer sb = new StringBuffer();
+ sb.append("zzz_");
+ sb.append(userName);
+ sb.append("_zzz_");
+ sb.append(catname);
+ String key = sb.toString();
+
+ Date updateTime = (Date)mDateCache.get(key);
+ if (updateTime == null)
+ {
+ mLogger.debug("Hitting database for update time: "+key);
+ Roller roller = RollerContext.getRoller(request);
+ roller.begin();
+ WeblogManager wmgr = roller.getWeblogManager();
+ updateTime = wmgr.getWeblogLastPublishTime(userName, catname);
+ mDateCache.put(key, updateTime);
+ }
+ return updateTime;
+ }
+
+ public static void purgeDateCache(UserData user)
+ {
+ String userName = (user != null) ? user.getUserName() : null;
+ StringBuffer sb = new StringBuffer();
+ sb.append("zzz_");
+ sb.append(userName);
+ sb.append("_zzz_");
+ mDateCache.purge(new String[] {sb.toString()});
+ }
+
+ /**
+ * @see javax.servlet.Filter#destroy()
+ */
+ public void destroy()
+ {
+ }
+}
Added: incubator/roller/trunk/src/org/roller/presentation/filters/LoginFilter.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/filters/LoginFilter.java?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/filters/LoginFilter.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/filters/LoginFilter.java Wed Jun 8 09:06:16 2005
@@ -0,0 +1,146 @@
+package org.roller.presentation.filters;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.roller.model.UserManager;
+import org.roller.pojos.UserData;
+import org.roller.presentation.RequestUtil;
+import org.roller.presentation.RollerRequest;
+import org.roller.util.Utilities;
+
+
+/**
+ * <p>Intercepts Login requests for "Remember Me" functionality.</p>
+ *
+ * @author Matt Raible
+ * @version $Revision: 1.1 $ $Date: 2004/03/21 04:33:55 $
+ *
+ * @web.filter display-name="Login Filter" name="loginFilter"
+ * @web.filter-init-param name="enabled" value="true"
+ */
+public final class LoginFilter implements Filter
+{
+ //~ Instance fields ========================================================
+
+ private Log mLogger = LogFactory.getLog(LoginFilter.class);
+ private FilterConfig mFilterConfig = null;
+ private boolean enabled = true;
+
+ //~ Methods ================================================================
+
+ public void doFilter(ServletRequest req, ServletResponse resp,
+ FilterChain chain)
+ throws IOException, ServletException
+ {
+
+ HttpServletRequest request = (HttpServletRequest) req;
+ HttpServletResponse response = (HttpServletResponse) resp;
+
+ // See if the user has a remember me cookie
+ Cookie c = RequestUtil.getCookie(request, RollerRequest.LOGIN_COOKIE);
+
+ try
+ {
+ RollerRequest rreq = RollerRequest.getRollerRequest(request);
+ UserManager mgr = rreq.getRoller().getUserManager();
+
+ // Check to see if the user is logging out, if so, remove all
+ // login cookies
+ if (request.getRequestURL().indexOf("logout") != -1)
+ {
+ if (mLogger.isDebugEnabled())
+ {
+ mLogger.debug("logging out '" + request.getRemoteUser() + "'");
+ }
+
+ mgr.removeLoginCookies(request.getRemoteUser());
+ rreq.getRoller().commit();
+ RequestUtil.deleteCookie(response, c, request.getContextPath());
+ }
+ else if (c != null && enabled)
+ {
+ String loginCookie = mgr.checkLoginCookie(c.getValue());
+ rreq.getRoller().commit();
+
+ if (loginCookie != null)
+ {
+ RequestUtil.setCookie(response, RollerRequest.LOGIN_COOKIE,
+ loginCookie,
+ request.getContextPath());
+ loginCookie = Utilities.decodeString(loginCookie);
+
+ String[] value = StringUtils.split(loginCookie, '|');
+
+ UserData user = mgr.getUser( value[0] );
+
+ // authenticate user without displaying login page
+ String route = "/auth?j_username=" +
+ user.getUserName() + "&j_password=" +
+ user.getPassword();
+
+ request.setAttribute("encrypt", "false");
+ request.getSession().setAttribute("cookieLogin", "true");
+
+ if (mLogger.isDebugEnabled())
+ {
+ mLogger.debug("I remember you '" + user.getUserName() +
+ "', attempting to authenticate...");
+ }
+
+ RequestDispatcher dispatcher =
+ request.getRequestDispatcher(route);
+ dispatcher.forward(request, response);
+
+ return;
+ }
+ }
+
+ } catch (Exception e)
+ {
+ // no big deal if cookie-based authentication fails
+ mLogger.warn(e.getMessage());
+ }
+
+ chain.doFilter(req, resp);
+ }
+
+ /**
+ * Initialize controller values of filter.
+ */
+ public void init(FilterConfig config)
+ {
+ this.mFilterConfig = config;
+
+ String param = config.getInitParameter("enabled");
+ enabled = Boolean.valueOf(param).booleanValue();
+
+ if (mLogger.isDebugEnabled())
+ {
+ mLogger.debug("Remember Me enabled: " + enabled);
+ }
+
+ config.getServletContext()
+ .setAttribute("rememberMeEnabled",
+ config.getInitParameter("enabled"));
+ }
+
+ /**
+ * destroy any instance values other than config *
+ */
+ public void destroy() {
+ }
+}
Added: incubator/roller/trunk/src/org/roller/presentation/filters/RefererFilter.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/filters/RefererFilter.java?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/filters/RefererFilter.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/filters/RefererFilter.java Wed Jun 8 09:06:16 2005
@@ -0,0 +1,121 @@
+package org.roller.presentation.filters;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.roller.model.RefererManager;
+import org.roller.presentation.RollerContext;
+import org.roller.presentation.RollerRequest;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+
+
+/**
+ * Keep track of referers.
+ *
+ * @web.filter name="RefererFilter"
+ * *web.filter-mapping url-pattern="/page/*"
+ *
+ * @author David M. Johnson
+ */
+public class RefererFilter implements Filter
+{
+ private FilterConfig mFilterConfig = null;
+ private static Log mLogger =
+ LogFactory.getFactory().getInstance(RefererFilter.class);
+
+ /**
+ * destroy
+ */
+ public void destroy()
+ {
+ }
+
+ /**
+ * doFilter
+ */
+ public void doFilter(
+ ServletRequest req, ServletResponse res, FilterChain chain)
+ throws IOException, ServletException
+ {
+ HttpServletRequest request = (HttpServletRequest)req;
+ try
+ {
+ RollerRequest rreq = RollerRequest.getRollerRequest(request);
+ RollerContext rctx = RollerContext.getRollerContext(
+ mFilterConfig.getServletContext());
+
+ if ( rreq.getUser() != null )
+ {
+ String userName = rreq.getUser().getUserName();
+
+ String referer = request.getHeader("Referer");
+
+ // Base page URLs, with and without www.
+ String basePageUrlWWW =
+ rctx.getAbsoluteContextUrl(request)+"/page/"+userName;
+ String basePageUrl = basePageUrlWWW;
+ if ( basePageUrlWWW.startsWith("http://www.") )
+ {
+ // chop off the http://www.
+ basePageUrl = "http://"+basePageUrlWWW.substring(11);
+ }
+
+ // Base comment URLs, with and without www.
+ String baseCommentsUrlWWW =
+ rctx.getAbsoluteContextUrl(request)+"/page/"+userName;
+ String baseCommentsUrl = baseCommentsUrlWWW;
+ if ( baseCommentsUrlWWW.startsWith("http://www.") )
+ {
+ // chop off the http://www.
+ baseCommentsUrl= "http://"+baseCommentsUrlWWW.substring(11);
+ }
+
+ // Don't process hits from same user's blogs as referers by
+ // ignoring Don't process referer from pages that start with base URLs.
+ if ( referer==null ||
+ (
+ !referer.startsWith( basePageUrl )
+ && !referer.startsWith( basePageUrlWWW )
+ && !referer.startsWith( baseCommentsUrl )
+ && !referer.startsWith( baseCommentsUrlWWW )
+ )
+ )
+ {
+ RefererManager refMgr =
+ rreq.getRoller().getRefererManager();
+ refMgr.processRequest(rreq);
+ }
+ else
+ {
+ if (mLogger.isDebugEnabled())
+ {
+ mLogger.debug("Ignoring referer="+referer);
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ mLogger.error("Processing referer",e);
+ }
+
+ chain.doFilter(req, res);
+ }
+
+ /**
+ * init
+ */
+ public void init(FilterConfig filterConfig) throws ServletException
+ {
+ mFilterConfig = filterConfig;
+ }
+}
Added: incubator/roller/trunk/src/org/roller/presentation/filters/RequestFilter.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/filters/RequestFilter.java?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/filters/RequestFilter.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/filters/RequestFilter.java Wed Jun 8 09:06:16 2005
@@ -0,0 +1,184 @@
+package org.roller.presentation.filters;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.Globals;
+import org.roller.RollerException;
+import org.roller.model.Roller;
+import org.roller.model.UserManager;
+import org.roller.presentation.RequestUtil;
+import org.roller.presentation.RollerContext;
+import org.roller.presentation.RollerRequest;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+import java.util.Locale;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.jsp.jstl.core.Config;
+
+
+/**
+ * Entry point filter for Weblog page and Editor UI, this filter
+ * creates a RollerRequest object to parse pathinfo and request parameters.
+ *
+ * @web.filter name="RequestFilter"
+ *
+ * @author David M. Johnson, Matt Raible
+ */
+public class RequestFilter implements Filter
+{
+ private FilterConfig mFilterConfig = null;
+ private static Log mLogger =
+ LogFactory.getFactory().getInstance(RequestFilter.class);
+
+ /**
+ * destroy
+ */
+ public void destroy()
+ {
+ }
+
+ /**
+ * As the first and last filter in the chain, it is necessary that
+ * RequestFilter releases its Roller resources before it returns.
+ */
+ public void doFilter(
+ ServletRequest req, ServletResponse res, FilterChain chain)
+ throws IOException, ServletException
+ {
+ try
+ {
+ // insure that incoming data is parsed as UTF-8
+ req.setCharacterEncoding("UTF-8");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new ServletException("Can't set incoming encoding to UTF-8");
+ }
+
+ // keep JSTL and Struts Locale's in sync
+ HttpSession session = ((HttpServletRequest)req).getSession();
+ if (null != session)
+ {
+ Locale locale = (Locale)session.getAttribute(Globals.LOCALE_KEY);
+ if (locale == null)
+ {
+ locale = req.getLocale();
+ }
+ if (req.getParameter("locale") != null)
+ {
+ locale = new Locale(req.getParameter("locale"));
+ }
+ session.setAttribute(Globals.LOCALE_KEY, locale);
+ Config.set(session, Config.FMT_LOCALE, locale);
+ }
+
+ HttpServletRequest request = (HttpServletRequest)req;
+ HttpServletResponse response = (HttpServletResponse)res;
+ Roller roller = RollerContext.getRoller( request );
+ RollerRequest rreq = null;
+ try
+ {
+ if (request.getAttribute("roller.begun") == null)
+ {
+ roller.begin();
+ }
+ else
+ {
+ mLogger.debug("Came through filter again on URL="+request.getRequestURL());
+ }
+ }
+ catch (Throwable t)
+ {
+ mLogger.error("ERROR creating persistence session", t);
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ try
+ {
+ rreq = RollerRequest.getRollerRequest(
+ request, mFilterConfig.getServletContext());
+
+ // if user wants to be remembered, create a remember me cookie
+ // TODO: Figure out a better place to put this - so it will
+ // only be called when the user initially logs in
+ String username = request.getRemoteUser();
+
+ if (username != null)
+ {
+ if (session.getAttribute(RollerRequest.LOGIN_COOKIE) != null)
+ {
+ session.removeAttribute(RollerRequest.LOGIN_COOKIE);
+
+ UserManager mgr = rreq.getRoller().getUserManager();
+
+ String loginCookie = mgr.createLoginCookie(username);
+ rreq.getRoller().commit();
+ RequestUtil.setCookie(response, RollerRequest.LOGIN_COOKIE,
+ loginCookie, request.getContextPath());
+ }
+ }
+
+ }
+ catch (RollerException e)
+ {
+ // An error initializing the request is considered to be a 404
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+
+ if (session != null)
+ {
+ // look for messages and errors in the request, and if they
+ // exist, stuff them in the request - in Struts 1.2, you don't
+ // need to do this
+ if (session.getAttribute(Globals.MESSAGE_KEY) != null)
+ {
+ request.setAttribute(Globals.MESSAGE_KEY,
+ session.getAttribute(Globals.MESSAGE_KEY));
+ session.removeAttribute(Globals.MESSAGE_KEY);
+ }
+ if (session.getAttribute(Globals.ERROR_KEY) != null)
+ {
+ request.setAttribute(Globals.ERROR_KEY,
+ session.getAttribute(Globals.ERROR_KEY));
+ session.removeAttribute(Globals.ERROR_KEY);
+ }
+ }
+
+ Date updateTime = null;
+ try
+ {
+ updateTime = IfModifiedFilter.getLastPublishedDate(request);
+ }
+ catch (RollerException e1)
+ {
+ mLogger.debug("Getting lastUpdateTime", e1);
+ }
+ if (updateTime != null)
+ {
+ request.setAttribute("updateTime", updateTime);
+ }
+
+ chain.doFilter(req, res);
+ }
+
+ /**
+ * init
+ */
+ public void init(FilterConfig filterConfig) throws ServletException
+ {
+ mFilterConfig = filterConfig;
+ }
+}
+
Added: incubator/roller/trunk/src/org/roller/presentation/filters/package.html
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/filters/package.html?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/filters/package.html (added)
+++ incubator/roller/trunk/src/org/roller/presentation/filters/package.html Wed Jun 8 09:06:16 2005
@@ -0,0 +1,9 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <title></title>
+</head>
+<body>
+Servlet filters<br>
+</body>
+</html>
Added: incubator/roller/trunk/src/org/roller/presentation/forms/package.html
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/forms/package.html?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/forms/package.html (added)
+++ incubator/roller/trunk/src/org/roller/presentation/forms/package.html Wed Jun 8 09:06:16 2005
@@ -0,0 +1,9 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <title></title>
+</head>
+<body>
+XDoclet generated form classes, one for each Roller bean.<br>
+</body>
+</html>
Added: incubator/roller/trunk/src/org/roller/presentation/newsfeeds/NewsfeedCache.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/newsfeeds/NewsfeedCache.java?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/newsfeeds/NewsfeedCache.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/newsfeeds/NewsfeedCache.java Wed Jun 8 09:06:16 2005
@@ -0,0 +1,153 @@
+package org.roller.presentation.newsfeeds;
+
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.roller.pojos.RollerConfig;
+import org.roller.util.LRUCache2;
+
+import de.nava.informa.core.ChannelIF;
+import de.nava.informa.core.ParseException;
+import de.nava.informa.impl.basic.ChannelBuilder;
+import de.nava.informa.parsers.RSSParser;
+
+/**
+ * Returns parsed RSS feed by pulling one from a cache or by retrieving and
+ * parging the specified feed using the Flock RSS parser.
+ *
+ * @author Lance Lavandowska
+ * @author Dave Johnson
+ */
+public class NewsfeedCache
+{
+ private static Log mLogger = LogFactory.getFactory().getInstance(
+ NewsfeedCache.class);
+
+ /** Static singleton * */
+ public static NewsfeedCache mInstance = null;
+
+ /** Instance vars * */
+ private RollerConfig mConfig = null;
+
+ /** LRU cache */
+ LRUCache2 mCache = null;
+
+ /** Constructor * */
+ public NewsfeedCache(RollerConfig config)
+ {
+ mConfig = config;
+ // TODO: make cache size configurable
+ mCache = new LRUCache2(100, mConfig.getRssCacheTime().intValue());
+ }
+
+ /** static singleton retriever * */
+ public static NewsfeedCache getInstance(RollerConfig config)
+ {
+ if (mInstance == null)
+ {
+ if (mLogger.isDebugEnabled())
+ {
+ mLogger.debug("Instantiating new NewsfeedCache");
+ }
+
+ synchronized (NewsfeedCache.class)
+ {
+ mInstance = new NewsfeedCache(config);
+ }
+ }
+ return mInstance;
+ }
+
+ /**
+ * Returns a Channel object for the supplied RSS newsfeed URL.
+ *
+ * @param feedUrl
+ * RSS newsfeed URL.
+ * @return FlockFeedI for specified RSS newsfeed URL.
+ */
+ public ChannelIF getChannel(String feedUrl)
+ {
+ ChannelIF channel = null;
+ //FlockFeed feed = null;
+ try
+ {
+ //FlockFeedFactory factory = new FlockFeedFactory();
+
+ // If aggregator has been disable return null
+ boolean enabled = mConfig.getEnableAggregator().booleanValue();
+ if (!enabled)
+ {
+ return null;
+ }
+
+ if (mConfig.getRssUseCache().booleanValue())
+ {
+ if (mLogger.isDebugEnabled())
+ {
+ mLogger.debug("Newsfeed: use Cache for " + feedUrl);
+ }
+
+ // Get pre-parsed feed from the cache
+ channel = (ChannelIF) mCache.get(feedUrl);
+ if (mLogger.isDebugEnabled())
+ {
+ mLogger.debug("Newsfeed: got from Cache");
+ }
+
+ if (channel == null)
+ {
+ try
+ {
+ // Parse the feed
+ channel = RSSParser.parse(
+ new ChannelBuilder(), new URL(feedUrl));
+ }
+ catch (ParseException e1)
+ {
+ mLogger.info("Error parsing RSS: " + feedUrl);
+ }
+ }
+
+ //channel = factory.createFeed(new URL(feedUrl));
+
+ // Store parsed feed in the cache
+ mCache.put(feedUrl, channel);
+ mLogger.debug("Newsfeed: not in Cache");
+ }
+ else
+ {
+ if (mLogger.isDebugEnabled())
+ {
+ mLogger.debug("Newsfeed: not using Cache for " + feedUrl);
+ }
+ try
+ {
+ // Parse the feed
+ channel = RSSParser.parse(new ChannelBuilder(), new URL(
+ feedUrl));
+ }
+ catch (ParseException e1)
+ {
+ mLogger.info("Error parsing RSS: " + feedUrl);
+ }
+ }
+ }
+ catch (IOException ioe)
+ {
+ if (mLogger.isDebugEnabled())
+ {
+ mLogger.debug("Newsfeed: Unexpected exception", ioe);
+ }
+ }
+ // catch (FlockResourceException e)
+ // {
+ // if (mLogger.isDebugEnabled())
+ // {
+ // mLogger.debug("Newsfeed: Flock parsing exception",e);
+ // }
+ // }
+ return channel;
+ }
+}
\ No newline at end of file
Added: incubator/roller/trunk/src/org/roller/presentation/newsfeeds/package.html
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/newsfeeds/package.html?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/newsfeeds/package.html (added)
+++ incubator/roller/trunk/src/org/roller/presentation/newsfeeds/package.html Wed Jun 8 09:06:16 2005
@@ -0,0 +1,9 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <title></title>
+</head>
+<body>
+Newsfeed parser and cache.<br>
+</body>
+</html>
Added: incubator/roller/trunk/src/org/roller/presentation/package.html
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/package.html?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/package.html (added)
+++ incubator/roller/trunk/src/org/roller/presentation/package.html Wed Jun 8 09:06:16 2005
@@ -0,0 +1,29 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <title></title>
+</head>
+<body>
+
+<p>
+The org.roller.presentation package and the packages under it comprise the
+<b>Presentation Layer</b> - the web-based Roller user interface.
+
+The code in the packages depend on Web APIs including the Servlet API, JSP API,
+Struts, Velocity and the like.
+</p>
+
+<p>
+These packages are also the home of Roller's Web Services interfaces -
+both the XML-RPC based Blogger/MetaWeblog APIs and the REST based Atom API
+Web Service interfaces are suppported.
+
+Generally speaking, the code in these packages implements presentation logic
+and presentation logic only.
+
+Wherever possible, application logic that can be made independent of Web APIs
+is implemented inside the Business Layer
+</p>
+
+</body>
+</html>
Added: incubator/roller/trunk/src/org/roller/presentation/pagecache/FilterHandler.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/pagecache/FilterHandler.java?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/pagecache/FilterHandler.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/pagecache/FilterHandler.java Wed Jun 8 09:06:16 2005
@@ -0,0 +1,37 @@
+package org.roller.presentation.pagecache;
+
+import org.roller.pojos.UserData;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+public interface FilterHandler
+{
+ /**
+ * Clean up anything necessary before destruction.
+ */
+ public void destroy();
+
+ /**
+ * Exactly as Filter.doFilter().
+ */
+ public void doFilter(ServletRequest request,
+ ServletResponse response, FilterChain chain)
+ throws ServletException, IOException;
+
+ /**
+ * Clear the entire cache.
+ */
+ public void flushCache(HttpServletRequest req);
+
+ /**
+ * Remove the entries for this User
+ * from the cache.
+ */
+ public void removeFromCache(HttpServletRequest req,UserData user);
+}
Added: incubator/roller/trunk/src/org/roller/presentation/pagecache/PageCache.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/pagecache/PageCache.java?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/pagecache/PageCache.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/pagecache/PageCache.java Wed Jun 8 09:06:16 2005
@@ -0,0 +1,117 @@
+
+package org.roller.presentation.pagecache;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.roller.pojos.UserData;
+import org.roller.presentation.filters.IfModifiedFilter;
+import org.roller.presentation.pagecache.rollercache.LRUCacheHandler2;
+
+
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * Roller's in memory page cache. See Javadoc for LRUCacheHandler for more
+ * information on configuring this cache.
+ *
+ * @web.filter name="PageCacheFilter"
+ *
+ * @web.filter-init-param name="size" value="100"
+ * description="Number of pages to keep in cache"
+ *
+ * @web.filter-init-param name="timeoutInterval" value="1800"
+ * description="Page Cache timeout interval in seconds (-1 to disable timeout, default is 30 minutes)"
+ *
+ * @web.filter-init-param name="timeoutRatio" value="1.0"
+ * description="Ratio of pages to be timed out on interval (1.0 means 100%, default is 1.0)"
+ *
+ * @author Lance Lavandowska
+ * @author David M Johnson
+ */
+public class PageCache implements Filter
+{
+ private static Log mLogger =
+ LogFactory.getFactory().getInstance(PageCache.class);
+
+ private FilterHandler mHandler = null;
+
+ private static ArrayList mHandlers = new ArrayList();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Initialize the filter.
+ * @param filerConfig The filter configuration
+ */
+ public void init(FilterConfig filterConfig)
+ {
+ String handlerClass = filterConfig.getInitParameter("handler");
+ if (mLogger.isDebugEnabled())
+ {
+ mLogger.debug(
+ "Initializing as filterName: "+filterConfig.getFilterName());
+ }
+ mHandler = new LRUCacheHandler2(filterConfig);
+ mHandlers.add(mHandler);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Filter clean-up
+ */
+ public void destroy()
+ {
+ mHandler.destroy();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Process the doFilter
+ * @param request The servlet request
+ * @param response The servlet response
+ * @param chain The filet chain
+ * @throws ServletException IOException
+ */
+ public void doFilter(ServletRequest request,
+ ServletResponse response, FilterChain chain)
+ throws ServletException, IOException
+ {
+ mHandler.doFilter(request, response, chain);
+ }
+
+ //-----------------------------------------------------------------------
+ /** Flush cache for all handlers of this class */
+ public static void flushCache(HttpServletRequest req)
+ {
+ Iterator iter = mHandlers.iterator();
+ while (iter.hasNext())
+ {
+ FilterHandler handler = (FilterHandler)iter.next();
+ handler.flushCache(req);
+ }
+ IfModifiedFilter.purgeDateCache(null);
+ }
+
+ //-----------------------------------------------------------------------
+ /** Remove from cache for all handlers of this class */
+ public static void removeFromCache(HttpServletRequest req, UserData user)
+ {
+ Iterator iter = mHandlers.iterator();
+ while (iter.hasNext())
+ {
+ FilterHandler handler = (FilterHandler)iter.next();
+ handler.removeFromCache(req, user);
+ }
+ IfModifiedFilter.purgeDateCache(user);
+ }
+}
Added: incubator/roller/trunk/src/org/roller/presentation/pagecache/package.html
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/pagecache/package.html?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/pagecache/package.html (added)
+++ incubator/roller/trunk/src/org/roller/presentation/pagecache/package.html Wed Jun 8 09:06:16 2005
@@ -0,0 +1,9 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <title></title>
+</head>
+<body>
+Roller's custom OSCache Servlet filter.<br>
+</body>
+</html>
Added: incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/CacheHttpServletResponseWrapper.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/CacheHttpServletResponseWrapper.java?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/CacheHttpServletResponseWrapper.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/CacheHttpServletResponseWrapper.java Wed Jun 8 09:06:16 2005
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package org.roller.presentation.pagecache.rollercache;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import java.util.Locale;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+/**
+ * CacheServletResponse is a serialized representation of a response
+ *
+ * @author <a href="mailto:sergek@lokitech.com">Serge Knystautas</a>
+ * @version $Revision: 1.1 $
+ */
+public class CacheHttpServletResponseWrapper extends HttpServletResponseWrapper {
+ private final Log log = LogFactory.getLog(this.getClass());
+
+ /**
+ * We cache the printWriter so we can maintain a single instance
+ * of it no matter how many times it is requested.
+ */
+ private PrintWriter cachedWriter;
+ private ResponseContent result = null;
+ private SplitServletOutputStream cacheOut = null;
+ private int status = SC_OK;
+
+ /**
+ * Constructor
+ *
+ * @param response The servlet response
+ */
+ public CacheHttpServletResponseWrapper(HttpServletResponse response) {
+ super(response);
+ result = new ResponseContent();
+ }
+
+ /**
+ * Get a response content
+ *
+ * @return The content
+ */
+ public ResponseContent getContent() {
+ //Create the byte array
+ result.commit();
+
+ //Return the result from this response
+ return result;
+ }
+
+ /**
+ * Set the content type
+ *
+ * @param value The content type
+ */
+ public void setContentType(String value) {
+ super.setContentType(value);
+ result.setContentType(value);
+ }
+
+ /**
+ * Set the date of a header
+ *
+ * @param name The header name
+ * @param value The date
+ */
+ public void setDateHeader(String name, long value) {
+ if (log.isDebugEnabled()) {
+ log.debug("dateheader: " + name + ": " + value);
+ }
+
+ super.setDateHeader(name, value);
+ }
+
+ /**
+ * Set a header field
+ *
+ * @param name The header name
+ * @param value The header value
+ */
+ public void setHeader(String name, String value) {
+ if (log.isDebugEnabled()) {
+ log.debug("header: " + name + ": " + value);
+ }
+
+ super.setHeader(name, value);
+ }
+
+ /**
+ * Set the int value of the header
+ *
+ * @param name The header name
+ * @param value The int value
+ */
+ public void setIntHeader(String name, int value) {
+ if (log.isDebugEnabled()) {
+ log.debug("intheader: " + name + ": " + value);
+ }
+
+ super.setIntHeader(name, value);
+ }
+
+ /**
+ * We override this so we can catch the response status. Only
+ * responses with a status of 200 (<code>SC_OK</code>) will
+ * be cached.
+ */
+ public void setStatus(int status) {
+ super.setStatus(status);
+ this.status = status;
+ }
+
+ /**
+ * We override this so we can catch the response status. Only
+ * responses with a status of 200 (<code>SC_OK</code>) will
+ * be cached.
+ */
+ public void sendError(int status, String string) throws IOException {
+ super.sendError(status, string);
+ this.status = status;
+ }
+
+ /**
+ * We override this so we can catch the response status. Only
+ * responses with a status of 200 (<code>SC_OK</code>) will
+ * be cached.
+ */
+ public void sendError(int status) throws IOException {
+ super.sendError(status);
+ this.status = status;
+ }
+
+ /**
+ * We override this so we can catch the response status. Only
+ * responses with a status of 200 (<code>SC_OK</code>) will
+ * be cached.
+ */
+ public void setStatus(int status, String string) {
+ super.setStatus(status, string);
+ this.status = status;
+ }
+
+ public void sendRedirect(String location) throws IOException {
+ this.status = SC_MOVED_TEMPORARILY;
+ super.sendRedirect(location);
+ }
+
+ /**
+ * Retrieves the captured HttpResponse status.
+ */
+ public int getStatus() {
+ return status;
+ }
+
+ /**
+ * Set the locale
+ *
+ * @param value The locale
+ */
+ public void setLocale(Locale value) {
+ super.setLocale(value);
+ result.setLocale(value);
+ }
+
+ /**
+ * Get an output stream
+ *
+ * @throws IOException
+ */
+ public ServletOutputStream getOutputStream() throws IOException {
+ // Pass this faked servlet output stream that captures what is sent
+ if (cacheOut == null) {
+ cacheOut = new SplitServletOutputStream(result.getOutputStream(), super.getOutputStream());
+ }
+
+ return cacheOut;
+ }
+
+ /**
+ * Get a print writer
+ *
+ * @throws IOException
+ */
+ public PrintWriter getWriter() throws IOException {
+ if (cachedWriter == null) {
+ cachedWriter = new PrintWriter(getOutputStream());
+ }
+
+ return cachedWriter;
+ }
+
+ public void flushBuffer() throws IOException {
+ super.flushBuffer();
+
+ if (cacheOut != null) {
+ cacheOut.flush();
+ }
+
+ if (cachedWriter != null) {
+ cachedWriter.flush();
+ }
+ }
+}
Added: incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/LRUCacheHandler.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/LRUCacheHandler.java?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/LRUCacheHandler.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/LRUCacheHandler.java Wed Jun 8 09:06:16 2005
@@ -0,0 +1,466 @@
+/*
+ * Created on Jun 15, 2004
+ */
+package org.roller.presentation.pagecache.rollercache;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.roller.pojos.UserData;
+import org.roller.presentation.LanguageUtil;
+import org.roller.presentation.pagecache.FilterHandler;
+import org.roller.util.LRUCache;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeMap;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Page cache implementation that uses a simple LRUCache. Can be configured
+ * using filter configuration parameters:
+ * <ul>
+ * <li><b>size</b>: number of pages to keep in cache. Once cache reaches
+ * this size, each new cache entry will push out the LRU cache entry.</li>
+ *
+ * <li><b>timeoutInterval</b>: interval to timeout pages in seconds
+ * (default is 1800 seconds). Sites with a large number of users, and thus a
+ * lot of cache churn which makes this check unnecessary, may wish to set this
+ * to -1 to disable timeout checking.</li>
+ *
+ * <li><b>timeoutRatio</b>: portion of old pages to expire on timeout
+ * interval where 1.0 is 100% (default is 1.0). This only applies if the
+ * timeoutInterval is set to something other than -1.</li>
+ * </ul>
+ * @author David M Johnson
+ */
+public class LRUCacheHandler implements FilterHandler
+{
+ private static Log mLogger =
+ LogFactory.getFactory().getInstance(LRUCacheHandler.class);
+
+ private Map mPageCache = null;
+
+ private String mName = null;
+
+ /** Timeout interval: how often to run timeout task (default 30 mintes) */
+ private long mTimeoutInterval = 30 * 60 * 1000; // milliseconds
+
+ /** Timeout ratio: % of cache to expire on each timeout (default 1.0) */
+ private float mTimeoutRatio = 1.0F;
+
+ // Statistics
+ private int misses = 0;
+ private int hits = 0;
+
+ private final static String FILE_SEPARATOR = "/";
+ private final static char FILE_SEPARATOR_CHAR = FILE_SEPARATOR.charAt(0);
+ private final static short AVERAGE_KEY_LENGTH = 30;
+ private static final String m_strBase64Chars =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+
+ public LRUCacheHandler(FilterConfig config)
+ {
+ mName = config.getFilterName();
+ mLogger.info("Initializing for: " + mName);
+ int size = 200;
+ try
+ {
+ size = Integer.parseInt(config.getInitParameter("size"));
+ }
+ catch (Exception e)
+ {
+ mLogger.warn(config.getFilterName()
+ + "Can't read cache size parameter, using default...");
+ }
+ mLogger.info(mName + " size=" + size);
+ mPageCache = Collections.synchronizedMap(new LRUCache(size));
+
+ long intervalSeconds = mTimeoutInterval / 1000L;
+ try
+ {
+ mTimeoutInterval = 1000L * Long.parseLong(
+ config.getInitParameter("timeoutInterval"));
+
+ if (mTimeoutInterval == -1)
+ {
+ mLogger.info(config.getFilterName()
+ + "timeoutInterval of -1: timeouts are disabled");
+ }
+ else if (mTimeoutInterval < (30 * 1000))
+ {
+ mTimeoutInterval = 30 * 1000;
+ mLogger.warn(config.getFilterName()
+ + "timeoutInterval cannot be less than 30 seconds");
+ }
+ }
+ catch (Exception e)
+ {
+ mLogger.warn(config.getFilterName()
+ + "Can't read timeoutInterval parameter, disabling timeout.");
+ mTimeoutInterval = -1;
+ }
+ mLogger.info(mName + " timeoutInterval=" + intervalSeconds);
+
+ try
+ {
+ mTimeoutRatio = Float.parseFloat(
+ config.getInitParameter("timeoutRatio"));
+ }
+ catch (Exception e)
+ {
+ mLogger.warn(config.getFilterName()
+ + "Can't read timeoutRatio parameter, using default...");
+ }
+ mLogger.info(mName + " timeoutRatio=" + mTimeoutRatio);
+
+ if (mTimeoutInterval != -1 && mTimeoutRatio != 0.0)
+ {
+ Timer timer = new Timer();
+ timer.scheduleAtFixedRate(new TimerTask() {
+ public void run() {
+ timeoutCache();
+ }
+ }, mTimeoutInterval, mTimeoutInterval);
+ }
+ }
+
+ /**
+ * @see org.roller.presentation.pagecache.FilterHandler#destroy()
+ */
+ public void destroy()
+ {
+ }
+
+ /**
+ * @see org.roller.presentation.pagecache.FilterHandler#doFilter(
+ * javax.servlet.ServletRequest, javax.servlet.ServletResponse,
+ * javax.servlet.FilterChain)
+ */
+ public void doFilter(ServletRequest req, ServletResponse res,
+ FilterChain chain) throws ServletException, IOException
+ {
+ HttpServletRequest request = (HttpServletRequest) req;
+ HttpServletResponse response = (HttpServletResponse) res;
+
+ // get locale
+ Locale locale = LanguageUtil.getViewLocale(request);
+
+ // generate language-sensitive cache-key
+ String generatedKey = null;
+ if (locale != null)
+ {
+ generatedKey = generateEntryKey(null,
+ request, 1, locale.getLanguage());
+ }
+ else
+ {
+ generatedKey = generateEntryKey(null,
+ request, 1, null);
+ }
+
+ // Add authenticated user name, if there is one, to cache key
+ java.security.Principal prince = request.getUserPrincipal();
+ StringBuffer keyb = new StringBuffer();
+ keyb.append(generatedKey);
+ if (prince != null)
+ {
+ keyb.append("_");
+ keyb.append(prince);
+ }
+ String key = keyb.toString();
+
+ ResponseContent respContent = (ResponseContent)getFromCache(key);
+ if (respContent == null)
+ {
+ try
+ {
+ CacheHttpServletResponseWrapper cacheResponse =
+ new CacheHttpServletResponseWrapper(response);
+ chain.doFilter(request, cacheResponse);
+
+ // Store as the cache content the result of the response
+ // if no exception was noted by content generator.
+ if (request.getAttribute("DisplayException") == null)
+ {
+ ResponseContent rc = cacheResponse.getContent();
+ putToCache(key, rc);
+ }
+ else
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Display exception, cache, key=");
+ sb.append(key);
+ mLogger.error(sb.toString());
+ }
+ }
+ catch (java.net.SocketException se)
+ {
+ // ignore socket exceptions
+ }
+ catch (Exception e)
+ {
+ // something unexpected and bad happened
+ StringBuffer sb = new StringBuffer();
+ sb.append("Error rendering page, key=");
+ sb.append(key);
+ mLogger.error(sb.toString());
+ }
+ }
+ else
+ {
+ try
+ {
+ respContent.writeTo(response);
+ }
+ catch (java.net.SocketException se)
+ {
+ // ignore socket exceptions
+ }
+ catch (Exception e)
+ {
+ if (mLogger.isDebugEnabled())
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Probably a client abort exception, key=");
+ sb.append(key);
+ mLogger.error(sb.toString());
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Purge entire cache.
+ */
+ public synchronized void flushCache(HttpServletRequest req)
+ {
+ mPageCache.clear();
+ }
+
+ /**
+ * Purge user's entries from cache.
+ */
+ public synchronized void removeFromCache(HttpServletRequest req, UserData user)
+ {
+ // TODO: can we make this a little more precise, perhaps via regex?
+ String rssString = "/rss/" + user.getUserName(); // user's pages
+ String pageString = "/page/" + user.getUserName(); // user's RSS feeds
+ String mainRssString = "/rss_"; // main RSS feed
+ List purgeList = new ArrayList();
+
+ Iterator keys = mPageCache.keySet().iterator();
+ while (keys.hasNext())
+ {
+ String key = (String) keys.next();
+
+ if (key.indexOf(rssString)!=-1 || key.indexOf(pageString)!=-1 || key.indexOf(mainRssString)!=-1)
+ {
+ purgeList.add(key);
+ }
+ }
+
+ Iterator purgeIter = purgeList.iterator();
+ while (purgeIter.hasNext())
+ {
+ String key = (String) purgeIter.next();
+ mPageCache.remove(key);
+ }
+
+ if (mLogger.isDebugEnabled())
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Purged, count=");
+ sb.append(purgeList.size());
+ sb.append(", user=");
+ sb.append(user.getUserName());
+ mLogger.debug(sb.toString());
+ }
+ }
+
+ public synchronized void timeoutCache()
+ {
+ if (mTimeoutRatio == 1.0)
+ {
+ mLogger.debug("Timing out whole cache: " + mName);
+ mPageCache.clear();
+ }
+ else
+ {
+ int numToTimeout = (int)(mTimeoutRatio * mPageCache.size());
+ mLogger.debug(
+ "Timing out " + numToTimeout + " of " + mPageCache.size()
+ + " entries from cache: " + mName);
+ ArrayList allKeys = new ArrayList(mPageCache.keySet());
+ for (int i=numToTimeout; i>0; i--)
+ {
+ mPageCache.remove(allKeys.get(i));
+ }
+ }
+ }
+
+ /**
+ * Get from cache. Synchronized because "In access-ordered linked hash
+ * maps, merely querying the map with get is a structural modification"
+ */
+ public synchronized Object getFromCache(String key)
+ {
+ Object entry = mPageCache.get(key);
+
+ if (entry != null && mLogger.isDebugEnabled())
+ {
+ hits++;
+ }
+ return entry;
+ }
+
+ public synchronized void putToCache(String key, Object entry)
+ {
+ mPageCache.put(key, entry);
+ if (mLogger.isDebugEnabled())
+ {
+ misses++;
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("Missed, cache size=");
+ sb.append(mPageCache.size());
+ sb.append(", hits=");
+ sb.append(hits);
+ sb.append(", misses=");
+ sb.append(misses);
+ sb.append(", key=");
+ sb.append(key);
+ mLogger.debug(sb.toString());
+ }
+ }
+
+ public String generateEntryKey(String key,
+ HttpServletRequest request, int scope, String language)
+ {
+ StringBuffer cBuffer = new StringBuffer(AVERAGE_KEY_LENGTH);
+ // Append the language if available
+ if (language != null)
+ {
+ cBuffer.append(FILE_SEPARATOR).append(language);
+ }
+
+ //cBuffer.append(FILE_SEPARATOR).append(request.getServerName());
+
+ if (key != null)
+ {
+ cBuffer.append(FILE_SEPARATOR).append(key);
+ }
+ else
+ {
+ String generatedKey = request.getRequestURI();
+ if (generatedKey.charAt(0) != FILE_SEPARATOR_CHAR)
+ {
+ cBuffer.append(FILE_SEPARATOR_CHAR);
+ }
+ cBuffer.append(generatedKey);
+ cBuffer.append("_").append(request.getMethod()).append("_");
+ generatedKey = getSortedQueryString(request);
+ if (generatedKey != null)
+ {
+ try
+ {
+ java.security.MessageDigest digest =
+ java.security.MessageDigest.getInstance("MD5");
+ byte[] b = digest.digest(generatedKey.getBytes());
+ cBuffer.append("_");
+ // Base64 encoding allows for unwanted slash characters.
+ cBuffer.append(toBase64(b).replace('/', '_'));
+ }
+ catch (Exception e)
+ {
+ // Ignore query string
+ }
+ }
+ }
+ return cBuffer.toString();
+ }
+
+ protected String getSortedQueryString(HttpServletRequest request)
+ {
+ Map paramMap = request.getParameterMap();
+ if (paramMap.isEmpty())
+ {
+ return null;
+ }
+ Set paramSet = new TreeMap(paramMap).entrySet();
+ StringBuffer buf = new StringBuffer();
+ boolean first = true;
+ for (Iterator it = paramSet.iterator(); it.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry) it.next();
+ String[] values = (String[]) entry.getValue();
+ for (int i = 0; i < values.length; i++)
+ {
+ String key = (String) entry.getKey();
+ if ((key.length() != 10) || !"jsessionid".equals(key))
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buf.append('&');
+ }
+ buf.append(key).append('=').append(values[i]);
+ }
+ }
+ }
+ // We get a 0 length buffer if the only parameter was a jsessionid
+ if (buf.length() == 0)
+ {
+ return null;
+ }
+ else
+ {
+ return buf.toString();
+ }
+ }
+
+ /**
+ * Convert a byte array into a Base64 string (as used in mime formats)
+ */
+ private static String toBase64(byte[] aValue)
+ {
+ int byte1;
+ int byte2;
+ int byte3;
+ int iByteLen = aValue.length;
+ StringBuffer tt = new StringBuffer();
+ for (int i = 0; i < iByteLen; i += 3)
+ {
+ boolean bByte2 = (i + 1) < iByteLen;
+ boolean bByte3 = (i + 2) < iByteLen;
+ byte1 = aValue[i] & 0xFF;
+ byte2 = (bByte2) ? (aValue[i + 1] & 0xFF) : 0;
+ byte3 = (bByte3) ? (aValue[i + 2] & 0xFF) : 0;
+ tt.append(m_strBase64Chars.charAt(byte1 / 4));
+ tt.append(m_strBase64Chars.charAt((byte2 / 16)
+ + ((byte1 & 0x3) * 16)));
+ tt.append(((bByte2) ? m_strBase64Chars.charAt((byte3 / 64)
+ + ((byte2 & 0xF) * 4)) : '='));
+ tt.append(((bByte3) ? m_strBase64Chars.charAt(byte3 & 0x3F) : '='));
+ }
+ return tt.toString();
+ }
+}
Added: incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/LRUCacheHandler2.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/LRUCacheHandler2.java?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/LRUCacheHandler2.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/LRUCacheHandler2.java Wed Jun 8 09:06:16 2005
@@ -0,0 +1,385 @@
+/*
+ * Created on Jun 15, 2004
+ */
+package org.roller.presentation.pagecache.rollercache;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.roller.pojos.UserData;
+import org.roller.presentation.LanguageUtil;
+import org.roller.presentation.pagecache.FilterHandler;
+import org.roller.util.LRUCache2;
+
+/**
+ * Page cache implementation that uses a simple LRUCache. Can be configured
+ * using filter configuration parameters:
+ * <ul>
+ * <li><b>size</b>: number of pages to keep in cache. Once cache reaches
+ * this size, each new cache entry will push out the LRU cache entry.</li>
+ * <li><b>timeout</b>: interval to timeout pages in milliseconds.</li>
+ * </ul>
+ * @author David M Johnson
+ */
+public class LRUCacheHandler2 implements FilterHandler
+{
+ private static Log mLogger =
+ LogFactory.getFactory().getInstance(LRUCacheHandler2.class);
+
+ private LRUCache2 mPageCache = null;
+ private String mName = null;
+
+ // Statistics
+ private int misses = 0;
+ private int hits = 0;
+
+ private final static String FILE_SEPARATOR = "/";
+ private final static char FILE_SEPARATOR_CHAR = FILE_SEPARATOR.charAt(0);
+ private final static short AVERAGE_KEY_LENGTH = 30;
+ private static final String m_strBase64Chars =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ public LRUCacheHandler2(FilterConfig config)
+ {
+ mName = config.getFilterName();
+ mLogger.info("Initializing for: " + mName);
+ int size = 200;
+ try
+ {
+ size = Integer.parseInt(config.getInitParameter("size"));
+ }
+ catch (Exception e)
+ {
+ mLogger.warn(config.getFilterName()
+ + "Can't read cache size parameter, using default...");
+ }
+ mLogger.info(mName + " size=" + size);
+
+ long timeout = 30000;
+ try
+ {
+ timeout = Long.parseLong(
+ config.getInitParameter("timeout"));
+
+ if (timeout < (30 * 1000))
+ {
+ timeout = 30 * 1000;
+ mLogger.warn(config.getFilterName()
+ + "timeout cannot be less than 30 seconds");
+ }
+ }
+ catch (Exception e)
+ {
+ mLogger.warn(config.getFilterName()
+ + "Can't read timeout parameter, using default.");
+ }
+ mLogger.info(mName + " timeout=" + timeout);
+
+ mPageCache = new LRUCache2(size, timeout);
+ }
+
+ /**
+ * @see org.roller.presentation.pagecache.FilterHandler#destroy()
+ */
+ public void destroy()
+ {
+ }
+
+ /**
+ * @see org.roller.presentation.pagecache.FilterHandler#doFilter(
+ * javax.servlet.ServletRequest, javax.servlet.ServletResponse,
+ * javax.servlet.FilterChain)
+ */
+ public void doFilter(ServletRequest req, ServletResponse res,
+ FilterChain chain) throws ServletException, IOException
+ {
+ HttpServletRequest request = (HttpServletRequest) req;
+ HttpServletResponse response = (HttpServletResponse) res;
+
+ // get locale
+ Locale locale = LanguageUtil.getViewLocale(request);
+
+ // generate language-sensitive cache-key
+ String generatedKey = null;
+ if (locale != null)
+ {
+ generatedKey = generateEntryKey(null,
+ request, 1, locale.getLanguage());
+ }
+ else
+ {
+ generatedKey = generateEntryKey(null,
+ request, 1, null);
+ }
+
+ // Add authenticated user name, if there is one, to cache key
+ java.security.Principal prince = request.getUserPrincipal();
+ StringBuffer keyb = new StringBuffer();
+ keyb.append(generatedKey);
+ if (prince != null)
+ {
+ keyb.append("_");
+ keyb.append(prince);
+ }
+ String key = keyb.toString();
+
+ ResponseContent respContent = (ResponseContent)getFromCache(key);
+ if (respContent == null)
+ {
+ try
+ {
+ CacheHttpServletResponseWrapper cacheResponse =
+ new CacheHttpServletResponseWrapper(response);
+ chain.doFilter(request, cacheResponse);
+
+ // Store as the cache content the result of the response
+ // if no exception was noted by content generator.
+ if (request.getAttribute("DisplayException") == null)
+ {
+ ResponseContent rc = cacheResponse.getContent();
+ putToCache(key, rc);
+ }
+ else
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Display exception, cache, key=");
+ sb.append(key);
+ mLogger.error(sb.toString());
+ }
+ }
+ catch (java.net.SocketException se)
+ {
+ // ignore socket exceptions
+ }
+ catch (Exception e)
+ {
+ // something unexpected and bad happened
+ StringBuffer sb = new StringBuffer();
+ sb.append("Error rendering page, key=");
+ sb.append(key);
+ mLogger.error(sb.toString());
+ }
+ }
+ else
+ {
+ try
+ {
+ respContent.writeTo(response);
+ }
+ catch (java.net.SocketException se)
+ {
+ // ignore socket exceptions
+ }
+ catch (Exception e)
+ {
+ if (mLogger.isDebugEnabled())
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Probably a client abort exception, key=");
+ sb.append(key);
+ mLogger.error(sb.toString());
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Purge entire cache.
+ */
+ public synchronized void flushCache(HttpServletRequest req)
+ {
+ mPageCache.purge();
+ }
+
+ /**
+ * Purge user's entries from cache.
+ */
+ public synchronized void removeFromCache(HttpServletRequest req, UserData user)
+ {
+ // TODO: can we make this a little more precise, perhaps via regex?
+ String rssString = "/rss/" + user.getUserName(); // user's pages
+ String pageString = "/page/" + user.getUserName(); // user's RSS feeds
+ String mainRssString = "/rss_"; // main RSS feed
+
+ int beforeSize = mPageCache.size();
+ mPageCache.purge(new String[] {rssString, pageString, mainRssString});
+ int afterSize = mPageCache.size();
+
+ if (mLogger.isDebugEnabled())
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Purged, count=");
+ sb.append(beforeSize - afterSize);
+ sb.append(", user=");
+ sb.append(user.getUserName());
+ mLogger.debug(sb.toString());
+ }
+ }
+
+ /**
+ * Get from cache. Synchronized because "In access-ordered linked hash
+ * maps, merely querying the map with get is a structural modification"
+ */
+ public synchronized Object getFromCache(String key)
+ {
+ Object entry = mPageCache.get(key);
+
+ if (entry != null && mLogger.isDebugEnabled())
+ {
+ hits++;
+ }
+ return entry;
+ }
+
+ public synchronized void putToCache(String key, Object entry)
+ {
+ mPageCache.put(key, entry);
+ if (mLogger.isDebugEnabled())
+ {
+ misses++;
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("Missed, cache size=");
+ sb.append(mPageCache.size());
+ sb.append(", hits=");
+ sb.append(hits);
+ sb.append(", misses=");
+ sb.append(misses);
+ sb.append(", key=");
+ sb.append(key);
+ mLogger.debug(sb.toString());
+ }
+ }
+
+ public String generateEntryKey(String key,
+ HttpServletRequest request, int scope, String language)
+ {
+ StringBuffer cBuffer = new StringBuffer(AVERAGE_KEY_LENGTH);
+ // Append the language if available
+ if (language != null)
+ {
+ cBuffer.append(FILE_SEPARATOR).append(language);
+ }
+
+ //cBuffer.append(FILE_SEPARATOR).append(request.getServerName());
+
+ if (key != null)
+ {
+ cBuffer.append(FILE_SEPARATOR).append(key);
+ }
+ else
+ {
+ String generatedKey = request.getRequestURI();
+ if (generatedKey.charAt(0) != FILE_SEPARATOR_CHAR)
+ {
+ cBuffer.append(FILE_SEPARATOR_CHAR);
+ }
+ cBuffer.append(generatedKey);
+ cBuffer.append("_").append(request.getMethod()).append("_");
+ generatedKey = getSortedQueryString(request);
+ if (generatedKey != null)
+ {
+ try
+ {
+ java.security.MessageDigest digest =
+ java.security.MessageDigest.getInstance("MD5");
+ byte[] b = digest.digest(generatedKey.getBytes());
+ cBuffer.append("_");
+ // Base64 encoding allows for unwanted slash characters.
+ cBuffer.append(toBase64(b).replace('/', '_'));
+ }
+ catch (Exception e)
+ {
+ // Ignore query string
+ }
+ }
+ }
+ return cBuffer.toString();
+ }
+
+ protected String getSortedQueryString(HttpServletRequest request)
+ {
+ Map paramMap = request.getParameterMap();
+ if (paramMap.isEmpty())
+ {
+ return null;
+ }
+ Set paramSet = new TreeMap(paramMap).entrySet();
+ StringBuffer buf = new StringBuffer();
+ boolean first = true;
+ for (Iterator it = paramSet.iterator(); it.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry) it.next();
+ String[] values = (String[]) entry.getValue();
+ for (int i = 0; i < values.length; i++)
+ {
+ String key = (String) entry.getKey();
+ if ((key.length() != 10) || !"jsessionid".equals(key))
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buf.append('&');
+ }
+ buf.append(key).append('=').append(values[i]);
+ }
+ }
+ }
+ // We get a 0 length buffer if the only parameter was a jsessionid
+ if (buf.length() == 0)
+ {
+ return null;
+ }
+ else
+ {
+ return buf.toString();
+ }
+ }
+
+ /**
+ * Convert a byte array into a Base64 string (as used in mime formats)
+ */
+ private static String toBase64(byte[] aValue)
+ {
+ int byte1;
+ int byte2;
+ int byte3;
+ int iByteLen = aValue.length;
+ StringBuffer tt = new StringBuffer();
+ for (int i = 0; i < iByteLen; i += 3)
+ {
+ boolean bByte2 = (i + 1) < iByteLen;
+ boolean bByte3 = (i + 2) < iByteLen;
+ byte1 = aValue[i] & 0xFF;
+ byte2 = (bByte2) ? (aValue[i + 1] & 0xFF) : 0;
+ byte3 = (bByte3) ? (aValue[i + 2] & 0xFF) : 0;
+ tt.append(m_strBase64Chars.charAt(byte1 / 4));
+ tt.append(m_strBase64Chars.charAt((byte2 / 16)
+ + ((byte1 & 0x3) * 16)));
+ tt.append(((bByte2) ? m_strBase64Chars.charAt((byte3 / 64)
+ + ((byte2 & 0xF) * 4)) : '='));
+ tt.append(((bByte3) ? m_strBase64Chars.charAt(byte3 & 0x3F) : '='));
+ }
+ return tt.toString();
+ }
+}
Added: incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/ResponseContent.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/ResponseContent.java?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/ResponseContent.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/ResponseContent.java Wed Jun 8 09:06:16 2005
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package org.roller.presentation.pagecache.rollercache;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.util.Locale;
+
+import javax.servlet.ServletResponse;
+
+/**
+ * Holds the servlet response in a byte array so that it can be held
+ * in the cache (and, since this class is serializable, optionally
+ * persisted to disk).
+ *
+ * @version $Revision: 1.1 $
+ * @author <a href="mailto:sergek@lokitech.com">Serge Knystautas</a>
+ */
+public class ResponseContent implements Serializable {
+ private transient ByteArrayOutputStream bout = new ByteArrayOutputStream(1000);
+ private Locale locale = null;
+ private String contentType = null;
+ private byte[] content = null;
+
+ /**
+ * Set the content type. We capture this so that when we serve this
+ * data from cache, we can set the correct content type on the response.
+ */
+ public void setContentType(String value) {
+ contentType = value;
+ }
+
+ /**
+ * Set the Locale. We capture this so that when we serve this data from
+ * cache, we can set the correct locale on the response.
+ */
+ public void setLocale(Locale value) {
+ locale = value;
+ }
+
+ /**
+ * Get an output stream. This is used by the {@link SplitServletOutputStream}
+ * to capture the original (uncached) response into a byte array.
+ */
+ public OutputStream getOutputStream() {
+ return bout;
+ }
+
+ /**
+ * Gets the size of this cached content.
+ *
+ * @return The size of the content, in bytes. If no content
+ * exists, this method returns <code>-1</code>.
+ */
+ public int getSize() {
+ return (content != null) ? content.length : (-1);
+ }
+
+ /**
+ * Called once the response has been written in its entirety. This
+ * method commits the response output stream by converting the output
+ * stream into a byte array.
+ */
+ public void commit() {
+ content = bout.toByteArray();
+ }
+
+ /**
+ * Writes this cached data out to the supplied <code>ServletResponse</code>.
+ *
+ * @param response The servlet response to output the cached content to.
+ * @throws IOException
+ */
+ public void writeTo(ServletResponse response) throws IOException {
+ //Send the content type and data to this response
+ if (contentType != null) {
+ response.setContentType(contentType);
+ }
+
+ response.setContentLength(content.length);
+
+ if (locale != null) {
+ response.setLocale(locale);
+ }
+
+ OutputStream out = new BufferedOutputStream(response.getOutputStream());
+ out.write(content);
+ out.flush();
+ }
+}
Added: incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/SplitServletOutputStream.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/SplitServletOutputStream.java?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/SplitServletOutputStream.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercache/SplitServletOutputStream.java Wed Jun 8 09:06:16 2005
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package org.roller.presentation.pagecache.rollercache;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.servlet.ServletOutputStream;
+
+/**
+ * Extends the base <code>ServletOutputStream</code> class so that
+ * the stream can be captured as it gets written. This is achieved
+ * by overriding the <code>write()</code> methods and outputting
+ * the data to two streams - the original stream and a secondary stream
+ * that is designed to capture the written data.
+ *
+ * @version $Revision: 1.1 $
+ * @author <a href="mailto:sergek@lokitech.com">Serge Knystautas</a>
+ */
+public class SplitServletOutputStream extends ServletOutputStream {
+ OutputStream captureStream = null;
+ OutputStream passThroughStream = null;
+
+ /**
+ * Constructs a split output stream that both captures and passes through
+ * the servlet response.
+ *
+ * @param captureStream The stream that will be used to capture the data.
+ * @param passThroughStream The pass-through <code>ServletOutputStream</code>
+ * that will write the response to the client as originally intended.
+ */
+ public SplitServletOutputStream(OutputStream captureStream, OutputStream passThroughStream) {
+ this.captureStream = captureStream;
+ this.passThroughStream = passThroughStream;
+ }
+
+ /**
+ * Writes the incoming data to both the output streams.
+ *
+ * @param value The int data to write.
+ * @throws IOException
+ */
+ public void write(int value) throws IOException {
+ captureStream.write(value);
+ passThroughStream.write(value);
+ }
+
+ /**
+ * Writes the incoming data to both the output streams.
+ *
+ * @param value The bytes to write to the streams.
+ * @throws IOException
+ */
+ public void write(byte[] value) throws IOException {
+ captureStream.write(value);
+ passThroughStream.write(value);
+ }
+
+ /**
+ * Writes the incoming data to both the output streams.
+ *
+ * @param b The bytes to write out to the streams.
+ * @param off The offset into the byte data where writing should begin.
+ * @param len The number of bytes to write.
+ * @throws IOException
+ */
+ public void write(byte[] b, int off, int len) throws IOException {
+ captureStream.write(b, off, len);
+ passThroughStream.write(b, off, len);
+ }
+}
Added: incubator/roller/trunk/src/org/roller/presentation/tags/DateTag.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/tags/DateTag.java?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/tags/DateTag.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/tags/DateTag.java Wed Jun 8 09:06:16 2005
@@ -0,0 +1,120 @@
+package org.roller.presentation.tags;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.TagSupport;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.Globals;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.RequestUtils;
+
+/**
+ * Struts-based date field tag that wraps Matt Kruze's JavaScript data chooser.
+ * @jsp.tag name="Date"
+ */
+public class DateTag extends TagSupport
+{
+
+ // Unique key prefix keeps us from colliding with other tags and user data
+ public static final String KEY_PREFIX = "ZZZ_DATETAG_ZZZ";
+
+ private String property = null;
+ private String dateFormat = null;
+ private Boolean readOnly = Boolean.FALSE;
+
+ private static Log mLogger =
+ LogFactory.getFactory().getInstance(DateTag.class);
+
+ /**
+ * Renders date field by calling a JSP page.
+ */
+ public int doStartTag() throws JspException
+ {
+
+ // Get form name
+ ActionMapping mapping =
+ (ActionMapping) pageContext.getRequest().getAttribute(
+ Globals.MAPPING_KEY);
+ String formName = mapping.getName();
+
+ // Get value of form field
+ Object value =
+ RequestUtils.lookup(pageContext, formName, property, null);
+ if (value == null)
+ value = "";
+
+ // put variables into request scope for view page
+ pageContext.getRequest().setAttribute(
+ KEY_PREFIX + "_formName",
+ formName);
+ pageContext.getRequest().setAttribute(
+ KEY_PREFIX + "_property",
+ property);
+ pageContext.getRequest().setAttribute(
+ KEY_PREFIX + "_dateFormat",
+ dateFormat);
+ pageContext.getRequest().setAttribute(
+ KEY_PREFIX + "_readOnly",
+ readOnly);
+ pageContext.getRequest().setAttribute(KEY_PREFIX + "_value", value);
+
+ // dispatch to view page
+ try
+ {
+ pageContext.include("/tags/date.jsp");
+ }
+ catch (Exception e)
+ {
+ // can't handle this here
+ throw new JspException("ERROR including date.jsp");
+ }
+
+ // Don't evaluate content of tag, just continue processing this page
+ return (SKIP_BODY);
+ }
+
+ /**
+ * Date format string to be used.
+ *
+ * @jsp.attribute required="true" rtexprvalue="true" type="java.lang.String"
+ */
+ public String getDateFormat()
+ {
+ return dateFormat;
+ }
+
+ /**
+ * Name of form property represented.
+ * @jsp.attribute required="true" rtexprvalue="true" type="java.lang.String"
+ */
+ public String getProperty()
+ {
+ return property;
+ }
+
+ /**
+ * True if field should be readOnly.
+ * @jsp.attribute required="false" rtexprvalue="true" type="java.lang.Boolean"
+ */
+ public Boolean getReadOnly()
+ {
+ return readOnly;
+ }
+
+ public void setDateFormat(String dateFormat)
+ {
+ this.dateFormat = dateFormat;
+ }
+
+ public void setProperty(String property)
+ {
+ this.property = property;
+ }
+
+ public void setReadOnly(Boolean readOnly)
+ {
+ this.readOnly = readOnly;
+ }
+
+}
Added: incubator/roller/trunk/src/org/roller/presentation/tags/HybridTag.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/tags/HybridTag.java?rev=189602&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/tags/HybridTag.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/tags/HybridTag.java Wed Jun 8 09:06:16 2005
@@ -0,0 +1,79 @@
+/*
+ * HybridTag.java
+ *
+ * Created on February 10, 2002, 11:12 PM
+ */
+
+package org.roller.presentation.tags;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.TagSupport;
+
+/**
+ * JSP tag designed to be used from JSP page or from Velocity page.
+ * Tag must be a standalone tag, design precludes contents.
+ * @author David M Johnson
+ */
+public abstract class HybridTag extends TagSupport
+{
+ private static Log mLogger =
+ LogFactory.getFactory().getInstance(HybridTag.class);
+
+ public HybridTag()
+ {
+ }
+
+ public String toString()
+ {
+ String ret = null;
+ try
+ {
+ StringWriter sw = new StringWriter();
+ doStartTag( new PrintWriter( sw, true ));
+ // See, design precludes contents
+ doEndTag( new PrintWriter( sw, true ));
+ ret = sw.toString();
+ }
+ catch (Exception e)
+ {
+ ret = "Exception in tag";
+ mLogger.error(ret,e);
+ }
+ return ret;
+ }
+
+ public String emit()
+ {
+ return toString();
+ }
+
+ public int doStartTag() throws JspException
+ {
+ return doStartTag( new PrintWriter( pageContext.getOut(), true) );
+ }
+
+
+ public int doEndTag() throws JspException
+ {
+ return doEndTag( new PrintWriter( pageContext.getOut(), true) );
+ }
+
+ /** Default processing of the end tag returning SKIP_BODY. */
+ public int doStartTag( PrintWriter pw ) throws JspException
+ {
+ return SKIP_BODY;
+ }
+
+ /** Default processing of the end tag returning EVAL_PAGE. */
+ public int doEndTag( PrintWriter pw ) throws JspException
+ {
+ return EVAL_PAGE;
+ }
+
+}