Skip to content

Commit bd3563f

Browse files
committed
feat: Add codec abstraction
This allows users to use their own serialization strategy for flash maps.
1 parent 8a9ce91 commit bd3563f

File tree

9 files changed

+151
-69
lines changed

9 files changed

+151
-69
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
<dependency>
125125
<groupId>com.fasterxml.jackson.core</groupId>
126126
<artifactId>jackson-databind</artifactId>
127+
<optional>true</optional>
127128
</dependency>
128129
<dependency>
129130
<groupId>org.springframework</groupId>

src/main/java/com/innoq/spring/cookie/flash/CookieFlashMapManager.java

Lines changed: 18 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -15,78 +15,58 @@
1515
*/
1616
package com.innoq.spring.cookie.flash;
1717

18-
import com.fasterxml.jackson.core.JsonProcessingException;
19-
import com.fasterxml.jackson.core.type.TypeReference;
20-
import com.fasterxml.jackson.databind.ObjectMapper;
21-
import com.fasterxml.jackson.databind.module.SimpleModule;
18+
import org.springframework.util.Assert;
2219
import org.springframework.web.servlet.FlashMap;
2320
import org.springframework.web.servlet.support.AbstractFlashMapManager;
24-
import org.springframework.web.util.WebUtils;
2521

2622
import javax.servlet.http.Cookie;
2723
import javax.servlet.http.HttpServletRequest;
2824
import javax.servlet.http.HttpServletResponse;
29-
import java.io.IOException;
30-
import java.util.Base64;
3125
import java.util.List;
3226

