blob: 15f4048ecaf1ef367ac00d6d8a1ac0843470d50e [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.util;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Default implementation of the {@link TimeoutMap}.
*
* @version $Revision$
*/
public class DefaultTimeoutMap implements TimeoutMap, Runnable {
private static final transient Log LOG = LogFactory.getLog(DefaultTimeoutMap.class);
private final Map map = new HashMap();
private SortedSet index = new TreeSet();
private ScheduledExecutorService executor;
private long purgePollTime;
public DefaultTimeoutMap() {
this(null, 1000L);
}
public DefaultTimeoutMap(ScheduledExecutorService executor, long requestMapPollTimeMillis) {
this.executor = executor;
this.purgePollTime = requestMapPollTimeMillis;
schedulePoll();
}
@SuppressWarnings("unchecked")
public Object get(Object key) {
TimeoutMapEntry entry = null;
synchronized (map) {
entry = (TimeoutMapEntry) map.get(key);
if (entry == null) {
return null;
}
index.remove(entry);
updateExpireTime(entry);
index.add(entry);
}
return entry.getValue();
}
@SuppressWarnings("unchecked")
public void put(Object key, Object value, long timeoutMillis) {
TimeoutMapEntry entry = new TimeoutMapEntry(key, value, timeoutMillis);
synchronized (map) {
Object oldValue = map.put(key, entry);
if (oldValue != null) {
index.remove(oldValue);
}
updateExpireTime(entry);
index.add(entry);
}
}
public void remove(Object id) {
synchronized (map) {
TimeoutMapEntry entry = (TimeoutMapEntry) map.remove(id);
if (entry != null) {
index.remove(entry);
}
}
}
@SuppressWarnings("unchecked")
public Object[] getKeys() {
Object[] keys = null;
synchronized (map) {
Set keySet = map.keySet();
keys = new Object[keySet.size()];
keySet.toArray(keys);
}
return keys;
}
public int size() {
synchronized (map) {
return map.size();
}
}
/**
* The timer task which purges old requests and schedules another poll
*/
public void run() {
purge();
schedulePoll();
}
public void purge() {
long now = currentTime();
synchronized (map) {
for (Iterator iter = index.iterator(); iter.hasNext();) {
TimeoutMapEntry entry = (TimeoutMapEntry) iter.next();
if (entry == null) {
break;
}
if (entry.getExpireTime() < now) {
if (isValidForEviction(entry)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Evicting inactive request for correlationID: " + entry);
}
map.remove(entry.getKey());
iter.remove();
}
} else {
break;
}
}
}
}
// Properties
// -------------------------------------------------------------------------
public long getPurgePollTime() {
return purgePollTime;
}
/**
* Sets the next purge poll time in milliseconds
*/
public void setPurgePollTime(long purgePollTime) {
this.purgePollTime = purgePollTime;
}
public ScheduledExecutorService getExecutor() {
return executor;
}
/**
* Sets the executor used to schedule purge events of inactive requests
*/
public void setExecutor(ScheduledExecutorService executor) {
this.executor = executor;
}
// Implementation methods
// -------------------------------------------------------------------------
/**
* lets schedule each time to allow folks to change the time at runtime
*/
protected void schedulePoll() {
if (executor != null) {
executor.schedule(this, purgePollTime, TimeUnit.MILLISECONDS);
}
}
/**
* A hook to allow derivations to avoid evicting the current entry
*/
protected boolean isValidForEviction(TimeoutMapEntry entry) {
return true;
}
protected void updateExpireTime(TimeoutMapEntry entry) {
long now = currentTime();
entry.setExpireTime(entry.getTimeout() + now);
}
protected long currentTime() {
return System.currentTimeMillis();
}
}