Bug Description
ApplicationDescriptorDeserializer.deserialize() calls .asText(), .asInt(), and .asLong() on the result of node.get() without checking if the node exists. If any required field is missing from the JSON, node.get() returns null, and calling methods on null causes NullPointerException.
Location
- File:
jplatform-cluster/src/main/java/org/flossware/jplatform/cluster/ApplicationDescriptorJsonModule.java
- Lines: 74-121
Problematic Code
@Override
public ApplicationDescriptor deserialize(JsonParser parser, DeserializationContext ctxt)
throws IOException {
JsonNode node = parser.getCodec().readTree(parser);
ApplicationDescriptor.Builder builder = ApplicationDescriptor.builder()
.applicationId(node.get("applicationId").asText()) // NPE if missing!
.name(node.get("name").asText()) // NPE if missing!
.version(node.get("version").asText()) // NPE if missing!
.mainClass(node.get("mainClass").asText()); // NPE if missing!
// ... later ...
JsonNode threadPool = node.get("threadPool");
if (threadPool != null) {
ThreadPoolConfig config = ThreadPoolConfig.builder()
.corePoolSize(threadPool.get("corePoolSize").asInt()) // NPE if field missing!
.maxPoolSize(threadPool.get("maxPoolSize").asInt()) // NPE if field missing!
.queueCapacity(threadPool.get("queueCapacity").asInt()) // NPE if field missing!
.keepAliveTimeSeconds(threadPool.get("keepAliveTimeSeconds").asLong()) // NPE if field missing!
.build();
builder.threadPoolConfig(config);
}
return builder.build();
}
Example Demonstrating the Bug
// Malformed JSON missing required fields
String json = "{\"applicationId\": \"app1\"}"; // Missing name, version, mainClass
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new ApplicationDescriptorJsonModule());
// Throws NullPointerException instead of helpful error
ApplicationDescriptor descriptor = mapper.readValue(json, ApplicationDescriptor.class);
// NPE: Cannot invoke "com.fasterxml.jackson.databind.JsonNode.asText()" because the return value of "com.fasterxml.jackson.databind.JsonNode.get(String)" is null
Similar issue with threadPool nested fields:
// ThreadPool object present but missing required fields
String json = "{
\"applicationId\": \"app1\",
\"name\": \"App\",
\"version\": \"1.0\",
\"mainClass\": \"com.App\",
\"threadPool\": {
\"corePoolSize\": 5
// Missing maxPoolSize, queueCapacity, keepAliveTimeSeconds
}
}";
// NPE when accessing missing threadPool fields
ApplicationDescriptor descriptor = mapper.readValue(json, ApplicationDescriptor.class);
Impact
- Uninformative errors: NullPointerException instead of clear validation message
- Debugging difficulty: Stack trace doesn't indicate which field is missing
- Cluster failures: When deserializing application state from distributed store, malformed data causes crashes
- Security: Malformed JSON from untrusted sources can cause DoS
Proposed Fix
Add null checks with informative error messages:
@Override
public ApplicationDescriptor deserialize(JsonParser parser, DeserializationContext ctxt)
throws IOException {
JsonNode node = parser.getCodec().readTree(parser);
// Validate required fields
JsonNode appIdNode = node.get("applicationId");
if (appIdNode == null) {
throw new IOException("Missing required field: applicationId");
}
JsonNode nameNode = node.get("name");
if (nameNode == null) {
throw new IOException("Missing required field: name");
}
JsonNode versionNode = node.get("version");
if (versionNode == null) {
throw new IOException("Missing required field: version");
}
JsonNode mainClassNode = node.get("mainClass");
if (mainClassNode == null) {
throw new IOException("Missing required field: mainClass");
}
ApplicationDescriptor.Builder builder = ApplicationDescriptor.builder()
.applicationId(appIdNode.asText())
.name(nameNode.asText())
.version(versionNode.asText())
.mainClass(mainClassNode.asText());
// ... classpath and properties ...
// Validate nested threadPool fields if present
JsonNode threadPool = node.get("threadPool");
if (threadPool != null) {
JsonNode coreSizeNode = threadPool.get("corePoolSize");
JsonNode maxSizeNode = threadPool.get("maxPoolSize");
JsonNode queueCapNode = threadPool.get("queueCapacity");
JsonNode keepAliveNode = threadPool.get("keepAliveTimeSeconds");
if (coreSizeNode == null || maxSizeNode == null ||
queueCapNode == null || keepAliveNode == null) {
throw new IOException("threadPool object is incomplete - missing required fields");
}
ThreadPoolConfig config = ThreadPoolConfig.builder()
.corePoolSize(coreSizeNode.asInt())
.maxPoolSize(maxSizeNode.asInt())
.queueCapacity(queueCapNode.asInt())
.keepAliveTimeSeconds(keepAliveNode.asLong())
.build();
builder.threadPoolConfig(config);
}
// ... rest of code ...
return builder.build();
}
This provides clear error messages when required fields are missing and prevents NullPointerException.
Bug Description
ApplicationDescriptorDeserializer.deserialize()calls.asText(),.asInt(), and.asLong()on the result ofnode.get()without checking if the node exists. If any required field is missing from the JSON,node.get()returns null, and calling methods on null causes NullPointerException.Location
jplatform-cluster/src/main/java/org/flossware/jplatform/cluster/ApplicationDescriptorJsonModule.javaProblematic Code
Example Demonstrating the Bug
Similar issue with threadPool nested fields:
Impact
Proposed Fix
Add null checks with informative error messages:
This provides clear error messages when required fields are missing and prevents NullPointerException.