Skip to content

Commit a0b41f6

Browse files
springbootdemo-tomcat11: fix Axis2 service loading, add FinancialBenchmarkService
Axis2WebAppInitializer: set axis2.repository.path init parameter explicitly using WarBasedAxisConfigurator.PARAM_AXIS2_REPOSITORY_PATH. Without this, WarBasedAxisConfigurator.loadServices() silently fails to scan WEB-INF/services/ *.aar archives on WildFly (lazy AxisServlet init) and intermittently on Tomcat. getRealPath() is called eagerly at startup and the result passed as a servlet init parameter, bypassing the lazy/VFS timing issue in both containers. pom.xml: bump java.version 17 → 21; add FinancialBenchmarkService.aar to both the deploy/ and exploded/ antrun targets. finbench_resources/services.xml: new file declaring the three public operations (portfolioVariance, monteCarlo, scenarioAnalysis). Omits runtimeInfo, which is a private helper method in FinancialBenchmarkService and cannot be exposed as an Axis2 operation — declaring it caused "Bad Request" on every call because reflection cannot access private methods. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f39a3b4 commit a0b41f6

10 files changed

Lines changed: 801 additions & 42 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ target
77
/modules/tool/axis2-eclipse-codegen-plugin/lib
88
/modules/tool/axis2-eclipse-service-plugin/META-INF
99
/modules/tool/axis2-eclipse-service-plugin/lib
10+
axis2-json-api*.log

modules/samples/userguide/src/userguide/springbootdemo-tomcat11/README.md

Lines changed: 71 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,18 @@ Tested with: **Tomcat 11.0.20** · **OpenJDK 21** · **Spring Boot 3.4.3**
2828

2929
## Services
3030

31-
| Service | Path | Auth |
32-
|---------|------|------|
33-
| `LoginService.login` | `POST /axis2-json-api/services/LoginService` | None (public) |
34-
| `TestwsService.testws` | `POST /axis2-json-api/services/TestwsService` | Bearer token |
35-
| `BigDataH2Service.processBigDataSet` | `POST /axis2-json-api/services/BigDataH2Service` | None (public) |
36-
| OpenAPI spec (JSON) | `GET /axis2-json-api/openapi.json` | None |
37-
| OpenAPI spec (YAML) | `GET /axis2-json-api/openapi.yaml` | None |
38-
| Swagger UI | `GET /axis2-json-api/swagger-ui` | None |
31+
| Service | Operation | Path | Auth |
32+
|---------|-----------|------|------|
33+
| `loginService` | `doLogin` | `POST /axis2-json-api/services/loginService` | None (public) |
34+
| `testws` | `doTestws` | `POST /axis2-json-api/services/testws` | Bearer token |
35+
| `BigDataH2Service` | `processBigDataSet` | `POST /axis2-json-api/services/BigDataH2Service` | Bearer token |
36+
| `FinancialBenchmarkService` | `portfolioVariance` | `POST /axis2-json-api/services/FinancialBenchmarkService` | Bearer token |
37+
| `FinancialBenchmarkService` | `monteCarlo` | `POST /axis2-json-api/services/FinancialBenchmarkService` | Bearer token |
38+
| `FinancialBenchmarkService` | `scenarioAnalysis` | `POST /axis2-json-api/services/FinancialBenchmarkService` | Bearer token |
39+
| OpenAPI spec (JSON) || `GET /axis2-json-api/openapi.json` | None |
40+
| OpenAPI spec (YAML) || `GET /axis2-json-api/openapi.yaml` | None |
41+
| Swagger UI || `GET /axis2-json-api/swagger-ui` | None |
42+
| OpenAPI MCP || `GET /axis2-json-api/openapi-mcp.json` | None |
3943

4044
---
4145

@@ -86,47 +90,76 @@ curl http://localhost:8080/axis2-json-api/swagger-ui
8690
### 2. Login (get Bearer token)
8791

8892
```bash
89-
curl -s -X POST http://localhost:8080/axis2-json-api/services/LoginService \
93+
curl -s -X POST http://localhost:8080/axis2-json-api/services/loginService \
9094
-H 'Content-Type: application/json' \
91-
-d '{"login":[{"request":{"email":"user@example.com","credentials":"password"}}]}'
95+
-d '{"doLogin":[{"arg0":{"email":"java-dev@axis.apache.org","credentials":"userguide"}}]}'
9296
```
9397

94-
Response: `{"loginResponse":{"token":"<JWT>","status":"OK"}}`
98+
Response: `{"response":{"token":"<TOKEN>","uuid":"<UUID>","status":"OK"}}`
9599

96-
### 3. Call protected service
100+
### 3. Call protected service (testws)
101+
102+
`messagein` must pass ESAPI `SafeString` validation (`[A-Za-z0-9.,\-_ ]*` — no `+` or special
103+
characters).
97104

98105
```bash
99-
TOKEN="<JWT from step 2>"
100-
curl -s -X POST http://localhost:8080/axis2-json-api/services/TestwsService \
106+
TOKEN="<token from step 2>"
107+
curl -s -X POST http://localhost:8080/axis2-json-api/services/testws \
101108
-H 'Content-Type: application/json' \
102109
-H "Authorization: Bearer $TOKEN" \
103-
-d '{"testws":[{"request":{"name":"World"}}]}'
110+
-d '{"doTestws":[{"arg0":{"messagein":"hello world"}}]}'
104111
```
105112

106-
### 4. Call public BigData service
113+
### 4. Call BigData service
107114

108-
Required fields: `datasetId` (non-empty string) and `datasetSize` (bytes). Size determines
109-
processing path: <10MB → standard, 10–50MB → multiplexing, >50MB → streaming.
115+
`datasetSize` is in bytes. Size determines processing path: <10 MB → standard,
116+
10–50 MB → multiplexing, >50 MB → streaming. Use at least 1 000 000 to get populated results.
110117

111118
```bash
112119
curl -s -X POST http://localhost:8080/axis2-json-api/services/BigDataH2Service \
113120
-H 'Content-Type: application/json' \
114-
-d '{"processBigDataSet":[{"request":{"datasetId":"test-dataset-001","datasetSize":1048576}}]}'
121+
-H "Authorization: Bearer $TOKEN" \
122+
-d '{"processBigDataSet":[{"arg0":{"datasetId":"test-001","datasetSize":1000000,"processingMode":"streaming","enableMemoryOptimization":true,"analyticsType":"summary"}}]}'
115123
```
116124

117-
Response includes `processedRecordCount`, `http2Optimized`, `memoryOptimized`, and (for <10MB
118-
datasets) a `processedRecords` array.
125+
Response includes `processedRecordCount`, `http2Optimized`, `memoryOptimized`, and a
126+
`processedRecords` array.
127+
128+
### 5. Financial Benchmark Service
129+
130+
```bash
131+
# Portfolio variance — O(n²) covariance matrix
132+
curl -s -X POST http://localhost:8080/axis2-json-api/services/FinancialBenchmarkService \
133+
-H 'Content-Type: application/json' \
134+
-H "Authorization: Bearer $TOKEN" \
135+
-d '{"portfolioVariance":[{"arg0":{"nAssets":2,"weights":[0.6,0.4],"covarianceMatrix":[[0.04,0.006],[0.006,0.09]],"normalizeWeights":false,"nPeriodsPerYear":252}}]}'
136+
137+
# Monte Carlo VaR — GBM simulation
138+
curl -s -X POST http://localhost:8080/axis2-json-api/services/FinancialBenchmarkService \
139+
-H 'Content-Type: application/json' \
140+
-H "Authorization: Bearer $TOKEN" \
141+
-d '{"monteCarlo":[{"arg0":{"nSimulations":10000,"nPeriods":252,"initialValue":100.0,"expectedReturn":0.08,"volatility":0.20,"nPeriodsPerYear":252,"randomSeed":42}}]}'
142+
143+
# Scenario analysis — probability-weighted expected return
144+
curl -s -X POST http://localhost:8080/axis2-json-api/services/FinancialBenchmarkService \
145+
-H 'Content-Type: application/json' \
146+
-H "Authorization: Bearer $TOKEN" \
147+
-d '{"scenarioAnalysis":[{"arg0":{"assets":[{"assetId":1,"currentPrice":100.0,"positionSize":100,"scenarios":[{"price":120.0,"probability":0.3},{"price":100.0,"probability":0.5},{"price":75.0,"probability":0.2}]}],"useHashLookup":true,"probTolerance":0.001}}]}'
148+
```
119149

120150
---
121151

122152
## Axis2 JSON-RPC request format
123153

124-
The top-level key is the **operation name**, and the body is wrapped in an array:
154+
The top-level key is the **operation name**, and the value is an array containing one object
155+
whose key is the argument name (conventionally `arg0`) and whose value is the request POJO:
125156

126157
```json
127-
{ "operationName": [{ "request": { ...fields... } }] }
158+
{ "operationName": [{ "arg0": { ...fields... } }] }
128159
```
129160

161+
This is mandated by `JsonUtils.invokeServiceClass()` in the `axis2-json` module.
162+
130163
---
131164

132165
## Architecture notes
@@ -135,7 +168,12 @@ The top-level key is the **operation name**, and the body is wrapped in an array
135168
`SpringApplication.run()`. Spring Boot only provides configuration; Tomcat is the server.
136169
- **No `DispatcherServlet`**`@GetMapping` annotations are dead code in this module. OpenAPI
137170
endpoints are served by `OpenApiServlet`, a plain `HttpServlet` registered directly in
138-
`Axis2WebAppInitializer` at `/openapi.json`, `/openapi.yaml`, and `/swagger-ui`.
171+
`Axis2WebAppInitializer` at `/openapi.json`, `/openapi.yaml`, `/swagger-ui`, and
172+
`/openapi-mcp.json`.
173+
- **Axis2 repository path**`Axis2WebAppInitializer` explicitly sets the
174+
`axis2.repository.path` servlet init parameter using `ServletContext.getRealPath("/WEB-INF")`.
175+
This is required on both Tomcat and WildFly to ensure `WarBasedAxisConfigurator` finds the
176+
`WEB-INF/services/*.aar` archives reliably, bypassing any VFS or lazy-init timing issues.
139177
- **OpenAPI module**`axis2-openapi-<version>.jar` is copied to
140178
`WEB-INF/modules/openapi-<version>.mar` by the Maven build. It must be present for
141179
`GET /openapi.*` and `GET /swagger-ui` to work.
@@ -144,14 +182,15 @@ The top-level key is the **operation name**, and the body is wrapped in an array
144182

145183
---
146184

147-
## Relationship to `springbootdemo` (WildFly)
185+
## Relationship to `springbootdemo-wildfly`
148186

149-
`springbootdemo-tomcat11` is derived from `springbootdemo` (which targets WildFly). The two
150-
modules share the same service logic but differ in:
187+
`springbootdemo-tomcat11` and `springbootdemo-wildfly` share all Java source and resources
188+
via `build-helper-maven-plugin`. The only differences are container-specific:
151189

152-
| Aspect | `springbootdemo` (WildFly) | `springbootdemo-tomcat11` |
153-
|--------|---------------------------|--------------------------|
154-
| Server | WildFly / embedded Undertow | Apache Tomcat 11 |
190+
| Aspect | `springbootdemo-tomcat11` | `springbootdemo-wildfly` |
191+
|--------|--------------------------|--------------------------|
192+
| Server | Apache Tomcat 11 | WildFly 32+ (Undertow) |
193+
| Tested on | Tomcat 11.0.20 / Java 21 | WildFly 39 / Java 25 |
194+
| WAR output | `target/deploy/axis2-json-api/` (no `.war` suffix) | `target/deploy/axis2-json-api/` |
195+
| Extra WEB-INF files || `jboss-deployment-structure.xml`, `jboss-web.xml`, `beans.xml` |
155196
| Context path | `/axis2-json-api` | `/axis2-json-api` |
156-
| OpenAPI routing | `OpenApiServlet` via `Axis2WebAppInitializer` | Same |
157-
| H2 BigData service | Yes | Yes |

modules/samples/userguide/src/userguide/springbootdemo-tomcat11/pom.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
<properties>
4141
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
4242
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
43-
<java.version>17</java.version>
43+
<java.version>21</java.version>
4444
<spring-boot.version>3.4.3</spring-boot.version>
4545
<axis2.version>2.0.1-SNAPSHOT</axis2.version>
4646
</properties>
@@ -366,6 +366,9 @@
366366
<jar jarfile="${project.build.directory}/deploy/axis2-json-api/WEB-INF/services/BigDataH2Service.aar">
367367
<metainf file="resources-axis2/bigdata_h2_resources/services.xml"/>
368368
</jar>
369+
<jar jarfile="${project.build.directory}/deploy/axis2-json-api/WEB-INF/services/FinancialBenchmarkService.aar">
370+
<metainf file="resources-axis2/finbench_resources/services.xml"/>
371+
</jar>
369372
<copy todir="${project.build.directory}/deploy/axis2-json-api/WEB-INF/conf">
370373
<fileset dir="resources-axis2/conf">
371374
<include name="axis2.xml"/>
@@ -385,6 +388,9 @@
385388
<jar jarfile="${project.build.directory}/exploded/WEB-INF/services/BigDataH2Service.aar">
386389
<metainf file="resources-axis2/bigdata_h2_resources/services.xml"/>
387390
</jar>
391+
<jar jarfile="${project.build.directory}/exploded/WEB-INF/services/FinancialBenchmarkService.aar">
392+
<metainf file="resources-axis2/finbench_resources/services.xml"/>
393+
</jar>
388394
<copy todir="${project.build.directory}/exploded/WEB-INF/conf">
389395
<fileset dir="resources-axis2/conf">
390396
<include name="axis2.xml"/>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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+
<service name="FinancialBenchmarkService" scope="application">
20+
<description>
21+
Financial Benchmark Service — portfolio variance, Monte Carlo VaR, scenario analysis.
22+
Demonstrates compute-intensive JSON-RPC services accessible via MCP bridge.
23+
</description>
24+
<parameter name="ServiceClass">userguide.springboot.webservices.FinancialBenchmarkService</parameter>
25+
<parameter name="ServiceObjectSupplier">org.apache.axis2.extensions.spring.receivers.SpringServletContextObjectSupplier</parameter>
26+
<parameter name="SpringBeanName">financialBenchmarkService</parameter>
27+
<parameter name="enableJSONOnly">true</parameter>
28+
<parameter name="disableSOAP">true</parameter>
29+
<operation name="portfolioVariance">
30+
<messageReceivers>
31+
<messageReceiver mep="http://www.w3.org/ns/wsdl/in-out"
32+
class="org.apache.axis2.json.moshi.rpc.JsonRpcMessageReceiver"/>
33+
<messageReceiver mep="http://www.w3.org/ns/wsdl/in-only"
34+
class="org.apache.axis2.json.moshi.rpc.JsonInOnlyRPCMessageReceiver"/>
35+
</messageReceivers>
36+
</operation>
37+
<operation name="monteCarlo">
38+
<messageReceivers>
39+
<messageReceiver mep="http://www.w3.org/ns/wsdl/in-out"
40+
class="org.apache.axis2.json.moshi.rpc.JsonRpcMessageReceiver"/>
41+
<messageReceiver mep="http://www.w3.org/ns/wsdl/in-only"
42+
class="org.apache.axis2.json.moshi.rpc.JsonInOnlyRPCMessageReceiver"/>
43+
</messageReceivers>
44+
</operation>
45+
<operation name="scenarioAnalysis">
46+
<messageReceivers>
47+
<messageReceiver mep="http://www.w3.org/ns/wsdl/in-out"
48+
class="org.apache.axis2.json.moshi.rpc.JsonRpcMessageReceiver"/>
49+
<messageReceiver mep="http://www.w3.org/ns/wsdl/in-only"
50+
class="org.apache.axis2.json.moshi.rpc.JsonInOnlyRPCMessageReceiver"/>
51+
</messageReceivers>
52+
</operation>
53+
</service>

