001/*
002 * Copyright 2009-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2009-2017 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 */
021package com.unboundid.util;
022
023
024
025import java.io.Serializable;
026import java.util.ArrayList;
027import java.util.Collections;
028import java.util.List;
029import java.util.Map;
030import java.util.TreeMap;
031import java.util.concurrent.ConcurrentHashMap;
032import java.util.concurrent.atomic.AtomicLong;
033import java.util.concurrent.atomic.AtomicReference;
034
035import 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)
047public 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}