001 /* 002 * JBoss DNA (http://www.jboss.org/dna) 003 * See the COPYRIGHT.txt file distributed with this work for information 004 * regarding copyright ownership. Some portions may be licensed 005 * to Red Hat, Inc. under one or more contributor license agreements. 006 * See the AUTHORS.txt file in the distribution for a full listing of 007 * individual contributors. 008 * 009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA 010 * is licensed to you under the terms of the GNU Lesser General Public License as 011 * published by the Free Software Foundation; either version 2.1 of 012 * the License, or (at your option) any later version. 013 * 014 * JBoss DNA is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 017 * Lesser General Public License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this software; if not, write to the Free 021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 023 */ 024 package org.jboss.dna.connector.federation.contribution; 025 026 import java.util.Collections; 027 import java.util.HashMap; 028 import java.util.LinkedList; 029 import java.util.List; 030 import java.util.Map; 031 import java.util.concurrent.ConcurrentHashMap; 032 import java.util.concurrent.ConcurrentMap; 033 import java.util.concurrent.atomic.AtomicLong; 034 import org.jboss.dna.common.util.CheckArg; 035 import org.jboss.dna.common.util.HashCode; 036 037 /** 038 * Simple utility class to record the distribution of contributions. 039 * 040 * @author Randall Hauch 041 */ 042 public class ContributionStatistics { 043 044 /** 045 * This should only be enabled when attempting to accumulate the distribution, and should <i>never</i> be enabled in a 046 * release. 047 */ 048 /*package*/static final boolean RECORD = false; 049 050 private static final ConcurrentMap<Stats, AtomicLong> DATA = new ConcurrentHashMap<Stats, AtomicLong>(); 051 052 /** 053 * Record a {@link Contribution} was created for the supplied number of properties and children. {@link #RECORD} should be 054 * checked <i>before</i> this method is called. 055 * 056 * @param propertyCount the number of properties 057 * @param childrenCount the number of children 058 */ 059 /*package*/static void record( int propertyCount, 060 int childrenCount ) { 061 Stats key = new Stats(propertyCount, childrenCount); 062 AtomicLong existing = DATA.putIfAbsent(key, new AtomicLong(1l)); 063 if (existing != null) existing.incrementAndGet(); 064 } 065 066 public boolean isRecording() { 067 return RECORD; 068 } 069 070 /** 071 * Get a copy of the raw statistics data. 072 * 073 * @return a copy of the data; never null 074 */ 075 public static Map<Stats, AtomicLong> getData() { 076 return new HashMap<Stats, AtomicLong>(DATA); 077 } 078 079 /** 080 * Get the N most populous combinations of properties and children counts. 081 * 082 * @param n the maximum number of data objects to find and return in the raw data; must be positive 083 * @return the list of N (or fewer) 084 */ 085 public static List<Data> getTop( int n ) { 086 CheckArg.isPositive(n, "n"); 087 LinkedList<Data> results = new LinkedList<Data>(); 088 for (Map.Entry<Stats, AtomicLong> entry : DATA.entrySet()) { 089 long value = entry.getValue().get(); 090 if (results.size() >= n) { 091 Data last = results.getLast(); 092 if (value <= last.getInstanceCount()) continue; 093 // The new count is larger than the smallest, so pop the smallest and add the newest ... 094 results.removeLast(); 095 } 096 results.add(new Data(entry.getKey(), value)); 097 Collections.sort(results); 098 } 099 return results; 100 } 101 102 public static final class Data implements Comparable<Data> { 103 private final int propertyCount; 104 private final int childrenCount; 105 private final long instanceCount; 106 107 protected Data( Stats stats, 108 long instanceCount ) { 109 this.propertyCount = stats.getPropertyCount(); 110 this.childrenCount = stats.getChildrenCount(); 111 this.instanceCount = instanceCount; 112 } 113 114 /** 115 * @return childrenCount 116 */ 117 public int getChildrenCount() { 118 return childrenCount; 119 } 120 121 /** 122 * @return propertyCount 123 */ 124 public int getPropertyCount() { 125 return propertyCount; 126 } 127 128 /** 129 * @return instanceCount 130 */ 131 public long getInstanceCount() { 132 return instanceCount; 133 } 134 135 /** 136 * {@inheritDoc} This orders the values in the opposite order, so that those with larger numbers of instances appear 137 * first. 138 * 139 * @see java.lang.Comparable#compareTo(java.lang.Object) 140 */ 141 public int compareTo( Data that ) { 142 long diff = that.getInstanceCount() - this.getInstanceCount(); // backwards 143 return diff < 0l ? -1 : diff > 0 ? 1 : 0; 144 } 145 146 /** 147 * {@inheritDoc} 148 * 149 * @see java.lang.Object#hashCode() 150 */ 151 @Override 152 public int hashCode() { 153 return HashCode.compute(this.propertyCount, this.childrenCount, this.instanceCount); 154 } 155 156 /** 157 * {@inheritDoc} 158 * 159 * @see java.lang.Object#equals(java.lang.Object) 160 */ 161 @Override 162 public boolean equals( Object obj ) { 163 if (obj instanceof Data) { 164 Data that = (Data)obj; 165 if (this.propertyCount != that.propertyCount) return false; 166 if (this.childrenCount != that.childrenCount) return false; 167 if (this.instanceCount != that.instanceCount) return false; 168 return true; 169 } 170 return false; 171 } 172 173 /** 174 * {@inheritDoc} 175 * 176 * @see java.lang.Object#toString() 177 */ 178 @Override 179 public String toString() { 180 return "(# props=" + this.getPropertyCount() + ", # children=" + this.getChildrenCount() + ") => " 181 + this.getInstanceCount(); 182 } 183 } 184 185 public static final class Stats { 186 private final int propertyCount; 187 private final int childrenCount; 188 189 protected Stats( int propertyCount, 190 int childrenCount ) { 191 this.propertyCount = propertyCount; 192 this.childrenCount = childrenCount; 193 } 194 195 /** 196 * @return childrenCount 197 */ 198 public int getChildrenCount() { 199 return childrenCount; 200 } 201 202 /** 203 * @return propertyCount 204 */ 205 public int getPropertyCount() { 206 return propertyCount; 207 } 208 209 /** 210 * {@inheritDoc} 211 * 212 * @see java.lang.Object#hashCode() 213 */ 214 @Override 215 public int hashCode() { 216 return HashCode.compute(this.propertyCount, this.childrenCount); 217 } 218 219 /** 220 * {@inheritDoc} 221 * 222 * @see java.lang.Object#equals(java.lang.Object) 223 */ 224 @Override 225 public boolean equals( Object obj ) { 226 if (obj instanceof Stats) { 227 Stats that = (Stats)obj; 228 if (this.propertyCount != that.propertyCount) return false; 229 if (this.childrenCount != that.childrenCount) return false; 230 return true; 231 } 232 return false; 233 } 234 } 235 236 }