Skip to content

Commit 341f0dd

Browse files
committed
#5127 Maintain case for proxy meta attributes when logging
1 parent 47dff1b commit 341f0dd

File tree

4 files changed

+147
-78
lines changed

4 files changed

+147
-78
lines changed
Lines changed: 64 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
package stroom.meta.api;
22

3-
import java.io.Serializable;
43
import java.util.Collection;
54
import java.util.HashMap;
65
import java.util.HashSet;
76
import java.util.Locale;
87
import java.util.Map;
98
import java.util.Objects;
109
import java.util.Set;
11-
import java.util.function.Function;
1210
import java.util.stream.Collectors;
1311

1412
/**
1513
* String hash map that does not care about key case.
1614
*/
1715
class CIStringHashMap implements Map<String, String> {
1816

19-
private final HashMap<CIString, String> map = new HashMap<>();
17+
private final HashMap<String, KV> map = new HashMap<>();
2018

2119
@Override
2220
public void clear() {
@@ -25,50 +23,69 @@ public void clear() {
2523

2624
@Override
2725
public boolean containsKey(final Object key) {
28-
return map.containsKey(new CIString((String) key));
26+
return map.containsKey(toLowerCase(key));
2927
}
3028

3129
@Override
3230
public boolean containsValue(final Object value) {
33-
return map.containsValue(value);
31+
return map.entrySet().stream().anyMatch(entry -> Objects.equals(entry.getValue().getValue(), value));
32+
}
33+
34+
private String toLowerCase(final Object key) {
35+
return ((String) key).toLowerCase(Locale.ROOT);
36+
}
37+
38+
private String getKey(final KV kv) {
39+
if (kv == null) {
40+
return null;
41+
}
42+
return kv.getKey();
43+
}
44+
45+
private String getValue(final KV kv) {
46+
if (kv == null) {
47+
return null;
48+
}
49+
return kv.getValue();
3450
}
3551

3652
@Override
3753
public String get(final Object key) {
38-
return map.get(new CIString((String) key));
54+
return getValue(map.get(toLowerCase(key)));
55+
}
56+
57+
public String getKey(final String key) {
58+
return getKey(map.get(toLowerCase(key)));
59+
}
60+
61+
public KV getEntry(final String key) {
62+
return map.get(toLowerCase(key));
3963
}
4064

4165
@Override
4266
public String getOrDefault(final Object key, final String defaultVal) {
43-
final String val = map.get(new CIString((String) key));
44-
return val == null
67+
final KV kv = map.get(toLowerCase(key));
68+
return kv == null
4569
? defaultVal
46-
: val;
70+
: kv.getKey();
4771
}
4872

4973
@Override
5074
public boolean isEmpty() {
5175
return map.isEmpty();
5276
}
5377

54-
public String computeIfAbsent(final String key, final Function<String, String> mappingFunction) {
55-
return map.computeIfAbsent(new CIString(key), k -> mappingFunction.apply(k.key));
56-
}
57-
5878
@Override
5979
public String put(final String key, String value) {
6080
if (value != null) {
6181
value = value.trim();
6282
}
63-
final CIString newKey = new CIString(key);
64-
final String oldValue = map.remove(newKey);
65-
map.put(newKey, value);
66-
return oldValue;
83+
return getValue(map.put(toLowerCase(key), new KV(key, value)));
6784
}
6885

6986
@Override
7087
public String remove(final Object key) {
71-
return map.remove(new CIString((String) key));
88+
return getValue(map.remove(toLowerCase(key)));
7289
}
7390

7491
@Override
@@ -82,9 +99,9 @@ public int size() {
8299
*/
83100
@Override
84101
public Set<Entry<String, String>> entrySet() {
85-
final Set<Entry<String, String>> rtnSet = new HashSet<>();
86-
for (final Entry<CIString, String> entry : map.entrySet()) {
87-
rtnSet.add(new CIEntryAdaptor(entry));
102+
final Set<Entry<String, String>> rtnSet = new HashSet<>(map.size());
103+
for (final KV kv : map.values()) {
104+
rtnSet.add(Map.entry(kv.getKey(), kv.getValue()));
88105
}
89106
return rtnSet;
90107
}
@@ -95,9 +112,9 @@ public Set<Entry<String, String>> entrySet() {
95112
*/
96113
@Override
97114
public Set<String> keySet() {
98-
final Set<String> rtnSet = new HashSet<>();
99-
for (final CIString entry : map.keySet()) {
100-
rtnSet.add(entry.key);
115+
final Set<String> rtnSet = new HashSet<>(map.size());
116+
for (final KV kv : map.values()) {
117+
rtnSet.add(kv.getKey());
101118
}
102119
return rtnSet;
103120
}
@@ -111,7 +128,7 @@ public void putAll(final Map<? extends String, ? extends String> m) {
111128

112129
@Override
113130
public Collection<String> values() {
114-
return map.values();
131+
return map.values().stream().map(KV::getValue).collect(Collectors.toSet());
115132
}
116133

117134
@Override
@@ -141,32 +158,36 @@ public int hashCode() {
141158
* will be left in their original case.
142159
*/
143160
public Map<String, String> asMap(final boolean normaliseKeys) {
144-
final Function<Entry<CIString, String>, String> keyMapper = normaliseKeys
145-
? entry -> entry.getKey().lowerKey
146-
: entry -> entry.getKey().key;
147-
148-
return map.entrySet()
161+
if (normaliseKeys) {
162+
return map
163+
.values()
164+
.stream()
165+
.collect(Collectors.toMap(kv -> kv.getKey().toLowerCase(Locale.ROOT), KV::getValue));
166+
}
167+
return map
168+
.values()
149169
.stream()
150-
.collect(Collectors.toMap(keyMapper, Entry::getValue));
170+
.collect(Collectors.toMap(KV::getKey, KV::getValue));
151171
}
152172

153-
// --------------------------------------------------------------------------------
154-
155-
156-
protected static class CIString implements Comparable<CIString>, Serializable {
173+
public static class KV {
157174

158175
private final String key;
159-
private final String lowerKey;
176+
private final String value;
160177

161-
CIString(final String key) {
162-
this.key = key.trim();
163-
this.lowerKey = this.key.toLowerCase(Locale.ENGLISH);
178+
public KV(final String key, final String value) {
179+
this.key = key;
180+
this.value = value;
164181
}
165182

166183
public String getKey() {
167184
return key;
168185
}
169186

187+
public String getValue() {
188+
return value;
189+
}
190+
170191
@Override
171192
public boolean equals(final Object o) {
172193
if (this == o) {
@@ -175,51 +196,18 @@ public boolean equals(final Object o) {
175196
if (o == null || getClass() != o.getClass()) {
176197
return false;
177198
}
178-
final CIString ciString = (CIString) o;
179-
return lowerKey.equals(ciString.lowerKey);
199+
final KV kv = (KV) o;
200+
return Objects.equals(value, kv.value);
180201
}
181202

182203
@Override
183204
public int hashCode() {
184-
return lowerKey.hashCode();
185-
}
186-
187-
@Override
188-
public int compareTo(final CIString o) {
189-
return lowerKey.compareTo(o.lowerKey);
205+
return Objects.hashCode(value);
190206
}
191207

192208
@Override
193209
public String toString() {
194-
return key;
195-
}
196-
}
197-
198-
199-
// --------------------------------------------------------------------------------
200-
201-
202-
private static class CIEntryAdaptor implements Entry<String, String> {
203-
204-
private final Entry<CIString, String> realEntry;
205-
206-
private CIEntryAdaptor(final Entry<CIString, String> realEntry) {
207-
this.realEntry = realEntry;
208-
}
209-
210-
@Override
211-
public String getKey() {
212-
return realEntry.getKey().key;
213-
}
214-
215-
@Override
216-
public String getValue() {
217-
return realEntry.getValue();
218-
}
219-
220-
@Override
221-
public String setValue(final String value) {
222-
return realEntry.setValue(value);
210+
return value;
223211
}
224212
}
225213
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package stroom.meta.api;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.assertj.core.api.Assertions.assertThat;
6+
7+
public class TestCIStringHashMap {
8+
9+
@Test
10+
void testKey() {
11+
final CIStringHashMap map = new CIStringHashMap();
12+
map.put("Test", "Test");
13+
assertThat(map.get("test")).isEqualTo("Test");
14+
assertThat(map.getKey("test")).isEqualTo("Test");
15+
}
16+
}

stroom-proxy/stroom-proxy-repo/src/main/java/stroom/proxy/repo/LogStream.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,13 @@ public Map<String, String> filterAttributes(final AttributeMap attributeMap) {
3030
final List<String> metaKeys = logStreamConfigProvider.get().getMetaKeys();
3131
if (NullSafe.hasItems(metaKeys)) {
3232
final Map<String, String> map = new LinkedHashMap<>(metaKeys.size());
33-
metaKeys.forEach(key ->
34-
map.put(key, attributeMap.get(key)));
33+
metaKeys.forEach(key -> {
34+
final AttributeMap.KV kv = attributeMap.getEntry(key);
35+
// Ignore nulls.
36+
if (kv != null) {
37+
map.put(kv.getKey(), kv.getValue());
38+
}
39+
});
3540
return map;
3641
} else {
3742
return Collections.emptyMap();
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
* Issue **#5127** : Maintain case for proxy meta attributes when logging.
2+
3+
4+
```sh
5+
# ********************************************************************************
6+
# Issue title: Stroom proxy log does not maintain case of attribute values
7+
# Issue link: https://github.com/gchq/stroom/issues/5127
8+
# ********************************************************************************
9+
10+
# ONLY the top line will be included as a change entry in the CHANGELOG.
11+
# The entry should be in GitHub flavour markdown and should be written on a SINGLE
12+
# line with no hard breaks. You can have multiple change files for a single GitHub issue.
13+
# The entry should be written in the imperative mood, i.e. 'Fix nasty bug' rather than
14+
# 'Fixed nasty bug'.
15+
#
16+
# Examples of acceptable entries are:
17+
#
18+
#
19+
# * Issue **123** : Fix bug with an associated GitHub issue in this repository
20+
#
21+
# * Issue **namespace/other-repo#456** : Fix bug with an associated GitHub issue in another repository
22+
#
23+
# * Fix bug with no associated GitHub issue.
24+
25+
26+
# --------------------------------------------------------------------------------
27+
# The following is random text to make this file unique for git's change detection
28+
# WoTVWsgHSjZzTemgk3dDaxY4xIFfe9sy0HnHQJhnLQDR9sbWRJNtMDoIEn053EzIPAYvsfVNgaMhSr09
29+
# jvbMoxKqJFvWehQnhca472v3226Iu6RYilCOOPFbHp2XwYE8SdRSrgPE5IxP45sKqpAcSlCNrw8ooR7Z
30+
# 2gGTLoq569GT2cJSKnytLN6u9MFrZgGedMsY3kEaF6TrPCo8FWLhW1jvfvWDMRV6Fs9U8ALRnLwzsLvw
31+
# qZtZyZXM6twu2bfaNy9vSC5SjZKBOQ144f1BcPmo4gRJjbE6J6TWloZ6gWcGyWI3IEDFM56vKP36fYSi
32+
# KElQUcLp0fvR7dMH7qAJFMMKGEeaMrxnKrv7CFhKKMxM9YEjntnH0cUebMf6t2uaOVYofmCypl1hFL6t
33+
# WFiuCeHppmZNrE6FpfUy2kooTpvMYPR49eCJ6HwgRbctzLXCRzGhvMVQOcf30t9SiRG7xz68HWQt2xX0
34+
# um5NBiJJN9yZJ33EdajVgRidTtkMmqnuE6Cy6kNptoo9GL62vG4iHPFYAcY3M8aEf8zHcngphGVrO75F
35+
# vL8D9pXU9BGuN1VbmASMqcR1sUiEvxlWm7P4brmuTDDDMRjg5BCPjwG6fFSCZyWwZkzCF7yRE1mjmWxR
36+
# p0Ql0uRkysW5hJRQrR6KWMwFIZ56NUNDggrBnF6L22qMr33s1IyCwOCo4bP7yHAGtQWyGfcFLnXuIRvu
37+
# dzMKCfFbuqSPFWGRvSh8mgBxK4YvkPj8G6lAzWsF9h0OgXMYJzLHR3cYA065edAFTymTtPjk4ZmwijGw
38+
# E1ekVtrRcgwHOGqhkOvXzgRhP60sQOgaozLDfWRKI3tjPriV3FXIG2OPytqQMVGRFRFyFbZa9SNvl4QD
39+
# CdvL34qRqRjSaRVotN6MJlAPGLNuG8TUOS0Xpx2dy0RwiI9915M8R16JGo9uQlYzwyp4kipZ89M2UsOJ
40+
# 5BsCbBwG22bbu33TYGpIUB8lZGeORX3yUvqqe7iJYDfsRelP2SYzuqOX8IDHrsGZTaQgJg5GMPhuwJ7n
41+
# 1pQQQyD1KFBjT359Edz33xFp7TKVvpzFTJVx0p3mh0AGq4Scod6ZPrD1ocyIIsYXIT913kADShCbWen4
42+
# LxWGPfKZwigK1AD5tzMNQdz1BhUFmhtsGUtYsUpgXFjzdHviZqyPJbJ0QtvSao7ZADiJXyGzj1A6OHcP
43+
# SUFQK9qrOa7mZ2a8fj5Focg7bUn6EOPwPIUWQPqs0X3OUo2xVClZQj1SIsmDDE3V9DZFuj4RbR0tIBtn
44+
# EEbUFubxcZ7WbA8P83dX71wAwcC0IyjsSUEAkPe4jK45g3YY8mEIqfr2uONCkacDDPVgURVpSFpKstm1
45+
# 5Ud9oBNCg3mkHfa9JUmGh9iX0QZd8nNog2f3MuxpyUkkOfCIxyKmEOcTMtMjATXlzs6hQRvGeqs9AAVk
46+
# ctaHgtddInGffBCoAHTJjp7iWXm7JZrQUOAJWG2Jzriel6MjL5hsDZ5cWLtFGJr5XtkytIO9GMkzlKbn
47+
# e66wofVypotn35jfn3n3iHxpgqfmjp1bG0idISsxGPhGLh8gziyX0L2umUXFK8NYjJLsfMAprdVbXOty
48+
# fKmoaRj7fBFnMgkqvM5Ts37d0O2wWIWTUvfJYLbXrQ2LU4zqZ28uRJzMEkpWZqzTEDF9Nxu5qf0rZ03E
49+
# 9mvS08hBQn6SDFi1qN1YVwXolFRwZGTpE9JOwoQHHYxjP4FZDV5JfrMaQGMXWWONwfScgtLV2W9t5W01
50+
# 0m1oMMpAKH0oZhRolVyfUxD6z76rOjb2YFS2z2IWWJZHLNPDaO77IBDEL0kC3uBwoW4fCbQm2BCZeuwD
51+
# bfoZ5ho7iaaSmWAILi9NriwRFAw93eTdysjJV8Tc5RzrqaAJoet3d7Bx16WNM8qlfg63CHvq9mRMKCTf
52+
# N5qlKCiFIlfByHDlJDKi6hordg5PAjSbeln6NNyzyDxMyYqjJReA7xmPaKTsqWj1Ix9wTuLDOYcceOY5
53+
# pPA5NYqMd0FVRozzD2bQ3ByuoMHRAtP8pYViygFLVLzKm3zIgGE2gJ31DWGX88LYKDqtgdnCrUu6uYNL
54+
# zHcFACn7IZ6M528iePId6il0ZHQjmYkdV39W8JMQy1on2TmWvlOnScWoLZR7mZvGUrXXZ5s3AXOP0EJ8
55+
# XPXOSBJXjb0OBNGdjPxcfQnTa8AekA4Q1qdrUPFOfTB54uft34dd8wFbYFE2GLlXw2vZTt1lzX0yHxWa
56+
# gKinvl9S4kyNeowrQBwFjSJdx11vMKGGhcgY4VvCyb2VrdRCnC5wvMkkL1sHPmzxeicNd2bGW4B8nHp3
57+
# kQnTQWyQ2mz0v9wXAryf5WU1zEFyr8pXmoFDt5kzLy7CpdbgIV8TMnjN1sjGa7EZDCupJRflZd3VFevC
58+
# --------------------------------------------------------------------------------
59+
60+
```

0 commit comments

Comments
 (0)