Skip to content

Commit 78234f3

Browse files
docs: Add JPA schema generation and pagination guides
Two xdoc documentation files for implemented features that had TOC entries but no docs: openapi-jpa-schema.xml — Documents the axis2-jpa-schema module: - JPA annotation introspection (@entity, @column, @id, etc.) - Hibernate XML mapping introspection (.hbm.xml) - Read vs Write schema generation (write excludes @GeneratedValue, @Version, custom audit annotations) - Custom write-exclude annotation registration - Relationship $ref convention for OpenAPI components - Type mapping tables for both annotation and HBM XML modes - 28 tests documented json-pagination.xml — Documents PaginatedResponse<T> and PaginationRequest: - Wire format with pagination metadata (offset, limit, totalCount, hasMore) - Service integration pattern (DAO offset/limit) - Safety: maxLimit clamping, negative offset handling - Frontend patterns: page controls, virtual scroll, SmartClient - Why offset/limit instead of cursor - 20 tests documented Also fixed: - TOC numbering (7.7.x → 7.6.x for JPA schema subsections) - json-rpc-mcp-guide.xml: updated pagination from "not implemented" to "offset/limit implemented" with link to new guide Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 45b1465 commit 78234f3

4 files changed

Lines changed: 393 additions & 10 deletions

File tree

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<!--
2+
~ Licensed to the Apache Software Foundation (ASF) under one
3+
~ or more contributor license agreements. See the NOTICE file
4+
~ distributed with this work for additional information
5+
~ regarding copyright ownership. The ASF licenses this file
6+
~ to you under the Apache License, Version 2.0 (the
7+
~ "License"); you may not use this file except in compliance
8+
~ with the License. You may obtain a copy of the License at
9+
~
10+
~ http://www.apache.org/licenses/LICENSE-2.0
11+
~
12+
~ Unless required by applicable law or agreed to in writing,
13+
~ software distributed under the License is distributed on an
14+
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
~ KIND, either express or implied. See the License for the
16+
~ specific language governing permissions and limitations
17+
~ under the License.
18+
-->
19+
20+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
21+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
22+
<html xmlns="http://www.w3.org/1999/xhtml">
23+
<head><title>Offset/Limit Pagination for JSON-RPC Services</title></head>
24+
<body>
25+
26+
<h1 id="overview">Offset/Limit Pagination for JSON-RPC Services</h1>
27+
28+
<p>Axis2 provides a generic pagination framework for JSON-RPC services
29+
backed by SQL databases. The two classes —
30+
<code>PaginationRequest</code> and <code>PaginatedResponse&lt;T&gt;</code> —
31+
map directly to JPA/Hibernate's <code>setFirstResult(offset)</code> and
32+
<code>setMaxResults(limit)</code> pattern.</p>
33+
34+
<p><strong>Package:</strong> <code>org.apache.axis2.json.rpc</code></p>
35+
36+
<h2 id="wire_format">Wire Format</h2>
37+
38+
<p>A paginated response wraps the result list with metadata:</p>
39+
40+
<pre>
41+
{
42+
"response": {
43+
"data": [ ... ],
44+
"pagination": {
45+
"offset": 0,
46+
"limit": 50,
47+
"totalCount": 1247,
48+
"hasMore": true
49+
}
50+
}
51+
}
52+
</pre>
53+
54+
<ul>
55+
<li><strong>offset</strong> — zero-based index of the first item in this page</li>
56+
<li><strong>limit</strong> — maximum items requested (page size)</li>
57+
<li><strong>totalCount</strong> — total items matching the query across all pages</li>
58+
<li><strong>hasMore</strong> — true when <code>offset + limit &lt; totalCount</code></li>
59+
</ul>
60+
61+
<h2 id="service_integration">Service Integration</h2>
62+
63+
<p>A typical service method delegates offset/limit to the DAO:</p>
64+
65+
<pre>
66+
public PaginatedResponse&lt;AssetBO&gt; findAssets(AssetQuery query) {
67+
List&lt;AssetBO&gt; items = dao.findList(query.getOffset(), query.getLimit());
68+
long total = dao.count(query);
69+
return PaginatedResponse.of(items, query.getOffset(), query.getLimit(), total);
70+
}
71+
</pre>
72+
73+
<p>The request POJO can embed <code>PaginationRequest</code> fields directly
74+
or accept them as separate parameters:</p>
75+
76+
<pre>
77+
// Client sends:
78+
{
79+
"searchTerm": "AAPL",
80+
"offset": 100,
81+
"limit": 50
82+
}
83+
</pre>
84+
85+
<h3>Unpaginated Responses</h3>
86+
87+
<p>For small lookup tables (e.g., a list of 15 departments), use the
88+
convenience factory to wrap the full list with <code>hasMore=false</code>:</p>
89+
90+
<pre>
91+
return PaginatedResponse.unpaginated(departments);
92+
// → offset=0, limit=15, totalCount=15, hasMore=false
93+
</pre>
94+
95+
<h2 id="safety">Safety: maxLimit Clamping and Input Validation</h2>
96+
97+
<p><code>PaginationRequest</code> enforces safety constraints at the getter level:</p>
98+
99+
<table border="1">
100+
<tr><th>Input</th><th>Behavior</th></tr>
101+
<tr><td><code>offset &lt; 0</code></td><td>Clamped to 0</td></tr>
102+
<tr><td><code>limit &lt;= 0</code></td><td>Default: 50</td></tr>
103+
<tr><td><code>limit &gt; maxLimit</code></td><td>Capped at maxLimit (default: 2000)</td></tr>
104+
</table>
105+
106+
<p>Services that handle expensive entities can lower the cap per-operation:</p>
107+
108+
<pre>
109+
// Large text fields — cap at 100 per page
110+
request.setMaxLimit(100);
111+
int safeLimit = request.getLimit(); // capped at 100
112+
</pre>
113+
114+
<h2 id="frontend_patterns">Frontend Patterns</h2>
115+
116+
<h3>Page Controls ("Showing 151–200 of 1,247")</h3>
117+
<pre>
118+
// JavaScript / TypeScript
119+
const { offset, limit, totalCount } = pagination;
120+
const currentPage = Math.floor(offset / limit) + 1;
121+
const totalPages = Math.ceil(totalCount / limit);
122+
const showingFrom = offset + 1;
123+
const showingTo = offset + data.length;
124+
</pre>
125+
126+
<h3>Virtual Scroll / Infinite Scroll</h3>
127+
<pre>
128+
// Load next chunk when user scrolls
129+
const nextOffset = pagination.offset + pagination.limit;
130+
if (pagination.hasMore) {
131+
fetchPage(nextOffset, pagination.limit);
132+
}
133+
</pre>
134+
135+
<h3>SmartClient startRow/endRow</h3>
136+
<pre>
137+
// SmartClient sends startRow=300, endRow=350
138+
// Service translates: offset = startRow, limit = endRow - startRow
139+
int offset = startRow;
140+
int limit = endRow - startRow;
141+
</pre>
142+
143+
<h2>Why Offset/Limit Instead of Cursor</h2>
144+
145+
<ul>
146+
<li><strong>DAO compatibility</strong> — existing Hibernate/JPA DAOs use
147+
<code>query.setFirstResult(offset)</code> and <code>query.setMaxResults(limit)</code>.
148+
Cursor pagination requires a stable sort key and stateful server-side tokens.</li>
149+
<li><strong>Frontend grids</strong> — SmartClient, AG Grid, and React Table natively
150+
speak offset/limit via <code>startRow</code>/<code>endRow</code> or
151+
<code>page</code>/<code>pageSize</code>.</li>
152+
<li><strong>totalCount</strong> — enables "Showing 1–50 of 1,247" UI patterns
153+
and page-count calculations. Cursor APIs typically omit total counts because
154+
they are expensive for the cursor model, but they are cheap when the DAO
155+
already runs <code>SELECT COUNT(*)</code>.</li>
156+
</ul>
157+
158+
<h2>Test Coverage</h2>
159+
160+
<p>The <code>PaginatedResponseTest</code> class provides 20 tests covering:</p>
161+
<ul>
162+
<li>First page, last page, partial last page, single page, empty result</li>
163+
<li>Null data treated as empty list</li>
164+
<li>Unpaginated convenience factory</li>
165+
<li>Negative offset clamping, zero/negative limit defaults, maxLimit enforcement</li>
166+
<li>Enterprise scenarios: 8,543-item virtual scroll, soft-delete filtering,
167+
service-specific maxLimit, SmartClient startRow/endRow translation</li>
168+
<li>Request → response round-trip simulation</li>
169+
</ul>
170+
171+
</body>
172+
</html>

