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     *
033     * @version $Revision: 642753 $
034     */
035    public class DefaultTimeoutMap implements TimeoutMap, Runnable {
036    
037        private static final Log LOG = LogFactory.getLog(DefaultTimeoutMap.class);
038    
039        private Map map = new HashMap();
040        private SortedSet index = new TreeSet();
041        private ScheduledExecutorService executor;
042        private long purgePollTime;
043    
044        public DefaultTimeoutMap() {
045            this(null, 1000L);
046        }
047    
048        public DefaultTimeoutMap(ScheduledExecutorService executor, long requestMapPollTimeMillis) {
049            this.executor = executor;
050            this.purgePollTime = requestMapPollTimeMillis;
051            schedulePoll();
052        }
053    
054        public Object get(Object key) {
055            TimeoutMapEntry entry = null;
056            synchronized (map) {
057                entry = (TimeoutMapEntry) map.get(key);
058                if (entry == null) {
059                    return null;
060                }
061                index.remove(entry);
062                updateExpireTime(entry);
063                index.add(entry);
064            }
065            return entry.getValue();
066        }
067    
068        public void put(Object key, Object value, long timeoutMillis) {
069            TimeoutMapEntry entry = new TimeoutMapEntry(key, value, timeoutMillis);
070            synchronized (map) {
071                Object oldValue = map.put(key, entry);
072                if (oldValue != null) {
073                    index.remove(oldValue);
074                }
075                updateExpireTime(entry);
076                index.add(entry);
077            }
078        }
079    
080        public void remove(Object id) {
081            synchronized (map) {
082                TimeoutMapEntry entry = (TimeoutMapEntry) map.remove(id);
083                if (entry != null) {
084                    index.remove(entry);
085                }
086            }
087        }
088    
089        /**
090         * Returns a copy of the keys in the map
091         */
092        public Object[] getKeys() {
093            Object[] keys = null;
094            synchronized (map) {
095                Set keySet = map.keySet();
096                keys = new String[keySet.size()];
097                keySet.toArray(keys);
098            }
099            return keys;
100        }
101    
102        /**
103         * The timer task which purges old requests and schedules another poll
104         */
105        public void run() {
106            purge();
107            schedulePoll();
108        }
109    
110        /**
111         * Purges any old entries from the map
112         */
113        public void purge() {
114            long now = currentTime();
115            synchronized (map) {
116                for (Iterator iter = index.iterator(); iter.hasNext();) {
117                    TimeoutMapEntry entry = (TimeoutMapEntry) iter.next();
118                    if (entry == null) {
119                        break;
120                    }
121                    if (entry.getExpireTime() < now) {
122                        if (isValidForEviction(entry)) {
123                            if (LOG.isDebugEnabled()) {
124                                LOG.debug("Evicting inactive request for correlationID: " + entry);
125                            }
126                            map.remove(entry.getKey());
127                            iter.remove();
128                        }
129                    } else {
130                        break;
131                    }
132                }
133            }
134        }
135    
136        // Properties
137        // -------------------------------------------------------------------------
138        public long getPurgePollTime() {
139            return purgePollTime;
140        }
141    
142        /**
143         * Sets the next purge poll time in milliseconds
144         */
145        public void setPurgePollTime(long purgePollTime) {
146            this.purgePollTime = purgePollTime;
147        }
148    
149        public ScheduledExecutorService getExecutor() {
150            return executor;
151        }
152    
153        /**
154         * Sets the executor used to schedule purge events of inactive requests
155         */
156        public void setExecutor(ScheduledExecutorService executor) {
157            this.executor = executor;
158        }
159    
160        // Implementation methods
161        // -------------------------------------------------------------------------
162    
163        /**
164         * lets schedule each time to allow folks to change the time at runtime
165         */
166        protected void schedulePoll() {
167            if (executor != null) {
168                executor.schedule(this, purgePollTime, TimeUnit.MILLISECONDS);
169            }
170        }
171    
172        /**
173         * A hook to allow derivations to avoid evicting the current entry
174         */
175        protected boolean isValidForEviction(TimeoutMapEntry entry) {
176            return true;
177        }
178    
179        protected void updateExpireTime(TimeoutMapEntry entry) {
180            long now = currentTime();
181            entry.setExpireTime(entry.getTimeout() + now);
182        }
183    
184        protected long currentTime() {
185            return System.currentTimeMillis();
186        }
187    }