package com.avaje.ebeaninternal.server.autofetch; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import com.avaje.ebean.bean.NodeUsageCollector; import com.avaje.ebean.bean.ObjectGraphNode; import com.avaje.ebean.bean.ObjectGraphOrigin; import com.avaje.ebean.meta.MetaAutoFetchStatistic; import com.avaje.ebean.meta.MetaAutoFetchStatistic.NodeUsageStats; import com.avaje.ebean.meta.MetaAutoFetchStatistic.QueryStats; import com.avaje.ebean.text.PathProperties; import com.avaje.ebean.text.PathProperties.Props; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; public class Statistics implements Serializable { private static final long serialVersionUID = -5586783791097230766L; private final ObjectGraphOrigin origin; private final boolean queryTuningAddVersion; private int counter; private Map queryStatsMap = new LinkedHashMap(); private Map nodeUsageMap = new LinkedHashMap(); private final String monitor = new String(); public Statistics(ObjectGraphOrigin origin, boolean queryTuningAddVersion) { this.origin = origin; this.queryTuningAddVersion = queryTuningAddVersion; } public ObjectGraphOrigin getOrigin() { return origin; } public TunedQueryInfo createTunedFetch(OrmQueryDetail newFetchDetail) { synchronized (monitor) { // NB: create a copy of queryPoint allowing garbage // collection of source... return new TunedQueryInfo(origin, newFetchDetail, counter); } } public MetaAutoFetchStatistic createPublicMeta() { synchronized (monitor) { StatisticsQuery[] sourceQueryStats = queryStatsMap.values().toArray(new StatisticsQuery[queryStatsMap.size()]); List destQueryStats = new ArrayList(sourceQueryStats.length); // copy the query statistics for (int i = 0; i < sourceQueryStats.length; i++) { destQueryStats.add(sourceQueryStats[i].createPublicMeta()); } StatisticsNodeUsage[] sourceNodeUsage = nodeUsageMap.values().toArray(new StatisticsNodeUsage[nodeUsageMap.size()]); List destNodeUsage = new ArrayList(sourceNodeUsage.length); // copy the node usage statistics for (int i = 0; i < sourceNodeUsage.length; i++) { destNodeUsage.add(sourceNodeUsage[i].createPublicMeta()); } return new MetaAutoFetchStatistic(origin, counter, destQueryStats, destNodeUsage); } } /** * Return the number of times the root query has executed. *

* This tells us how much profiling we have done for this query. * For example, after 100 times we may stop collecting more profiling info. *

*/ public int getCounter() { return counter; } /** * Return true if this has usage statistics. */ public boolean hasUsage() { synchronized (monitor) { return !nodeUsageMap.isEmpty(); } } public OrmQueryDetail buildTunedFetch(BeanDescriptor rootDesc){ synchronized (monitor) { if (nodeUsageMap.isEmpty()){ return null; } PathProperties pathProps = new PathProperties(); Iterator it = nodeUsageMap.values().iterator(); while (it.hasNext()) { StatisticsNodeUsage statsNode = it.next(); statsNode.buildTunedFetch(pathProps, rootDesc); } OrmQueryDetail detail = new OrmQueryDetail(); Collection pathProperties = pathProps.getPathProps(); for (Props props : pathProperties) { if (!props.isEmpty()){ detail.addFetch(props.getPath(), props.getPropertiesAsString(), null); } } detail.sortFetchPaths(rootDesc); return detail; } } public void collectQueryInfo(ObjectGraphNode node, int beansLoaded, int micros) { synchronized (monitor) { String key = node.getPath(); if (key == null){ key = ""; // this is basically the number of times the root query // has executed which gives us an indication of how // much profiling information we have gathered. counter++; } StatisticsQuery stats = queryStatsMap.get(key); if (stats == null){ stats = new StatisticsQuery(key); queryStatsMap.put(key, stats); } stats.add(beansLoaded, micros); } } /** * Collect the usage information for from a instance for this node. */ public void collectUsageInfo(NodeUsageCollector profile) { if (profile.isEmpty()){ // no usage was collected } else { ObjectGraphNode node = profile.getNode(); StatisticsNodeUsage nodeStats = getNodeStats(node.getPath()); nodeStats.publish(profile); } } private StatisticsNodeUsage getNodeStats(String path) { synchronized (monitor) { StatisticsNodeUsage nodeStats = nodeUsageMap.get(path); if (nodeStats == null) { nodeStats = new StatisticsNodeUsage(path, queryTuningAddVersion); nodeUsageMap.put(path, nodeStats); } return nodeStats; } } public String getUsageDebug() { synchronized (monitor) { StringBuilder sb = new StringBuilder(); sb.append("root[").append(origin.getBeanType()).append("] "); for (StatisticsNodeUsage node : nodeUsageMap.values()) { sb.append(node.toString()).append("\n"); } return sb.toString(); } } public String getQueryStatDebug() { synchronized (monitor) { StringBuilder sb = new StringBuilder(); for (StatisticsQuery queryStat : queryStatsMap.values()) { sb.append(queryStat.toString()).append("\n"); } return sb.toString(); } } public String toString() { synchronized (monitor) { return getUsageDebug(); } } }