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 }