This is an automated email from the ASF dual-hosted git repository.

robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-core.git

commit 08a8b2b24c96d011b8ed9392591ba6e62d25e12b
Author: Robert Lazarski <[email protected]>
AuthorDate: Sun Apr 5 17:19:59 2026 -1000

    openapi: replace custom Jackson mappers with swagger-core Json/Yaml 
utilities
    
    OpenAPI spec serialization now delegates entirely to 
io.swagger.v3.core.util.Json
    and io.swagger.v3.core.util.Yaml rather than constructing custom 
ObjectMappers.
    This removes all direct Jackson imports from OpenApiSpecGenerator; Jackson 
remains
    on the classpath transitively from swagger-core.
    
    swagger-core's ObjectMapperFactory already configures NON_NULL, disables
    WRITE_DATES_AS_TIMESTAMPS, and disables FAIL_ON_EMPTY_BEANS — the same 
settings
    we had manually, now handled by the library. Pretty vs compact output is 
selected
    at serialization time via Json.pretty()/Yaml.pretty() vs 
mapper().writeValueAsString().
    
    Tests: add testPrettyAndCompactJsonOutput and 
testPrettyAndCompactYamlOutput to
    exercise both branches of the isPrettyPrint() path through the swagger-core 
utilities.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
---
 modules/openapi/README.md                          |  2 +-
 modules/openapi/pom.xml                            |  4 +-
 .../apache/axis2/openapi/OpenApiSpecGenerator.java | 61 +++++-----------------
 .../axis2/openapi/OpenApiSpecGeneratorTest.java    | 47 ++++++++++++++++-
 4 files changed, 62 insertions(+), 52 deletions(-)

diff --git a/modules/openapi/README.md b/modules/openapi/README.md
index add52816ca..674d065892 100644
--- a/modules/openapi/README.md
+++ b/modules/openapi/README.md
@@ -182,4 +182,4 @@ String yaml = generator.generateOpenApiYaml(httpRequest);
 
 - **Request body schema** — all operations are typed as `object` because Axis2 
JSON-RPC services use `JsonRpcMessageReceiver` and have no annotation-level 
parameter metadata. Schema details must be added via `OpenApiCustomizer` or by 
serving a static schema file.
 - **GET operations** — all operations are mapped to `POST`; override via 
`OpenApiCustomizer` if GET endpoints are needed.
-- **YAML format** — uses `jackson-dataformat-yaml` (transitive from 
`swagger-core`); no additional dependency required.
+- **YAML format** — delegates to `io.swagger.v3.core.util.Yaml`, which uses 
`jackson-dataformat-yaml` internally; no additional dependency required.
diff --git a/modules/openapi/pom.xml b/modules/openapi/pom.xml
index 052a61c9bf..0a54f060b2 100644
--- a/modules/openapi/pom.xml
+++ b/modules/openapi/pom.xml
@@ -64,7 +64,9 @@
             <artifactId>swagger-annotations</artifactId>
         </dependency>
 
-        <!-- Jackson comes transitively from swagger-core dependency -->
+        <!-- No direct Jackson dependency: serialization delegates to 
swagger-core's
+             Json/Yaml utilities (io.swagger.v3.core.util), which manage their 
own
+             Jackson mapper internally. Jackson arrives transitively from 
swagger-core. -->
 
         <!-- Moshi for JSON serialization (preferred over Jackson where 
possible) -->
         <dependency>
diff --git 
a/modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiSpecGenerator.java
 
b/modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiSpecGenerator.java
index 0ea1a3224d..d67a3f02fe 100644
--- 
a/modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiSpecGenerator.java
+++ 
b/modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiSpecGenerator.java
@@ -44,11 +44,8 @@ import org.apache.axis2.engine.AxisConfiguration;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
-import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
+import io.swagger.v3.core.util.Json;
+import io.swagger.v3.core.util.Yaml;
 import com.squareup.moshi.Moshi;
 import com.squareup.moshi.JsonAdapter;
 import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter;