modules/samples/userguide/src/userguide/springbootdemo-tomcat11/src/main/java/userguide/springboot/configuration/Axis2WebAppInitializer.java

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@
2121

2222
import org.apache.logging.log4j.LogManager;
2323
import org.apache.logging.log4j.Logger;
24+
import org.apache.axis2.deployment.WarBasedAxisConfigurator;
2425
import org.apache.axis2.transport.http.AxisServlet;
2526
import org.springframework.boot.web.servlet.ServletContextInitializer;
26-
import org.springframework.context.annotation.Configuration;
27-
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
27+
import org.springframework.context.annotation.Configuration;
28+
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
2829
import org.springframework.core.annotation.Order;
2930

3031
import org.springframework.beans.factory.annotation.Autowired;
@@ -33,8 +34,8 @@
3334
import org.springframework.context.annotation.PropertySource;
3435
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
3536

36-
import jakarta.servlet.ServletContext;
37-
import jakarta.servlet.ServletRegistration;
37+
import jakarta.servlet.ServletContext;
38+
import jakarta.servlet.ServletRegistration;
3839

3940
import java.util.Set;
4041

@@ -69,12 +70,21 @@ private void addAxis2Servlet(ServletContext container, AnnotationConfigWebApplic
6970
ServletRegistration.Dynamic dispatcher = container.addServlet(
7071
"AxisServlet", new AxisServlet());
7172
dispatcher.setLoadOnStartup(1);
73+
74+
// Explicitly set the Axis2 repository path so WarBasedAxisConfigurator finds
75+
// WEB-INF/services/*.aar on both Tomcat and WildFly (bypasses getRealPath() VFS issues).
76+
String webInfPath = container.getRealPath("/WEB-INF");
77+
logger.warn("addAxis2Servlet: axis2.repository.path = " + webInfPath);
78+
if (webInfPath != null) {
79+
dispatcher.setInitParameter(WarBasedAxisConfigurator.PARAM_AXIS2_REPOSITORY_PATH, webInfPath);
80+
}
81+
7282
Set<String> mappingConflicts = dispatcher.addMapping(SERVICES_MAPPING);
73-
if (!mappingConflicts.isEmpty()) {
74-
for (String s : mappingConflicts) {
75-
logger.error("Mapping conflict: " + s);
76-
}
77-
throw new IllegalStateException("'AxisServlet' could not be mapped to '" + SERVICES_MAPPING + "'");
83+
if (!mappingConflicts.isEmpty()) {
84+
for (String s : mappingConflicts) {
85+
logger.error("Mapping conflict: " + s);
86+
}
87+
throw new IllegalStateException("'AxisServlet' could not be mapped to '" + SERVICES_MAPPING + "'");
7888
}
7989
}
8090

0 commit comments

Comments
 (0)