1 /***
2 *
3 * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 **/
18 package org.logicblaze.lingo.util;
19
20 import edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService;
21 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
22
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.SortedSet;
31 import java.util.TreeSet;
32
33 /***
34 *
35 * @version $Revision$
36 */
37 public class DefaultTimeoutMap implements TimeoutMap, Runnable {
38
39 private static final Log log = LogFactory.getLog(DefaultTimeoutMap.class);
40
41 private Map map = new HashMap();
42 private SortedSet index = new TreeSet();
43 private ScheduledExecutorService executor;
44 private long purgePollTime;
45
46 public DefaultTimeoutMap() {
47 this(null, 1000L);
48 }
49
50 public DefaultTimeoutMap(ScheduledExecutorService executor, long requestMapPollTimeMillis) {
51 this.executor = executor;
52 this.purgePollTime = purgePollTime;
53 schedulePoll();
54 }
55
56 public Object get(Object key) {
57 TimeoutMapEntry entry = null;
58 synchronized (map) {
59 entry = (TimeoutMapEntry) map.get(key);
60 if (entry == null) {
61 return null;
62 }
63 index.remove(entry);
64 updateExpireTime(entry);
65 index.add(entry);
66 }
67 return entry.getValue();
68 }
69
70 public void put(Object key, Object value, long timeoutMillis) {
71 TimeoutMapEntry entry = new TimeoutMapEntry(key, value, timeoutMillis);
72 synchronized (map) {
73 Object oldValue = map.put(key, entry);
74 if (oldValue != null) {
75 index.remove(oldValue);
76 }
77 updateExpireTime(entry);
78 index.add(entry);
79 }
80 }
81
82 public void remove(Object id) {
83 synchronized (map) {
84 TimeoutMapEntry entry = (TimeoutMapEntry) map.remove(id);
85 if (entry != null) {
86 index.remove(entry);
87 }
88 }
89 }
90
91 /***
92 * Returns a copy of the keys in the map
93 */
94 public Object[] getKeys() {
95 Object[] keys = null;
96 synchronized (map) {
97 Set keySet = map.keySet();
98 keys = new String[keySet.size()];
99 keySet.toArray(keys);
100 }
101 return keys;
102 }
103
104 /***
105 * The timer task which purges old requests and schedules another poll
106 */
107 public void run() {
108 purge();
109 schedulePoll();
110 }
111
112 /***
113 * Purges any old entries from the map
114 */
115 public void purge() {
116 long now = currentTime();
117 synchronized (map) {
118 for (Iterator iter = index.iterator(); iter.hasNext();) {
119 TimeoutMapEntry entry = (TimeoutMapEntry) iter.next();
120 if (entry == null) {
121 break;
122 }
123 if (entry.getExpireTime() < now) {
124 if (isValidForEviction(entry)) {
125 if (log.isDebugEnabled()) {
126 log.debug("Evicting inactive request for correlationID: " + entry);
127 }
128 System.out.println("Evicting inactive request for correlationID: " + entry);
129 map.remove(entry.getKey());
130 iter.remove();
131 }
132 }
133 else {
134 break;
135 }
136 }
137 }
138 }
139
140
141
142 public long getPurgePollTime() {
143 return purgePollTime;
144 }
145
146 /***
147 * Sets the next purge poll time in milliseconds
148 */
149 public void setPurgePollTime(long purgePollTime) {
150 this.purgePollTime = purgePollTime;
151 }
152
153 public ScheduledExecutorService getExecutor() {
154 return executor;
155 }
156
157 /***
158 * Sets the executor used to schedule purge events of inactive requests
159 */
160 public void setExecutor(ScheduledExecutorService executor) {
161 this.executor = executor;
162 }
163
164
165
166
167 /***
168 * lets schedule each time to allow folks to change the time at runtime
169 */
170 protected void schedulePoll() {
171 if (executor != null) {
172 executor.schedule(this, purgePollTime, TimeUnit.MILLISECONDS);
173 }
174 }
175
176 /***
177 * A hook to allow derivations to avoid evicting the current entry
178 *
179 * @param entry
180 * @return
181 */
182 protected boolean isValidForEviction(TimeoutMapEntry entry) {
183 return true;
184 }
185
186 protected void updateExpireTime(TimeoutMapEntry entry) {
187 long now = currentTime();
188 entry.setExpireTime(entry.getTimeout() + now);
189 }
190
191 protected long currentTime() {
192 return System.currentTimeMillis();
193 }
194 }