blob: 08f7c88232499d5fd1d28fc566487569e68fb0ce [file] [log] [blame]
Jian Li29986d82016-12-01 03:25:12 +09001/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Jian Li5e505c62016-12-05 02:44:24 +090016package org.onosproject.lisp.ctl.impl.map;
Jian Li29986d82016-12-01 03:25:12 +090017
18import org.slf4j.Logger;
19import org.slf4j.LoggerFactory;
20
21import java.util.Set;
22import java.util.Timer;
23import java.util.TimerTask;
24import java.util.concurrent.ConcurrentHashMap;
25import java.util.concurrent.ConcurrentMap;
26import java.util.concurrent.locks.Lock;
27import java.util.concurrent.locks.ReentrantLock;
28
29/**
30 * Default implementation of ExpireMap.
31 */
32public class ExpireHashMap<K, V> implements ExpireMap<K, V> {
33
34 private final Logger log = LoggerFactory.getLogger(getClass());
35
36 private static final long DEFAULT_TTL = 60000L;
37 private final ConcurrentMap<K, ExpiredObject<K, V>> map = new ConcurrentHashMap<>();
38 private final Lock writeLock = new ReentrantLock();
39 private final Timer timer = new Timer("ExpireMapTimer", true);
40
41 /**
42 * An expired object that associates with a TimerTask instance.
43 *
44 * @param <K1> key type K1
45 * @param <V1> value type V1
46 */
47 class ExpiredObject<K1, V1> {
48 private final V1 value;
49 private final ExpiryTask<K1> task;
50 private final long ttl;
51
52 public ExpiredObject(K1 key, V1 value) {
53 this(key, value, DEFAULT_TTL);
54 }
55
56 ExpiredObject(K1 key, V1 value, long ttl) {
57 this.value = value;
58 this.task = new ExpiryTask<>(key);
59 this.ttl = ttl;
60 timer.schedule(this.task, ttl);
61 }
62
63 ExpiryTask<K1> getTask() {
64 return task;
65 }
66
67 V1 getValue() {
68 return value;
69 }
70
71 long getTtl() {
72 return ttl;
73 }
74 }
75
76 /**
77 * A TimerTask that removes its associated map entry from the internal map.
78 *
79 * @param <K2> object key
80 */
81 class ExpiryTask<K2> extends TimerTask {
82 private final K2 key;
83
84 ExpiryTask(K2 key) {
85 this.key = key;
86 }
87
88 K2 getKey() {
89 return key;
90 }
91
92 @Override
93 public void run() {
94 log.info("Removing element with key [{}]", key);
95 try {
96 writeLock.lock();
97 if (map.containsKey(key)) {
98 map.remove(getKey());
99 }
100 } finally {
101 writeLock.unlock();
102 }
103 }
104 }
105
106 @Override
107 public void put(K key, V value, long expireMs) {
108 try {
109 writeLock.lock();
110
111 // if we have a value which is previously associated with the given
112 // key, we simply replace it with new value, and invalidate the
113 // previously associated value
114 final ExpiredObject<K, V> object =
115 map.putIfAbsent(key, new ExpiredObject<>(key, value, expireMs));
116
117 if (object != null) {
118 object.getTask().cancel();
119 }
120 } finally {
121 writeLock.unlock();
122 }
123 }
124
125 @Override
126 public void put(K key, V value) {
127 put(key, value, DEFAULT_TTL);
128 }
129
130 @Override
131 public V get(K key) {
132 return map.containsKey(key) ? map.get(key).getValue() : null;
133 }
134
135 @Override
136 public void clear() {
137 try {
138 writeLock.lock();
139 for (ExpiredObject<K, V> object : map.values()) {
140 object.getTask().cancel();
141 }
142 map.clear();
143 timer.purge();
144 } finally {
145 writeLock.unlock();
146 }
147 }
148
149 @Override
150 public boolean containsKey(K key) {
151 return map.containsKey(key);
152 }
153
154 @Override
155 public Set<K> keySet() {
156 return map.keySet();
157 }
158
159 @Override
160 public boolean isEmpty() {
161 return map.isEmpty();
162 }
163
164 @Override
165 public V remove(K key) {
166 final ExpiredObject<K, V> object;
167 try {
168 writeLock.lock();
169 object = map.remove(key);
170 if (object != null) {
171 object.getTask().cancel();
172 }
173 } finally {
174 writeLock.unlock();
175 }
176 return (object == null ? null : object.getValue());
177 }
178
179 @Override
180 public int size() {
181 return map.size();
182 }
183}