@@ -78,8 +75,6 @@ public class OpenApiSpecGenerator {
 
     private final ConfigurationContext configurationContext;
     private final ServiceIntrospector serviceIntrospector;
-    private final ObjectMapper objectMapper;  // Required for Swagger OpenAPI 
model serialization (JSON)
-    private final ObjectMapper yamlMapper;    // Jackson with YAMLFactory for 
YAML output
     private final Moshi moshi;  // Preferred for general JSON operations
     private final JsonProcessingMetrics metrics;
     private final OpenApiConfiguration configuration;
@@ -99,36 +94,11 @@ public class OpenApiSpecGenerator {
         this.configuration = config != null ? config : new 
OpenApiConfiguration();
         this.serviceIntrospector = new ServiceIntrospector(configContext);
 
-        // Configure Jackson for OpenAPI model serialization with HTTP/2 
optimization metrics.
-        // Fix: NON_NULL inclusion is required because the Swagger model POJOs 
(Info, Contact,
-        // License, Schema, etc.) declare many optional fields that default to 
null. Without this,
-        // Jackson serializes every null field as "key": null, inflating a 
simple 3-service spec
-        // by ~300 null entries and producing invalid Swagger UI display 
artifacts.
-        this.objectMapper = new ObjectMapper();
-        
this.objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
-        
this.objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
-        this.objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
-        if (configuration.isPrettyPrint()) {
-            this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
-        }
-
-        // Fix: generateOpenApiYaml() was returning JSON because it delegated 
to
-        // generateOpenApiJson(). A second ObjectMapper backed by YAMLFactory 
produces
-        // proper YAML. WRITE_DOC_START_MARKER is disabled to suppress the 
"---" header
-        // that confuses some YAML parsers. jackson-dataformat-yaml is already 
on the
-        // classpath transitively from io.swagger.core.v3:swagger-core.
-        YAMLFactory yamlFactory = YAMLFactory.builder()
-            .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER)
-            .build();
-        this.yamlMapper = new ObjectMapper(yamlFactory);
-        
this.yamlMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
-        
this.yamlMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
-        this.yamlMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
-        if (configuration.isPrettyPrint()) {
-            this.yamlMapper.enable(SerializationFeature.INDENT_OUTPUT);
-        }
+        // OpenAPI spec serialization delegates entirely to swagger-core's 
Json/Yaml utilities,
+        // which already configure NON_NULL, WRITE_DATES_AS_TIMESTAMPS=false, 
and FAIL_ON_EMPTY_BEANS=false
+        // on their internal Jackson mapper. No direct Jackson dependency is 
needed in this class.
 
-        // Initialize Moshi for general JSON operations (preferred over 
Jackson where possible)
+        // Initialize Moshi for general JSON operations (Axis2 preference)
         this.moshi = new Moshi.Builder()
             .add(Date.class, new Rfc3339DateJsonAdapter())
             .build();
@@ -136,7 +106,7 @@ public class OpenApiSpecGenerator {
         // Initialize performance metrics from moshih2 package for HTTP/2 
optimization tracking
         this.metrics = new JsonProcessingMetrics();
 
-        log.info("OpenAPI JSON processing configured with Moshi + Jackson 
hybrid approach (Jackson only for Swagger model serialization, Moshi preferred 
for other JSON operations, moshih2 performance tracking enabled)");
+        log.info("OpenAPI spec generator configured: swagger-core Json/Yaml 
utilities for spec serialization, Moshi for general JSON, moshih2 HTTP/2 
metrics enabled");
     }
 
     /**
@@ -180,24 +150,19 @@ public class OpenApiSpecGenerator {
         try {
             OpenAPI spec = generateOpenApiSpec(request);
 
-            // Use Jackson for OpenAPI model serialization (required by 
Swagger library) with enhanced HTTP/2 performance metrics tracking
             long startTime = System.currentTimeMillis();
-            String jsonSpec = objectMapper.writeValueAsString(spec);
+            String jsonSpec = configuration.isPrettyPrint() ? 
Json.pretty(spec) : Json.mapper().writeValueAsString(spec);
             long processingTime = System.currentTimeMillis() - startTime;
 
-            // Record performance metrics using moshih2 infrastructure
             long specSize = jsonSpec.getBytes().length;
             metrics.recordProcessingStart(requestId, specSize, false);
             metrics.recordProcessingComplete(requestId, specSize, 
processingTime);
 
-            // Pretty printing is handled by Jackson configuration in 
constructor
-
-            log.debug("Generated OpenAPI JSON specification (" + (specSize / 
1024) + "KB) in " + processingTime + "ms using Jackson with HTTP/2 metrics");
+            log.debug("Generated OpenAPI JSON specification (" + (specSize / 
1024) + "KB) in " + processingTime + "ms");
             return jsonSpec;
         } catch (Exception e) {
-            long errorTime = 0; // Error occurred, no meaningful processing 
time
-            metrics.recordProcessingError(requestId, e, errorTime);
-            log.error("Failed to generate OpenAPI JSON using Jackson with 
HTTP/2 metrics", e);
+            metrics.recordProcessingError(requestId, e, 0);
+            log.error("Failed to generate OpenAPI JSON", e);
             return "{\"error\":\"Failed to generate OpenAPI specification\"}";
         }
     }
@@ -210,7 +175,7 @@ public class OpenApiSpecGenerator {
         try {
             OpenAPI spec = generateOpenApiSpec(request);
             long startTime = System.currentTimeMillis();
-            String yamlSpec = yamlMapper.writeValueAsString(spec);
+            String yamlSpec = configuration.isPrettyPrint() ? 
Yaml.pretty(spec) : Yaml.mapper().writeValueAsString(spec);
             long processingTime = System.currentTimeMillis() - startTime;
             long specSize = yamlSpec.getBytes().length;
             metrics.recordProcessingStart(requestId, specSize, false);
@@ -664,7 +629,7 @@ public class OpenApiSpecGenerator {
     public String getOptimizationRecommendations() {
         JsonProcessingMetrics.Statistics stats = metrics.getStatistics();
         StringBuilder recommendations = new StringBuilder();
-        recommendations.append("OpenAPI JSON Processing Performance Analysis 
(Jackson + HTTP/2 Metrics):\n");
+        recommendations.append("OpenAPI JSON Processing Performance Analysis 
(swagger-core + HTTP/2 Metrics):\n");
         recommendations.append("  - Total specifications generated: 
").append(stats.getTotalRequests()).append("\n");
         recommendations.append("  - Average processing time: 
").append(stats.getAverageProcessingTimeMs()).append("ms\n");
         recommendations.append("  - Total data processed: 
").append(stats.getTotalBytes() / 1024).append("KB\n");
diff --git 
a/modules/openapi/src/test/java/org/apache/axis2/openapi/OpenApiSpecGeneratorTest.java
 
b/modules/openapi/src/test/java/org/apache/axis2/openapi/OpenApiSpecGeneratorTest.java
index 08e80b3e2b..57901b9e22 100644
--- 
a/modules/openapi/src/test/java/org/apache/axis2/openapi/OpenApiSpecGeneratorTest.java
+++ 
b/modules/openapi/src/test/java/org/apache/axis2/openapi/OpenApiSpecGeneratorTest.java
@@ -406,8 +406,9 @@ public class OpenApiSpecGeneratorTest extends TestCase {
 
     /**
      * Test that generated JSON contains no null fields.
-     * Jackson must be configured with Include.NON_NULL so null-valued model
-     * fields (e.g. termsOfService, extensions, summary) are omitted entirely.
+     * swagger-core's ObjectMapperFactory configures NON_NULL on the shared 
mapper,
+     * so null-valued model fields (e.g. termsOfService, extensions, summary) 
are
+     * omitted entirely without any additional configuration in this module.
      */
     public void testNoNullFieldsInJson() throws Exception {
         String json = generator.generateOpenApiJson(mockRequest);
@@ -416,6 +417,48 @@ public class OpenApiSpecGeneratorTest extends TestCase {
         assertFalse("JSON output must not contain ':null' entries", 
json.contains(":null"));
     }
 
+    /**
+     * Test compact vs pretty JSON output — exercises the isPrettyPrint() 
branch
+     * that selects between Json.pretty(spec) and 
Json.mapper().writeValueAsString(spec).
+     * Both paths delegate to swagger-core's Json utility.
+     */
+    public void testPrettyAndCompactJsonOutput() throws Exception {
+        OpenApiConfiguration prettyConfig = new OpenApiConfiguration();
+        prettyConfig.setPrettyPrint(true);
+        OpenApiSpecGenerator prettyGen = new 
OpenApiSpecGenerator(configurationContext, prettyConfig);
+        String prettyJson = prettyGen.generateOpenApiJson(mockRequest);
+        assertTrue("Pretty JSON must contain newlines", 
prettyJson.contains("\n"));
+        assertFalse("Pretty JSON must not contain null fields", 
prettyJson.contains(": null"));
+
+        OpenApiConfiguration compactConfig = new OpenApiConfiguration();
+        compactConfig.setPrettyPrint(false);
+        OpenApiSpecGenerator compactGen = new 
OpenApiSpecGenerator(configurationContext, compactConfig);
+        String compactJson = compactGen.generateOpenApiJson(mockRequest);
+        // Compact output may not have newlines (single-line) but must still 
be valid JSON
+        assertTrue("Compact JSON must start with '{'", 
compactJson.trim().startsWith("{"));
+        assertFalse("Compact JSON must not contain null fields", 
compactJson.contains(":null"));
+    }
+
+    /**
+     * Test compact vs pretty YAML output — exercises the isPrettyPrint() 
branch
+     * that selects between Yaml.pretty(spec) and 
Yaml.mapper().writeValueAsString(spec).
+     */
+    public void testPrettyAndCompactYamlOutput() throws Exception {
+        OpenApiConfiguration prettyConfig = new OpenApiConfiguration();
+        prettyConfig.setPrettyPrint(true);
+        OpenApiSpecGenerator prettyGen = new 
OpenApiSpecGenerator(configurationContext, prettyConfig);
+        String prettyYaml = prettyGen.generateOpenApiYaml(mockRequest);
+        assertFalse("Pretty YAML must not start with '{'", 
prettyYaml.trim().startsWith("{"));
+        assertTrue("Pretty YAML must contain openapi key", 
prettyYaml.contains("openapi:"));
+
+        OpenApiConfiguration compactConfig = new OpenApiConfiguration();
+        compactConfig.setPrettyPrint(false);
+        OpenApiSpecGenerator compactGen = new 
OpenApiSpecGenerator(configurationContext, compactConfig);
+        String compactYaml = compactGen.generateOpenApiYaml(mockRequest);
+        assertFalse("Compact YAML must not start with '{'", 
compactYaml.trim().startsWith("{"));
+        assertTrue("Compact YAML must contain openapi key", 
compactYaml.contains("openapi:"));
+    }
+
     /**
      * Test that each generated operation carries a non-null requestBody.
      * All JSON-RPC services accept a POST body; omitting requestBody leaves

Reply via email to