AnalyticsComponentQueryCache.java 5.72 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  javax.jcr.Session
 *  javax.jcr.Value
 *  javax.jcr.Workspace
 *  javax.jcr.query.Query
 *  javax.jcr.query.QueryManager
 *  javax.jcr.query.QueryResult
 *  javax.jcr.query.Row
 *  javax.jcr.query.RowIterator
 *  org.apache.felix.scr.annotations.Activate
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Deactivate
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.sling.commons.osgi.PropertiesUtil
 *  org.osgi.service.component.ComponentContext
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.day.cq.analytics.impl;

import java.lang.ref.SoftReference;
import java.util.Arrays;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.Workspace;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.jcr.query.QueryResult;
import javax.jcr.query.Row;
import javax.jcr.query.RowIterator;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(name="Analytics Component Query Cache Service", label="Analytics Component Query Cache Service", description="Caches the 'cq:trackvars' and 'cq:trackevents' query valued results to improve performance", metatype=1, immediate=1)
@Service(value={AnalyticsComponentQueryCache.class})
public class AnalyticsComponentQueryCache {
    private static final Logger LOG = LoggerFactory.getLogger(AnalyticsComponentQueryCache.class);
    @Property(label="Analytics component SQL2 query cache size", description="Number of query results for the Analytics component the cache holds", longValue={2000}, propertyPrivate=0)
    protected static final String CACHE_SIZE = "cq.analytics.component.query.cache.size";
    private static final long DEFAULT_CACHE_SIZE = 2000;
    private long cacheSize;
    private Map<String, SoftReference<String[]>> listCache = null;

    @Activate
    protected void activate(ComponentContext ctx) {
        this.listCache = new HashMap<String, SoftReference<String[]>>();
        Dictionary dict = ctx.getProperties();
        this.cacheSize = PropertiesUtil.toLong(dict.get("cq.analytics.component.query.cache.size"), (long)2000);
    }

    @Deactivate
    protected void deactivate(ComponentContext ctx) {
        this.listCache = null;
    }

    private String[] getCachedValue(String userId, String queryString) {
        SoftReference<String[]> cachedSoftRef = this.listCache.get(this.buildUserQueryKey(userId, queryString));
        if (cachedSoftRef != null) {
            return cachedSoftRef.get();
        }
        return null;
    }

    private void storeValue(String userId, String queryString, String[] list) {
        if ((long)this.listCache.size() >= this.cacheSize) {
            LOG.debug("Cache size reached {0} limit, removing a cached entry...", (Object)this.cacheSize);
            String removeKey = null;
            if (this.listCache.size() > 0) {
                removeKey = this.listCache.keySet().iterator().next();
            }
            if (removeKey != null) {
                LOG.debug("Removing cached entry for query {0}", (Object)removeKey);
                this.listCache.remove(removeKey);
            }
        }
        this.listCache.put(this.buildUserQueryKey(userId, queryString), new SoftReference<String[]>(list));
    }

    private String buildUserQueryKey(String userId, String queryString) {
        return userId + "|" + queryString;
    }

    public String[] getQueryListProperty(String queryString, Session session) {
        String[] list = new String[]{};
        try {
            if (queryString != null && queryString.startsWith("SELECT ")) {
                LOG.debug("Searching cached query result query {0}", (Object)queryString);
                String[] cachedList = this.getCachedValue(session.getUserID(), queryString);
                if (cachedList == null) {
                    LOG.debug("Cache miss for query {0} ", (Object)queryString);
                    QueryManager qm = session.getWorkspace().getQueryManager();
                    Query query = qm.createQuery(queryString, "JCR-SQL2");
                    QueryResult res = query.execute();
                    RowIterator rows = res.getRows();
                    TreeSet<String> listSet = new TreeSet<String>();
                    while (rows.hasNext()) {
                        Row row = rows.nextRow();
                        Value[] props = row.getValues();
                        for (int i = 0; i < props.length; ++i) {
                            listSet.addAll(Arrays.asList(props[i].getString().split("\\s*[,\\s]\\s*")));
                        }
                    }
                    list = listSet.toArray(list);
                    LOG.debug("Storing list result for query {0} in the cache ", (Object)queryString);
                    this.storeValue(session.getUserID(), queryString, list);
                } else {
                    LOG.debug("Cache hit for query {0}, using cached result!");
                    list = Arrays.copyOf(cachedList, cachedList.length);
                }
            }
        }
        catch (Exception e) {
            LOG.error("Can't execute query " + queryString, (Throwable)e);
        }
        return list;
    }
}