| /* |
| * Copyright 2016-present Open Networking Laboratory |
| * |
| * Licensed 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.onosproject.lisp.ctl.impl.map; |
| |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Set; |
| import java.util.Timer; |
| import java.util.TimerTask; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.ConcurrentMap; |
| import java.util.concurrent.locks.Lock; |
| import java.util.concurrent.locks.ReentrantLock; |
| |
| /** |
| * Default implementation of ExpireMap. |
| */ |
| public class ExpireHashMap<K, V> implements ExpireMap<K, V> { |
| |
| private final Logger log = LoggerFactory.getLogger(getClass()); |
| |
| private static final long DEFAULT_TTL = 60000L; |
| private final ConcurrentMap<K, ExpiredObject<K, V>> map = new ConcurrentHashMap<>(); |
| private final Lock writeLock = new ReentrantLock(); |
| private final Timer timer = new Timer("ExpireMapTimer", true); |
| |
| /** |
| * An expired object that associates with a TimerTask instance. |
| * |
| * @param <K1> key type K1 |
| * @param <V1> value type V1 |
| */ |
| class ExpiredObject<K1, V1> { |
| private final V1 value; |
| private final ExpiryTask<K1> task; |
| private final long ttl; |
| |
| public ExpiredObject(K1 key, V1 value) { |
| this(key, value, DEFAULT_TTL); |
| } |
| |
| ExpiredObject(K1 key, V1 value, long ttl) { |
| this.value = value; |
| this.task = new ExpiryTask<>(key); |
| this.ttl = ttl; |
| timer.schedule(this.task, ttl); |
| } |
| |
| ExpiryTask<K1> getTask() { |
| return task; |
| } |
| |
| V1 getValue() { |
| return value; |
| } |
| |
| long getTtl() { |
| return ttl; |
| } |
| } |
| |
| /** |
| * A TimerTask that removes its associated map entry from the internal map. |
| * |
| * @param <K2> object key |
| */ |
| class ExpiryTask<K2> extends TimerTask { |
| private final K2 key; |
| |
| ExpiryTask(K2 key) { |
| this.key = key; |
| } |
| |
| K2 getKey() { |
| return key; |
| } |
| |
| @Override |
| public void run() { |
| log.info("Removing element with key [{}]", key); |
| try { |
| writeLock.lock(); |
| if (map.containsKey(key)) { |
| map.remove(getKey()); |
| } |
| } finally { |
| writeLock.unlock(); |
| } |
| } |
| } |
| |
| @Override |
| public void put(K key, V value, long expireMs) { |
| try { |
| writeLock.lock(); |
| |
| // if we have a value which is previously associated with the given |
| // key, we simply replace it with new value, and invalidate the |
| // previously associated value |
| final ExpiredObject<K, V> object = |
| map.putIfAbsent(key, new ExpiredObject<>(key, value, expireMs)); |
| |
| if (object != null) { |
| object.getTask().cancel(); |
| } |
| } finally { |
| writeLock.unlock(); |
| } |
| } |
| |
| @Override |
| public void put(K key, V value) { |
| put(key, value, DEFAULT_TTL); |
| } |
| |
| @Override |
| public V get(K key) { |
| return map.containsKey(key) ? map.get(key).getValue() : null; |
| } |
| |
| @Override |
| public void clear() { |
| try { |
| writeLock.lock(); |
| for (ExpiredObject<K, V> object : map.values()) { |
| object.getTask().cancel(); |
| } |
| map.clear(); |
| timer.purge(); |
| } finally { |
| writeLock.unlock(); |
| } |
| } |
| |
| @Override |
| public boolean containsKey(K key) { |
| return map.containsKey(key); |
| } |
| |
| @Override |
| public Set<K> keySet() { |
| return map.keySet(); |
| } |
| |
| @Override |
| public Collection<V> values() { |
| Collection<V> values = Collections.emptyList(); |
| map.values().forEach(v -> values.add(v.getValue())); |
| return values; |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return map.isEmpty(); |
| } |
| |
| @Override |
| public V remove(K key) { |
| final ExpiredObject<K, V> object; |
| try { |
| writeLock.lock(); |
| object = map.remove(key); |
| if (object != null) { |
| object.getTask().cancel(); |
| } |
| } finally { |
| writeLock.unlock(); |
| } |
| return (object == null ? null : object.getValue()); |
| } |
| |
| @Override |
| public int size() { |
| return map.size(); |
| } |
| } |