001 /*
002 * Copyright 2009-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2009-2014 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.util;
022
023
024
025 import java.io.Serializable;
026 import java.util.ArrayList;
027 import java.util.Collections;
028 import java.util.List;
029 import java.util.Map;
030 import java.util.TreeMap;
031 import java.util.concurrent.ConcurrentHashMap;
032 import java.util.concurrent.atomic.AtomicLong;
033 import java.util.concurrent.atomic.AtomicReference;
034
035 import com.unboundid.ldap.sdk.ResultCode;
036
037
038
039 /**
040 * This class provides a utility that may be used to count operation results and
041 * categorize them based on the total number of results of each type. It also
042 * provides a method for retrieving result code counts, sorted by the number of
043 * occurrences for each.
044 */
045 @Mutable()
046 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
047 public final class ResultCodeCounter
048 implements Serializable
049 {
050 /**
051 * The serial version UID for this serializable class.
052 */
053 private static final long serialVersionUID = -2280620218815022241L;
054
055
056
057 // The reference to the current map used to hold result code counts.
058 private final AtomicReference<ConcurrentHashMap<ResultCode,AtomicLong>> rcMap;
059
060
061
062 /**
063 * Creates a new instance of this result code counter.
064 */
065 public ResultCodeCounter()
066 {
067 rcMap = new AtomicReference<ConcurrentHashMap<ResultCode,AtomicLong>>();
068 rcMap.set(new ConcurrentHashMap<ResultCode,AtomicLong>());
069 }
070
071
072
073 /**
074 * Increments the count for the provided result code.
075 *
076 * @param resultCode The result code for which to increment the count.
077 */
078 public void increment(final ResultCode resultCode)
079 {
080 increment(resultCode, 1);
081 }
082
083
084
085 /**
086 * Increments the count for the provided result code by the specified amount.
087 *
088 * @param resultCode The result code for which to increment the count.
089 * @param amount The amount by which to increment the count.
090 */
091 public void increment(final ResultCode resultCode, final int amount)
092 {
093 final ConcurrentHashMap<ResultCode,AtomicLong> m = rcMap.get();
094
095 AtomicLong l = m.get(resultCode);
096 if (l == null)
097 {
098 l = new AtomicLong(0L);
099 final AtomicLong l2 = m.putIfAbsent(resultCode, l);
100 if (l2 != null)
101 {
102 l = l2;
103 }
104 }
105
106 l.addAndGet(amount);
107 }
108
109
110
111 /**
112 * Clears all collected data from the result code counter. Any
113 * previously-collected data will be lost.
114 */
115 public void reset()
116 {
117 rcMap.set(new ConcurrentHashMap<ResultCode, AtomicLong>());
118 }
119
120
121
122 /**
123 * Retrieves a list of the result codes of each type along with their
124 * respective counts. The returned list will be sorted by number of
125 * occurrences, from most frequent to least frequent.
126 *
127 * @param reset Indicates whether to clear the results after obtaining
128 * them.
129 *
130 * @return A list of the result codes of each type along with their
131 * respective counts.
132 */
133 public List<ObjectPair<ResultCode,Long>> getCounts(final boolean reset)
134 {
135 final ConcurrentHashMap<ResultCode,AtomicLong> m;
136 if (reset)
137 {
138 m = rcMap.getAndSet(new ConcurrentHashMap<ResultCode,AtomicLong>());
139 }
140 else
141 {
142 m = new ConcurrentHashMap<ResultCode,AtomicLong>(rcMap.get());
143 }
144
145
146 if (m.isEmpty())
147 {
148 return Collections.emptyList();
149 }
150
151
152 final TreeMap<Long,TreeMap<Integer,ResultCode>> sortedMap =
153 new TreeMap<Long,TreeMap<Integer,ResultCode>>(
154 new ReverseComparator<Long>());
155 for (final Map.Entry<ResultCode,AtomicLong> e : m.entrySet())
156 {
157 final long l = e.getValue().longValue();
158 TreeMap<Integer,ResultCode> rcByValue = sortedMap.get(l);
159 if (rcByValue == null)
160 {
161 rcByValue = new TreeMap<Integer,ResultCode>();
162 sortedMap.put(l, rcByValue);
163 }
164
165 final ResultCode rc = e.getKey();
166 rcByValue.put(rc.intValue(), rc);
167 }
168
169
170 final ArrayList<ObjectPair<ResultCode,Long>> rcCounts =
171 new ArrayList<ObjectPair<ResultCode,Long>>(2*sortedMap.size());
172 for (final Map.Entry<Long,TreeMap<Integer,ResultCode>> e :
173 sortedMap.entrySet())
174 {
175 final long count = e.getKey();
176 for (final ResultCode rc : e.getValue().values())
177 {
178 rcCounts.add(new ObjectPair<ResultCode,Long>(rc, count));
179 }
180 }
181
182 return Collections.unmodifiableList(rcCounts);
183 }
184 }