27+
import static org.springframework.web.util.WebUtils.getCookie;
28+
3329
public final class CookieFlashMapManager extends AbstractFlashMapManager {
3430

3531
private static final String DEFAULT_COOKIE_NAME = "flash";
3632

37-
private final ObjectMapper objectMapper = new ObjectMapper()
38-
.registerModule(new SimpleModule()
39-
.addSerializer(FlashMap.class, new FlashMapSerializer())
40-
.addDeserializer(FlashMap.class, new FlashMapDeserializer()));
33+
private final FlashMapListCodec codec;
4134
private final String cookieName;
4235

43-
public CookieFlashMapManager() {
44-
this(DEFAULT_COOKIE_NAME);
36+
public CookieFlashMapManager(FlashMapListCodec codec) {
37+
this(codec, DEFAULT_COOKIE_NAME);
4538
}
4639

47-
public CookieFlashMapManager(String cookieName) {
48-
// TODO: assert not null/empty
40+
public CookieFlashMapManager(FlashMapListCodec codec, String cookieName) {
41+
Assert.notNull(codec, "FlashMapListCodec must not be null");
42+
Assert.hasText(cookieName, "Cookie name must not be null or empty");
43+
this.codec = codec;
4944
this.cookieName = cookieName;
5045
}
5146

5247
@Override
5348
protected List<FlashMap> retrieveFlashMaps(HttpServletRequest request) {
54-
Cookie cookie = WebUtils.getCookie(request, cookieName);
49+
final Cookie cookie = getCookie(request, cookieName);
5550
if (cookie == null) {
5651
return null;
5752
}
5853

59-
String value = cookie.getValue();
60-
byte[] payload = Base64.getDecoder().decode(value);
61-
62-
try {
63-
return this.objectMapper.readValue(payload, new TypeReference<List<FlashMap>>() {});
64-
} catch (IOException e) {
65-
// TODO
66-
e.printStackTrace();
67-
return null;
68-
}
54+
final String value = cookie.getValue();
55+
return codec.decode(value);
6956
}
7057

7158
@Override
72-
protected void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request, HttpServletResponse response) {
73-
Cookie cookie = new Cookie(cookieName, null);
59+
protected void updateFlashMaps(List<FlashMap> flashMaps,
60+
HttpServletRequest request, HttpServletResponse response) {
61+
final Cookie cookie = new Cookie(cookieName, null);
7462
cookie.setHttpOnly(true);
7563
cookie.setPath("/");
7664

7765
if (flashMaps.isEmpty()) {
7866
cookie.setMaxAge(0);
7967
} else {
80-
try {
81-
byte[] payload = this.objectMapper.writeValueAsBytes(flashMaps);
82-
String value = Base64.getEncoder().encodeToString(payload);
83-
84-
// TODO: max-age?
85-
cookie.setValue(value);
86-
} catch (JsonProcessingException e) {
87-
// TODO
88-
e.printStackTrace();
89-
}
68+
final String value = codec.encode(flashMaps);
69+
cookie.setValue(value);
9070
}
9171
response.addCookie(cookie);
9272
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Copyright 2018 innoQ Deutschland GmbH
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+
*/
16+
package com.innoq.spring.cookie.flash;
17+
18+
import org.springframework.web.servlet.FlashMap;
19+
20+
import java.util.List;
21+
22+
public interface FlashMapListCodec {
23+
24+
List<FlashMap> decode(String encodedFlashMaps);
25+
26+
String encode(List<FlashMap> flashMaps);
27+
}

src/main/java/com/innoq/spring/cookie/flash/FlashMapDeserializer.java renamed to src/main/java/com/innoq/spring/cookie/flash/codec/jackson/FlashMapDeserializer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.innoq.spring.cookie.flash;
16+
package com.innoq.spring.cookie.flash.codec.jackson;
1717

1818
import com.fasterxml.jackson.core.JsonParser;
1919
import com.fasterxml.jackson.core.type.TypeReference;

src/main/java/com/innoq/spring/cookie/flash/FlashMapSerializer.java renamed to src/main/java/com/innoq/spring/cookie/flash/codec/jackson/FlashMapSerializer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.innoq.spring.cookie.flash;
16+
package com.innoq.spring.cookie.flash.codec.jackson;
1717

1818
import com.fasterxml.jackson.core.JsonGenerator;
1919
import com.fasterxml.jackson.databind.SerializerProvider;
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/**
2+
* Copyright 2018 innoQ Deutschland GmbH
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+
*/
16+
package com.innoq.spring.cookie.flash.codec.jackson;
17+
18+
import com.fasterxml.jackson.core.JsonProcessingException;
19+
import com.fasterxml.jackson.core.type.TypeReference;
20+
import com.fasterxml.jackson.databind.ObjectMapper;
21+
import com.fasterxml.jackson.databind.module.SimpleModule;
22+
import com.innoq.spring.cookie.flash.FlashMapListCodec;
23+
import org.springframework.util.Assert;
24+
import org.springframework.web.servlet.FlashMap;
25+
26+
import java.io.IOException;
27+
import java.util.Base64;
28+
import java.util.List;
29+
30+
public final class JacksonFlashMapListCodec implements FlashMapListCodec {
31+
32+
private static TypeReference<List<FlashMap>> FLASH_MAPS_TYPE_REFERENCE =
33+
new TypeReference<List<FlashMap>>() {};
34+
35+
private final ObjectMapper mapper;
36+
37+
private JacksonFlashMapListCodec(ObjectMapper mapper) {
38+
Assert.notNull(mapper, "ObjectMapper must not be null");
39+
this.mapper = mapper;
40+
}
41+
42+
@Override
43+
public List<FlashMap> decode(String encodedFlashMaps) {
44+
final byte[] json = fromBase64(encodedFlashMaps);
45+
return fromJson(json);
46+
}
47+
48+
@Override
49+
public String encode(List<FlashMap> flashMaps) {
50+
final byte[] json = toJson(flashMaps);
51+
return toBase64(json);
52+
}
53+
54+
private byte[] toJson(List<FlashMap> flashMaps) {
55+
try {
56+
return mapper.writeValueAsBytes(flashMaps);
57+
} catch (JsonProcessingException e) {
58+
// TODO: use own exception?
59+
throw new IllegalArgumentException("Unable to convert flash maps to JSON", e);
60+
}
61+
}
62+
63+
private List<FlashMap> fromJson(byte[] json) {
64+
try {
65+
return mapper.readValue(json, FLASH_MAPS_TYPE_REFERENCE);
66+
} catch (IOException e) {
67+
// TODO: use own exception?
68+
throw new IllegalArgumentException("Unable to convert JSON to flash maps", e);
69+
}
70+
}
71+
72+
private static byte[] fromBase64(String string) {
73+
return Base64.getDecoder().decode(string);
74+
}
75+
76+
private static String toBase64(byte[] bytes) {
77+
return Base64.getEncoder().encodeToString(bytes);
78+
}
79+
80+
public static JacksonFlashMapListCodec create() {
81+
final SimpleModule module = new SimpleModule();
82+
module.addSerializer(FlashMap.class, new FlashMapSerializer());
83+
module.addDeserializer(FlashMap.class, new FlashMapDeserializer());
84+
85+
final ObjectMapper mapper = new ObjectMapper();
86+
mapper.registerModule(module);
87+
88+
return create(mapper);
89+
}
90+
91+
public static JacksonFlashMapListCodec create(ObjectMapper objectMapper) {
92+
return new JacksonFlashMapListCodec(objectMapper);
93+
}
94+
}

src/test/java/com/innoq/spring/cookie/flash/CookieFlashMapManagerTest.java

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,25 @@
1515
*/
1616
package com.innoq.spring.cookie.flash;
1717

18+
import com.innoq.spring.cookie.flash.codec.jackson.JacksonFlashMapListCodec;
1819
import org.junit.jupiter.api.Test;
1920
import org.springframework.mock.web.MockHttpServletRequest;
2021
import org.springframework.mock.web.MockHttpServletResponse;
2122
import org.springframework.web.servlet.FlashMap;
2223

2324
import javax.servlet.http.Cookie;
24-
import java.util.Base64;
2525
import java.util.List;
2626
import java.util.Map;
2727

28-
import static java.nio.charset.StandardCharsets.UTF_8;
2928
import static java.util.Arrays.asList;
3029
import static java.util.Collections.emptyList;
3130
import static org.assertj.core.api.Assertions.assertThat;
3231
import static org.assertj.core.api.Assertions.entry;
3332

3433
class CookieFlashMapManagerTest {
3534

36-
CookieFlashMapManager sut = new CookieFlashMapManager();
35+
CookieFlashMapManager sut = new CookieFlashMapManager(
36+
JacksonFlashMapListCodec.create());
3737

3838
@Test
3939
void retrieveFlashMaps_withNoCookiePresent_returnsNull() {
@@ -46,22 +46,7 @@ void retrieveFlashMaps_withNoCookiePresent_returnsNull() {
4646

4747
@Test
4848
void retrieveFlashMaps_withValidCookie_returnsFlashMaps() {
49-
String json =
50-
"[{" +
51-
"\"attributes\":{" +
52-
"\"foo\":null," +
53-
"\"bar\":4711," +
54-
"\"baz\":\"lorem ipsum\"" +
55-
"}," +
56-
"\"expirationTime\":4711," +
57-
"\"targetRequestParams\":{" +
58-
"\"foo\":[]," +
59-
"\"bar\":[\"foo\"]," +
60-
"\"baz\":[\"lorem\",\"ipsum\"]" +
61-
"}," +
62-
"\"targetRequestPath\":\"/foo\"" +
63-
"}]";
64-
String cookieValue = Base64.getEncoder().encodeToString(json.getBytes(UTF_8));
49+
String cookieValue = "W3siYXR0cmlidXRlcyI6eyJmb28iOm51bGwsImJhciI6NDcxMSwiYmF6IjoibG9yZW0gaXBzdW0ifSwiZXhwaXJhdGlvblRpbWUiOjQ3MTEsInRhcmdldFJlcXVlc3RQYXJhbXMiOnsiZm9vIjpbXSwiYmFyIjpbImZvbyJdLCJiYXoiOlsibG9yZW0iLCJpcHN1bSJdfSwidGFyZ2V0UmVxdWVzdFBhdGgiOiIvZm9vIn1dCg==";
6550

6651
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
6752
request.setCookies(new Cookie("flash", cookieValue));
@@ -97,14 +82,7 @@ void updateFlashMaps_withSingleFlashMap_writesCookie() {
9782
.hasFieldOrPropertyWithValue("httpOnly", true);
9883

9984
String cookieValue = response.getCookie("flash").getValue();
100-
String json = new String(Base64.getDecoder().decode(cookieValue), UTF_8);
101-
assertThat(json).isEqualTo(
102-
"[{" +
103-
"\"attributes\":{}," +
104-
"\"expirationTime\":-1," +
105-
"\"targetRequestParams\":{}," +
106-
"\"targetRequestPath\":null" +
107-
"}]");
85+
assertThat(cookieValue).isEqualTo("W3siYXR0cmlidXRlcyI6e30sImV4cGlyYXRpb25UaW1lIjotMSwidGFyZ2V0UmVxdWVzdFBhcmFtcyI6e30sInRhcmdldFJlcXVlc3RQYXRoIjpudWxsfV0=");
10886
}
10987

11088
@Test

src/test/java/com/innoq/spring/cookie/flash/FlashMapDeserializerTest.java renamed to src/test/java/com/innoq/spring/cookie/flash/codec/jackson/FlashMapDeserializerTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.innoq.spring.cookie.flash;
16+
package com.innoq.spring.cookie.flash.codec.jackson;
1717

1818
import com.fasterxml.jackson.core.type.TypeReference;
1919
import com.fasterxml.jackson.databind.ObjectMapper;
2020
import com.fasterxml.jackson.databind.module.SimpleModule;
21+
import com.innoq.spring.cookie.flash.codec.jackson.FlashMapDeserializer;
2122
import org.junit.jupiter.api.Test;
2223
import org.springframework.web.servlet.FlashMap;
2324

src/test/java/com/innoq/spring/cookie/flash/FlashMapSerializerTest.java renamed to src/test/java/com/innoq/spring/cookie/flash/codec/jackson/FlashMapSerializerTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.innoq.spring.cookie.flash;
16+
package com.innoq.spring.cookie.flash.codec.jackson;
1717

1818
import com.fasterxml.jackson.databind.ObjectMapper;
1919
import com.fasterxml.jackson.databind.module.SimpleModule;
20+
import com.innoq.spring.cookie.flash.codec.jackson.FlashMapSerializer;
2021
import org.junit.jupiter.api.Test;
2122
import org.springframework.web.servlet.FlashMap;
2223

0 commit comments

Comments
 (0)