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}