org.centralperf.service.RunStatisticsService.java Source code

Java tutorial

Introduction

Here is the source code for org.centralperf.service.RunStatisticsService.java

Source

/*
 * Copyright (C) 2014  The Central Perf authors
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.centralperf.service;

import java.util.Iterator;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.centralperf.model.dao.Run;
import org.centralperf.model.graph.ErrorRateGraph;
import org.centralperf.model.graph.ResponseSizeGraph;
import org.centralperf.model.graph.ResponseTimeGraph;
import org.centralperf.model.graph.RunStats;
import org.centralperf.model.graph.SummaryGraph;
import org.centralperf.repository.RunRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

/**
 * This class is singleton service to collect run data and graphs.<br>
 * A cache mechanism with time eviction is used to avoid database overload if too many refresh
 * are asked by users.
 * @since 1.0
 */

@Service
public class RunStatisticsService {

    @Resource
    private RunRepository runRepository;

    @PersistenceContext
    private EntityManager em;

    @Resource
    private ScriptLauncherService scriptLauncherService;

    @Value("#{appProperties['report.cache.delay.seconds']}")
    private Long cacheRefreshDelay;

    @Value("#{appProperties['report.scaling.level1.seconds']}")
    private Long limitOfFisrtScaling;

    @Value("#{appProperties['report.scaling.level2.seconds']}")
    private Long limitOfSecondScaling;

    //Cache can't be load on object construction as refresh delay is not yet set
    private LoadingCache<Long, SummaryGraph> summaryGraphCache;
    private LoadingCache<Long, ResponseTimeGraph> responseTimeGraphCache;
    private LoadingCache<Long, ResponseSizeGraph> responseSizeGraphCache;
    private LoadingCache<Long, ErrorRateGraph> errorRateGraphCache;
    private LoadingCache<Long, RunStats> runStatsCache;

    private static final Logger log = LoggerFactory.getLogger(RunStatisticsService.class);

    /*
     * Use of cache is necessary to avoid database saturation if more than one user
     * is looking UI run detail
     */
    private CacheLoader<Long, SummaryGraph> summaryGraphLoader = new CacheLoader<Long, SummaryGraph>() {
        @SuppressWarnings("unchecked")
        @Override
        public SummaryGraph load(Long runId) throws Exception {
            //Load stats from database
            log.debug("Loading SummaryGraph datas from database (not in cache)");
            Query q = em
                    .createQuery("select to_char(timestamp, 'DD-MM-YYYY HH24:MI:SS') from Sample s where run_fk='"
                            + runId + "' group by to_char(timestamp, 'DD-MM-YYYY HH24:MI:SS')");
            int nbSeconds = q.getResultList().size();
            log.debug("Check for scaling [seconds:" + nbSeconds + " - Fisrt scaling after: " + limitOfFisrtScaling
                    + " s - second scaling after: " + limitOfSecondScaling + " s");
            // First limit : a line per second, or less
            if (nbSeconds < limitOfFisrtScaling) {
                q = em.createQuery(
                        "select to_char(timestamp, 'DD-MM-YYYY HH24:MI:SS'), round(avg(elapsed),0), count(*) from Sample s "
                                + "where run_fk='" + runId
                                + "' group by to_char(timestamp, 'DD-MM-YYYY HH24:MI:SS') order by to_char(timestamp, 'DD-MM-YYYY HH24:MI:SS')");
            }
            // Second limit : a line each 10 seconds
            else if (nbSeconds < limitOfSecondScaling) {
                q = em.createQuery(
                        "select concat(substring(to_char(timestamp, 'DD-MM-YYYY HH24:MI:SS'),0,19),'0') , round(avg(elapsed),0), round(count(*)/10.0,2), substring(to_char(timestamp, 'DD-MM-YYYY HH24:MI:SS'),0,19) from Sample s "
                                + "where run_fk='" + runId
                                + "'group by substring(to_char(timestamp, 'DD-MM-YYYY HH24:MI:SS'),0,19) order by substring(to_char(timestamp, 'DD-MM-YYYY HH24:MI:SS'),0,19)");
            }
            // greater than second limit : a line
            else {
                q = em.createQuery(
                        "select to_char(timestamp, 'DD-MM-YYYY HH24:MI:00'), round(avg(elapsed),0), round(count(*)/60.0,2) from Sample s "
                                + "where run_fk='" + runId
                                + "' group by to_char(timestamp, 'DD-MM-YYYY HH24:MI:00') order by to_char(timestamp, 'DD-MM-YYYY HH24:MI:00')");
            }

            Run run = runRepository.findOne(runId);
            return new SummaryGraph(q.getResultList().iterator(), run.getStartDate());
        }
    };
    private CacheLoader<Long, ResponseTimeGraph> responseTimeGraphLoader = new CacheLoader<Long, ResponseTimeGraph>() {
        @SuppressWarnings("unchecked")
        @Override
        public ResponseTimeGraph load(Long runId) throws Exception {
            //Load stats from database
            log.debug("Loading ResponseTimeGraph datas from database (not in cache)");
            Query q = em.createQuery(
                    "SELECT  sampleName, round(avg(elapsed),0), round(avg(latency),0) from Sample s where run_fk='"
                            + runId + "'   GROUP BY sampleName order by sampleName");
            return new ResponseTimeGraph(q.getResultList().iterator());
        }
    };
    private CacheLoader<Long, ResponseSizeGraph> responseSizeGraphLoader = new CacheLoader<Long, ResponseSizeGraph>() {
        @SuppressWarnings("unchecked")
        @Override
        public ResponseSizeGraph load(Long runId) throws Exception {
            //Load stats from database
            log.debug("Loading ResponseSizeGraph datas from database (not in cache)");
            Query q = em.createQuery("SELECT  sampleName, round(avg(sizeInOctet),0)  from Sample s where run_fk='"
                    + runId + "'   GROUP BY sampleName order by sampleName");
            return new ResponseSizeGraph(q.getResultList().iterator());
        }
    };
    private CacheLoader<Long, ErrorRateGraph> errorRateGraphLoader = new CacheLoader<Long, ErrorRateGraph>() {
        @SuppressWarnings("unchecked")
        @Override
        public ErrorRateGraph load(Long runId) throws Exception {
            //Load stats from database
            log.debug("Loading ErrorRateGraph datas from database (not in cache)");
            Query q = em.createQuery(
                    "SELECT sampleName, count(CASE WHEN assertResult IS true THEN 1 ELSE null END), count(CASE WHEN assertResult IS TRUE THEN null ELSE true END) from Sample s where run_fk='"
                            + runId + "' GROUP BY sampleName  order by sampleName");
            return new ErrorRateGraph(q.getResultList().iterator());
        }
    };
    private CacheLoader<Long, RunStats> runStatsLoader = new CacheLoader<Long, RunStats>() {
        @Override
        public RunStats load(Long runId) throws Exception {
            //Load stats from database
            log.debug("Loading RunStat datas from database (not in cache)");
            Query q = em.createQuery(
                    "SELECT count(*), sum(sizeInOctet), min(timestamp), max(timestamp), sum(elapsed)/count(*), sum(latency)/count(*), max(allThreads),sum(case when assertResult=true then 0 else 1 end) from Sample s where run_fk='"
                            + runId + "'");
            @SuppressWarnings("rawtypes")
            Iterator results = q.getResultList().iterator();
            RunStats runStats = null;
            String runOutput = null;
            boolean running = false;
            Run run = runRepository.findOne(runId);
            if (run != null) {
                runOutput = run.getProcessOutput();
                running = run.isRunning();
            }
            if (results.hasNext()) {
                runStats = new RunStats((Object[]) results.next(), runOutput, running);
            }
            return runStats;
        }
    };

