This is an automated email from the ASF dual-hosted git repository.
jlfsdtc pushed a commit to branch kylin5
in repository https://gitbox.apache.org/repos/asf/kylin.git
The following commit(s) were added to refs/heads/kylin5 by this push:
new bedcc2fa3a KYLIN-6092 Add polymorphic type validator for secure JSON
deserialization
bedcc2fa3a is described below
commit bedcc2fa3afb306d0ea58c88660c7265840fa3dc
Author: jlfsdtc <[email protected]>
AuthorDate: Mon Aug 31 17:17:02 2026 +0800
KYLIN-6092 Add polymorphic type validator for secure JSON deserialization
* KYLIN-6092 Add polymorphic type validator to restrict classes loadable
during JSON deserialization
Event, SegmentRange and BroadcastEventReadyNotifier use
@JsonTypeInfo(Id.CLASS), which lets an attacker-supplied @class id load
arbitrary classes (deserialization gadget attack). Set a
BasicPolymorphicTypeValidator that only allows org.apache.kylin.*
subtypes on the JsonUtil mappers and on the Spring HTTP converter
ObjectMapper in AppConfig.
Co-Authored-By: Claude Fable 5 <[email protected]>
* KYLIN-6092 Fix flaky UT and ignore test-generated files
- BaseSchedulerTest: stop the scheduler and wait for JobWorker threads to
unwind before cleanup, so a lingering worker does not steal driver memory
from the next test's baseline (ResourceAcquirer's semaphore is static).
- KylinConfigBaseTest: compare ext jar paths order-insensitively.
- .gitignore: exclude UT temp files and generated query result artifacts.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
* KYLIN-6092 Replace ad-hoc ObjectMapper instances with JsonUtil
Route JSON reads/writes through JsonUtil so they inherit the shared
polymorphic type validator instead of using raw ObjectMapper instances.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
* KYLIN-6092 Build JsonUtil object mappers via JsonMapper.builder
Replace the static initializer block with builder-based field
initializers. The active serialization view still has to be applied on
the built mapper, since MapperBuilder exposes no setter for it.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---------
Co-authored-by: Claude Fable 5 <[email protected]>
---
.../org/apache/kylin/rest/config/AppConfig.java | 2 +
.../org/apache/kylin/common/util/JsonUtil.java | 140 +++++++++++++--------
.../apache/kylin/tool/restclient/RestClient.java | 5 +-
.../apache/kylin/common/KylinConfigBaseTest.java | 2 +-
.../common/util/JsonUtilPolymorphicTypeTest.java | 41 ++++++
.../job/impl/threadpool/BaseSchedulerTest.java | 13 ++
.../upgrade/common/MetadataVersionRefresher.java | 10 +-
.../metadata/cube/model/NSegmentConfigHelper.java | 11 +-
.../kylin/rest/service/AsyncQueryService.java | 5 +-
.../kylin/cluster/parser/SchedulerParser.scala | 6 +-
.../apache/kylin/parser/TimedJsonStreamParser.java | 6 +-
.../apache/kylin/streaming/rest/RestSupport.java | 4 +-
.../tool/discovery/ServiceInstanceSerializer.java | 7 +-
.../apache/kylin/tool/setup/KapGetClusterInfo.java | 6 +-
14 files changed, 167 insertions(+), 91 deletions(-)
diff --git
a/src/common-service/src/main/java/org/apache/kylin/rest/config/AppConfig.java
b/src/common-service/src/main/java/org/apache/kylin/rest/config/AppConfig.java
index fffb25458a..97680aea12 100644
---
a/src/common-service/src/main/java/org/apache/kylin/rest/config/AppConfig.java
+++
b/src/common-service/src/main/java/org/apache/kylin/rest/config/AppConfig.java
@@ -27,6 +27,7 @@ import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
import org.apache.kylin.common.util.DefaultHostInfoFetcher;
import org.apache.kylin.common.util.HostInfoFetcher;
+import org.apache.kylin.common.util.JsonUtil;
import org.apache.kylin.common.util.TimeUtil;
import org.apache.kylin.rest.cluster.ClusterManager;
import org.apache.kylin.rest.cluster.DefaultClusterManager;
@@ -189,6 +190,7 @@ public class AppConfig implements WebMvcConfigurer {
public ObjectMapper getObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
+
objectMapper.setPolymorphicTypeValidator(JsonUtil.KYLIN_TYPE_VALIDATOR);
FilterProvider filterProvider = new
SimpleFilterProvider().addFilter("passwordFilter",
SimpleBeanPropertyFilter.serializeAllExcept("password"));
diff --git
a/src/core-common/src/main/java/org/apache/kylin/common/util/JsonUtil.java
b/src/core-common/src/main/java/org/apache/kylin/common/util/JsonUtil.java
index 32a85dd05e..988897d8bf 100644
--- a/src/core-common/src/main/java/org/apache/kylin/common/util/JsonUtil.java
+++ b/src/core-common/src/main/java/org/apache/kylin/common/util/JsonUtil.java
@@ -46,47 +46,55 @@ import
org.apache.kylin.shaded.jackson.datatype.guava.GuavaModule;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.json.JsonMapper;
+import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
+import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.LRUMap;
-import com.fasterxml.jackson.databind.util.LookupCache;
public class JsonUtil {
- // reuse the object mapper to save memory footprint
- private static final ObjectMapper mapper = new ObjectMapper();
- private static final ObjectMapper indentMapper = new ObjectMapper();
- private static final SimpleFilterProvider simpleFilterProvider = new
SimpleFilterProvider()
+ private static final SimpleFilterProvider SIMPLE_FILTER_PROVIDER = new
SimpleFilterProvider()
.setFailOnUnknownId(false);
+ public static final TypeFactory CUSTOM_TYPE_FACTORY =
TypeFactory.defaultInstance()
+ .withCache(new LRUMap<>(16, 2000));
+ // restrict classes loadable via @JsonTypeInfo(Id.CLASS) ids (Event,
SegmentRange, ...)
+ // to Kylin's own types, blocking deserialization gadget attacks
+ public static final PolymorphicTypeValidator KYLIN_TYPE_VALIDATOR =
BasicPolymorphicTypeValidator.builder()
+ .allowIfSubType("org.apache.kylin.").build();
+
+ // reuse the object mapper to save memory footprint
+ private static final ObjectMapper MAPPER =
withPersistenceView(JsonMapper.builder()
+ .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false)
+
.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true)
+
.filterProvider(SIMPLE_FILTER_PROVIDER).typeFactory(CUSTOM_TYPE_FACTORY).addModule(new
GuavaModule())
+ .polymorphicTypeValidator(KYLIN_TYPE_VALIDATOR).build());
+
+ private static final ObjectMapper INDENT_MAPPER =
withPersistenceView(JsonMapper.builder()
+ .configure(SerializationFeature.INDENT_OUTPUT,
true).filterProvider(SIMPLE_FILTER_PROVIDER)
+ .typeFactory(CUSTOM_TYPE_FACTORY).addModule(new GuavaModule())
+ .polymorphicTypeValidator(KYLIN_TYPE_VALIDATOR).build());
+
+ private static final ObjectMapper DEFAULT_MAPPER = JsonMapper.builder()
+ .polymorphicTypeValidator(KYLIN_TYPE_VALIDATOR).build();
- static {
- LookupCache<Object, JavaType> cache = new LRUMap<>(16, 2000);
- TypeFactory customTypeFactory =
TypeFactory.defaultInstance().withCache(cache);
- mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false)
-
.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true)
-
.setConfig(mapper.getSerializationConfig().withView(PersistenceView.class));
- mapper.setFilterProvider(simpleFilterProvider);
- mapper.setTypeFactory(customTypeFactory);
- mapper.registerModule(new GuavaModule());
- indentMapper.configure(SerializationFeature.INDENT_OUTPUT, true)
-
.setConfig(indentMapper.getSerializationConfig().withView(PersistenceView.class));
- indentMapper.setFilterProvider(simpleFilterProvider);
- indentMapper.setTypeFactory(customTypeFactory);
- indentMapper.registerModule(new GuavaModule());
+ // MapperBuilder has no active-view setter, so apply it on the built mapper
+ private static ObjectMapper withPersistenceView(ObjectMapper mapper) {
+ return
mapper.setConfig(mapper.getSerializationConfig().withView(PersistenceView.class));
}
public static ArrayNode createArrayNode() {
- return mapper.createArrayNode();
+ return MAPPER.createArrayNode();
}
public static <T> T readValue(File src, Class<T> valueType) throws
IOException {
- return mapper.readValue(src, valueType);
+ return MAPPER.readValue(src, valueType);
}
public static <T> T readValueQuietly(File src, Class<T> valueType) {
@@ -106,65 +114,85 @@ public class JsonUtil {
}
public static <T> T readValue(String content, Class<T> valueType) throws
IOException {
- return mapper.readValue(content, valueType);
+ return MAPPER.readValue(content, valueType);
+ }
+
+ public static <T> T readValueDefault(String content, Class<T> valueType)
throws IOException {
+ return DEFAULT_MAPPER.readValue(content, valueType);
}
public static <T> T readValue(Reader src, Class<T> valueType) throws
IOException {
- return mapper.readValue(src, valueType);
+ return MAPPER.readValue(src, valueType);
}
public static <T> T readValue(InputStream src, Class<T> valueType) throws
IOException {
- return mapper.readValue(src, valueType);
+ return MAPPER.readValue(src, valueType);
}
public static <T> T readValue(byte[] src, Class<T> valueType) throws
IOException {
- return mapper.readValue(src, valueType);
+ return MAPPER.readValue(src, valueType);
}
public static <T> ObjectNode valueToTree(T value) {
- return mapper.valueToTree(value);
+ return MAPPER.valueToTree(value);
}
public static <T> T readValue(String content, TypeReference<T>
valueTypeRef) throws IOException {
- return mapper.readValue(content, valueTypeRef);
+ return MAPPER.readValue(content, valueTypeRef);
}
public static <T> T readValue(File src, TypeReference<T> valueTypeRef)
throws IOException {
- return mapper.readValue(src, valueTypeRef);
+ return MAPPER.readValue(src, valueTypeRef);
}
public static <T> T readValue(InputStream src, TypeReference<T>
valueTypeRef) throws IOException {
- return mapper.readValue(src, valueTypeRef);
+ return MAPPER.readValue(src, valueTypeRef);
}
public static Map<String, String> readValueAsMap(String content) throws
IOException {
TypeReference<HashMap<String, String>> typeRef = new
TypeReference<HashMap<String, String>>() {
};
- return mapper.readValue(content, typeRef);
+ return MAPPER.readValue(content, typeRef);
}
public static Set<String> readValueAsSet(String content) throws
IOException {
TypeReference<HashSet<String>> typeRef = new
TypeReference<HashSet<String>>() {
};
- return mapper.readValue(content, typeRef);
+ return MAPPER.readValue(content, typeRef);
}
public static List<String> readValueAsList(String content) throws
IOException {
TypeReference<ArrayList<String>> typeRef = new
TypeReference<ArrayList<String>>() {
};
- return mapper.readValue(content, typeRef);
+ return MAPPER.readValue(content, typeRef);
}
public static JsonNode readValueAsTree(String content) throws IOException {
- return mapper.readTree(content);
+ return MAPPER.readTree(content);
+ }
+
+ public static JsonNode readValueAsTreeDefault(String content) throws
IOException {
+ return DEFAULT_MAPPER.readTree(content);
+ }
+
+ public static JsonNode readValueAsTreeDefault(byte[] content) throws
IOException {
+ return DEFAULT_MAPPER.readTree(content);
+ }
+
+ public static JsonNode readValueAsTreeDefault(InputStream iso) throws
IOException {
+ return DEFAULT_MAPPER.readTree(iso);
}
public static void writeValueIndent(OutputStream out, Object value) throws
IOException {
- indentMapper.writeValue(out, value);
+ INDENT_MAPPER.writeValue(out, value);
+ }
+
+ public static void writeValueIndentDefault(OutputStream out, Object value)
throws IOException {
+ DEFAULT_MAPPER.writerWithDefaultPrettyPrinter().writeValue(out, value);
}
public static void writeValue(OutputStream out, Object value) throws
IOException {
- mapper.writeValue(out, value);
+ MAPPER.writeValue(out, value);
}
public static void writeValue(File out, Object value) throws IOException {
@@ -173,11 +201,15 @@ public class JsonUtil {
public static <T> String writeValueAsStringForCollection(Object value,
TypeReference<T> ref)
throws JsonProcessingException {
- return mapper.writerFor(ref).writeValueAsString(value);
+ return MAPPER.writerFor(ref).writeValueAsString(value);
}
public static String writeValueAsString(Object value) throws
JsonProcessingException {
- return mapper.writeValueAsString(value);
+ return MAPPER.writeValueAsString(value);
+ }
+
+ public static String writeValueAsStringDefault(Object value) throws
JsonProcessingException {
+ return DEFAULT_MAPPER.writeValueAsString(value);
}
public static String writeValueAsStringQuietly(Object value) {
@@ -189,37 +221,41 @@ public class JsonUtil {
}
public static byte[] writeValueAsBytes(Object value) throws
JsonProcessingException {
- return mapper.writeValueAsBytes(value);
+ return MAPPER.writeValueAsBytes(value);
}
public static byte[] writeValueAsIndentBytes(Object value) throws
JsonProcessingException {
- return indentMapper.writeValueAsBytes(value);
+ return INDENT_MAPPER.writeValueAsBytes(value);
}
public static String writeValueAsIndentString(Object value) throws
JsonProcessingException {
- return indentMapper.writeValueAsString(value);
+ return INDENT_MAPPER.writeValueAsString(value);
}
public static String writeValueAsStringWithPretty(Object value) throws
JsonProcessingException {
- return
indentMapper.writerWithDefaultPrettyPrinter().writeValueAsString(value);
+ return
INDENT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(value);
}
public static <T> T convert(Object obj, Class<T> valueType) {
- return mapper.convertValue(obj, valueType);
+ return MAPPER.convertValue(obj, valueType);
+ }
+
+ public static <T> T convertDefault(Object obj, Class<T> valueType) {
+ return DEFAULT_MAPPER.convertValue(obj, valueType);
}
public static <T> T convert(Object obj, TypeReference<T> valueType) {
- return mapper.convertValue(obj, valueType);
+ return MAPPER.convertValue(obj, valueType);
}
public static <T> T deepCopy(T src, Class<T> valueType) throws IOException
{
- String s = mapper.writeValueAsString(src);
- return mapper.readValue(s, valueType);
+ String s = MAPPER.writeValueAsString(src);
+ return MAPPER.readValue(s, valueType);
}
public static <T> T deepCopy(T src, TypeReference<T> valueType) throws
IOException {
- String s = mapper.writeValueAsString(src);
- return mapper.readValue(s, valueType);
+ String s = MAPPER.writeValueAsString(src);
+ return MAPPER.readValue(s, valueType);
}
public static <T> T deepCopyQuietly(T src, Class<T> valueType) {
@@ -240,10 +276,10 @@ public class JsonUtil {
public static <T extends RootPersistentEntity> T copyForWrite(T entity,
Serializer<T> serializer,
@Nullable BiConsumer<T, String> initEntityAfterReload) {
- if (!entity.isCachedAndShared())
+ if (!entity.isCachedAndShared()) {
return entity;
- else
- return copyBySerialization(entity, serializer,
initEntityAfterReload);
+ }
+ return copyBySerialization(entity, serializer, initEntityAfterReload);
}
public static <T extends RootPersistentEntity> T copyBySerialization(T
entity, Serializer<T> serializer,
@@ -275,7 +311,7 @@ public class JsonUtil {
public static boolean isJson(String content) {
try {
- mapper.readTree(content);
+ MAPPER.readTree(content);
return true;
} catch (IOException e) {
return false;
diff --git
a/src/core-common/src/main/java/org/apache/kylin/tool/restclient/RestClient.java
b/src/core-common/src/main/java/org/apache/kylin/tool/restclient/RestClient.java
index 63269f1782..2292749031 100644
---
a/src/core-common/src/main/java/org/apache/kylin/tool/restclient/RestClient.java
+++
b/src/core-common/src/main/java/org/apache/kylin/tool/restclient/RestClient.java
@@ -65,7 +65,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
/**
*/
@@ -155,7 +154,7 @@ public class RestClient {
HashMap<String, String> paraMap = new HashMap<>();
paraMap.put("sql", sql);
paraMap.put("project", project);
- String jsonMsg = new ObjectMapper().writeValueAsString(paraMap);
+ String jsonMsg = JsonUtil.writeValueAsStringDefault(paraMap);
post.setEntity(new StringEntity(jsonMsg, "UTF-8"));
return client.execute(post);
}
@@ -383,7 +382,7 @@ public class RestClient {
paraMap.put("stage", stage);
paraMap.put("progress", progress);
paraMap.put("updateTime", updateTime);
- put.setEntity(new StringEntity(new
ObjectMapper().writeValueAsString(paraMap), "UTF-8"));
+ put.setEntity(new
StringEntity(JsonUtil.writeValueAsStringDefault(paraMap), "UTF-8"));
response = client.execute(put);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
String msg = EntityUtils.toString(response.getEntity());
diff --git
a/src/core-common/src/test/java/org/apache/kylin/common/KylinConfigBaseTest.java
b/src/core-common/src/test/java/org/apache/kylin/common/KylinConfigBaseTest.java
index 29a665bb0d..f3b3c07e99 100644
---
a/src/core-common/src/test/java/org/apache/kylin/common/KylinConfigBaseTest.java
+++
b/src/core-common/src/test/java/org/apache/kylin/common/KylinConfigBaseTest.java
@@ -1516,7 +1516,7 @@ class KylinConfigBaseTest {
val withoutGluten = config.getKylinExtJarsPath(false);
val withoutExpected = "," + celebornJar.getAbsolutePath() + "," +
mysqlJar.getAbsolutePath();
- Assertions.assertEquals(withoutExpected, withoutGluten);
+ assertUnorderedEqualsIgnoringEmpty(withoutExpected, withoutGluten);
}
@Test
diff --git
a/src/core-common/src/test/java/org/apache/kylin/common/util/JsonUtilPolymorphicTypeTest.java
b/src/core-common/src/test/java/org/apache/kylin/common/util/JsonUtilPolymorphicTypeTest.java
new file mode 100644
index 0000000000..e896326178
--- /dev/null
+++
b/src/core-common/src/test/java/org/apache/kylin/common/util/JsonUtilPolymorphicTypeTest.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kylin.common.util;
+
+import org.apache.kylin.common.persistence.event.Event;
+import org.apache.kylin.common.persistence.event.ResourceDeleteEvent;
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
+
+public class JsonUtilPolymorphicTypeTest {
+
+ @Test
+ public void testKylinEventSubTypeAllowed() throws Exception {
+ String json = JsonUtil.writeValueAsString(new
ResourceDeleteEvent("PROJECT/p1"));
+ Event event = JsonUtil.readValue(json, Event.class);
+ Assert.assertTrue(event instanceof ResourceDeleteEvent);
+ }
+
+ @Test
+ public void testNonKylinClassIdRejected() {
+ String json =
"{\"@class\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://evil/x\"}";
+ Assert.assertThrows(InvalidTypeIdException.class, () ->
JsonUtil.readValue(json, Event.class));
+ }
+}
diff --git
a/src/core-job/src/test/java/org/apache/kylin/job/impl/threadpool/BaseSchedulerTest.java
b/src/core-job/src/test/java/org/apache/kylin/job/impl/threadpool/BaseSchedulerTest.java
index bfd943d7b3..1108303a2e 100644
---
a/src/core-job/src/test/java/org/apache/kylin/job/impl/threadpool/BaseSchedulerTest.java
+++
b/src/core-job/src/test/java/org/apache/kylin/job/impl/threadpool/BaseSchedulerTest.java
@@ -70,10 +70,23 @@ public abstract class BaseSchedulerTest extends
NLocalFileMetadataTestCase {
@After
public void tearDown() throws Exception {
+ JobContextUtil.stopScheduler();
+ awaitJobWorkersStopped();
JobContextUtil.cleanUp();
cleanupTestMetadata();
}
+ /**
+ * Stopping the scheduler only interrupts the job workers, it does not
wait for them: the driver memory permit is
+ * given back later, while the worker unwinds (JobExecutor#close).
ResourceAcquirer's semaphore is static and never
+ * reset, so a worker outliving its test steals memory from the next
test's baseline.
+ */
+ private void awaitJobWorkersStopped() {
+ with().pollInterval(10, TimeUnit.MILLISECONDS).await().atMost(30,
TimeUnit.SECONDS)
+ .until(() -> Thread.getAllStackTraces().keySet().stream()
+ .noneMatch(t -> t.getName().startsWith("JobWorker(")));
+ }
+
protected void waitForJobFinish(String jobId) {
waitForJobFinish(jobId, 120000);
}
diff --git
a/src/core-metadata/src/main/java/org/apache/kylin/cube/upgrade/common/MetadataVersionRefresher.java
b/src/core-metadata/src/main/java/org/apache/kylin/cube/upgrade/common/MetadataVersionRefresher.java
index 005fce51f8..76d6a74a15 100644
---
a/src/core-metadata/src/main/java/org/apache/kylin/cube/upgrade/common/MetadataVersionRefresher.java
+++
b/src/core-metadata/src/main/java/org/apache/kylin/cube/upgrade/common/MetadataVersionRefresher.java
@@ -26,14 +26,13 @@ import java.util.NavigableSet;
import org.apache.kylin.common.persistence.ResourceStore;
import org.apache.kylin.common.util.HadoopUtil;
+import org.apache.kylin.common.util.JsonUtil;
import org.apache.kylin.guava30.shaded.common.collect.Lists;
import org.apache.kylin.guava30.shaded.common.io.ByteSource;
import org.apache.kylin.metadata.MetadataConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
@@ -54,9 +53,6 @@ public class MetadataVersionRefresher {
}
public void refresh() throws IOException {
- ObjectMapper mapper = new ObjectMapper();
- mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
-
List<String> all = Lists.newArrayList();
collectFiles(this.store, "/", all);
@@ -66,11 +62,11 @@ public class MetadataVersionRefresher {
logger.info("Updating metadata version of path {}", path);
ObjectNode objectNode;
try (InputStream is =
this.store.getResource(path).getByteSource().openStream()) {
- objectNode = (ObjectNode) mapper.readTree(is);
+ objectNode = (ObjectNode)
JsonUtil.readValueAsTreeDefault(is);
}
objectNode.put("version", version);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- mapper.writeValue(baos, objectNode);
+ JsonUtil.writeValueIndentDefault(baos, objectNode);
this.store.checkAndPutResource(path,
ByteSource.wrap(baos.toByteArray()), -1);
}
}
diff --git
a/src/core-metadata/src/main/java/org/apache/kylin/metadata/cube/model/NSegmentConfigHelper.java
b/src/core-metadata/src/main/java/org/apache/kylin/metadata/cube/model/NSegmentConfigHelper.java
index e8d64d4cea..cd23ebf61b 100644
---
a/src/core-metadata/src/main/java/org/apache/kylin/metadata/cube/model/NSegmentConfigHelper.java
+++
b/src/core-metadata/src/main/java/org/apache/kylin/metadata/cube/model/NSegmentConfigHelper.java
@@ -21,20 +21,17 @@ package org.apache.kylin.metadata.cube.model;
import java.util.Map;
import org.apache.kylin.common.KylinConfig;
+import org.apache.kylin.common.util.JsonUtil;
import org.apache.kylin.guava30.shaded.common.base.Preconditions;
import org.apache.kylin.metadata.model.NDataModel;
import org.apache.kylin.metadata.model.NDataModelManager;
import org.apache.kylin.metadata.model.SegmentConfig;
import org.apache.kylin.metadata.project.NProjectManager;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
import lombok.val;
public class NSegmentConfigHelper {
- final static ObjectMapper mapper = new ObjectMapper();
-
public static SegmentConfig getModelSegmentConfig(String project, String
model) {
val kylinConfig = KylinConfig.getInstanceFromEnv();
NDataModel dataModel = NDataModelManager.getInstance(kylinConfig,
project).getDataModelDesc(model);
@@ -59,8 +56,8 @@ public class NSegmentConfigHelper {
if (secondSegmentConfig == null) {
return firstSegmentConfig;
}
- Map<String, Object> firstSegmentConfigMap =
mapper.convertValue(firstSegmentConfig, Map.class);
- Map<String, Object> secondSegmentConfigMap =
mapper.convertValue(secondSegmentConfig, Map.class);
+ Map<String, Object> firstSegmentConfigMap =
JsonUtil.convertDefault(firstSegmentConfig, Map.class);
+ Map<String, Object> secondSegmentConfigMap =
JsonUtil.convertDefault(secondSegmentConfig, Map.class);
secondSegmentConfigMap.entrySet().forEach(entry -> {
val key = entry.getKey();
val value = entry.getValue();
@@ -68,7 +65,7 @@ public class NSegmentConfigHelper {
firstSegmentConfigMap.put(key, value);
}
});
- return mapper.convertValue(firstSegmentConfigMap, SegmentConfig.class);
+ return JsonUtil.convertDefault(firstSegmentConfigMap,
SegmentConfig.class);
}
private static SegmentConfig getFromProject(String project, KylinConfig
kylinConfig) {
diff --git
a/src/query-service/src/main/java/org/apache/kylin/rest/service/AsyncQueryService.java
b/src/query-service/src/main/java/org/apache/kylin/rest/service/AsyncQueryService.java
index f692bb2c47..4f9bc46da1 100644
---
a/src/query-service/src/main/java/org/apache/kylin/rest/service/AsyncQueryService.java
+++
b/src/query-service/src/main/java/org/apache/kylin/rest/service/AsyncQueryService.java
@@ -53,6 +53,7 @@ import org.apache.kylin.common.exception.KylinException;
import org.apache.kylin.common.msg.Message;
import org.apache.kylin.common.msg.MsgPicker;
import org.apache.kylin.common.util.FileSystemUtil;
+import org.apache.kylin.common.util.JsonUtil;
import org.apache.kylin.guava30.shaded.common.collect.Lists;
import org.apache.kylin.metadata.project.NProjectManager;
import org.apache.kylin.metadata.project.ProjectInstance;
@@ -65,8 +66,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
import lombok.Getter;
import lombok.Setter;
@@ -346,7 +345,7 @@ public class AsyncQueryService extends BasicService {
}
}
}
- String json = new ObjectMapper().writeValueAsString(rowResults);
+ String json = JsonUtil.writeValueAsStringDefault(rowResults);
IOUtils.copy(IOUtils.toInputStream(json), outputStream);
}
diff --git
a/src/spark-project/engine-spark/src/main/scala/org/apache/kylin/cluster/parser/SchedulerParser.scala
b/src/spark-project/engine-spark/src/main/scala/org/apache/kylin/cluster/parser/SchedulerParser.scala
index 9db043abc7..5f7e548a53 100644
---
a/src/spark-project/engine-spark/src/main/scala/org/apache/kylin/cluster/parser/SchedulerParser.scala
+++
b/src/spark-project/engine-spark/src/main/scala/org/apache/kylin/cluster/parser/SchedulerParser.scala
@@ -18,18 +18,18 @@
package org.apache.kylin.cluster.parser
-import com.fasterxml.jackson.databind.{JsonNode, ObjectMapper}
+import com.fasterxml.jackson.databind.JsonNode
import org.apache.kylin.cluster.AvailableResource
+import org.apache.kylin.common.util.JsonUtil
import org.apache.spark.internal.Logging
trait SchedulerParser extends Logging {
protected var root: JsonNode = _
- protected lazy val mapper = new ObjectMapper
def availableResource(queueName: String): AvailableResource
def parse(schedulerInfo: String): Unit = {
- this.root = mapper.readTree(schedulerInfo)
+ this.root = JsonUtil.readValueAsTreeDefault(schedulerInfo)
}
// value in some scheduler info format to "value"
diff --git
a/src/streaming/src/main/java/org/apache/kylin/parser/TimedJsonStreamParser.java
b/src/streaming/src/main/java/org/apache/kylin/parser/TimedJsonStreamParser.java
index 83feb7e2b4..8a4673304c 100644
---
a/src/streaming/src/main/java/org/apache/kylin/parser/TimedJsonStreamParser.java
+++
b/src/streaming/src/main/java/org/apache/kylin/parser/TimedJsonStreamParser.java
@@ -24,9 +24,9 @@ import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.lang3.StringUtils;
+import org.apache.kylin.common.util.JsonUtil;
import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ValueNode;
@@ -39,13 +39,11 @@ import lombok.SneakyThrows;
*/
public class TimedJsonStreamParser extends AbstractDataParser<ByteBuffer> {
- private final ObjectMapper mapper = new ObjectMapper();
-
@SneakyThrows
@Override
protected Map<String, Object> parse(ByteBuffer input) {
Map<String, Object> flatMap = new
TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- traverseJsonNode("", mapper.readTree(input.array()), flatMap);
+ traverseJsonNode("", JsonUtil.readValueAsTreeDefault(input.array()),
flatMap);
return flatMap;
}
diff --git
a/src/streaming/src/main/java/org/apache/kylin/streaming/rest/RestSupport.java
b/src/streaming/src/main/java/org/apache/kylin/streaming/rest/RestSupport.java
index 55f4529422..b76f906ffa 100644
---
a/src/streaming/src/main/java/org/apache/kylin/streaming/rest/RestSupport.java
+++
b/src/streaming/src/main/java/org/apache/kylin/streaming/rest/RestSupport.java
@@ -43,7 +43,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.val;
import lombok.var;
@@ -81,9 +80,8 @@ public class RestSupport implements Closeable {
while (retry++ < MAX_RETRY) {
try {
if (param != null && httpReqBase instanceof
HttpEntityEnclosingRequestBase) {
- ObjectMapper mapper = new ObjectMapper();
((HttpEntityEnclosingRequestBase) httpReqBase)
- .setEntity(new
StringEntity(mapper.writeValueAsString(param), "UTF-8"));
+ .setEntity(new
StringEntity(JsonUtil.writeValueAsStringDefault(param), "UTF-8"));
}
HttpResponse response = httpClient.execute(httpReqBase);
diff --git
a/src/tool/src/main/java/org/apache/kylin/tool/discovery/ServiceInstanceSerializer.java
b/src/tool/src/main/java/org/apache/kylin/tool/discovery/ServiceInstanceSerializer.java
index 368d6ce822..3ae9dfeaa4 100644
---
a/src/tool/src/main/java/org/apache/kylin/tool/discovery/ServiceInstanceSerializer.java
+++
b/src/tool/src/main/java/org/apache/kylin/tool/discovery/ServiceInstanceSerializer.java
@@ -23,13 +23,10 @@ import java.util.Map;
import org.apache.curator.x.discovery.ServiceInstance;
import org.apache.curator.x.discovery.ServiceType;
import org.apache.curator.x.discovery.details.JsonInstanceSerializer;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.kylin.common.util.JsonUtil;
public class ServiceInstanceSerializer<T> extends JsonInstanceSerializer<T> {
- private ObjectMapper mapper = new ObjectMapper();
-
public ServiceInstanceSerializer(Class<T> payloadClass) {
super(payloadClass);
}
@@ -37,7 +34,7 @@ public class ServiceInstanceSerializer<T> extends
JsonInstanceSerializer<T> {
@Override
public ServiceInstance<T> deserialize(byte[] bytes) throws Exception {
String content = new String(bytes, Charset.defaultCharset());
- Map map = mapper.readValue(content, Map.class);
+ Map map = JsonUtil.readValueDefault(content, Map.class);
return castToServiceInstance(map);
}
diff --git
a/src/tool/src/main/java/org/apache/kylin/tool/setup/KapGetClusterInfo.java
b/src/tool/src/main/java/org/apache/kylin/tool/setup/KapGetClusterInfo.java
index ceefb75c8d..cde148e0c3 100644
--- a/src/tool/src/main/java/org/apache/kylin/tool/setup/KapGetClusterInfo.java
+++ b/src/tool/src/main/java/org/apache/kylin/tool/setup/KapGetClusterInfo.java
@@ -33,6 +33,7 @@ import org.apache.kylin.cluster.SchedulerInfoCmdHelper;
import org.apache.kylin.common.KylinConfig;
import org.apache.kylin.common.util.BufferedLogger;
import org.apache.kylin.common.util.HadoopUtil;
+import org.apache.kylin.common.util.JsonUtil;
import org.apache.kylin.common.util.ShellException;
import org.apache.kylin.common.util.StringHelper;
import org.apache.kylin.common.util.Unsafe;
@@ -41,7 +42,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.val;
@@ -121,7 +121,7 @@ public class KapGetClusterInfo {
throw new IllegalStateException(
"Cannot get yarn metrics with url: " +
yarnMasterUrlBase + YARN_METRICS_SUFFIX);
}
- clusterMetrics = new
ObjectMapper().readTree(response).path("clusterMetrics");
+ clusterMetrics =
JsonUtil.readValueAsTreeDefault(response).path("clusterMetrics");
} catch (Exception e) {
logger.warn("Attempt 1 failed to get clusterMetrics from cluster
via curl command.", e);
}
@@ -132,7 +132,7 @@ public class KapGetClusterInfo {
}
// attempt 2
try {
- clusterMetrics = new
ObjectMapper().readTree(SchedulerInfoCmdHelper.metricsInfo()).path("clusterMetrics");
+ clusterMetrics =
JsonUtil.readValueAsTreeDefault(SchedulerInfoCmdHelper.metricsInfo()).path("clusterMetrics");
} catch (IOException | RuntimeException exception) {
logger.warn("Attempt 2 failed to get clusterMetrics from cluster
via SchedulerInfoCmdHelper.", exception);
}