Bug Summary
The DotCDNAPIImpl.invalidate() method fails when purging individual URLs via the Bunny.net API (POST https://api.bunny.net/purge?url=...). The Bunny.net purge endpoint returns HTTP 200 with an empty body on success, but the code attempts to parse the empty response as JSON, causing a JSONException.
Root Cause
This is a regression introduced in commit 3e0bfcd ("moving away from objectMapper") in the com.dotcms.dotcdn plugin. The change replaced ObjectMapper-based JSON parsing with JSONObject in the getData() method but removed the error-handling safety net (Try.of(...).getOrElse(Map.of())).
Before (working):
private Map<String, Object> getData(CircuitBreakerUrlBuilder url) {
final String response = Try.of((() -> url.build().doString())).getOrElse("{\"response\":\"FAILURE\"}");
return Try.of(() -> ObjectMapperProvider.mapper.get().readValue(response, Map.class))
.getOrElse(Map.of());
}
When Bunny returned an empty body, ObjectMapper.readValue("", Map.class) would throw, the Try caught it, and Map.of() was returned — which didn't contain "response" -> "FAILURE", so the purge was correctly treated as successful.
After (broken):
private Map<String, Object> getData(CircuitBreakerUrlBuilder url) {
final String response = Try.of((() -> url.build().doString())).getOrElse("{\"response\":\"FAILURE\"}");
JSONObject jsonObject = new JSONObject(response);
return jsonObject;
}
new JSONObject("") throws a JSONException with no try/catch, crashing the entire invalidate() call.
Impact
- Purging individual URLs via the
/api/v1/dotcdn DELETE endpoint fails even when Bunny.net successfully processes the purge.
- The
invalidateAll() method (which uses RestClientBuilder and checks HTTP status directly) is NOT affected.
- Actionlet-triggered invalidations are also affected since they go through the same
invalidate() code path.
Steps to Reproduce
- Configure dotCDN app with valid Bunny.net API key, pull zone ID, and CDN domain.
- Send a DELETE request to
/api/v1/dotcdn to purge cache for a specific URL.
- Observe that the operation fails with a
JSONException even though Bunny.net returns HTTP 200.
Fix
The invalidate() method should use CircuitBreakerUrl.doResponse() to get the HTTP status code directly instead of routing through getData(), since the Bunny purge endpoint does not return a JSON response body. Success should be determined by statusCode == 200. Additionally, request/response logging should be added for observability.
Affected File
com.dotcms.dotcdn plugin: src/main/java/com/dotcms/cloud/cdn/api/DotCDNAPIImpl.java
Acceptance Criteria
dotCMS Version
Current Evergreen
26.04.02-01
Severity
High - Major functionality broken
Links
https://helpdesk.dotcms.com/a/tickets/36175
Bug Summary
The
DotCDNAPIImpl.invalidate()method fails when purging individual URLs via the Bunny.net API (POST https://api.bunny.net/purge?url=...). The Bunny.net purge endpoint returns HTTP 200 with an empty body on success, but the code attempts to parse the empty response as JSON, causing aJSONException.Root Cause
This is a regression introduced in commit
3e0bfcd("moving away from objectMapper") in thecom.dotcms.dotcdnplugin. The change replacedObjectMapper-based JSON parsing withJSONObjectin thegetData()method but removed the error-handling safety net (Try.of(...).getOrElse(Map.of())).Before (working):
When Bunny returned an empty body,
ObjectMapper.readValue("", Map.class)would throw, theTrycaught it, andMap.of()was returned — which didn't contain"response" -> "FAILURE", so the purge was correctly treated as successful.After (broken):
new JSONObject("")throws aJSONExceptionwith no try/catch, crashing the entireinvalidate()call.Impact
/api/v1/dotcdnDELETE endpoint fails even when Bunny.net successfully processes the purge.invalidateAll()method (which usesRestClientBuilderand checks HTTP status directly) is NOT affected.invalidate()code path.Steps to Reproduce
/api/v1/dotcdnto purge cache for a specific URL.JSONExceptioneven though Bunny.net returns HTTP 200.Fix
The
invalidate()method should useCircuitBreakerUrl.doResponse()to get the HTTP status code directly instead of routing throughgetData(), since the Bunny purge endpoint does not return a JSON response body. Success should be determined bystatusCode == 200. Additionally, request/response logging should be added for observability.Affected File
com.dotcms.dotcdnplugin:src/main/java/com/dotcms/cloud/cdn/api/DotCDNAPIImpl.javaAcceptance Criteria
invalidate()no longer routes purge calls throughgetData()/ JSON parsinginvalidateAll()behavior remains unchangedgetStats()) behavior remains unchangeddotCMS Version
Current Evergreen
26.04.02-01
Severity
High - Major functionality broken
Links
https://helpdesk.dotcms.com/a/tickets/36175