All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.camel.support.DefaultTimeoutMap Maven / Gradle / Ivy

There is a newer version: 4.6.0
Show newest version
/**
 * 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.support;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.camel.TimeoutMap;
import org.apache.camel.util.ObjectHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Default implementation of the {@link TimeoutMap}.
 * 

* This implementation supports thread safe and non thread safe, in the manner you can enable locking or not. * By default locking is enabled and thus we are thread safe. *

* You must provide a {@link java.util.concurrent.ScheduledExecutorService} in the constructor which is used * to schedule a background task which check for old entries to purge. This implementation will shutdown the scheduler * if its being stopped. * You must also invoke {@link #start()} to startup the timeout map, before its ready to be used. * And you must invoke {@link #stop()} to stop the map when no longer in use. * * @version */ public class DefaultTimeoutMap extends ServiceSupport implements TimeoutMap, Runnable { protected final Logger log = LoggerFactory.getLogger(getClass()); private final ConcurrentMap> map = new ConcurrentHashMap>(); private final ScheduledExecutorService executor; private volatile ScheduledFuture future; private final long purgePollTime; private final Lock lock = new ReentrantLock(); private boolean useLock = true; public DefaultTimeoutMap(ScheduledExecutorService executor) { this(executor, 1000); } public DefaultTimeoutMap(ScheduledExecutorService executor, long requestMapPollTimeMillis) { this(executor, requestMapPollTimeMillis, true); } public DefaultTimeoutMap(ScheduledExecutorService executor, long requestMapPollTimeMillis, boolean useLock) { ObjectHelper.notNull(executor, "ScheduledExecutorService"); this.executor = executor; this.purgePollTime = requestMapPollTimeMillis; this.useLock = useLock; } public V get(K key) { TimeoutMapEntry entry; if (useLock) { lock.lock(); } try { entry = map.get(key); if (entry == null) { return null; } updateExpireTime(entry); } finally { if (useLock) { lock.unlock(); } } return entry.getValue(); } public V put(K key, V value, long timeoutMillis) { TimeoutMapEntry entry = new TimeoutMapEntry(key, value, timeoutMillis); if (useLock) { lock.lock(); } try { updateExpireTime(entry); TimeoutMapEntry result = map.put(key, entry); return result != null ? result.getValue() : null; } finally { if (useLock) { lock.unlock(); } } } public V putIfAbsent(K key, V value, long timeoutMillis) { TimeoutMapEntry entry = new TimeoutMapEntry(key, value, timeoutMillis); if (useLock) { lock.lock(); } try { updateExpireTime(entry); //Just make sure we don't override the old entry TimeoutMapEntry result = map.putIfAbsent(key, entry); return result != null ? result.getValue() : null; } finally { if (useLock) { lock.unlock(); } } } public V remove(K key) { TimeoutMapEntry entry; if (useLock) { lock.lock(); } try { entry = map.remove(key); } finally { if (useLock) { lock.unlock(); } } return entry != null ? entry.getValue() : null; } public Object[] getKeys() { Object[] keys; if (useLock) { lock.lock(); } try { Set keySet = map.keySet(); keys = new Object[keySet.size()]; keySet.toArray(keys); } finally { if (useLock) { lock.unlock(); } } return keys; } public int size() { return map.size(); } /** * The timer task which purges old requests and schedules another poll */ public void run() { // only run if allowed if (!isRunAllowed()) { log.trace("Purge task not allowed to run"); return; } log.trace("Running purge task to see if any entries has been timed out"); try { purge(); } catch (Throwable t) { // must catch and log exception otherwise the executor will now schedule next run log.warn("Exception occurred during purge task. This exception will be ignored.", t); } } public void purge() { log.trace("There are {} in the timeout map", map.size()); if (map.isEmpty()) { return; } long now = currentTime(); List> expired = new ArrayList>(); if (useLock) { lock.lock(); } try { // need to find the expired entries and add to the expired list for (Map.Entry> entry : map.entrySet()) { if (entry.getValue().getExpireTime() < now) { if (isValidForEviction(entry.getValue())) { log.debug("Evicting inactive entry ID: {}", entry.getValue()); expired.add(entry.getValue()); } } } // if we found any expired then we need to sort, onEviction and remove if (!expired.isEmpty()) { // sort according to the expired time so we got the first expired first expired.sort(new Comparator>() { public int compare(TimeoutMapEntry a, TimeoutMapEntry b) { long diff = a.getExpireTime() - b.getExpireTime(); if (diff == 0) { return 0; } return diff > 0 ? 1 : -1; } }); List evicts = new ArrayList(expired.size()); try { // now fire eviction notification for (TimeoutMapEntry entry : expired) { boolean evict = false; try { evict = onEviction(entry.getKey(), entry.getValue()); } catch (Throwable t) { log.warn("Exception happened during eviction of entry ID {}, won't evict and will continue trying: {}", entry.getValue(), t); } if (evict) { // okay this entry should be evicted evicts.add(entry.getKey()); } } } finally { // and must remove from list after we have fired the notifications for (K key : evicts) { map.remove(key); } } } } finally { if (useLock) { lock.unlock(); } } } // Properties // ------------------------------------------------------------------------- public long getPurgePollTime() { return purgePollTime; } public ScheduledExecutorService getExecutor() { return executor; } // Implementation methods // ------------------------------------------------------------------------- /** * lets schedule each time to allow folks to change the time at runtime */ protected void schedulePoll() { future = executor.scheduleWithFixedDelay(this, 0, purgePollTime, TimeUnit.MILLISECONDS); } /** * A hook to allow derivations to avoid evicting the current entry */ protected boolean isValidForEviction(TimeoutMapEntry entry) { return true; } public boolean onEviction(K key, V value) { return true; } protected void updateExpireTime(TimeoutMapEntry entry) { long now = currentTime(); entry.setExpireTime(entry.getTimeout() + now); } protected long currentTime() { return System.currentTimeMillis(); } @Override protected void doStart() throws Exception { if (executor.isShutdown()) { throw new IllegalStateException("The ScheduledExecutorService is shutdown"); } schedulePoll(); } @Override protected void doStop() throws Exception { if (future != null) { future.cancel(false); future = null; } // clear map if we stop map.clear(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy