Skip to content

Commit 64bb308

Browse files
committed
GsonBuilderUtils for programmatic Base64 serialization setup; common Base64Utils class adapts between Java 8 and Commons Codec
Issue: SPR-9488
1 parent 674bad4 commit 64bb308

File tree

5 files changed

+267
-110
lines changed

5 files changed

+267
-110
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ project("spring-core") {
293293

294294
compile(files(cglibRepackJar))
295295
compile("commons-logging:commons-logging:1.1.3")
296+
optional("commons-codec:commons-codec:1.9")
296297
optional("org.aspectj:aspectjweaver:${aspectjVersion}")
297298
optional("net.sf.jopt-simple:jopt-simple:4.6")
298299
optional("log4j:log4j:1.2.17")
@@ -625,7 +626,6 @@ project("spring-web") {
625626
optional("org.apache.httpcomponents:httpasyncclient:4.0.1")
626627
optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}")
627628
optional("com.google.code.gson:gson:${gsonVersion}")
628-
optional("commons-codec:commons-codec:1.9")
629629
optional("rome:rome:1.0")
630630
optional("org.eclipse.jetty:jetty-servlet:${jettyVersion}") {
631631
exclude group: "javax.servlet", module: "javax.servlet-api"
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/*
2+
* Copyright 2002-2014 the original author or authors.
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+
17+
package org.springframework.util;
18+
19+
import java.nio.charset.Charset;
20+
import java.util.Base64;
21+
22+
/**
23+
* A simple utility class for Base64 encoding and decoding.
24+
*
25+
* <p>Adapts to either Java 8's {@link java.util.Base64} class or Apache
26+
* Commons Codec's {@link org.apache.commons.codec.binary.Base64} class.
27+
* With neither Java 8 nor Commons Codec present, encode/decode calls
28+
* will fail with an IllegalStateException.
29+
*
30+
* @author Juergen Hoeller
31+
* @since 4.1
32+
* @see java.util.Base64
33+
* @see org.apache.commons.codec.binary.Base64
34+
*/
35+
public abstract class Base64Utils {
36+
37+
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
38+
39+
40+
private static final Base64Delegate delegate;
41+
42+
static {
43+
Base64Delegate delegateToUse = null;
44+
// JDK 8's java.util.Base64 class present?
45+
if (ClassUtils.isPresent("java.util.Base64", Base64Utils.class.getClassLoader())) {
46+
delegateToUse = new JdkBase64Delegate();
47+
}
48+
// Apache Commons Codec present on the classpath?
49+
else if (ClassUtils.isPresent("org.apache.commons.codec.binary.Base64", Base64Utils.class.getClassLoader())) {
50+
delegateToUse = new CommonsCodecBase64Delegate();
51+
}
52+
delegate = delegateToUse;
53+
}
54+
55+
/**
56+
* Assert that Byte64 encoding is actually supported.
57+
* @throws IllegalStateException if neither Java 8 nor Apache Commons Codec is present
58+
*/
59+
private static void assertSupported() {
60+
Assert.state(delegate != null, "Neither Java 8 nor Apache Commons Codec found - Base64 encoding not supported");
61+
}
62+
63+
64+
/**
65+
* Base64-encode the given byte array.
66+
* @param src the original byte array (may be {@code null})
67+
* @return the encoded byte array (or {@code null} if the input was {@code null})
68+
* @throws IllegalStateException if Base64 encoding is not supported,
69+
* i.e. neither Java 8 nor Apache Commons Codec is present at runtime
70+
*/
71+
public static byte[] encode(byte[] src) {
72+
assertSupported();
73+
return delegate.encode(src);
74+
}
75+
76+
/**
77+
* Base64-encode the given byte array to a String.
78+
* @param src the original byte array (may be {@code null})
79+
* @return the encoded byte array as a UTF-8 String
80+
* (or {@code null} if the input was {@code null})
81+
* @throws IllegalStateException if Base64 encoding is not supported,
82+
* i.e. neither Java 8 nor Apache Commons Codec is present at runtime
83+
*/
84+
public static String encodeToString(byte[] src) {
85+
assertSupported();
86+
if (src == null) {
87+
return null;
88+
}
89+
if (src.length == 0) {
90+
return "";
91+
}
92+
return new String(delegate.encode(src), DEFAULT_CHARSET);
93+
}
94+
95+
/**
96+
* Base64-decode the given byte array.
97+
* @param src the encoded byte array (may be {@code null})
98+
* @return the original byte array (or {@code null} if the input was {@code null})
99+
* @throws IllegalStateException if Base64 encoding is not supported,
100+
* i.e. neither Java 8 nor Apache Commons Codec is present at runtime
101+
*/
102+
public static byte[] decode(byte[] src) {
103+
assertSupported();
104+
return delegate.decode(src);
105+
}
106+
107+
/**
108+
* Base64-decode the given byte array from an UTF-8 String.
109+
* @param src the encoded UTF-8 String (may be {@code null})
110+
* @return the original byte array (or {@code null} if the input was {@code null})
111+
* @throws IllegalStateException if Base64 encoding is not supported,
112+
* i.e. neither Java 8 nor Apache Commons Codec is present at runtime
113+
*/
114+
public static byte[] decodeFromString(String src) {
115+
assertSupported();
116+
if (src == null) {
117+
return null;
118+
}
119+
if (src.length() == 0) {
120+
return new byte[0];
121+
}
122+
return delegate.decode(src.getBytes(DEFAULT_CHARSET));
123+
}
124+
125+
126+
private interface Base64Delegate {
127+
128+
byte[] encode(byte[] src);
129+
130+
byte[] decode(byte[] src);
131+
}
132+
133+
134+
private static class JdkBase64Delegate implements Base64Delegate {
135+
136+
public byte[] encode(byte[] src) {
137+
if (src == null || src.length == 0) {
138+
return src;
139+
}
140+
return Base64.getEncoder().encode(src);
141+
}
142+
143+
public byte[] decode(byte[] src) {
144+
if (src == null || src.length == 0) {
145+
return src;
146+
}
147+
return Base64.getDecoder().decode(src);
148+
}
149+
}
150+
151+
152+
private static class CommonsCodecBase64Delegate implements Base64Delegate {
153+
154+
private final org.apache.commons.codec.binary.Base64 base64 = new org.apache.commons.codec.binary.Base64();
155+
156+
public byte[] encode(byte[] src) {
157+
return this.base64.encode(src);
158+
}
159+
160+
public byte[] decode(byte[] src) {
161+
return this.base64.decode(src);
162+
}
163+
}
164+
165+
}

spring-web/src/main/java/org/springframework/http/converter/json/GsonBase64ByteArrayJsonTypeAdapter.java

Lines changed: 0 additions & 61 deletions
This file was deleted.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2002-2014 the original author or authors.
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+
17+
package org.springframework.http.converter.json;
18+
19+
import java.lang.reflect.Type;
20+
21+
import com.google.gson.GsonBuilder;
22+
import com.google.gson.JsonDeserializationContext;
23+
import com.google.gson.JsonDeserializer;
24+
import com.google.gson.JsonElement;
25+
import com.google.gson.JsonPrimitive;
26+
import com.google.gson.JsonSerializationContext;
27+
import com.google.gson.JsonSerializer;
28+
29+
import org.springframework.util.Base64Utils;
30+
31+
/**
32+
* A simple utility class for obtaining a Google Gson 2.x {@link GsonBuilder}
33+
* which Base64-encodes {@code byte[]} properties when reading and writing JSON.
34+
*
35+
* @author Juergen Hoeller
36+
* @author Roy Clarkson
37+
* @since 4.1
38+
* @see GsonFactoryBean#setBase64EncodeByteArrays
39+
* @see org.springframework.util.Base64Utils
40+
*/
41+
public abstract class GsonBuilderUtils {
42+
43+
/**
44+
* Obtain a {@link GsonBuilder} which Base64-encodes {@code byte[]}
45+
* properties when reading and writing JSON.
46+
* <p>A custom {@link com.google.gson.TypeAdapter} will be registered via
47+
* {@link GsonBuilder#registerTypeHierarchyAdapter(Class, Object)} which
48+
* serializes a {@code byte[]} property to and from a Base64-encoded String
49+
* instead of a JSON array.
50+
* <p><strong>NOTE:</strong> Use of this option requires the presence of the
51+
* Apache Commons Codec library on the classpath when running on Java 6 or 7.
52+
* On Java 8, the standard {@link java.util.Base64} facility is used instead.
53+
*/
54+
public static GsonBuilder gsonBuilderWithBase64EncodedByteArrays() {
55+
// Assert that Base64 support is available, as long we're not on Java 8+
56+
Base64Utils.encode(null);
57+
58+
// Now, construct a pre-configured GsonBuilder...
59+
GsonBuilder builder = new GsonBuilder();
60+
builder.registerTypeHierarchyAdapter(byte[].class, new Base64TypeAdapter());
61+
return builder;
62+
}
63+
64+
65+
private static class Base64TypeAdapter implements JsonSerializer<byte[]>, JsonDeserializer<byte[]> {
66+
67+
@Override
68+
public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) {
69+
return new JsonPrimitive(Base64Utils.encodeToString(src));
70+
}
71+
72+
@Override
73+
public byte[] deserialize(JsonElement json, Type type, JsonDeserializationContext cxt) {
74+
return Base64Utils.decodeFromString(json.getAsString());
75+
}
76+
}
77+
78+
}

0 commit comments

Comments
 (0)