001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.util;
018
019 import java.util.HashMap;
020 import java.util.Iterator;
021 import java.util.Map;
022 import java.util.Set;
023 import java.util.SortedSet;
024 import java.util.TreeSet;
025 import java.util.concurrent.ScheduledExecutorService;
026 import java.util.concurrent.TimeUnit;
027
028 import org.apache.commons.logging.Log;
029 import org.apache.commons.logging.LogFactory;
030
031 /**
032 * Default implementation of the {@link TimeoutMap}.
033 *
034 * @version $Revision: 747062 $
035 */
036 public class DefaultTimeoutMap implements TimeoutMap, Runnable {
037
038 private static final transient Log LOG = LogFactory.getLog(DefaultTimeoutMap.class);
039
040 private final Map map = new HashMap();
041 private SortedSet index = new TreeSet();
042 private ScheduledExecutorService executor;
043 private long purgePollTime;
044
045 public DefaultTimeoutMap() {
046 this(null, 1000L);
047 }
048
049 public DefaultTimeoutMap(ScheduledExecutorService executor, long requestMapPollTimeMillis) {
050 this.executor = executor;
051 this.purgePollTime = requestMapPollTimeMillis;
052 schedulePoll();
053 }
054
055 @SuppressWarnings("unchecked")
056 public Object get(Object key) {
057 TimeoutMapEntry entry = null;
058 synchronized (map) {
059 entry = (TimeoutMapEntry) map.get(key);
060 if (entry == null) {
061 return null;
062 }
063 index.remove(entry);
064 updateExpireTime(entry);
065 index.add(entry);
066 }
067 return entry.getValue();
068 }
069
070 @SuppressWarnings("unchecked")
071 public void put(Object key, Object value, long timeoutMillis) {
072 TimeoutMapEntry entry = new TimeoutMapEntry(key, value, timeoutMillis);
073 synchronized (map) {
074 Object oldValue = map.put(key, entry);
075 if (oldValue != null) {
076 index.remove(oldValue);
077 }
078 updateExpireTime(entry);
079 index.add(entry);
080 }
081 }
082
083 public void remove(Object id) {
084 synchronized (map) {
085 TimeoutMapEntry entry = (TimeoutMapEntry) map.remove(id);
086 if (entry != null) {
087 index.remove(entry);
088 }
089 }
090 }
091
092 @SuppressWarnings("unchecked")
093 public Object[] getKeys() {
094 Object[] keys = null;
095 synchronized (map) {
096 Set keySet = map.keySet();
097 keys = new Object[keySet.size()];
098 keySet.toArray(keys);
099 }
100 return keys;
101 }
102
103 public int size() {
104 synchronized (map) {
105 return map.size();
106 }
107 }
108
109 /**
110 * The timer task which purges old requests and schedules another poll
111 */
112 public void run() {
113 purge();
114 schedulePoll();
115 }
116
117 public void purge() {
118 long now = currentTime();
119 synchronized (map) {
120 for (Iterator iter = index.iterator(); iter.hasNext();) {
121 TimeoutMapEntry entry = (TimeoutMapEntry) iter.next();
122 if (entry == null) {
123 break;
124 }
125 if (entry.getExpireTime() < now) {
126 if (isValidForEviction(entry)) {
127 if (LOG.isDebugEnabled()) {
128 LOG.debug("Evicting inactive request for correlationID: " + entry);
129 }
130 map.remove(entry.getKey());
131 iter.remove();
132 }
133 } else {
134 break;
135 }
136 }
137 }
138 }
139
140 // Properties
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 // Implementation methods
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 protected boolean isValidForEviction(TimeoutMapEntry entry) {
180 return true;
181 }
182
183 protected void updateExpireTime(TimeoutMapEntry entry) {
184 long now = currentTime();
185 entry.setExpireTime(entry.getTimeout() + now);
186 }
187
188 protected long currentTime() {
189 return System.currentTimeMillis();
190 }
191 }