No axis2.xml changes are required for HTTP/2 and JSON support in Axis2/C. This document explains why Axis2/C differs from Axis2/Java in this regard and provides a comprehensive comparison of the architectural approaches.
Unlike Axis2/Java, Axis2/C automatically detects and handles HTTP/2 with JSON content without requiring any axis2.xml configuration changes. The request processing is determined at runtime through intelligent header analysis rather than static configuration.
Axis2/C uses the axis2_apache2_request_processor_is_json_http2_request() function in src/core/transport/http/server/apache2/axis2_apache2_request_processor_factory.c to automatically detect:
request->protocol for “HTTP/2” or “HTTP/2.0” stringsapplication/jsontext/jsonapplication/hal+jsonapplication/vnd.api+jsonAXIS2_EXTERN axis2_bool_t AXIS2_CALL axis2_apache2_request_processor_is_json_http2_request(request_rec* request) { const axis2_char_t* content_type = NULL; const axis2_char_t* protocol = NULL; if (!request) return AXIS2_FALSE; /* Check protocol version */ protocol = request->protocol; if (!protocol || (!strstr(protocol, "HTTP/2") && !strstr(protocol, "HTTP/2.0"))) { return AXIS2_FALSE; } /* Check content type */ content_type = apr_table_get(request->headers_in, "Content-Type"); if (!content_type) { content_type = apr_table_get(request->headers_in, "content-type"); } if (!content_type) return AXIS2_FALSE; /* JSON content type detection */ return (strstr(content_type, "application/json") != NULL || strstr(content_type, "text/json") != NULL || strstr(content_type, "application/hal+json") != NULL || strstr(content_type, "application/vnd.api+json") != NULL) ? AXIS2_TRUE : AXIS2_FALSE; }
The factory method at axis2_apache2_request_processor_factory.c:66-135 implements a decision matrix:
Simply send an HTTP/2 request with the appropriate Content-Type header:
curl -k --http2 -H "Content-Type: application/json" \ -X POST -d '{"getCameraStatus":[]}' \ https://localhost:8443/axis2/services/CameraControlService
No axis2.xml configuration is needed - the system automatically routes to the appropriate processor.
Based on the Spring Boot user guide (src/site/xdoc/docs/json-springboot-userguide.xml in axis-axis2-java-core), Axis2/Java requires explicit axis2.xml configuration because it uses a static configuration model where message processors are pre-configured rather than dynamically selected.
<message name="requestMessage"> <messageFormatter contentType="application/json" class="org.apache.axis2.json.moshi.JsonFormatter"/> </message>
Required classes mentioned in the Java documentation:
JsonRpcMessageReceiverJsonInOnlyRPCMessageReceiverJsonBuilderJSONBasedDefaultDispatcherJSONMessageHandler<transportSender name="h2" class="org.apache.axis2.transport.h2.impl.httpclient5.H2TransportSender"> <parameter name="PROTOCOL">HTTP/2.0</parameter> <parameter name="maxConcurrentStreams">100</parameter> <parameter name="initialWindowSize">65536</parameter> <parameter name="serverPushEnabled">false</parameter> <parameter name="connectionTimeout">30000</parameter> <parameter name="responseTimeout">300000</parameter> <parameter name="streamingBufferSize">65536</parameter> <parameter name="memoryPressureThreshold">0.8</parameter> <!-- Enterprise Big Data Configuration --> <parameter name="enableStreamingOptimization">true</parameter> <parameter name="enableMemoryOptimization">true</parameter> <parameter name="largePayloadThreshold">52428800</parameter> <!-- 50MB --> </transportSender>
Note on Buffer Sizes: The 64KB values shown above (initialWindowSize, streamingBufferSize) are Axis2/Java defaults. Axis2/C uses an incremental buffer growth approach:
maxJSONPayloadSize in services.xml (default 10MB)malloc/realloc since AXIS2_REALLOC is unreliableMemory Efficiency Comparison:
| Payload | Axis2/C (incremental) | Static 10MB | Savings |
|---|---|---|---|
| IoT/Camera (~24B) | 64KB | 10MB | 160x |
| Medium JSON (~50KB) | 64KB | 10MB | 160x |
| Large portfolio (~235KB) | 256KB | 10MB | 40x |
Content-Type: application/jsonThe fundamental difference between Axis2/C and Axis2/Java lies in their architectural philosophy:
This makes Axis2/C significantly easier to deploy for HTTP/2 and JSON scenarios while maintaining full backward compatibility with existing SOAP services. The intelligent request processor factory ensures that each request type gets appropriate handling without any manual configuration.
For HTTP/2 and JSON support, developers can focus on service implementation rather than configuration management, as the framework handles protocol and content-type detection transparently.
| Axis2/Java | Axis2/C | |
|---|---|---|
| Serialization | Streaming — Moshi/GSON writes fields directly to a JsonWriter backed by an OutputStream. No intermediate tree. | Tree — json-c builds a json_object* in memory, then json_object_to_json_string() serializes the whole tree at once. |
| Memory profile | Only the current field's value is in memory during serialization. A 50 MB response never exists as a 50 MB buffer. | The full json-c tree is in memory before serialization. For typical responses (1-10 KB) this is negligible; for large responses Java is more memory-efficient. |
| Axis2/Java | Axis2/C | |
|---|---|---|
| Mechanism | FlushingOutputStream wraps the transport OutputStream and flushes every 64 KB. The flush happens DURING Moshi/GSON serialization — each field written eventually triggers a flush, converting one buffered response into a stream of HTTP/2 DATA frames. | ap_rflush(r) called periodically during response generation via Apache httpd mod_h2. Achieves similar chunked delivery but is manual — each service must call ap_rflush() at the right points in its serialization code. |
| Scope | Framework-level — configure MoshiStreamingMessageFormatter in axis2.xml and ALL services get 64 KB streaming automatically. | Service-level — each service decides when to flush. The financial benchmark service uses the same 64 KB interval as Java. |
?fields=)| Axis2/Java | Axis2/C | |
|---|---|---|
| Approach | True streaming (Option C): reflection-based selective serialization inside MoshiStreamingMessageFormatter.writeFilteredObject(). Non-selected fields are NEVER serialized, never buffered, never written to the wire. | Post-build tree filtering: json-c builds the full json_object* tree, then finbench_filter_json_response() deletes unwanted keys via json_object_object_del(), then re-serializes. The full tree is briefly in memory. |
| Scope | Framework-level — FieldFilteringMessageFormatter wraps any MessageFormatter. Configure once in axis2.xml, all services get ?fields= for free. | Service-level — implemented in the financial benchmark service handler. Other C services would need their own filter call. |
| Code | ~320 lines (FieldFilteringMessageFormatter.java) + ~100 lines added to MoshiStreamingMessageFormatter.java) | ~60 lines in financial_benchmark_service_handler.c (finbench_extract_fields_param + finbench_filter_json_response) |
Axis2/Java has a pluggable message formatter pipeline in the kernel. MessageFormatter is an interface that the engine calls to serialize every outbound message. FlushingOutputStream, FieldFilteringMessageFormatter, and MoshiStreamingMessageFormatter all live in this pipeline. Any service deployed to any Axis2/Java instance gets these features by changing one line in axis2.xml.
Axis2/C has no equivalent pluggable JSON formatter pipeline. The C architecture was designed for SOAP (Axiom / libxml2). JSON services bypass the SOAP pipeline entirely by handling serialization themselves — each service builds its own json-c tree and calls json_object_to_json_string(). This is simpler per-service but means streaming and field filtering must be implemented in each service's handler rather than once in the framework.
The finbench_filter_json_response() function is already generic — it operates on any JSON string using json-c and axutil_strdup. To make field filtering available to all Axis2/C JSON services:
finbench_extract_fields_param() and finbench_filter_json_response() into an axutil shared utility (e.g., axutil_json_field_filter.c).axis2_json_rpc_msg_recv), check for ?fields= on the request URI and apply the filter to the outbound JSON string before sending.For now, the per-service approach in the financial benchmark handler is correct and KISS — it is ~60 lines of self-contained C that achieves feature parity with the Axis2/Java deployment.
| Feature | Axis2/Java | Axis2/C | Parity |
|---|---|---|---|
| HTTP/2 transport | modules/transport-h2 | mod_h2 via Apache httpd | ✅ |
| 64 KB streaming flush | FlushingOutputStream | ap_rflush(r) | ✅ |
?fields= response filtering | FieldFilteringMessageFormatter | finbench_filter_json_response() | ✅ |
| Pluggable for all services | Yes (axis2.xml config) | No (per-service) | ⚠️ Java ahead |
| Streaming during serialization | Yes (Moshi → Okio → flush) | No (json-c tree → serialize) | ⚠️ Java ahead |
| MCP tool catalog | openapi-mcp.json auto-generated | mcp_catalog_handler.c | ✅ |
| Financial benchmark parity | portfolioVariance, monteCarlo, scenarioAnalysis | Same three services | ✅ |
See also: