Return-Path: Delivered-To: apmail-incubator-roller-commits-archive@www.apache.org Received: (qmail 92020 invoked from network); 8 Jun 2005 16:09:17 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 8 Jun 2005 16:09:17 -0000 Received: (qmail 34543 invoked by uid 500); 8 Jun 2005 16:09:16 -0000 Delivered-To: apmail-incubator-roller-commits-archive@incubator.apache.org Received: (qmail 34489 invoked by uid 500); 8 Jun 2005 16:09:15 -0000 Mailing-List: contact roller-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: roller-dev@incubator.apache.org Delivered-To: mailing list roller-commits@incubator.apache.org Received: (qmail 34420 invoked by uid 99); 8 Jun 2005 16:09:14 -0000 X-ASF-Spam-Status: No, hits=-9.8 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from minotaur.apache.org (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.28) with SMTP; Wed, 08 Jun 2005 09:09:05 -0700 Received: (qmail 91343 invoked by uid 65534); 8 Jun 2005 16:08:44 -0000 Message-ID: <20050608160844.91342.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: 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/ Date: Wed, 08 Jun 2005 16:06:46 -0000 To: roller-commits@incubator.apache.org From: snoopdave@apache.org X-Mailer: svnmailer-1.0.0-dev X-Virus-Checked: Checked X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Added: incubator/roller/trunk/src/org/roller/presentation/filters/IfModifie= dFilter.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/pr= esentation/filters/IfModifiedFilter.java?rev=3D189602&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/roller/trunk/src/org/roller/presentation/filters/IfModifiedFi= lter.java (added) +++ incubator/roller/trunk/src/org/roller/presentation/filters/IfModifiedFi= lter.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=20 + * 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=3D"IfModifiedFilter" + * + * @author David M Johnson + */ +public class IfModifiedFilter implements Filter +{ + private static Log mLogger =3D + LogFactory.getFactory().getInstance(IfModifiedFilter.class); + + private ServletContext mContext; + private FilterConfig mConfig; + + // TODO: make cache configurable + private static LRUCache2 mDateCache =3D new LRUCache2(300, 20000); + SimpleDateFormat dateFormatter =3D 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 =3D 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 =3D (HttpServletRequest) req; + HttpServletResponse response =3D (HttpServletResponse) res; + Roller roller =3D RollerContext.getRoller( request ); + + Date updateTime =3D null; + try + { + updateTime =3D getLastPublishedDate(request); + + // RSS context loader needs updateTime, so stash it + request.setAttribute("updateTime", updateTime); + + // Check the incoming if-modified-since header + Date sinceDate =3D + new Date(request.getDateHeader("If-Modified-Since")); + + if (updateTime !=3D null) + { + // convert date (JDK 1.5 workaround) + String date =3D dateFormatter.format(updateTime); + updateTime =3D new Date(date); + if (updateTime.compareTo(sinceDate) <=3D 0) + { + response.setStatus(HttpServletResponse.SC_NOT_MODIFIE= D); + 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 MODIFI= ED + // response is not set. + } + + // Set outgoing last modified header + if (updateTime !=3D 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 =3D null; + String pathInfo =3D request.getPathInfo(); + pathInfo =3D pathInfo !=3D null ? pathInfo : ""; + String[] pathInfoArray =3D StringUtils.split(pathInfo, "/"); + if (pathInfoArray.length =3D=3D 1) + { + userName =3D pathInfoArray[0]; + } + else if (pathInfoArray.length > 1)=20 + { + // request is for a specific date or anchor, can't return 304 + return null; + } +=09 + // Get last pub time for specific weblog category requested + String catname =3D + request.getParameter(RollerRequest.WEBLOGCATEGORYNAME_KEY); +=09 + // update times are cached to reduce database queries per request + StringBuffer sb =3D new StringBuffer(); + sb.append("zzz_"); + sb.append(userName); + sb.append("_zzz_"); + sb.append(catname); + String key =3D sb.toString(); +=09 + Date updateTime =3D (Date)mDateCache.get(key); + if (updateTime =3D=3D null) + { + mLogger.debug("Hitting database for update time: "+key); + Roller roller =3D RollerContext.getRoller(request); + roller.begin(); + WeblogManager wmgr =3D roller.getWeblogManager(); + updateTime =3D wmgr.getWeblogLastPublishTime(userName, catname);=09 + mDateCache.put(key, updateTime); + } + return updateTime; + } + =20 + public static void purgeDateCache(UserData user)=20 + { + String userName =3D (user !=3D null) ? user.getUserName() : null; + StringBuffer sb =3D 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/LoginFilt= er.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/pr= esentation/filters/LoginFilter.java?rev=3D189602&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- 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; + + +/** + *

Intercepts Login requests for "Remember Me" functionality.

+ * + * @author Matt Raible + * @version $Revision: 1.1 $ $Date: 2004/03/21 04:33:55 $ + * + * @web.filter display-name=3D"Login Filter" name=3D"loginFilter" + * @web.filter-init-param name=3D"enabled" value=3D"true" + */ +public final class LoginFilter implements Filter=20 +{ + //~ Instance fields =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + + private Log mLogger =3D LogFactory.getLog(LoginFilter.class); + private FilterConfig mFilterConfig =3D null; + private boolean enabled =3D true; + + //~ Methods =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + + public void doFilter(ServletRequest req, ServletResponse resp, + FilterChain chain) + throws IOException, ServletException=20 + { + + HttpServletRequest request =3D (HttpServletRequest) req; + HttpServletResponse response =3D (HttpServletResponse) resp; + + // See if the user has a remember me cookie + Cookie c =3D RequestUtil.getCookie(request, RollerRequest.LOGIN_CO= OKIE); + + try=20 + { + RollerRequest rreq =3D RollerRequest.getRollerRequest(request); + UserManager mgr =3D rreq.getRoller().getUserManager(); + =20 + // Check to see if the user is logging out, if so, remove all + // login cookies + if (request.getRequestURL().indexOf("logout") !=3D -1)=20 + { + if (mLogger.isDebugEnabled())=20 + { + mLogger.debug("logging out '" + request.getRemoteUser(= ) + "'"); + } + =20 + mgr.removeLoginCookies(request.getRemoteUser()); + rreq.getRoller().commit(); + RequestUtil.deleteCookie(response, c, request.getContextPa= th()); + }=20 + else if (c !=3D null && enabled)=20 + { + String loginCookie =3D mgr.checkLoginCookie(c.getValue()); + rreq.getRoller().commit(); + + if (loginCookie !=3D null)=20 + { + RequestUtil.setCookie(response, RollerRequest.LOGIN_CO= OKIE, + loginCookie, + request.getContextPath()); + loginCookie =3D Utilities.decodeString(loginCookie); + + String[] value =3D StringUtils.split(loginCookie, '|'); + + UserData user =3D mgr.getUser( value[0] ); + + // authenticate user without displaying login page + String route =3D "/auth?j_username=3D" + + user.getUserName() + "&j_password=3D" + + user.getPassword(); + + request.setAttribute("encrypt", "false"); + request.getSession().setAttribute("cookieLogin", "true= "); + + if (mLogger.isDebugEnabled())=20 + { + mLogger.debug("I remember you '" + user.getUserNam= e() + + "', attempting to authenticate..."); + } + + RequestDispatcher dispatcher =3D + request.getRequestDispatcher(route); + dispatcher.forward(request, response); + + return; + } + } + =20 + } catch (Exception e)=20 + { + // 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)=20 + { + this.mFilterConfig =3D config; + + String param =3D config.getInitParameter("enabled"); + enabled =3D Boolean.valueOf(param).booleanValue(); + + if (mLogger.isDebugEnabled())=20 + { + 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/RefererFi= lter.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/pr= esentation/filters/RefererFilter.java?rev=3D189602&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/roller/trunk/src/org/roller/presentation/filters/RefererFilte= r=2Ejava (added) +++ incubator/roller/trunk/src/org/roller/presentation/filters/RefererFilte= r=2Ejava 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. + *=20 + * @web.filter name=3D"RefererFilter" + * *web.filter-mapping url-pattern=3D"/page/*" + *=20 + * @author David M. Johnson + */ +public class RefererFilter implements Filter +{ + private FilterConfig mFilterConfig =3D null; + private static Log mLogger =3D=20 + LogFactory.getFactory().getInstance(RefererFilter.class); + =20 + /** + * destroy + */ + public void destroy() + { + } + + /** + * doFilter + */ + public void doFilter( + ServletRequest req, ServletResponse res, FilterChain chain) + throws IOException, ServletException + { + HttpServletRequest request =3D (HttpServletRequest)req; =20 + try + { + RollerRequest rreq =3D RollerRequest.getRollerRequest(request); + RollerContext rctx =3D RollerContext.getRollerContext( + mFilterConfig.getServletContext()); + + if ( rreq.getUser() !=3D null ) + { + String userName =3D rreq.getUser().getUserName(); + =20 + String referer =3D request.getHeader("Referer"); + =20 + // Base page URLs, with and without www. + String basePageUrlWWW =3D=20 + rctx.getAbsoluteContextUrl(request)+"/page/"+userName;= =20 + String basePageUrl =3D basePageUrlWWW; =20 + if ( basePageUrlWWW.startsWith("http://www.") ) + { + // chop off the http://www. + basePageUrl =3D "http://"+basePageUrlWWW.substring(11); + } + =20 + // Base comment URLs, with and without www. =20 + String baseCommentsUrlWWW =3D=20 + rctx.getAbsoluteContextUrl(request)+"/page/"+userName;= =20 + String baseCommentsUrl =3D baseCommentsUrlWWW; =20 + if ( baseCommentsUrlWWW.startsWith("http://www.") ) + { + // chop off the http://www. + baseCommentsUrl=3D "http://"+baseCommentsUrlWWW.substr= ing(11); + } + =20 + // Don't process hits from same user's blogs as referers by + // ignoring Don't process referer from pages that start wi= th base URLs. =20 + if ( referer=3D=3Dnull || + (=20 + !referer.startsWith( basePageUrl )=20 + && !referer.startsWith( basePageUrlWWW ) + && !referer.startsWith( baseCommentsUrl ) + && !referer.startsWith( baseCommentsUrlWWW ) + ) + ) + { + RefererManager refMgr =3D=20 + rreq.getRoller().getRefererManager(); + refMgr.processRequest(rreq); + } + else + { + if (mLogger.isDebugEnabled()) + { + mLogger.debug("Ignoring referer=3D"+referer); + } + } + } + } + catch (Exception e) + { + mLogger.error("Processing referer",e); + } + =20 + chain.doFilter(req, res); + } + + /** + * init + */ + public void init(FilterConfig filterConfig) throws ServletException + { + mFilterConfig =3D filterConfig; + } +} Added: incubator/roller/trunk/src/org/roller/presentation/filters/RequestFi= lter.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/pr= esentation/filters/RequestFilter.java?rev=3D189602&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/roller/trunk/src/org/roller/presentation/filters/RequestFilte= r=2Ejava (added) +++ incubator/roller/trunk/src/org/roller/presentation/filters/RequestFilte= r=2Ejava 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=20 + * creates a RollerRequest object to parse pathinfo and request parameters. + *=20 + * @web.filter name=3D"RequestFilter" + *=20 + * @author David M. Johnson, Matt Raible + */ +public class RequestFilter implements Filter +{ + private FilterConfig mFilterConfig =3D null; + private static Log mLogger =3D + 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 =3D ((HttpServletRequest)req).getSession(); + if (null !=3D session) + { + Locale locale =3D (Locale)session.getAttribute(Globals.LOCALE_= KEY); + if (locale =3D=3D null) + { + locale =3D req.getLocale(); + } + if (req.getParameter("locale") !=3D null) + { + locale =3D new Locale(req.getParameter("locale")); + } + session.setAttribute(Globals.LOCALE_KEY, locale); + Config.set(session, Config.FMT_LOCALE, locale); + } + + HttpServletRequest request =3D (HttpServletRequest)req; + HttpServletResponse response =3D (HttpServletResponse)res; + Roller roller =3D RollerContext.getRoller( request ); + RollerRequest rreq =3D null; + try + { + if (request.getAttribute("roller.begun") =3D=3D null) + { + roller.begin(); + } + else=20 + { + mLogger.debug("Came through filter again on URL=3D"+reques= t=2EgetRequestURL()); =20 + } + } + catch (Throwable t) + { + mLogger.error("ERROR creating persistence session", t); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERRO= R); + } + + try=20 + { + rreq =3D 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 =3D request.getRemoteUser(); + + if (username !=3D null) + { + if (session.getAttribute(RollerRequest.LOGIN_COOKIE) !=3D = null) + { + session.removeAttribute(RollerRequest.LOGIN_COOKIE); + + UserManager mgr =3D rreq.getRoller().getUserManager(); + + String loginCookie =3D mgr.createLoginCookie(username); + rreq.getRoller().commit(); + RequestUtil.setCookie(response, RollerRequest.LOGIN_CO= OKIE, + loginCookie, request.getContextP= ath()); + } + } + + } + catch (RollerException e) + { + // An error initializing the request is considered to be a 404 + response.sendError(HttpServletResponse.SC_NOT_FOUND); + return; + } + + if (session !=3D 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) !=3D null) + { + request.setAttribute(Globals.MESSAGE_KEY, + session.getAttribute(Globals.MESSAGE_KEY)); + session.removeAttribute(Globals.MESSAGE_KEY); + } + if (session.getAttribute(Globals.ERROR_KEY) !=3D null) + { + request.setAttribute(Globals.ERROR_KEY, + session.getAttribute(Globals.ERROR_KEY)); + session.removeAttribute(Globals.ERROR_KEY); + } + } + + Date updateTime =3D null; + try + { + updateTime =3D IfModifiedFilter.getLastPublishedDate(request); + } + catch (RollerException e1) + { + mLogger.debug("Getting lastUpdateTime", e1); + } + if (updateTime !=3D null) + { + request.setAttribute("updateTime", updateTime); + } + + chain.doFilter(req, res); + } + + /** + * init + */ + public void init(FilterConfig filterConfig) throws ServletException + { + mFilterConfig =3D filterConfig; + } +} + Added: incubator/roller/trunk/src/org/roller/presentation/filters/package.h= tml URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/pr= esentation/filters/package.html?rev=3D189602&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- 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 @@ + + + + + + +Servlet filters
+ + Added: incubator/roller/trunk/src/org/roller/presentation/forms/package.html URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/pr= esentation/forms/package.html?rev=3D189602&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/roller/trunk/src/org/roller/presentation/forms/package.html (= added) +++ incubator/roller/trunk/src/org/roller/presentation/forms/package.html W= ed Jun 8 09:06:16 2005 @@ -0,0 +1,9 @@ + + + + + + +XDoclet generated form classes, one for each Roller bean.
+ + Added: incubator/roller/trunk/src/org/roller/presentation/newsfeeds/Newsfee= dCache.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/pr= esentation/newsfeeds/NewsfeedCache.java?rev=3D189602&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/roller/trunk/src/org/roller/presentation/newsfeeds/NewsfeedCa= che.java (added) +++ incubator/roller/trunk/src/org/roller/presentation/newsfeeds/NewsfeedCa= che.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 an= d + * parging the specified feed using the Flock RSS parser. + *=20 + * @author Lance Lavandowska + * @author Dave Johnson + */ +public class NewsfeedCache +{ + private static Log mLogger =3D LogFactory.getFactory().getInstance( + NewsfeedCache.class); + + /** Static singleton * */ + public static NewsfeedCache mInstance =3D null; + + /** Instance vars * */ + private RollerConfig mConfig =3D null; + + /** LRU cache */ + LRUCache2 mCache =3D null; + + /** Constructor * */ + public NewsfeedCache(RollerConfig config) + { + mConfig =3D config; + // TODO: make cache size configurable + mCache =3D new LRUCache2(100, mConfig.getRssCacheTime().intValue()= ); + } + + /** static singleton retriever * */ + public static NewsfeedCache getInstance(RollerConfig config) + { + if (mInstance =3D=3D null) + { + if (mLogger.isDebugEnabled()) + { + mLogger.debug("Instantiating new NewsfeedCache"); + } + + synchronized (NewsfeedCache.class) + { + mInstance =3D new NewsfeedCache(config); + } + } + return mInstance; + } + + /** + * Returns a Channel object for the supplied RSS newsfeed URL. + *=20 + * @param feedUrl + * RSS newsfeed URL. + * @return FlockFeedI for specified RSS newsfeed URL. + */ + public ChannelIF getChannel(String feedUrl) + { + ChannelIF channel =3D null; + //FlockFeed feed =3D null; + try + { + //FlockFeedFactory factory =3D new FlockFeedFactory(); + + // If aggregator has been disable return null + boolean enabled =3D 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 =3D (ChannelIF) mCache.get(feedUrl); + if (mLogger.isDebugEnabled()) + { + mLogger.debug("Newsfeed: got from Cache"); + } + + if (channel =3D=3D null) + { + try + { + // Parse the feed + channel =3D RSSParser.parse( + new ChannelBuilder(), new URL(feedUrl)); + } + catch (ParseException e1) + { + mLogger.info("Error parsing RSS: " + feedUrl); + } + } + + //channel =3D 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 " + feedU= rl); + } + try + { + // Parse the feed + channel =3D 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/pr= esentation/newsfeeds/package.html?rev=3D189602&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/roller/trunk/src/org/roller/presentation/newsfeeds/package.ht= ml (added) +++ incubator/roller/trunk/src/org/roller/presentation/newsfeeds/package.ht= ml Wed Jun 8 09:06:16 2005 @@ -0,0 +1,9 @@ + + + + + + +Newsfeed parser and cache.
+ + Added: incubator/roller/trunk/src/org/roller/presentation/package.html URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/pr= esentation/package.html?rev=3D189602&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- 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 @@ + + + + + + + +

+The org.roller.presentation package and the packages under it comprise the=20 +Presentation Layer - the web-based Roller user interface. +=20 +The code in the packages depend on Web APIs including the Servlet API, JSP= API,=20 +Struts, Velocity and the like.=20 +

+ +

+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 log= ic +and presentation logic only. + +Wherever possible, application logic that can be made independent of Web A= PIs +is implemented inside the Business Layer +

+ + + Added: incubator/roller/trunk/src/org/roller/presentation/pagecache/FilterH= andler.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/pr= esentation/pagecache/FilterHandler.java?rev=3D189602&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/roller/trunk/src/org/roller/presentation/pagecache/FilterHand= ler.java (added) +++ incubator/roller/trunk/src/org/roller/presentation/pagecache/FilterHand= ler.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/PageCac= he.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/pr= esentation/pagecache/PageCache.java?rev=3D189602&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- 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=20 + * information on configuring this cache.=20 + *=20 + * @web.filter name=3D"PageCacheFilter" + *=20 + * @web.filter-init-param name=3D"size" value=3D"100" + * description=3D"Number of pages to keep in cache" + *=20 + * @web.filter-init-param name=3D"timeoutInterval" value=3D"1800" + * description=3D"Page Cache timeout interval in seconds (-1 to disabl= e timeout, default is 30 minutes)" + *=20 + * @web.filter-init-param name=3D"timeoutRatio" value=3D"1.0" + * description=3D"Ratio of pages to be timed out on interval (1.0 mean= s 100%, default is 1.0)" + *=20 + * @author Lance Lavandowska + * @author David M Johnson + */ +public class PageCache implements Filter +{ + private static Log mLogger =3D=20 + LogFactory.getFactory().getInstance(PageCache.class); + =20 + private FilterHandler mHandler =3D null; + =20 + private static ArrayList mHandlers =3D new ArrayList(); + + //--------------------------------------------------------------------= --- + /** + * Initialize the filter. + * @param filerConfig The filter configuration + */ + public void init(FilterConfig filterConfig) + { + String handlerClass =3D filterConfig.getInitParameter("handler"); + if (mLogger.isDebugEnabled()) + { + mLogger.debug( + "Initializing as filterName: "+filterConfig.getFilterName(= )); + } =20 + mHandler =3D new LRUCacheHandler2(filterConfig); + mHandlers.add(mHandler); + } + + //--------------------------------------------------------------------= --- + /** + * Filter clean-up + */ + public void destroy() =20 + { + 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 =3D mHandlers.iterator(); + while (iter.hasNext()) + { + FilterHandler handler =3D (FilterHandler)iter.next(); + handler.flushCache(req); =20 + } + IfModifiedFilter.purgeDateCache(null); + } + + //--------------------------------------------------------------------= --- + /** Remove from cache for all handlers of this class */ + public static void removeFromCache(HttpServletRequest req, UserData us= er) + { + Iterator iter =3D mHandlers.iterator(); + while (iter.hasNext()) + { + FilterHandler handler =3D (FilterHandler)iter.next(); + handler.removeFromCache(req, user); + } =20 + 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/pr= esentation/pagecache/package.html?rev=3D189602&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/roller/trunk/src/org/roller/presentation/pagecache/package.ht= ml (added) +++ incubator/roller/trunk/src/org/roller/presentation/pagecache/package.ht= ml Wed Jun 8 09:06:16 2005 @@ -0,0 +1,9 @@ + + + + + + +Roller's custom OSCache Servlet filter.
+ + Added: incubator/roller/trunk/src/org/roller/presentation/pagecache/rollerc= ache/CacheHttpServletResponseWrapper.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/pr= esentation/pagecache/rollercache/CacheHttpServletResponseWrapper.java?rev= =3D189602&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercach= e/CacheHttpServletResponseWrapper.java (added) +++ incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercach= e/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 Serge Knystautas + * @version $Revision: 1.1 $ + */ +public class CacheHttpServletResponseWrapper extends HttpServletResponseWr= apper { + private final Log log =3D 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 =3D null; + private SplitServletOutputStream cacheOut =3D null; + private int status =3D SC_OK; + + /** + * Constructor + * + * @param response The servlet response + */ + public CacheHttpServletResponseWrapper(HttpServletResponse response) { + super(response); + result =3D 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 (SC_OK) will + * be cached. + */ + public void setStatus(int status) { + super.setStatus(status); + this.status =3D status; + } + + /** + * We override this so we can catch the response status. Only + * responses with a status of 200 (SC_OK) will + * be cached. + */ + public void sendError(int status, String string) throws IOException { + super.sendError(status, string); + this.status =3D status; + } + + /** + * We override this so we can catch the response status. Only + * responses with a status of 200 (SC_OK) will + * be cached. + */ + public void sendError(int status) throws IOException { + super.sendError(status); + this.status =3D status; + } + + /** + * We override this so we can catch the response status. Only + * responses with a status of 200 (SC_OK) will + * be cached. + */ + public void setStatus(int status, String string) { + super.setStatus(status, string); + this.status =3D status; + } + + public void sendRedirect(String location) throws IOException { + this.status =3D 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 =3D=3D null) { + cacheOut =3D new SplitServletOutputStream(result.getOutputStre= am(), super.getOutputStream()); + } + + return cacheOut; + } + + /** + * Get a print writer + * + * @throws IOException + */ + public PrintWriter getWriter() throws IOException { + if (cachedWriter =3D=3D null) { + cachedWriter =3D new PrintWriter(getOutputStream()); + } + + return cachedWriter; + } + + public void flushBuffer() throws IOException { + super.flushBuffer(); + + if (cacheOut !=3D null) { + cacheOut.flush(); + } + + if (cachedWriter !=3D null) { + cachedWriter.flush(); + } + } +} Added: incubator/roller/trunk/src/org/roller/presentation/pagecache/rollerc= ache/LRUCacheHandler.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/pr= esentation/pagecache/rollercache/LRUCacheHandler.java?rev=3D189602&view=3Da= uto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercach= e/LRUCacheHandler.java (added) +++ incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercach= e/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 configure= d=20 + * using filter configuration parameters: + *
    + *
  • size: number of pages to keep in cache. Once cache reaches + * this size, each new cache entry will push out the LRU cache entry.
  • + *=20 + *
  • timeoutInterval: interval to timeout pages in seconds + * (default is 1800 seconds). Sites with a large number of users, and thus= a=20 + * lot of cache churn which makes this check unnecessary, may wish to set= this + * to -1 to disable timeout checking.
  • + *=20 + *
  • timeoutRatio: portion of old pages to expire on timeout=20 + * interval where 1.0 is 100% (default is 1.0). This only applies if the=20 + * timeoutInterval is set to something other than -1.
  • + *
=20 + * @author David M Johnson + */ +public class LRUCacheHandler implements FilterHandler +{ + private static Log mLogger =3D=20 + LogFactory.getFactory().getInstance(LRUCacheHandler.class); + + private Map mPageCache =3D null; + =20 + private String mName =3D null; + =20 + /** Timeout interval: how often to run timeout task (default 30 mintes= ) */ + private long mTimeoutInterval =3D 30 * 60 * 1000; // milliseconds + =20 + /** Timeout ratio: % of cache to expire on each timeout (default 1.0) = */ + private float mTimeoutRatio =3D 1.0F; + =20 + // Statistics + private int misses =3D 0; + private int hits =3D 0; + =20 + private final static String FILE_SEPARATOR =3D "/"; + private final static char FILE_SEPARATOR_CHAR =3D FILE_SEPARATOR.charA= t(0); + private final static short AVERAGE_KEY_LENGTH =3D 30; + private static final String m_strBase64Chars =3D=20 + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + =20 + + public LRUCacheHandler(FilterConfig config) + { =20 + mName =3D config.getFilterName(); + mLogger.info("Initializing for: " + mName); + int size =3D 200; + try + { + size =3D Integer.parseInt(config.getInitParameter("size")); + } + catch (Exception e) + { + mLogger.warn(config.getFilterName()=20 + + "Can't read cache size parameter, using default..."); + } + mLogger.info(mName + " size=3D" + size); + mPageCache =3D Collections.synchronizedMap(new LRUCache(size)); + + long intervalSeconds =3D mTimeoutInterval / 1000L; + try + { + mTimeoutInterval =3D 1000L * Long.parseLong( + config.getInitParameter("timeoutInterval")); + =20 + if (mTimeoutInterval =3D=3D -1) + { + mLogger.info(config.getFilterName()=20 + + "timeoutInterval of -1: timeouts are disabled"); + } + else if (mTimeoutInterval < (30 * 1000)) + { + mTimeoutInterval =3D 30 * 1000; + mLogger.warn(config.getFilterName()=20 + + "timeoutInterval cannot be less than 30 seconds"); + } + } + catch (Exception e) + { + mLogger.warn(config.getFilterName()=20 + + "Can't read timeoutInterval parameter, disabling timeout= ."); + mTimeoutInterval =3D -1; + } + mLogger.info(mName + " timeoutInterval=3D" + intervalSeconds); + =20 + try + { + mTimeoutRatio =3D Float.parseFloat( + config.getInitParameter("timeoutRatio")); + } + catch (Exception e) + { + mLogger.warn(config.getFilterName()=20 + + "Can't read timeoutRatio parameter, using default..."); + } + mLogger.info(mName + " timeoutRatio=3D" + mTimeoutRatio); + =20 + if (mTimeoutInterval !=3D -1 && mTimeoutRatio !=3D 0.0) + { + Timer timer =3D new Timer(); =20 + timer.scheduleAtFixedRate(new TimerTask() { + public void run() { + timeoutCache(); + } + }, mTimeoutInterval, mTimeoutInterval); =20 + } + } + =20 + /** + * @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 =3D (HttpServletRequest) req; + HttpServletResponse response =3D (HttpServletResponse) res; + + // get locale + Locale locale =3D LanguageUtil.getViewLocale(request); + =20 + // generate language-sensitive cache-key + String generatedKey =3D null; + if (locale !=3D null) + { + generatedKey =3D generateEntryKey(null,=20 + request, 1, locale.getLanguage()); + } + else + { + generatedKey =3D generateEntryKey(null,=20 + request, 1, null); + } + =20 + // Add authenticated user name, if there is one, to cache key + java.security.Principal prince =3D request.getUserPrincipal(); = =20 + StringBuffer keyb =3D new StringBuffer(); + keyb.append(generatedKey); + if (prince !=3D null) + { + keyb.append("_"); + keyb.append(prince); + } + String key =3D keyb.toString(); + =20 + ResponseContent respContent =3D (ResponseContent)getFromCache(key); + if (respContent =3D=3D null)=20 + { + try + { + CacheHttpServletResponseWrapper cacheResponse =3D=20 + new CacheHttpServletResponseWrapper(response); + chain.doFilter(request, cacheResponse); + =20 + // Store as the cache content the result of the response + // if no exception was noted by content generator. + if (request.getAttribute("DisplayException") =3D=3D null) + { + ResponseContent rc =3D cacheResponse.getContent(); + putToCache(key, rc); + } + else + { + StringBuffer sb =3D new StringBuffer(); + sb.append("Display exception, cache, key=3D"); + 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 =3D new StringBuffer(); + sb.append("Error rendering page, key=3D"); + sb.append(key); + mLogger.error(sb.toString()); + } =20 + } + else + { + try + { + respContent.writeTo(response); + } + catch (java.net.SocketException se) + { + // ignore socket exceptions + } + catch (Exception e) + { + if (mLogger.isDebugEnabled()) + { + StringBuffer sb =3D new StringBuffer(); + sb.append("Probably a client abort exception, key=3D"); + sb.append(key); + mLogger.error(sb.toString()); + } + } + =20 + } + } + + /** + * Purge entire cache. + */ + public synchronized void flushCache(HttpServletRequest req) + { + mPageCache.clear(); + } + + /** + * Purge user's entries from cache. + */ + public synchronized void removeFromCache(HttpServletRequest req, UserD= ata user) + { + // TODO: can we make this a little more precise, perhaps via regex? + String rssString =3D "/rss/" + user.getUserName(); // user's pages + String pageString =3D "/page/" + user.getUserName(); // user's RSS= feeds + String mainRssString =3D "/rss_"; // main RSS feed + List purgeList =3D new ArrayList(); + =20 + Iterator keys =3D mPageCache.keySet().iterator(); + while (keys.hasNext()) + { + String key =3D (String) keys.next(); + =20 + if (key.indexOf(rssString)!=3D-1 || key.indexOf(pageString)!= =3D-1 || key.indexOf(mainRssString)!=3D-1)=20 + { + purgeList.add(key); + } + } + =20 + Iterator purgeIter =3D purgeList.iterator(); + while (purgeIter.hasNext()) + { + String key =3D (String) purgeIter.next(); + mPageCache.remove(key); + } + =20 + if (mLogger.isDebugEnabled()) + { + StringBuffer sb =3D new StringBuffer(); + sb.append("Purged, count=3D"); + sb.append(purgeList.size()); + sb.append(", user=3D"); + sb.append(user.getUserName()); + mLogger.debug(sb.toString()); + } =20 + } + =20 + public synchronized void timeoutCache()=20 + { + if (mTimeoutRatio =3D=3D 1.0) + { + mLogger.debug("Timing out whole cache: " + mName); + mPageCache.clear(); =20 + } + else=20 + { + int numToTimeout =3D (int)(mTimeoutRatio * mPageCache.size()); + mLogger.debug( + "Timing out " + numToTimeout + " of " + mPageCache.size() + + " entries from cache: " + mName); + ArrayList allKeys =3D new ArrayList(mPageCache.keySet()); + for (int i=3DnumToTimeout; i>0; i--)=20 + { + mPageCache.remove(allKeys.get(i)); + } + } + } + =20 + /**=20 + * Get from cache. Synchronized because "In access-ordered linked hash=20 + * maps, merely querying the map with get is a structural modification= "=20 + */ + public synchronized Object getFromCache(String key)=20 + { + Object entry =3D mPageCache.get(key); + =20 + if (entry !=3D null && mLogger.isDebugEnabled()) + { + hits++; + } + return entry; + } + + public synchronized void putToCache(String key, Object entry)=20 + { + mPageCache.put(key, entry); + if (mLogger.isDebugEnabled()) + { + misses++; + =20 + StringBuffer sb =3D new StringBuffer(); + sb.append("Missed, cache size=3D"); + sb.append(mPageCache.size()); + sb.append(", hits=3D"); + sb.append(hits); + sb.append(", misses=3D"); + sb.append(misses); + sb.append(", key=3D"); + sb.append(key); + mLogger.debug(sb.toString()); + } + } + + public String generateEntryKey(String key,=20 + HttpServletRequest request, int scope, String langu= age) + { + StringBuffer cBuffer =3D new StringBuffer(AVERAGE_KEY_LENGTH); + // Append the language if available + if (language !=3D null) + { + cBuffer.append(FILE_SEPARATOR).append(language); + } + =20 + //cBuffer.append(FILE_SEPARATOR).append(request.getServerName()); + =20 + if (key !=3D null) + { + cBuffer.append(FILE_SEPARATOR).append(key); + } + else + { + String generatedKey =3D request.getRequestURI(); + if (generatedKey.charAt(0) !=3D FILE_SEPARATOR_CHAR) + { + cBuffer.append(FILE_SEPARATOR_CHAR); + } + cBuffer.append(generatedKey); + cBuffer.append("_").append(request.getMethod()).append("_"); + generatedKey =3D getSortedQueryString(request); + if (generatedKey !=3D null) + { + try + { + java.security.MessageDigest digest =3D=20 + java.security.MessageDigest.getInstance("MD5"); + byte[] b =3D digest.digest(generatedKey.getBytes()); + cBuffer.append("_"); + // Base64 encoding allows for unwanted slash character= s=2E + cBuffer.append(toBase64(b).replace('/', '_')); + } + catch (Exception e) + { + // Ignore query string + } + } + } + return cBuffer.toString(); + } + + protected String getSortedQueryString(HttpServletRequest request) + { + Map paramMap =3D request.getParameterMap(); + if (paramMap.isEmpty()) + { + return null; + } + Set paramSet =3D new TreeMap(paramMap).entrySet(); + StringBuffer buf =3D new StringBuffer(); + boolean first =3D true; + for (Iterator it =3D paramSet.iterator(); it.hasNext();) + { + Map.Entry entry =3D (Map.Entry) it.next(); + String[] values =3D (String[]) entry.getValue(); + for (int i =3D 0; i < values.length; i++) + { + String key =3D (String) entry.getKey(); + if ((key.length() !=3D 10) || !"jsessionid".equals(key)) + { + if (first) + { + first =3D false; + } + else + { + buf.append('&'); + } + buf.append(key).append('=3D').append(values[i]); + } + } + } + // We get a 0 length buffer if the only parameter was a jsessionid + if (buf.length() =3D=3D 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 =3D aValue.length; + StringBuffer tt =3D new StringBuffer(); + for (int i =3D 0; i < iByteLen; i +=3D 3) + { + boolean bByte2 =3D (i + 1) < iByteLen; + boolean bByte3 =3D (i + 2) < iByteLen; + byte1 =3D aValue[i] & 0xFF; + byte2 =3D (bByte2) ? (aValue[i + 1] & 0xFF) : 0; + byte3 =3D (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)) : '=3D')); + tt.append(((bByte3) ? m_strBase64Chars.charAt(byte3 & 0x3F) : = '=3D')); + } + return tt.toString(); + } +} Added: incubator/roller/trunk/src/org/roller/presentation/pagecache/rollerc= ache/LRUCacheHandler2.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/pr= esentation/pagecache/rollercache/LRUCacheHandler2.java?rev=3D189602&view=3D= auto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercach= e/LRUCacheHandler2.java (added) +++ incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercach= e/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 configure= d=20 + * using filter configuration parameters: + *
    + *
  • size: number of pages to keep in cache. Once cache reaches + * this size, each new cache entry will push out the LRU cache entry.
  • + *
  • timeout: interval to timeout pages in milliseconds.
  • + *
=20 + * @author David M Johnson + */ +public class LRUCacheHandler2 implements FilterHandler +{ + private static Log mLogger =3D=20 + LogFactory.getFactory().getInstance(LRUCacheHandler2.class); + + private LRUCache2 mPageCache =3D null; =20 + private String mName =3D null; + =20 + // Statistics + private int misses =3D 0; + private int hits =3D 0; + =20 + private final static String FILE_SEPARATOR =3D "/"; + private final static char FILE_SEPARATOR_CHAR =3D FILE_SEPARATOR.charA= t(0); + private final static short AVERAGE_KEY_LENGTH =3D 30; + private static final String m_strBase64Chars =3D=20 + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"= ; =20 + + public LRUCacheHandler2(FilterConfig config) + { =20 + mName =3D config.getFilterName(); + mLogger.info("Initializing for: " + mName); + int size =3D 200; + try + { + size =3D Integer.parseInt(config.getInitParameter("size")); + } + catch (Exception e) + { + mLogger.warn(config.getFilterName()=20 + + "Can't read cache size parameter, using default..."); + } + mLogger.info(mName + " size=3D" + size); + + long timeout =3D 30000; + try + { + timeout =3D Long.parseLong( + config.getInitParameter("timeout")); + =20 + if (timeout < (30 * 1000)) + { + timeout =3D 30 * 1000; + mLogger.warn(config.getFilterName()=20 + + "timeout cannot be less than 30 seconds"); + } + } + catch (Exception e) + { + mLogger.warn(config.getFilterName()=20 + + "Can't read timeout parameter, using default."); + } + mLogger.info(mName + " timeout=3D" + timeout); + + mPageCache =3D new LRUCache2(size, timeout); + } + =20 + /** + * @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 =3D (HttpServletRequest) req; + HttpServletResponse response =3D (HttpServletResponse) res; + + // get locale + Locale locale =3D LanguageUtil.getViewLocale(request); + =20 + // generate language-sensitive cache-key + String generatedKey =3D null; + if (locale !=3D null) + { + generatedKey =3D generateEntryKey(null,=20 + request, 1, locale.getLanguage()); + } + else + { + generatedKey =3D generateEntryKey(null,=20 + request, 1, null); + } + =20 + // Add authenticated user name, if there is one, to cache key + java.security.Principal prince =3D request.getUserPrincipal(); = =20 + StringBuffer keyb =3D new StringBuffer(); + keyb.append(generatedKey); + if (prince !=3D null) + { + keyb.append("_"); + keyb.append(prince); + } + String key =3D keyb.toString(); + =20 + ResponseContent respContent =3D (ResponseContent)getFromCache(key); + if (respContent =3D=3D null)=20 + { + try + { + CacheHttpServletResponseWrapper cacheResponse =3D=20 + new CacheHttpServletResponseWrapper(response); + chain.doFilter(request, cacheResponse); + =20 + // Store as the cache content the result of the response + // if no exception was noted by content generator. + if (request.getAttribute("DisplayException") =3D=3D null) + { + ResponseContent rc =3D cacheResponse.getContent(); + putToCache(key, rc); + } + else + { + StringBuffer sb =3D new StringBuffer(); + sb.append("Display exception, cache, key=3D"); + 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 =3D new StringBuffer(); + sb.append("Error rendering page, key=3D"); + sb.append(key); + mLogger.error(sb.toString()); + } =20 + } + else + { + try + { + respContent.writeTo(response); + } + catch (java.net.SocketException se) + { + // ignore socket exceptions + } + catch (Exception e) + { + if (mLogger.isDebugEnabled()) + { + StringBuffer sb =3D new StringBuffer(); + sb.append("Probably a client abort exception, key=3D"); + sb.append(key); + mLogger.error(sb.toString()); + } + } + =20 + } + } + + /** + * Purge entire cache. + */ + public synchronized void flushCache(HttpServletRequest req) + { + mPageCache.purge(); + } + + /** + * Purge user's entries from cache. + */ + public synchronized void removeFromCache(HttpServletRequest req, UserD= ata user) + { + // TODO: can we make this a little more precise, perhaps via regex? + String rssString =3D "/rss/" + user.getUserName(); // user's pages + String pageString =3D "/page/" + user.getUserName(); // user's RSS= feeds + String mainRssString =3D "/rss_"; // main RSS feed + =20 + int beforeSize =3D mPageCache.size(); + mPageCache.purge(new String[] {rssString, pageString, mainRssStrin= g}); + int afterSize =3D mPageCache.size(); + =20 + if (mLogger.isDebugEnabled()) + { + StringBuffer sb =3D new StringBuffer(); + sb.append("Purged, count=3D"); + sb.append(beforeSize - afterSize); + sb.append(", user=3D"); + sb.append(user.getUserName()); + mLogger.debug(sb.toString()); + } =20 + } + =20 + /**=20 + * Get from cache. Synchronized because "In access-ordered linked hash=20 + * maps, merely querying the map with get is a structural modification= "=20 + */ + public synchronized Object getFromCache(String key)=20 + { + Object entry =3D mPageCache.get(key); + =20 + if (entry !=3D null && mLogger.isDebugEnabled()) + { + hits++; + } + return entry; + } + + public synchronized void putToCache(String key, Object entry)=20 + { + mPageCache.put(key, entry); + if (mLogger.isDebugEnabled()) + { + misses++; + =20 + StringBuffer sb =3D new StringBuffer(); + sb.append("Missed, cache size=3D"); + sb.append(mPageCache.size()); + sb.append(", hits=3D"); + sb.append(hits); + sb.append(", misses=3D"); + sb.append(misses); + sb.append(", key=3D"); + sb.append(key); + mLogger.debug(sb.toString()); + } + } + + public String generateEntryKey(String key,=20 + HttpServletRequest request, int scope, String langu= age) + { + StringBuffer cBuffer =3D new StringBuffer(AVERAGE_KEY_LENGTH); + // Append the language if available + if (language !=3D null) + { + cBuffer.append(FILE_SEPARATOR).append(language); + } + =20 + //cBuffer.append(FILE_SEPARATOR).append(request.getServerName()); + =20 + if (key !=3D null) + { + cBuffer.append(FILE_SEPARATOR).append(key); + } + else + { + String generatedKey =3D request.getRequestURI(); + if (generatedKey.charAt(0) !=3D FILE_SEPARATOR_CHAR) + { + cBuffer.append(FILE_SEPARATOR_CHAR); + } + cBuffer.append(generatedKey); + cBuffer.append("_").append(request.getMethod()).append("_"); + generatedKey =3D getSortedQueryString(request); + if (generatedKey !=3D null) + { + try + { + java.security.MessageDigest digest =3D=20 + java.security.MessageDigest.getInstance("MD5"); + byte[] b =3D digest.digest(generatedKey.getBytes()); + cBuffer.append("_"); + // Base64 encoding allows for unwanted slash character= s=2E + cBuffer.append(toBase64(b).replace('/', '_')); + } + catch (Exception e) + { + // Ignore query string + } + } + } + return cBuffer.toString(); + } + + protected String getSortedQueryString(HttpServletRequest request) + { + Map paramMap =3D request.getParameterMap(); + if (paramMap.isEmpty()) + { + return null; + } + Set paramSet =3D new TreeMap(paramMap).entrySet(); + StringBuffer buf =3D new StringBuffer(); + boolean first =3D true; + for (Iterator it =3D paramSet.iterator(); it.hasNext();) + { + Map.Entry entry =3D (Map.Entry) it.next(); + String[] values =3D (String[]) entry.getValue(); + for (int i =3D 0; i < values.length; i++) + { + String key =3D (String) entry.getKey(); + if ((key.length() !=3D 10) || !"jsessionid".equals(key)) + { + if (first) + { + first =3D false; + } + else + { + buf.append('&'); + } + buf.append(key).append('=3D').append(values[i]); + } + } + } + // We get a 0 length buffer if the only parameter was a jsessionid + if (buf.length() =3D=3D 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 =3D aValue.length; + StringBuffer tt =3D new StringBuffer(); + for (int i =3D 0; i < iByteLen; i +=3D 3) + { + boolean bByte2 =3D (i + 1) < iByteLen; + boolean bByte3 =3D (i + 2) < iByteLen; + byte1 =3D aValue[i] & 0xFF; + byte2 =3D (bByte2) ? (aValue[i + 1] & 0xFF) : 0; + byte3 =3D (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)) : '=3D')); + tt.append(((bByte3) ? m_strBase64Chars.charAt(byte3 & 0x3F) : = '=3D')); + } + return tt.toString(); + } +} Added: incubator/roller/trunk/src/org/roller/presentation/pagecache/rollerc= ache/ResponseContent.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/pr= esentation/pagecache/rollercache/ResponseContent.java?rev=3D189602&view=3Da= uto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercach= e/ResponseContent.java (added) +++ incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercach= e/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 Serge Knystautas + */ +public class ResponseContent implements Serializable { + private transient ByteArrayOutputStream bout =3D new ByteArrayOutputSt= ream(1000); + private Locale locale =3D null; + private String contentType =3D null; + private byte[] content =3D 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 respons= e=2E + */ + public void setContentType(String value) { + contentType =3D 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 =3D value; + } + + /** + * Get an output stream. This is used by the {@link SplitServletOutput= Stream} + * 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 -1. + */ + public int getSize() { + return (content !=3D 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 =3D bout.toByteArray(); + } + + /** + * Writes this cached data out to the supplied ServletResponse. + * + * @param response The servlet response to output the cached content t= o=2E + * @throws IOException + */ + public void writeTo(ServletResponse response) throws IOException { + //Send the content type and data to this response + if (contentType !=3D null) { + response.setContentType(contentType); + } + + response.setContentLength(content.length); + + if (locale !=3D null) { + response.setLocale(locale); + } + + OutputStream out =3D new BufferedOutputStream(response.getOutputSt= ream()); + out.write(content); + out.flush(); + } +} Added: incubator/roller/trunk/src/org/roller/presentation/pagecache/rollerc= ache/SplitServletOutputStream.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/pr= esentation/pagecache/rollercache/SplitServletOutputStream.java?rev=3D189602= &view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercach= e/SplitServletOutputStream.java (added) +++ incubator/roller/trunk/src/org/roller/presentation/pagecache/rollercach= e/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 ServletOutputStream class so that + * the stream can be captured as it gets written. This is achieved + * by overriding the write() 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 Serge Knystautas + */ +public class SplitServletOutputStream extends ServletOutputStream { + OutputStream captureStream =3D null; + OutputStream passThroughStream =3D null; + + /** + * Constructs a split output stream that both captures and passes thro= ugh + * the servlet response. + * + * @param captureStream The stream that will be used to capture the da= ta. + * @param passThroughStream The pass-through ServletOutputStream= + * that will write the response to the client as originally intended. + */ + public SplitServletOutputStream(OutputStream captureStream, OutputStre= am passThroughStream) { + this.captureStream =3D captureStream; + this.passThroughStream =3D 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/pr= esentation/tags/DateTag.java?rev=3D189602&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/roller/trunk/src/org/roller/presentation/tags/DateTag.java (a= dded) +++ incubator/roller/trunk/src/org/roller/presentation/tags/DateTag.java We= d 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 cho= oser. + * @jsp.tag name=3D"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 =3D "ZZZ_DATETAG_ZZZ"; + + private String property =3D null; + private String dateFormat =3D null; + private Boolean readOnly =3D Boolean.FALSE; + + private static Log mLogger =3D + LogFactory.getFactory().getInstance(DateTag.class); + + /** + * Renders date field by calling a JSP page. + */ + public int doStartTag() throws JspException + { + + // Get form name + ActionMapping mapping =3D + (ActionMapping) pageContext.getRequest().getAttribute( + Globals.MAPPING_KEY); + String formName =3D mapping.getName(); + + // Get value of form field + Object value =3D + RequestUtils.lookup(pageContext, formName, property, null); + if (value =3D=3D null) + value =3D ""; + + // 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 pa= ge + return (SKIP_BODY); + } + + /** + * Date format string to be used. + *=20 + * @jsp.attribute required=3D"true" rtexprvalue=3D"true" type=3D"java.= lang.String" + */ + public String getDateFormat() + { + return dateFormat; + } + + /** + * Name of form property represented.=20 + * @jsp.attribute required=3D"true" rtexprvalue=3D"true" type=3D"java.= lang.String" + */ + public String getProperty() + { + return property; + } + + /** + * True if field should be readOnly.=20 + * @jsp.attribute required=3D"false" rtexprvalue=3D"true" type=3D"java= .lang.Boolean" + */ + public Boolean getReadOnly() + { + return readOnly; + } + + public void setDateFormat(String dateFormat) + { + this.dateFormat =3D dateFormat; + } + + public void setProperty(String property) + { + this.property =3D property; + } + + public void setReadOnly(Boolean readOnly) + { + this.readOnly =3D readOnly; + } + +} Added: incubator/roller/trunk/src/org/roller/presentation/tags/HybridTag.ja= va URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/pr= esentation/tags/HybridTag.java?rev=3D189602&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- 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=20 +{ + private static Log mLogger =3D=20 + LogFactory.getFactory().getInstance(HybridTag.class); + + public HybridTag()=20 + { + } + + public String toString() + { + String ret =3D null; + try=20 + { + StringWriter sw =3D new StringWriter(); + doStartTag( new PrintWriter( sw, true )); + // See, design precludes contents=20 + doEndTag( new PrintWriter( sw, true )); + ret =3D sw.toString(); + } + catch (Exception e) + { + ret =3D "Exception in tag"; + mLogger.error(ret,e); + } + return ret; + } + =20 + public String emit() + { + return toString(); + } + + public int doStartTag() throws JspException=20 + { + return doStartTag( new PrintWriter( pageContext.getOut(), true) ); + } + + + public int doEndTag() throws JspException=20 + { + 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; + } + +}