/*
* Copyright 2014 Martin Kouba
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.trimou.engine.listener;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Predicate;
import org.trimou.engine.MustacheEngine;
import org.trimou.engine.cache.ComputingCache;
import org.trimou.util.ImmutableMap;
import org.trimou.util.ImmutableMap.ImmutableMapBuilder;
import org.trimou.util.ImmutableSet;
import org.trimou.util.ImmutableSet.ImmutableSetBuilder;
/**
* A simple {@link MustacheListener} collecting template rendering statistics.
*
* Note that the template is identified with the name/id - so data will not be
* correct if there's more than one templates with the same name (which is
* possible if using {@link MustacheEngine#compileMustache(String, String)}).
*
* This listener is not able to detect rendering errors.
*
* @author Martin Kouba
*/
public class SimpleStatsCollector extends AbstractStatsCollector {
public static final String COMPUTING_CACHE_CONSUMER_ID = SimpleStatsCollector.class
.getName();
/**
* Map of name to map of time to amount
*/
protected ComputingCache<String, ComputingCache<Long, AtomicLong>> data;
/**
*
*/
public SimpleStatsCollector() {
this(null, null);
}
/**
*
* @param templatePredicate
* @param timeUnit
*/
public SimpleStatsCollector(Predicate<String> templatePredicate,
TimeUnit timeUnit) {
super(templatePredicate, timeUnit);
}
@Override
public void init() {
// Use computing cache because of concurrent access is required and the
// data set is not known beforehand
this.data = configuration.getComputingCacheFactory().create(
COMPUTING_CACHE_CONSUMER_ID,
key -> configuration.getComputingCacheFactory()
.create(COMPUTING_CACHE_CONSUMER_ID, k ->
new AtomicLong(0), null, null, null), null, null, null);
}
@Override
public void renderingStarted(final MustacheRenderingEvent event) {
if (isApplied(event.getMustacheName())) {
final long start = System.nanoTime();
event.registerReleaseCallback(() -> data.get(event.getMustacheName())
.get(convert(System.nanoTime() - start))
.incrementAndGet());
}
}
/**
* Drop all the collected data.
*/
public void clearData() {
data.clear();
}
/**
*
* @param templateId
* @return data for the given template
*/
public Map<Long, Long> getData(String templateId) {
ComputingCache<Long, AtomicLong> templateData = data
.getIfPresent(templateId);
if (templateData != null) {
return getImmutableTemplateData(templateData);
}
return null;
}
/**
*
* @return all collected data
*/
public Map<String, Map<Long, Long>> getData() {
if (data.size() == 0) {
return Collections.emptyMap();
}
ImmutableMapBuilder<String, Map<Long, Long>> builder = ImmutableMap
.builder();
for (Entry<String, ComputingCache<Long, AtomicLong>> entry : data
.getAllPresent().entrySet()) {
builder.put(entry.getKey(),
getImmutableTemplateData(entry.getValue()));
}
return builder.build();
}
/**
*
* @param templateId
* @return a simple statistics for the given template
*/
public SimpleStats getSimpleStats(String templateId) {
ComputingCache<Long, AtomicLong> entry = data.getIfPresent(templateId);
if (entry != null) {
return new SimpleStats(templateId, entry.getAllPresent());
}
return null;
}
/**
*
* @return all available simple statistics
*/
public Set<SimpleStats> getSimpleStats() {
if (data.size() == 0) {
return Collections.emptySet();
}
ImmutableSetBuilder<SimpleStats> buidler = ImmutableSet.builder();
for (Entry<String, ComputingCache<Long, AtomicLong>> entry : data
.getAllPresent().entrySet()) {
buidler.add(new SimpleStats(entry.getKey(), entry.getValue()
.getAllPresent()));
}
return buidler.build();
}
private Map<Long, Long> getImmutableTemplateData(
ComputingCache<Long, AtomicLong> templateData) {
ImmutableMapBuilder<Long, Long> builder = ImmutableMap.builder();
for (Entry<Long, AtomicLong> entry : templateData.getAllPresent()
.entrySet()) {
builder.put(entry.getKey(), entry.getValue().get());
}
return builder.build();
}
public class SimpleStats {
private final String name;
private final long executions;
private final long totalTime;
private final long meanTime;
private final long minTime;
private final long maxTime;
SimpleStats(String name, Map<Long, AtomicLong> data) {
this.name = name;
long executions = 0L;
long totalTime = 0L;
for (Entry<Long, AtomicLong> entry : data.entrySet()) {
executions += entry.getValue().get();
totalTime += entry.getKey() * entry.getValue().get();
}
this.executions = executions;
this.totalTime = totalTime;
this.meanTime = (totalTime / executions);
this.minTime = Collections.min(data.keySet());
this.maxTime = Collections.max(data.keySet());
}
public String getName() {
return name;
}
public long getExecutions() {
return executions;
}
public long getTotalTime() {
return totalTime;
}
public long getMeanTime() {
return meanTime;
}
public long getMinTime() {
return minTime;
}
public long getMaxTime() {
return maxTime;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
SimpleStats other = (SimpleStats) obj;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
@Override
public String toString() {
return String
.format("SimpleStats [name: %s, executions: %s, totalTime: %s, meanTime: %s, minTime: %s, maxTime: %s]",
name, executions, totalTime, meanTime, minTime,
maxTime);
}
}
}