    private LoadingCache<Long, SummaryGraph> getSummaryGraphCache() {
        CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
        builder.expireAfterWrite(cacheRefreshDelay, TimeUnit.SECONDS);
        return builder.build(summaryGraphLoader);
    }

    private LoadingCache<Long, ResponseTimeGraph> getResponseTimeGraphCache() {
        CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
        builder.expireAfterWrite(cacheRefreshDelay, TimeUnit.SECONDS);
        return builder.build(responseTimeGraphLoader);
    }

    private LoadingCache<Long, ResponseSizeGraph> getResponseSizeGraphCache() {
        CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
        builder.expireAfterWrite(cacheRefreshDelay, TimeUnit.SECONDS);
        return builder.build(responseSizeGraphLoader);
    }

    private LoadingCache<Long, ErrorRateGraph> getErrorRateGraphCache() {
        CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
        builder.expireAfterWrite(cacheRefreshDelay, TimeUnit.SECONDS);
        return builder.build(errorRateGraphLoader);
    }

    public LoadingCache<Long, RunStats> getRunStatsCache() {
        CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
        builder.expireAfterWrite(cacheRefreshDelay, TimeUnit.SECONDS);
        return builder.build(runStatsLoader);
    }

    /**
     * Get summary graph data as CSV String
     * @param runId id of the run
     * @return String with csv of summary graph
     * @throws ExecutionException On cache read error
     */
    public String getSummaryGraph(Long runId) throws ExecutionException {
        if (summaryGraphCache == null) {
            summaryGraphCache = this.getSummaryGraphCache();
        }
        return this.summaryGraphCache.get(runId).toString();
    }

    /**
     * Get responseTime graph data as CSV String
     * @param runId id of the run
     * @return String with csv of response time graph
     * @throws ExecutionException On cache read error
     */
    public String getResponseTimeGraph(Long runId) throws ExecutionException {
        if (responseTimeGraphCache == null) {
            responseTimeGraphCache = this.getResponseTimeGraphCache();
        }
        return this.responseTimeGraphCache.get(runId).toString();
    }

    /**
     * Get response size graph data as CSV String
     * @param runId id of the run
     * @return String with csv of response size graph
     * @throws ExecutionException On cache read error
     */
    public String getResponseSizeGraph(Long runId) throws ExecutionException {
        if (responseSizeGraphCache == null) {
            responseSizeGraphCache = this.getResponseSizeGraphCache();
        }
        return this.responseSizeGraphCache.get(runId).toString();
    }

    /**
     * Get error rate graph data as CSV String
     * @param runId id of the run
     * @return String with csv of error rate graph
     * @throws ExecutionException On cache read error
     */
    public String getErrorRateGraph(Long runId) throws ExecutionException {
        if (errorRateGraphCache == null) {
            errorRateGraphCache = this.getErrorRateGraphCache();
        }
        return this.errorRateGraphCache.get(runId).toString();
    }

    /**
     * Get run statistics data as RunStats to JSon conversion
     * @param runId id of the run
     * @return Runstats object with run statistics datas
     * @throws ExecutionException On cache read error
     */
    public RunStats getRunStats(Long runId) throws ExecutionException {
        if (runStatsCache == null) {
            runStatsCache = this.getRunStatsCache();
        }
        return this.runStatsCache.get(runId);
    }
}