package com.psddev.cms.db; import java.io.IOException; import java.util.List; import java.util.Random; import java.util.UUID; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.common.primitives.Longs; import com.psddev.cms.tool.AuthenticationFilter; import com.psddev.cms.tool.CmsTool; import com.psddev.dari.db.ApplicationFilter; import com.psddev.dari.db.Database; import com.psddev.dari.db.Query; import com.psddev.dari.db.State; import com.psddev.dari.util.AbstractFilter; import com.psddev.dari.util.ObjectUtils; import com.psddev.dari.util.StringUtils; public class AbFilter extends AbstractFilter implements AbstractFilter.Auto { private static final String SEED_COOKIE = "cms.ab"; private static final String ATTRIBUTE_PREFIX = AbFilter.class.getName() + "."; private static final String SEED_ATTRIBUTE = ATTRIBUTE_PREFIX + "seed"; @Override public void updateDependencies( Class<? extends AbstractFilter> filterClass, List<Class<? extends Filter>> dependencies) { if (ApplicationFilter.class.isAssignableFrom(filterClass)) { dependencies.add(getClass()); } } @Override protected void doRequest( HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { CmsTool cms = Query.from(CmsTool.class).first(); if (cms == null || !cms.isEnableAbTesting()) { chain.doFilter(request, response); return; } if (AuthenticationFilter.Static.isAuthenticated(request)) { chain.doFilter(request, response); return; } String seed = null; if (!"reset".equals(request.getParameter("_ab"))) { Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie c : cookies) { if (SEED_COOKIE.equals(c.getName())) { seed = c.getValue(); break; } } } } if (ObjectUtils.isBlank(seed)) { seed = UUID.randomUUID().toString(); Cookie seedCookie = new Cookie(SEED_COOKIE, seed.toString()); seedCookie.setPath("/"); response.addCookie(seedCookie); } request.setAttribute(SEED_ATTRIBUTE, seed); AbDatabase ab = new AbDatabase(); ab.setDelegate(Database.Static.getDefault()); ab.setRequest(request); try { Database.Static.overrideDefault(ab); chain.doFilter(request, response); } finally { Database.Static.restoreDefault(); } } /** * {@link AbFilter} utility methods. */ public static class Static { public static double random(HttpServletRequest request, State state, String fieldName) { byte[] md5 = StringUtils.md5(String.valueOf(request.getAttribute(SEED_ATTRIBUTE)) + state.getId() + fieldName); long seed = Longs.fromByteArray(md5); double random = new Random(seed).nextDouble(); return random; } } }