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 = 0;
156        m_lock.readLock().lock();
157        
158        try
159        {
160            for( final CounterItem< T > i : m_list )
161            {
162                if( i.m_obj.equals( obj ) )
163                {
164                    c++;
165                }
166            }
167        }
168        finally
169        {
170            m_lock.readLock().unlock();
171        }
172        
173        return c;
174    }
175    
176    /**
177     *  Performs a cleanup of all items older than maxage.
178     *  
179     *  @param maxage The maximum age in milliseconds after an item is removed.
180     */
181    public void cleanup(final long maxage )
182    {
183        m_lock.writeLock().lock();
184        
185        try
186        {
187            final long now = System.currentTimeMillis();
188        
189            for(final Iterator<CounterItem<T>> i = m_list.iterator(); i.hasNext(); )
190            {
191                final CounterItem<T> ci = i.next();
192            
193                final long age = now - ci.m_addTime;
194            
195                if( age > maxage )
196                {
197                    i.remove();
198                }
199            }
200        }
201        finally
202        {
203            m_lock.writeLock().unlock();
204        }
205    }
206    
207    /**
208     *  Returns the time when this particular item was added on the list.
209     *  
210     *  @param index The index of the object.
211     *  @return The addition time in milliseconds (@see System.currentTimeMillis()).
212     */
213    public long getAddTime(final int index )
214    {
215        m_lock.readLock().lock();
216        long res;
217        
218        try
219        {
220            res = m_list.get( index ).m_addTime;
221        }
222        finally
223        {
224            m_lock.readLock().unlock();
225        }
226        
227        return res;
228    }
229    
230    private static class CounterItem<E>
231    {
232        private final E      m_obj;
233        private final long   m_addTime;
234        
235        public CounterItem(final E o)
236        {
237            m_addTime = System.currentTimeMillis();
238            m_obj = o;
239        }
240    }
241
242
243}