| type | architecture |
|---|---|
| created | 2026-04-06 |
| updated | 2026-05-04 |
| status | Active |
BLUF: Axis2 becomes a multi-protocol service platform — one service implementation serving JSON-RPC (existing callers), REST (Data API consumers), and MCP (AI agents) simultaneously. The Spring Boot starter removes adoption tax. OpenAPI generation makes every Axis2 service AI-discoverable. A native MCP transport eliminates the wrapper layer entirely. No other Java framework can do all three from the same service deployment.
Foundation already in place:
springbootdemo-tomcat11— working reference implementation (Spring Boot 3.x + Axis2 + Tomcat 11 + Java 25)axis2-openapimodule — OpenAPI spec served at/openapi.json,/openapi.yaml,/swagger-ui,/openapi-mcp.jsonmodules/transport-h2— HTTP/2 transport module (proof of concept, tested)modules/spring-boot-starter— functional Spring Boot autoconfiguration- ThreadPool instance-field fix committed (
27860ddf9f) - Java 25 + Tomcat 11.0.20 full end-to-end test passing
| Phase | Status | Notes |
|---|---|---|
| Immediate Track (B1-B3, C3, D1-D3) | DONE | MCP catalog, inputSchema, authScope, streaming, resources, error hardening |
| Phase 1 — Spring Boot Starter | DONE | Full autoconfiguration: AxisServlet, AAR/MAR scanning, OpenAPI+MCP endpoints, SOAP/JSON mode. JWT security intentionally deferred (deployment-specific). |
| Phase A — Error Contracts | DONE | Axis2JsonErrorResponse, JsonRpcFaultException, 422/429/503 HTTP status codes, OpenAPI ErrorResponse schema, MCP _meta.errorContract |
| Phase B — MCP Schema Completion | DONE | Java type introspection for request/response schemas in both OpenAPI and MCP catalog. mcpAuthScope, mcpStreaming parameters. |
| Phase 2 — OpenAPI Annotation Bridge | PARTIAL | POJO introspection done. springdoc @Operation/@Parameter annotation processing not yet started. |
| Phase 3 — REST Transport | NOT STARTED | Dual-protocol JSON-RPC + REST from same service |
| Phase 4 — MCP Bridge | NOT STARTED | OpenAPI-driven MCP wrapper |
| Phase 5 — HTTP/2 Publication | NOT STARTED | transport-h2 graduation to supported module |
| Phase 6 — Native MCP Transport | DEFERRED | Bridge approach sufficient; prove MCP catalog adoption first |
| Phase 7 — Community | NOT STARTED | Blog post, migration guide |
| NEW Phase JPA — JPA/Hibernate Schema Generation | DONE | See below |
| NEW Phase PG — Offset/Limit Pagination | DONE | See below |
| NEW Phase TX — Transaction Demarcation Module | PLANNED | See below |
Goal: Auto-generate OpenAPI components/schemas from JPA-annotated entity classes
AND Hibernate XML mapping files (.hbm.xml). This makes Axis2 the first framework that
can serve OpenAPI schemas for any Hibernate project regardless of mapping style.
Bang: 8/10 | Effort: days
The original pickup doc rejected "OpenAPI from hbm.xml" as too narrow. That was correct for hbm.xml alone — but the real opportunity is broader:
- JPA annotations (
@Entity,@Column,@ManyToOne) — the standard for new projects and the pattern used by typical financial applications (dozens of entities, Spring Data JPA repositories, Jakarta Persistence) - hbm.xml — still in production at many organizations (legacy mapping files, Hibernate 3.0 DTD, backtick-quoted SQL Server columns). These projects won't rewrite to annotations just for OpenAPI support.
Supporting both from one module covers ~95% of Hibernate deployments.
Two metadata extraction strategies, one unified schema output:
┌──────────────────────┐ ┌──────────────────────┐
│ JPA Annotation │ │ HBM.XML Parser │
│ Introspector │ │ (DOM/SAX) │
│ │ │ │
│ @Entity │ │ <class name="..."> │
│ @Column │ │ <property> │
│ @ManyToOne │ │ <many-to-one> │
│ @Id/@GeneratedValue │ │ <id>/<generator> │
│ @Transient │ │ <version> │
└──────────┬───────────┘ └──────────┬────────────┘
│ │
▼ ▼
┌───────────────────────────────────────┐
│ EntitySchemaModel (unified) │
│ - entityName, tableName │
│ - fields: name, type, nullable, │
│ readOnly, relationship │
│ - idFields, versionField │
│ - ignoredFields (audit/system) │
└──────────────────┬────────────────────┘
│
▼
┌───────────────────────────────────────┐
│ OpenAPI Schema Generator │
│ - Read schema (all fields) │
│ - Write schema (excludes @Id with │
│ @GeneratedValue, @IgnoreChanges, │
│ version fields) │
│ - $ref for relationships │
└───────────────────────────────────────┘
| JPA Annotation | Schema Effect |
|---|---|
@Entity |
Top-level schema object |
@Table(name=...) |
Schema title / x-table-name |
@Column(nullable=false) |
Added to required array |
@Column(length=255) |
maxLength: 255 |
@Id |
Marked in schema description |
@Id + @GeneratedValue |
readOnly: true in write schema |
@ManyToOne / @OneToOne |
$ref to related entity schema |
@OneToMany / @ManyToMany |
type: array, items: {$ref: ...} |
@Transient |
Excluded from schema |
@Version |
Excluded from write schema |
@Enumerated |
type: string, enum: [...] |
BigDecimal (ID context) |
type: integer |
Double / Float |
type: number |
Byte (boolean context) |
type: boolean |
Timestamp / Date |
type: string, format: date-time |
| HBM Element/Attribute | Schema Effect |
|---|---|
<class name="..." table="..."> |
Top-level schema object |
<property not-null="true"> |
Added to required array |
<property type="java.lang.String"> |
type: string |
<property type="java.lang.Long"> |
type: integer |
<property type="java.lang.Boolean"> |
type: boolean |
<property type="timestamp"> |
type: string, format: date-time |
<id> + <generator> |
readOnly: true in write schema |
<version> |
Excluded from write schema |
<many-to-one class="..."> |
$ref to related entity schema |
<set> / <bag> with <one-to-many> |
type: array, items: {$ref: ...} |
<component> |
Inline object properties (flattened) |
Column with sql-type="nvarchar(max)" |
type: string (no maxLength) |
Projects may use custom annotations that carry business semantics:
| Custom Annotation | Schema Effect |
|---|---|
@IgnoreChanges |
Excluded from write schema (audit fields) |
@IncludeInUpdate |
Included in write schema (explicitly) |
The module accepts a configurable list of "exclude from write" annotation class names so any project can declare their own audit-field markers.
Plugs into the existing addComponents() method. When axis2-jpa-schema is on the
classpath and a service's parameter or return type is an @Entity class (or has a
companion .hbm.xml), the JPA introspector generates the schema instead of the
generic POJO introspector.
modules/jpa-schema/src/main/java/org/apache/axis2/jpa/schema/JpaSchemaGenerator.javamodules/jpa-schema/src/main/java/org/apache/axis2/jpa/schema/EntitySchemaModel.javamodules/jpa-schema/src/main/java/org/apache/axis2/jpa/schema/AnnotationIntrospector.javamodules/jpa-schema/src/main/java/org/apache/axis2/jpa/schema/HbmXmlIntrospector.java
None — optional module. Works standalone or with the Spring Boot starter.
Goal: A .mar module that wraps service invocations in JTA transactions. Deploy the
module, set <parameter name="transactional">true</parameter> in services.xml, and
every operation on that service gets automatic begin/commit/rollback.
Bang: 7/10 | Effort: days
Axis2 already has the plumbing:
Axis2UserTransactioninmodules/kernelwraps JTAUserTransactionTransactionConfigurationprovides JNDI-based transaction manager lookupAbstractMessageReceiver.receive()setsSET_ROLLBACK_ONLYon faultAbstractTemplatedHandlerprovidesshouldInvoke()/doInvoke()separation- Handler
flowComplete()is called in reverse order after processing (for cleanup)
The module injects a TransactionHandler into the OperationInPhase:
InFlow phases:
Transport → Addressing → Security → PreDispatch → Dispatch
→ OperationInPhase:
[TransactionHandler.invoke()] ← BEGIN transaction
[other handlers]
[MessageReceiver.receive()] ← service method runs
→ OperationOutPhase
OutFlow / flowComplete:
[TransactionHandler.flowComplete()] ← COMMIT or ROLLBACK
<module name="axis2-tx" class="org.apache.axis2.tx.TransactionModule">
<InFlow>
<handler name="TransactionHandler"
class="org.apache.axis2.tx.TransactionHandler">
<order phase="OperationInPhase" phaseFirst="true"/>
</handler>
</InFlow>
</module>shouldInvoke(): checks service-leveltransactionalparameterdoInvoke(): begins JTA transaction, stores ref in MessageContextflowComplete(): ifSET_ROLLBACK_ONLYor AxisFault occurred → rollback; else → commit
<service name="PortfolioAssetService">
<parameter name="transactional">true</parameter>
<parameter name="transactionTimeout">30</parameter>
<!-- operations... -->
</service>modules/tx/src/main/java/org/apache/axis2/tx/TransactionModule.javamodules/tx/src/main/java/org/apache/axis2/tx/TransactionHandler.javamodules/tx/src/META-INF/module.xml
Requires JTA API on classpath (provided by WildFly, Tomcat with Atomikos/Narayana, etc.).
Goal: Provide a standard pagination envelope that works with existing DAO layers
using setFirstResult(offset) / setMaxResults(limit) (the standard JPA/Hibernate
query pattern), giving frontend grids and API consumers the metadata they need to
render paging controls or implement infinite scroll.
Bang: 7/10 | Effort: hours
Most enterprise applications paginate with integer offsets because:
-
DAO compatibility — existing service layers pass
firstResult/maxResultto Hibernate's Query API. Cursor pagination requires a stable sort key and stateful server-side tokens — added complexity with no benefit when the query is already offset-based. -
Frontend grid compatibility — SmartClient, AG Grid, React Table, and MUI DataGrid all natively speak offset/limit via
startRow/endRoworpage/pageSize. Cursor tokens require client-side adaptation. -
Total count is cheap — offset-based APIs can include
totalCount(from a parallelSELECT COUNT(*)) to enable "Showing 1–50 of 1,247" UI patterns. Cursor APIs typically omit totals because they are expensive for the cursor model. -
Virtual scrolling — grids that load the next chunk as the user scrolls use
hasMoreto decide whether to fetch. This maps directly tooffset + limit < totalCount.
Cursor pagination remains valuable for append-only feeds (activity logs, message streams) where offset instability is a real problem. The framework does not preclude adding cursor support later — it simply does not ship cursor tokens by default because the common case does not need them.
- No cursor tokens (UUID-based cursors add complexity most deployments don't need)
- No GraphQL relay-style
edges/pageInfo/Connectiontypes - No Spring Data
Pageable/Page<T>integration (many projects don't use Spring Data)
{
"response": {
"data": [ ... ],
"pagination": {
"offset": 100,
"limit": 50,
"totalCount": 1247,
"hasMore": true
}
}
}PaginatedResponse<T> — generic response wrapper with data (list) and
pagination (metadata). Factory methods:
PaginatedResponse.of(items, offset, limit, totalCount)— standard paged responsePaginatedResponse.unpaginated(items)— wrap a full result set with no pagination
PaginationRequest — request-side helper with safe defaults:
offsetdefaults to 0, negative values clampedlimitdefaults to 50, capped at configurablemaxLimit(default 2000)- Services can embed these fields in their request POJO or accept separately
// React / TypeScript — infinite scroll
const { data, pagination } = await fetchPage(offset, limit);
setItems(prev => [...prev, ...data]);
if (pagination.hasMore) {
setNextOffset(pagination.offset + pagination.limit);
}
// React / TypeScript — page controls
const totalPages = Math.ceil(pagination.totalCount / pagination.limit);
const currentPage = Math.floor(pagination.offset / pagination.limit) + 1;PaginatedResponse is a plain POJO — serializes identically across all four Axis2
JSON formatters (Gson, Moshi, EnhancedGson/H2, EnhancedMoshi/H2) with no custom
adapters. The data list elements serialize as their runtime type regardless of
generic type erasure.
modules/json/src/org/apache/axis2/json/gson/rpc/PaginatedResponse.javamodules/json/src/org/apache/axis2/json/gson/rpc/PaginationRequest.javamodules/json/test/org/apache/axis2/json/gson/rpc/PaginatedResponseTest.java(22 tests)
None — zero-dependency POJO pattern. Works with any JSON formatter.
Status: DONE — all steps completed.
DONE. Both Option 1 (static declaration in services.xml) and Option 2 (auto-generated
from Java type introspection) are implemented and tested. OpenApiSpecGenerator.generateMcpCatalogJson()
reads mcpInputSchema param with Jackson validation, falls back to auto-introspection
via generateSchemaFromServiceClass(), then falls back to empty schema.
DONE. Reads via getMcpStringParam(), emits x-authScope in tool node. Tests cover
operation-level, service-level, and absent cases.
DONE. Boolean mcpStreaming parameter adds x-streaming: true. Absent/false
suppresses the field entirely (compact catalog).
DONE. generateMcpResourcesJson() returns resources/list response with URI,
name, description, mimeType, and metadata (wsdlUrl, operations, requiresAuth) for
each deployed service.
Axis2/C implementation — tracked separately in axis-axis2-c-core repo.
Axis2/C implementation — tracked separately.
DONE. All three Java financial benchmark operations have explicit mcpInputSchema
in services.xml with full JSON Schema definitions.
Status: DONE.
Delivered as modules/spring-boot-starter with:
Axis2AutoConfiguration— master switch, respectsaxis2.enabledAxis2RepositoryAutoConfiguration— stages axis2.xml (SOAP or JSON mode templates)Axis2ServletAutoConfiguration— registers AxisServlet with configurable pathAxis2OpenApiAutoConfiguration— registers OpenAPI/MCP servlet endpointsAxis2Properties— externalized configuration viaapplication.properties- Built-in
axis2-soap.xmlandaxis2-json.xmltemplates - WildFly VFS compatibility
- Full test suite
Not included (intentional): JWT/Spring Security autoconfiguration — deployment-specific,
consuming apps provide their own SecurityFilterChain.
Status: PARTIAL.
Done:
- POJO introspection for request/response types →
components/schemas - Structured error response schema (
ErrorResponse) with 422/429/503 responses - MCP tool definitions with
inputSchemaauto-generated from Java types - Request body and 200 response schemas linked via
$ref
Remaining:
- springdoc/Swagger annotation processing (
@Operation,@Parameter,@ApiResponse) - OpenAPI 3.1 output (currently 3.0.1)
Phase 1 (starter) — satisfied.
Status: NOT STARTED.
Goal: Same Axis2 @WebService class reachable via both JSON-RPC and REST paths.
- REST dispatcher activation in autoconfiguration
@RestMappingannotation for URL templates- HTTP method routing (GET/POST/PUT/DELETE → 405 enforcement)
- Parallel transports — same service, no code duplication
- OpenAPI spec reflects REST paths when
@RestMappingpresent
Phase 1 (starter), Phase 2 (REST paths in OpenAPI spec).
Status: NOT STARTED.
Thin Spring Boot app that reads /openapi-mcp.json and implements MCP protocol
(initialize, tools/list, tools/call). Forwards calls to Axis2 endpoints.
Phase 2 (requires /openapi-mcp.json endpoint — satisfied).
Status: NOT STARTED.
Graduate modules/transport-h2 to supported module with formal test suite,
performance benchmarks, and starter integration.
Phase 1 (starter) — satisfied.
Status: DEFERRED.
Bridge approach (Phase 4) sufficient. Prove MCP catalog adoption before investing in native transport. Build when there's demand.
Status: NOT STARTED.
Apache blog post, migration guide, reference implementation documentation.
| Phase | Deliverable | Status |
|---|---|---|
| Immediate Track | MCP catalog, inputSchema, resources | DONE |
| 1 | axis2-spring-boot-starter |
DONE |
| A | Error contracts (422/429/503) | DONE |
| B | MCP schema completion | DONE |
| JPA | JPA/Hibernate → OpenAPI schema generation | DONE |
| PG | Offset/limit pagination envelope | DONE |
| TX | Transaction demarcation .mar module | PLANNED |
| 2 | OpenAPI annotation bridge (springdoc) | PARTIAL |
| 3 | REST transport + dual-protocol | NOT STARTED |
| 4 | MCP bridge wrapper | NOT STARTED |
| 5 | HTTP/2 transport publication | NOT STARTED |
| 6 | Native MCP transport | DEFERRED |
| 7 | Community posts, migration guide | NOT STARTED |
A single Axis2 service deployment, configured once, serves:
Claude Desktop / AI agent → MCP (bridge Phase 4, or native Phase 6)
↓
Data API / React frontend → REST (Phase 3) ──► Axis2 Service
↑ (one implementation)
Existing JSON-RPC callers → JSON-RPC (unchanged)
With:
- OpenAPI spec and MCP tool definitions auto-generated (Phase 2 + JPA)
- JPA entity schemas for any Hibernate project (Phase JPA)
- Automatic transaction management (Phase TX)
- Spring Boot starter (Phase 1) — single Maven dependency
- Structured error contracts with proper HTTP status codes (Phase A)