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<CounterItem<T>>(); 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<T>(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 = 0; 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<T>(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 = 0; 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}