1 package emissary.directory;
2
3 import jakarta.annotation.Nullable;
4 import org.slf4j.Logger;
5 import org.slf4j.LoggerFactory;
6
7 import java.util.ArrayList;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.concurrent.ConcurrentHashMap;
11
12
13
14
15
16 public class DirectoryEntryMap extends ConcurrentHashMap<String, DirectoryEntryList> {
17
18
19 static final long serialVersionUID = 9097156614421373808L;
20
21 private static final Logger logger = LoggerFactory.getLogger(DirectoryEntryMap.class);
22
23
24 public static final boolean DEEP_COPY = true;
25
26
27 public static final boolean SHALLOW_COPY = false;
28
29
30
31
32 public DirectoryEntryMap() {
33 super(1024, 0.8f, 3);
34 }
35
36
37
38
39
40
41 public DirectoryEntryMap(final int initialCapacity) {
42 super(initialCapacity, 0.8f, 3);
43 }
44
45
46
47
48
49
50
51
52 public DirectoryEntryMap(final int initialCapacity, final float loadFactor, final int concurrencyLevel) {
53 super(initialCapacity, loadFactor, concurrencyLevel);
54 }
55
56
57
58
59
60
61 public DirectoryEntryMap(final DirectoryEntryMap map) {
62 this(map, SHALLOW_COPY);
63 }
64
65
66
67
68
69
70
71 public DirectoryEntryMap(@Nullable final DirectoryEntryMap map, final boolean deepCopy) {
72 this();
73 if (map != null) {
74 for (final Map.Entry<String, DirectoryEntryList> entry : map.entrySet()) {
75 if (deepCopy) {
76 final DirectoryEntryList dl = new DirectoryEntryList(entry.getValue(), DirectoryEntryList.DEEP_COPY);
77 this.put(entry.getKey(), dl);
78 } else {
79 this.put(entry.getKey(), entry.getValue());
80 }
81 }
82 }
83 }
84
85
86
87
88
89
90
91 public void addEntry(final DirectoryEntry d) {
92 final String dataId = KeyManipulator.getDataId(d.getKey());
93 addEntry(dataId, d);
94 }
95
96
97
98
99
100
101
102 protected void addEntry(final String key, final DirectoryEntry d) {
103 DirectoryEntryList list = get(key);
104
105 if (list == null) {
106 list = new DirectoryEntryList();
107 put(key, list);
108 }
109 final int beforeSize = list.size();
110 list.add(d);
111 final int afterSize = list.size();
112
113 if (logger.isDebugEnabled()) {
114
115
116 if (afterSize > beforeSize) {
117 logger.debug("Appears to have added entry {} list is now size {}", d.getKey(), afterSize);
118 } else {
119 logger.debug("Appear to have dropped or replaced entry {} list is still size {}", d.getKey(), afterSize);
120 }
121 }
122 }
123
124
125
126
127
128
129 public List<DirectoryEntry> allEntries() {
130 final List<DirectoryEntry> list = new ArrayList<>();
131
132 for (final DirectoryEntryList d : values()) {
133 list.addAll(d);
134 }
135 return list;
136 }
137
138
139
140
141
142
143 public List<String> allEntryKeys() {
144 final List<String> list = new ArrayList<>();
145
146 for (final DirectoryEntryList d : values()) {
147 for (final DirectoryEntry e : d) {
148 list.add(e.getFullKey());
149 }
150 }
151 return list;
152 }
153
154
155
156
157
158
159 public int entryCount() {
160 int count = 0;
161 for (final DirectoryEntryList d : values()) {
162 count += d.size();
163 }
164 return count;
165 }
166
167
168
169
170
171
172
173 public DirectoryEntry removeEntry(final String entryKey) {
174 final String dataId = KeyManipulator.getDataId(entryKey);
175 return removeEntry(dataId, entryKey);
176 }
177
178
179
180
181
182
183
184
185 protected DirectoryEntry removeEntry(final String key, final String entryKey) {
186 DirectoryEntry removed = null;
187 final DirectoryEntryList list = get(key);
188 if (list != null) {
189
190 for (int i = 0; i < list.size(); i++) {
191 final DirectoryEntry entry = list.get(i);
192 if (entry.getKey().equals(entryKey)) {
193 removed = entry;
194 list.remove(i);
195 break;
196 }
197 }
198
199
200 if (list.isEmpty()) {
201 this.remove(key);
202 }
203 }
204
205 return removed;
206 }
207
208
209
210
211
212
213
214 public List<DirectoryEntry> removeAllMatching(final String key) {
215 return removeAllMatching(key, Long.MAX_VALUE);
216 }
217
218
219
220
221
222
223
224
225
226 public List<DirectoryEntry> removeAllMatching(final String key, final long checkpoint) {
227 final List<DirectoryEntry> removed = new ArrayList<>();
228
229 for (final DirectoryEntryList list : values()) {
230
231
232 for (int i = 0; i < list.size(); i++) {
233 final DirectoryEntry entry = list.get(i);
234 if (KeyManipulator.gmatch(entry.getKey(), key) && entry.getAge() < checkpoint) {
235 removed.add(entry);
236 }
237 }
238 }
239
240 for (final DirectoryEntry d : removed) {
241 removeEntry(d.getKey());
242 }
243
244 return removed;
245 }
246
247
248
249
250
251
252
253 public List<DirectoryEntry> removeAllOnDirectory(final String key) {
254
255 final String wckey = KeyManipulator.getHostMatchKey(key);
256 return removeAllMatching(wckey);
257 }
258
259
260
261
262
263
264
265
266 public List<DirectoryEntry> removeAllOnDirectory(final String key, final long checkpoint) {
267
268 final String wckey = KeyManipulator.getHostMatchKey(key);
269 return removeAllMatching(wckey, checkpoint);
270 }
271
272
273
274
275
276
277
278 public List<DirectoryEntry> collectAllMatching(final String key) {
279 final List<DirectoryEntry> match = new ArrayList<>();
280
281 final String dataId = KeyManipulator.getDataId(key);
282 if (dataId.contains("*") || dataId.contains("?")) {
283 for (final DirectoryEntryList list : values()) {
284 for (final DirectoryEntry entry : list) {
285 if (KeyManipulator.gmatch(entry.getKey(), key)) {
286 match.add(entry);
287 }
288 }
289 }
290 } else {
291 final DirectoryEntryList list = this.get(dataId);
292 if (list != null) {
293 for (final DirectoryEntry entry : list) {
294 if (KeyManipulator.gmatch(entry.getKey(), key)) {
295 match.add(entry);
296 }
297 }
298 }
299 }
300 return match;
301 }
302
303
304
305
306
307
308
309 public List<DirectoryEntry> collectAllOnDirectory(final String key) {
310
311 final String wckey = KeyManipulator.getHostMatchKey(key);
312 return collectAllMatching(wckey);
313 }
314
315
316
317
318
319
320
321 public int countAllMatching(final String key) {
322 int count = 0;
323
324 for (final DirectoryEntryList list : values()) {
325 for (DirectoryEntry entry : list) {
326 if (KeyManipulator.gmatch(entry.getKey(), key)) {
327 count++;
328 }
329 }
330 }
331 return count;
332 }
333
334
335
336
337
338
339
340 public int countAllOnDirectory(final String key) {
341
342 final String wckey = KeyManipulator.getHostMatchKey(key);
343 return countAllMatching(wckey);
344 }
345
346
347
348
349
350
351 public void addEntries(@Nullable final List<DirectoryEntry> entryList) {
352 if (entryList != null) {
353 for (final DirectoryEntry d : entryList) {
354 addEntry(d);
355 }
356 }
357 }
358
359
360
361
362
363
364 public void addEntries(@Nullable final DirectoryEntryMap that) {
365 if (that != null) {
366 for (final Map.Entry<String, DirectoryEntryList> entry : that.entrySet()) {
367
368 DirectoryEntryList list = this.get(entry.getKey());
369 if (list == null) {
370 list = new DirectoryEntryList();
371 put(entry.getKey(), list);
372 }
373 list.addAll(entry.getValue());
374 }
375 }
376 }
377
378
379
380
381
382
383
384
385 public List<String> addCostToMatching(final String key, final int increment) {
386 final List<DirectoryEntry> list = collectAllMatching(key);
387 final List<String> ret = new ArrayList<>();
388 for (final DirectoryEntry e : list) {
389 e.addCost(increment);
390 ret.add(e.getFullKey());
391 }
392
393
394 if (!ret.isEmpty()) {
395 sort();
396 }
397
398 return ret;
399 }
400
401
402
403
404 public void sort() {
405 for (final DirectoryEntryList list : values()) {
406 list.sort();
407 }
408 }
409 }