001    /*
002     * Copyright 2009-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2009-2016 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    }