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    }