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