Skip to content

Commit fa9bb0d

Browse files
authored
HADOOP-19231. Add JacksonUtil to manage Jackson classes (#6953)
New class org.apache.hadoop.util.JacksonUtil centralizes construction of Jackson ObjectMappers and JsonFactories. Contributed by PJ Fanning
1 parent 55a5769 commit fa9bb0d

File tree

71 files changed

+392
-296
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+392
-296
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/conf/Configuration.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import com.ctc.wstx.io.StreamBootstrapper;
2323
import com.ctc.wstx.io.SystemId;
2424
import com.ctc.wstx.stax.WstxInputFactory;
25-
import com.fasterxml.jackson.core.JsonFactory;
2625
import com.fasterxml.jackson.core.JsonGenerator;
2726

2827
import java.io.BufferedInputStream;
@@ -101,6 +100,7 @@
101100
import org.apache.hadoop.security.alias.CredentialProviderFactory;
102101
import org.apache.hadoop.thirdparty.com.google.common.base.Strings;
103102
import org.apache.hadoop.util.ConfigurationHelper;
103+
import org.apache.hadoop.util.JacksonUtil;
104104
import org.apache.hadoop.util.Preconditions;
105105
import org.apache.hadoop.util.ReflectionUtils;
106106
import org.apache.hadoop.util.StringInterner;
@@ -3792,8 +3792,7 @@ public static void dumpConfiguration(Configuration config,
37923792
throw new IllegalArgumentException("Property " +
37933793
propertyName + " not found");
37943794
} else {
3795-
JsonFactory dumpFactory = new JsonFactory();
3796-
JsonGenerator dumpGenerator = dumpFactory.createGenerator(out);
3795+
JsonGenerator dumpGenerator = JacksonUtil.getSharedWriter().createGenerator(out);
37973796
dumpGenerator.writeStartObject();
37983797
dumpGenerator.writeFieldName("property");
37993798
appendJSONProperty(dumpGenerator, config, propertyName,
@@ -3831,8 +3830,7 @@ public static void dumpConfiguration(Configuration config,
38313830
*/
38323831
public static void dumpConfiguration(Configuration config,
38333832
Writer out) throws IOException {
3834-
JsonFactory dumpFactory = new JsonFactory();
3835-
JsonGenerator dumpGenerator = dumpFactory.createGenerator(out);
3833+
JsonGenerator dumpGenerator = JacksonUtil.getSharedWriter().createGenerator(out);
38363834
dumpGenerator.writeStartObject();
38373835
dumpGenerator.writeFieldName("properties");
38383836
dumpGenerator.writeStartArray();

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/kms/KMSClientProvider.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.apache.hadoop.security.token.delegation.AbstractDelegationTokenSelector;
4242
import org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticatedURL;
4343
import org.apache.hadoop.util.HttpExceptionUtils;
44+
import org.apache.hadoop.util.JacksonUtil;
4445
import org.apache.hadoop.util.JsonSerialization;
4546
import org.apache.hadoop.util.KMSUtil;
4647
import org.apache.http.client.utils.URIBuilder;
@@ -78,7 +79,6 @@
7879
import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension;
7980
import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension.CryptoExtension;
8081

81-
import com.fasterxml.jackson.databind.ObjectMapper;
8282
import org.apache.hadoop.classification.VisibleForTesting;
8383
import org.apache.hadoop.util.Preconditions;
8484
import org.apache.hadoop.thirdparty.com.google.common.base.Strings;
@@ -592,11 +592,10 @@ private <T> T call(HttpURLConnection conn, Object jsonOutput,
592592
&& conn.getContentType().trim().toLowerCase()
593593
.startsWith(APPLICATION_JSON_MIME)
594594
&& klass != null) {
595-
ObjectMapper mapper = new ObjectMapper();
596595
InputStream is = null;
597596
try {
598597
is = conn.getInputStream();
599-
ret = mapper.readValue(is, klass);
598+
ret = JacksonUtil.getSharedReader().readValue(is, klass);
600599
} finally {
601600
IOUtils.closeStream(is);
602601
}

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/DecayRpcScheduler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@
3838

3939
import javax.management.ObjectName;
4040

41-
import com.fasterxml.jackson.databind.ObjectMapper;
4241
import com.fasterxml.jackson.databind.ObjectWriter;
4342

4443
import org.apache.hadoop.security.UserGroupInformation;
44+
import org.apache.hadoop.util.JacksonUtil;
4545
import org.apache.hadoop.util.Preconditions;
4646
import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.AtomicDoubleArray;
4747
import org.apache.commons.lang3.exception.ExceptionUtils;
@@ -146,7 +146,7 @@ public class DecayRpcScheduler implements RpcScheduler,
146146
public static final Logger LOG =
147147
LoggerFactory.getLogger(DecayRpcScheduler.class);
148148

149-
private static final ObjectWriter WRITER = new ObjectMapper().writer();
149+
private static final ObjectWriter WRITER = JacksonUtil.getSharedWriter();
150150

151151
// Track the decayed and raw (no decay) number of calls for each schedulable
152152
// identity from all previous decay windows: idx 0 for decayed call cost and

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Server.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
import org.apache.hadoop.security.token.SecretManager.InvalidToken;
122122
import org.apache.hadoop.security.token.TokenIdentifier;
123123
import org.apache.hadoop.util.ExitUtil;
124+
import org.apache.hadoop.util.JacksonUtil;
124125
import org.apache.hadoop.util.ProtoUtil;
125126
import org.apache.hadoop.util.StringUtils;
126127
import org.apache.hadoop.util.Time;
@@ -130,7 +131,6 @@
130131
import org.apache.hadoop.tracing.TraceScope;
131132
import org.apache.hadoop.tracing.Tracer;
132133
import org.apache.hadoop.tracing.TraceUtils;
133-
import com.fasterxml.jackson.databind.ObjectMapper;
134134
import org.apache.hadoop.classification.VisibleForTesting;
135135

136136
import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder;
@@ -3843,9 +3843,8 @@ public int getNumOpenConnections() {
38433843
* @return Get the NumOpenConnections/User.
38443844
*/
38453845
public String getNumOpenConnectionsPerUser() {
3846-
ObjectMapper mapper = new ObjectMapper();
38473846
try {
3848-
return mapper
3847+
return JacksonUtil.getSharedWriter()
38493848
.writeValueAsString(connectionManager.getUserToConnectionsMap());
38503849
} catch (IOException ignored) {
38513850
}

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/jmx/JMXJsonServlet.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@
4343
import javax.servlet.http.HttpServletRequest;
4444
import javax.servlet.http.HttpServletResponse;
4545

46-
import com.fasterxml.jackson.core.JsonFactory;
4746
import com.fasterxml.jackson.core.JsonGenerator;
4847
import org.slf4j.Logger;
4948
import org.slf4j.LoggerFactory;
5049

5150
import org.apache.commons.lang3.NotImplementedException;
5251
import org.apache.hadoop.http.HttpServer2;
52+
import org.apache.hadoop.util.JacksonUtil;
5353

5454
/*
5555
* This servlet is based off of the JMXProxyServlet from Tomcat 7.0.14. It has
@@ -134,19 +134,13 @@ public class JMXJsonServlet extends HttpServlet {
134134
*/
135135
protected transient MBeanServer mBeanServer;
136136

137-
/**
138-
* Json Factory to create Json generators for write objects in json format
139-
*/
140-
protected transient JsonFactory jsonFactory;
141-
142137
/**
143138
* Initialize this servlet.
144139
*/
145140
@Override
146141
public void init() throws ServletException {
147142
// Retrieve the MBean server
148143
mBeanServer = ManagementFactory.getPlatformMBeanServer();
149-
jsonFactory = new JsonFactory();
150144
}
151145

152146
protected boolean isInstrumentationAccessAllowed(HttpServletRequest request,
@@ -187,7 +181,7 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) {
187181
response.setHeader(ACCESS_CONTROL_ALLOW_METHODS, "GET");
188182
response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
189183

190-
jg = jsonFactory.createGenerator(writer);
184+
jg = JacksonUtil.getSharedWriter().createGenerator(writer);
191185
jg.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
192186
jg.useDefaultPrettyPrinter();
193187
jg.writeStartObject();

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/metrics2/MetricsJsonBuilder.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
import org.apache.commons.lang3.exception.ExceptionUtils;
2222
import org.apache.hadoop.classification.InterfaceAudience;
2323
import org.apache.hadoop.classification.InterfaceStability;
24+
import org.apache.hadoop.util.JacksonUtil;
2425

25-
import com.fasterxml.jackson.databind.ObjectMapper;
2626
import com.fasterxml.jackson.databind.ObjectWriter;
2727
import org.slf4j.Logger;
2828
import org.slf4j.LoggerFactory;
@@ -46,8 +46,7 @@ public class MetricsJsonBuilder extends MetricsRecordBuilder {
4646
private final MetricsCollector parent;
4747
private Map<String, Object> innerMetrics = new LinkedHashMap<>();
4848

49-
private static final ObjectWriter WRITER =
50-
new ObjectMapper().writer();
49+
private static final ObjectWriter WRITER = JacksonUtil.getSharedWriter();
5150

5251
/**
5352
* Build an instance.

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/web/DelegationTokenAuthenticationHandler.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.apache.hadoop.security.token.delegation.AbstractDelegationTokenIdentifier;
4747
import org.apache.hadoop.security.token.delegation.AbstractDelegationTokenSecretManager;
4848
import org.apache.hadoop.util.HttpExceptionUtils;
49+
import org.apache.hadoop.util.JacksonUtil;
4950
import org.apache.hadoop.util.StringUtils;
5051
import org.slf4j.Logger;
5152
import org.slf4j.LoggerFactory;
@@ -165,7 +166,7 @@ public void initTokenManager(Properties config) {
165166
@VisibleForTesting
166167
public void initJsonFactory(Properties config) {
167168
boolean hasFeature = false;
168-
JsonFactory tmpJsonFactory = new JsonFactory();
169+
JsonFactory tmpJsonFactory = JacksonUtil.createBasicJsonFactory();
169170

170171
for (Map.Entry entry : config.entrySet()) {
171172
String key = (String)entry.getKey();
@@ -335,7 +336,7 @@ public boolean managementOperation(AuthenticationToken token,
335336
if (map != null) {
336337
response.setContentType(MediaType.APPLICATION_JSON);
337338
Writer writer = response.getWriter();
338-
ObjectMapper jsonMapper = new ObjectMapper(jsonFactory);
339+
ObjectMapper jsonMapper = JacksonUtil.createObjectMapper(jsonFactory);
339340
jsonMapper.writeValue(writer, map);
340341
writer.write(ENTER);
341342
writer.flush();
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.util;
19+
20+
import com.fasterxml.jackson.core.JsonFactory;
21+
import com.fasterxml.jackson.databind.ObjectMapper;
22+
import com.fasterxml.jackson.databind.ObjectReader;
23+
import com.fasterxml.jackson.databind.ObjectWriter;
24+
import com.fasterxml.jackson.databind.json.JsonMapper;
25+
26+
import org.apache.hadoop.classification.InterfaceAudience.Private;
27+
28+
/**
29+
* Utility for sharing code related to Jackson usage in Hadoop.
30+
*/
31+
@Private
32+
public final class JacksonUtil {
33+
34+
private static final ObjectMapper SHARED_BASIC_OBJECT_MAPPER = createBasicObjectMapper();
35+
private static final ObjectReader SHARED_BASIC_OBJECT_READER =
36+
SHARED_BASIC_OBJECT_MAPPER.reader();
37+
private static final ObjectWriter SHARED_BASIC_OBJECT_WRITER =
38+
SHARED_BASIC_OBJECT_MAPPER.writer();
39+
private static final ObjectWriter SHARED_BASIC_OBJECT_WRITER_PRETTY =
40+
SHARED_BASIC_OBJECT_MAPPER.writerWithDefaultPrettyPrinter();
41+
42+
/**
43+
* Creates a new {@link JsonFactory} instance with basic configuration.
44+
*
45+
* @return an {@link JsonFactory} with basic configuration
46+
*/
47+
public static JsonFactory createBasicJsonFactory() {
48+
// deliberately return a new instance instead of sharing one because we can't trust
49+
// that users won't modify this instance
50+
return new JsonFactory();
51+
}
52+
53+
/**
54+
* Creates a new {@link ObjectMapper} instance with basic configuration.
55+
*
56+
* @return an {@link ObjectMapper} with basic configuration
57+
*/
58+
public static ObjectMapper createBasicObjectMapper() {
59+
// deliberately return a new instance instead of sharing one because we can't trust
60+
// that users won't modify this instance
61+
return JsonMapper.builder(createBasicJsonFactory()).build();
62+
}
63+
64+
/**
65+
* Creates a new {@link ObjectMapper} instance based on the configuration
66+
* in the input {@link JsonFactory}.
67+
*
68+
* @param jsonFactory a pre-configured {@link JsonFactory}
69+
* @return an {@link ObjectMapper} with configuration set by the input {@link JsonFactory}.
70+
*/
71+
public static ObjectMapper createObjectMapper(final JsonFactory jsonFactory) {
72+
return JsonMapper.builder(jsonFactory).build();
73+
}
74+
75+
/**
76+
* Returns a shared {@link ObjectReader} instance with basic configuration.
77+
*
78+
* @return a shared {@link ObjectReader} instance with basic configuration
79+
*/
80+
public static ObjectReader getSharedReader() {
81+
return SHARED_BASIC_OBJECT_READER;
82+
}
83+
84+
/**
85+
* Returns an {@link ObjectReader} for the given type instance with basic configuration.
86+
*
87+
* @param type the class that the reader has to support
88+
* @return an {@link ObjectReader} instance with basic configuration
89+
*/
90+
public static ObjectReader createBasicReaderFor(Class<?> type) {
91+
return SHARED_BASIC_OBJECT_MAPPER.readerFor(type);
92+
}
93+
94+
/**
95+
* Returns a shared {@link ObjectWriter} instance with basic configuration.
96+
*
97+
* @return a shared {@link ObjectWriter} instance with basic configuration
98+
*/
99+
public static ObjectWriter getSharedWriter() {
100+
return SHARED_BASIC_OBJECT_WRITER;
101+
}
102+
103+
/**
104+
* Returns a shared {@link ObjectWriter} instance with pretty print and basic configuration.
105+
*
106+
* @return a shared {@link ObjectWriter} instance with pretty print and basic configuration
107+
*/
108+
public static ObjectWriter getSharedWriterWithPrettyPrint() {
109+
return SHARED_BASIC_OBJECT_WRITER_PRETTY;
110+
}
111+
112+
/**
113+
* Returns an {@link ObjectWriter} for the given type instance with basic configuration.
114+
*
115+
* @param type the class that the writer has to support
116+
* @return an {@link ObjectWriter} instance with basic configuration
117+
*/
118+
public static ObjectWriter createBasicWriterFor(Class<?> type) {
119+
return SHARED_BASIC_OBJECT_MAPPER.writerFor(type);
120+
}
121+
122+
private JacksonUtil() {}
123+
}

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/JsonSerialization.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,8 @@ public class JsonSerialization<T> {
7676
private final Class<T> classType;
7777
private final ObjectMapper mapper;
7878

79-
private static final ObjectWriter WRITER =
80-
new ObjectMapper().writerWithDefaultPrettyPrinter();
81-
82-
private static final ObjectReader MAP_READER =
83-
new ObjectMapper().readerFor(Map.class);
79+
private static final ObjectWriter WRITER = JacksonUtil.getSharedWriterWithPrettyPrint();
80+
private static final ObjectReader MAP_READER = JacksonUtil.createBasicReaderFor(Map.class);
8481

8582
/**
8683
* @return an ObjectWriter which pretty-prints its output
@@ -106,7 +103,7 @@ public JsonSerialization(Class<T> classType,
106103
boolean failOnUnknownProperties, boolean pretty) {
107104
Preconditions.checkArgument(classType != null, "null classType");
108105
this.classType = classType;
109-
this.mapper = new ObjectMapper();
106+
this.mapper = JacksonUtil.createBasicObjectMapper();
110107
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
111108
failOnUnknownProperties);
112109
mapper.configure(SerializationFeature.INDENT_OUTPUT, pretty);

hadoop-common-project/hadoop-kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSJSONReader.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@
1717
*/
1818
package org.apache.hadoop.crypto.key.kms.server;
1919

20-
import com.fasterxml.jackson.databind.ObjectMapper;
21-
2220
import org.apache.hadoop.classification.InterfaceAudience;
21+
import org.apache.hadoop.util.JacksonUtil;
2322

2423
import javax.ws.rs.Consumes;
2524
import javax.ws.rs.WebApplicationException;
@@ -38,7 +37,6 @@
3837
@Consumes(MediaType.APPLICATION_JSON)
3938
@InterfaceAudience.Private
4039
public class KMSJSONReader implements MessageBodyReader<Object> {
41-
private static final ObjectMapper MAPPER = new ObjectMapper();
4240

4341
@Override
4442
public boolean isReadable(Class<?> type, Type genericType,
@@ -52,6 +50,6 @@ public Object readFrom(Class<Object> type, Type genericType,
5250
Annotation[] annotations, MediaType mediaType,
5351
MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
5452
throws IOException, WebApplicationException {
55-
return MAPPER.readValue(entityStream, type);
53+
return JacksonUtil.getSharedReader().readValue(entityStream, type);
5654
}
5755
}

0 commit comments

Comments
 (0)