src/site/xdoc/docs/json-rpc-mcp-guide.xml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -602,9 +602,11 @@ Each item notes whether the gap is architectural (won't be added to Axis2) or de
602602
<td>When set to <code>ServiceName/operationName</code>, <code>_meta.tickerResolveEndpoint</code> is added to the catalog. Omitted entirely when not configured so deployments without a ticker service are unaffected.</td>
603603
</tr>
604604
<tr>
605-
<td>Cursor-based pagination</td>
606-
<td>Not implemented</td>
607-
<td>Axis2 operations return their full result sets. Cursor-based pagination would need to be implemented in a separate REST layer.</td>
605+
<td>Pagination</td>
606+
<td><strong>Offset/limit implemented</strong> — see <a href="json-pagination.html">Pagination Guide</a></td>
607+
<td>Axis2 provides <code>PaginatedResponse&lt;T&gt;</code> and <code>PaginationRequest</code>
608+
for offset/limit pagination with maxLimit clamping.
609+
Cursor-based pagination is not implemented (offset/limit maps directly to JPA/Hibernate DAO patterns).</td>
608610
</tr>
609611
<tr>
610612
<td>RFC 7807 Problem Details error format</td>
@@ -638,7 +640,7 @@ uses determines what limitations apply:</p>
638640
<tr><td>Auth</td><td>email + password → Bearer token (loginService)</td><td>API key + secret → scoped JWT</td></tr>
639641
<tr><td>Query semantics</td><td>Per-operation parameters in arg0</td><td>Uniform filter/sort/fields on every resource</td></tr>
640642
<tr><td>Error format</td><td>SOAP fault with correlation ID UUID</td><td>RFC 7807 Problem Details (JSON, per-field)</td></tr>
641-
<tr><td>Pagination</td><td>Full result sets only</td><td>Cursor-based</td></tr>
643+
<tr><td>Pagination</td><td>Offset/limit (<a href="json-pagination.html">PaginatedResponse</a>)</td><td>Cursor-based</td></tr>
642644
<tr><td>inputSchema</td><td>Full JSON Schema via <code>mcpInputSchema</code> in services.xml (hand-authored)</td><td>Full JSON Schema from OpenAPI annotations (auto-generated)</td></tr>
643645
</table>
644646

0 commit comments

Comments
 (0)