    001/*
    002    Licensed to the Apache Software Foundation (ASF) under one
    003    or more contributor license agreements.  See the NOTICE file
    004    distributed with this work for additional information
    005    regarding copyright ownership.  The ASF licenses this file
    006    to you under the Apache License, Version 2.0 (the
    007    "License"); you may not use this file except in compliance
    008    with the License.  You may obtain a copy of the License at
    009
    010       http://www.apache.org/licenses/LICENSE-2.0
    011
    012    Unless required by applicable law or agreed to in writing,
    013    software distributed under the License is distributed on an
    014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015    KIND, either express or implied.  See the License for the
    016    specific language governing permissions and limitations
    017    under the License.
    018 */
    019package org.apache.wiki.util;
    020
    021import java.util.AbstractList;
    022import java.util.ArrayList;
    023import java.util.Iterator;
    024import java.util.concurrent.locks.ReadWriteLock;
    025import java.util.concurrent.locks.ReentrantReadWriteLock;
    026
    027/**
    028 *  Provides a List in which all items store their addition time. This
    029 *  can then be used to clean the list from old items.
    030 *  <p>
    031 *  This class is thread-safe - all modifications are blocking, but
    032 *  reading is non-blocking (unless a write is ongoing).
    033 *
    034 *  @param <T> The class you wish to store here
    035 *  @since 2.8
    036 */
    037public class TimedCounterList<T> extends AbstractList<T>
    038{
    039    private final ArrayList<CounterItem<T>> m_list = new ArrayList<>();
    040    private final ReadWriteLock             m_lock = new ReentrantReadWriteLock();
    041
    042    /**
    043     *  {@inheritDoc}
    044     */
    045    @Override
    046    public T set(final int index, final T element )
    047    {
    048        m_lock.writeLock().lock();
    049
    050        T t;
    051
    052        try
    053        {
    054            t = m_list.set(index, new CounterItem<>(element)).m_obj;
    055        }
    056        finally
    057        {
    058            m_lock.writeLock().unlock();
    059        }
    060
    061        return t;
    062    }
    063
    064    /**
    065     *  {@inheritDoc}
    066     */
    067    @Override
    068    public T get(final int index )
    069    {
    070        m_lock.readLock().lock();
    071
    072        T t;
    073
    074        try
    075        {
    076            t = m_list.get(index).m_obj;
    077        }
    078        finally
    079        {
    080            m_lock.readLock().unlock();
    081        }
    082
    083        return t;
    084    }
    085
    086    /**
    087     *  {@inheritDoc}
    088     */
    089    @Override
    090    public int size()
    091    {
    092        m_lock.readLock().lock();
    093        int size;
    094
    095        try
    096        {
    097            size = m_list.size();
    098        }
    099        finally
    100        {
    101            m_lock.readLock().unlock();
    102        }
    103
    104        return size;
    105    }
    106
    107    /**
    108     *  {@inheritDoc}
    109     */
    110    @Override
    111    public void add(final int index, final T element )
    112    {
    113        m_lock.writeLock().lock();
    114
    115        try
    116        {
    117            m_list.add(index, new CounterItem<>(element));
    118        }
    119        finally
    120        {
    121            m_lock.writeLock().unlock();
    122        }
    123    }
    124
    125    /**
    126     *  {@inheritDoc}
    127     */
    128    @Override
    129    public T remove(final int index )
    130    {
    131        m_lock.writeLock().lock();
    132        T t;
    133
    134        try
    135        {
    136            t = m_list.remove( index ).m_obj;
    137        }
    138        finally
    139        {
    140            m_lock.writeLock().unlock();
    141        }
    142
    143        return t;
    144    }
    145
    146    /**
    147     *  Returns the count how many times this object is available in
    148     *  this list, using equals().
    149     *
    150     *  @param obj The object to count.
    151     *  @return The count of the objects.
    152     */
    153    public int count(final T obj )
    154    {
    155        int c;
    156        m_lock.readLock().lock();
    157
    158        try
    159        {
    160            c = (int) m_list.stream().filter(i -> i.m_obj.equals(obj)).count();
    161        }
    162        finally
    163        {
    164            m_lock.readLock().unlock();
    165        }
    166
    167        return c;
    168    }
    169
    170    /**
    171     *  Performs a cleanup of all items older than maxage.
    172     *
    173     *  @param maxage The maximum age in milliseconds after an item is removed.
    174     */
    175    public void cleanup(final long maxage )
    176    {
    177        m_lock.writeLock().lock();
    178
    179        try
    180        {
    181            final long now = System.currentTimeMillis();
    182
    183            for(final Iterator<CounterItem<T>> i = m_list.iterator(); i.hasNext(); )
    184            {
    185                final CounterItem<T> ci = i.next();
    186
    187                final long age = now - ci.m_addTime;
    188
    189                if( age > maxage )
    190                {
    191                    i.remove();
    192                }
    193            }
    194        }
    195        finally
    196        {
    197            m_lock.writeLock().unlock();
    198        }
    199    }
    200
    201    /**
    202     *  Returns the time when this particular item was added on the list.
    203     *
    204     *  @param index The index of the object.
    205     *  @return The addition time in milliseconds (@see System.currentTimeMillis()).
    206     */
    207    public long getAddTime(final int index )
    208    {
    209        m_lock.readLock().lock();
    210        long res;
    211
    212        try
    213        {
    214            res = m_list.get( index ).m_addTime;
    215        }
    216        finally
    217        {
    218            m_lock.readLock().unlock();
    219        }
    220
    221        return res;
    222    }
    223
    224    private static class CounterItem<E>
    225    {
    226        private final E      m_obj;
    227        private final long   m_addTime;
    228
    229        public CounterItem(final E o)
    230        {
    231            m_addTime = System.currentTimeMillis();
    232            m_obj = o;
    233        }
    234    }
    235
    236
    237}