-- **`JaxwsEndpointApiCtClassBuilder`** — generates `@WebService` endpoint classes
- (name, target namespace, service name, WSDL provider mode, addressing, service
- mode, bound data, fields and SOAP methods).
-- **`JaxrsEndpointApiCtClassBuilder`** — generates JAX-RS resource classes
- (`@Path`, produces, REST methods with `HttpMethodEnum` / `RestParam`,
- bound data, fields).
+---
-Generated classes extend the shared `EndpointApi` base class; with
-`toInstance(InvocationHandler)` the builder returns an instance whose method calls
-are dispatched to your handler — ideal for proxied / pluggable endpoint logic.
+> **Current version** (depends on branch, see §9): `3.0.x.x-SNAPSHOT`
+> **JDK baselines**: 8 / 17 / 21 (four long-term worktree branches maintained in parallel)
+> **Build tool**: Maven 3.0+ (Maven Wrapper `./mvnw` included)
+> **Last verified**: 2026-08-20 (`mvn test` green across all four branches: 202 / 202 / 202 / 224 tests)
-The builders rely on the `io.github.easy4j:javassist-plus` companion library
-(`CtFieldBuilder`, `ClassPoolFactory`, `JavassistUtils`).
+## 1. Project Overview
-What it is **not**:
+**`cxf-rt-javassist` is a runtime bytecode-generation library for Java framework / dynamic-gateway developers. Built on top of [Javassist](https://www.javassist.org) and `io.github.easy4j:javassist-extension`, it translates declarative SOAP / REST endpoint definitions into loadable `Class` objects carrying the correct `@WebService` or JAX-RS annotations.**
-- Not the CXF runtime itself — Apache CXF frontend artifacts are required at
- runtime to serve the generated endpoints.
-- Not a Spring Boot starter — no auto-configuration is provided.
+| Dimension | Positioning |
+| :--- | :--- |
+| What it is | A runtime bytecode generator; single-module jar |
+| Consumers | Spring Boot Starters, API gateways, generic proxy frameworks, dynamically exported endpoints |
+| Core capabilities | ① JAX-WS endpoint class generation ② JAX-RS resource class generation ③ three outputs (bytecode / `Class>` / proxied instance) ④ fully symmetric SOAP ↔ REST builder design |
+| JDK (multi-line) | `feature/1.0.x` → JDK 8; `feature/2.0.x` → JDK 17; `feature/3.0.x` → JDK 21; `main` → primary JDK 17/21 release line |
+| Coordinates | `io.github.easy4j:cxf-rt-javassist` |
+| Property prefix | None (everything is driven by fluent builder APIs; no files are read) |
-Typical scenarios:
+### 1.1 What it is NOT
-| Scenario | What you use |
-| :--- | :--- |
-| Generate a `@WebService` endpoint class at runtime | `JaxwsEndpointApiCtClassBuilder` + `webService(...)` / `newMethod(...)` |
-| Generate a JAX-RS resource class at runtime | `JaxrsEndpointApiCtClassBuilder` + `path(...)` / `newMethod(...)` |
-| Dispatch generated calls to a handler | `toInstance(InvocationHandler)` |
-| Export generated bytecode for offline inspection | `build()` → `CtClass` → `toBytecode()` / `writeFile()` |
+- Not the Apache CXF runtime itself. To actually expose the generated SOAP / REST endpoints, you also need `cxf-rt-frontend-jaxws` / `cxf-rt-frontend-jaxrs` on the runtime classpath.
+- Not a Spring Boot Starter. No auto-configuration; no Spring dependency is ever pulled by this artifact.
+- No commitment to unlisted JDK combinations (e.g. JDK 11); only 8 / 17 / 21 are long-term lines.
-## 2. Features & Status
+### 1.2 Typical use cases
-| Capability | Status | Notes |
+| Scenario | How to use | Outcome |
| :--- | :--- | :--- |
-| JAX-WS class builder | Stable | `webService`, `webServiceProvider`, `addressing`, `serviceMode`, `bind`, `makeField`, `makeMethod`, `newMethod`, `build` / `toClass` / `toInstance` |
-| JAX-RS class builder | Stable | `path`, `produces`, `bind`, `makeField`, `newField`, `removeField`, `newMethod` (with `HttpMethodEnum` / `RestMethod`), `removeMethod`, `build` / `toClass` / `toInstance` |
-| SOAP definition model | Stable | `SoapService`, `SoapMethod`, `SoapParam`, `SoapResult`, `SoapBound` |
-| REST definition model | Stable | `RestBound`, `RestMethod`, `RestParam`, `RestProduce`, `HttpMethodEnum`, `HttpParamEnum` |
-| Utility helpers | Stable | `JaxwsEndpointApiUtils`, `JaxrsEndpointApiUtils` |
-| Tests | Stable | `JaxwsApiCtClassBuilder_Test`, `JaxrsApiCtClassBuilder_Test` + samples (`EndpointApiSample`, `EndpointApiInvocationHandler`, `Customer`) |
+| Generic proxy gateway | `JaxrsEndpointApiCtClassBuilder → toInstance(InvocationHandler)` | Every generated method call dispatches to your handler |
+| Import a 3rd-party OpenAPI on the fly | Parse OpenAPI → materialise `RestMethod[]` → call `Jaxrs*Builder.newMethod(...)` repeatedly | A bundle of resource classes with fully-populated `@Path / @GET / @PathParam` annotations |
+| Automatically proxy legacy SOAP services | `JaxwsEndpointApiCtClassBuilder.webService(...) + toInstance(handler)` | A `@WebService` class whose every method enters your dispatch handler |
+| Offline bytecode analysis | `build() → CtClass → toBytecode() / writeFile(dumpDir)` | Bytecode written to disk; readable via javap / IDA / annotation reflect |
+
+## 2. Features & Status
+
+| Capability | Status | Details | Evidence |
+| :--- | :---: | :--- | :--- |
+| JAX-WS endpoint Builder | ✅ Stable | `webService / webServiceProvider / addressing / serviceMode / bind / makeField / makeMethod / newMethod / build / toClass / toInstance` | `JaxwsEndpointApiImplCtClassBuilderTest` (13 tests) |
+| JAX-RS resource Builder | ✅ Stable | `path / produces / bind / makeField / newField / removeField / newMethod(rtClass, HttpMethodEnum, name, path, RestParam...) / removeMethod / build / toClass / toInstance` | `JaxrsEndpointApiCtClassBuilderTest` (26 tests) |
+| SOAP definition objects | ✅ Stable | `SoapService / SoapMethod / SoapParam / SoapResult / SoapBound` | Corresponding POJO tests cover all 4 non-default fields |
+| REST definition objects | ✅ Stable | `RestBound / RestMethod / RestParam / RestProduce / HttpMethodEnum / HttpParamEnum` | `RestParamTest` (8 tests) + builder regressions (26+26+10) |
+| Utils / annotation injection | ✅ Stable (**Bug#3 fixed 2026-08-20**) | `JaxwsEndpointApiUtils.annotParams` / `JaxrsEndpointApiUtils.annotParams` translate definition POJOs → Javassist `Annotation[][]` | `Jaxrs*InterfaceCtClassBuilderTest` (26 tests) |
+| Bytecode export + `toInstance(InvocationHandler)` | ✅ Stable | `CtClass.toClass()`, `toBytecode()`, `writeFile()`, plus `Proxy.newProxyInstance` | `EndpointApiSample` / `EndpointApiInvocationHandler` / `Customer` |
+
+> For the 2026-08-20 CodeGraph-based fix details see [TECHNICAL-DESIGN.md §3 / §4](./TECHNICAL-DESIGN.md#3-codegraph-语义代码审查结论) (Chinese; all code snippets and anchors are still readable to English readers).
## 3. Requirements & Compatibility
-| Requirement | Version / Notes |
-| :--- | :--- |
-| JDK | 17+ |
-| Maven | 3.0+ (enforced; Maven Wrapper `./mvnw` included) |
-| Apache CXF | 4.x (`cxf-rt-frontend-jaxws` / `cxf-rt-frontend-jaxrs`, managed by this pom) |
-| Javassist | 3.30.2-GA (managed by this pom) |
-| Companion library | `io.github.easy4j:javassist-plus` (same version line) |
+### 3.1 Baseline requirements
-Version lines:
+| Dependency | Minimum version | Recommended version | Notes |
+| :--- | ---: | ---: | :--- |
+| JDK (branch-dependent) | see §9 | 8u3xx / 17u20 / latest 21 | Long-term parallel maintenance on 3 baselines |
+| Maven | 3.0+ | 3.9.16 | Maven Enforcer rule in pom.xml |
+| Apache CXF | 4.x | 4.0.x (1.0.x) / 4.1.x (2.0.x) / 4.2.x (3.0.x / main) | Locked via dependencyManagement in each line |
+| Javassist | 3.30.2-GA | 3.30.2-GA | Same |
+| javassist-extension | Same as this artifact | Same | `io.github.easy4j:javassist-extension`, released together |
+| commons-lang3 | 3.20.0 | 3.20.0 | Final JDK 8-compatible release; shared across 4 lines (see §10.2) |
+| commons-io | 2.22.0 | 2.22.0 | Final JDK 8-compatible release; shared across 4 lines |
+| commons-beanutils | 1.11.0 | 1.11.0 | Latest 1.x release (beanutils 2.x not GA); shared across 4 lines |
-| Branch | JDK | Version |
-| :--- | :--- | :--- |
-| `feature/1.0.x` | 8 | `1.0.x.*` |
-| `feature/2.0.x` | 17 | `2.0.x.*` |
-| `feature/3.0.x` | 21 | `3.0.x.*` |
+### 3.2 Compatibility matrix
+
+| Line | JDK | CXF baseline | Status | Maintenance policy |
+| :--- | :---: | :---: | :---: | :--- |
+| `feature/3.0.x` | 21 | CXF 4.2.x | ✅ Active dev | New features + bug fixes |
+| `main` | 17 / 21 | CXF 4.2.x | ✅ Primary release | Syncs fixes from 3.0.x; GA channel |
+| `feature/2.0.x` | 17 | CXF 4.1.x | 🛠️ Maintenance | Severe bugs, CVE, dependency bumps only |
+| `feature/1.0.x` | 8 | CXF 4.0.x | 🛠️ Maintenance | Blocking bugs + CVE only; JDK-11+ only dependencies are FORBIDDEN |
+
+### 3.3 Dependency boundary
+
+- This project is a **single-module jar**. Dependencies are strictly kept to the "bytecode-generation minimal set": Javassist, javassist-extension, CXF frontends (compile scope because generated bytecodes require annotation types resolvable in the ClassPool), plus the Apache Commons trio for JSON binding / bean-copy utility.
+- No Spring, Jakarta Servlet, Jakarta Validation, or any container-level dependency is introduced. Downstream consumers bring their own container.
## 4. Architecture & Modules
+### 4.1 High-level view
+
```text
-+------------------+ +------------------------------------------+
-| Developer | | cxf-rt-javassist |
-| |-->| JaxwsEndpointApiCtClassBuilder |
-| declarative | | (webService / newMethod / bind) |
-| endpoint spec | | JaxrsEndpointApiCtClassBuilder |
-| | | (path / produces / newMethod) |
-| | | definitions: Soap*, Rest* |
-| | | base: EndpointApi (shared) |
-+------------------+ +-------------------+----------------------+
- |
- v
- +-------------------------------------------+
- | CtClass -> Class / instance |
- | toClass / toInstance(handler) |
- | -> CXF JAX-WS / JAX-RS implementation |
- +-------------------------------------------+
+┌─────────────── Business app ───────────────┐
+│ Gateway / Proxy / Dynamic exporter │
+│ pulls: io.github.easy4j:cxf-rt-javassist │
+└──────────────┬─────────────────────────────┘
+ │ Declarative: SoapBound / RestBound
+ ▼
+┌──────────────────────────────────────────────────────┐
+│ cxf-rt-javassist (single-module jar) │
+│ ┌──────────────────────┐ ┌──────────────────────┐ │
+│ │ Jaxws builder side │ │ Jaxrs builder side │ │
+│ │ JaxwsEndpointApiCt… │ │ JaxrsEndpointApiCt… │ │
+│ │ definition: Soap* │ │ definition: Rest* │ │
+│ └──────────┬────────────┘ └──────────┬────────────┘ │
+│ ▼ shared EndpointApi base ▼ │
+│ EndpointApi (implements IEndpointApi) │
+│ Utils: JaxwsEndpointApiUtils / JaxrsEndpointApiUtils │
+└───────────────────────┬────────────────────────────────┘
+ │ CtClass / Class / Proxied instance
+ ▼
+ Apache CXF 4.x frontend runtime
+ ┌─ JAX-WS Endpoint.publish ─┐
+ └─ JAX-RS Server / Feature ─┘
```
-Single-module Maven project (`packaging: jar`). No child modules.
+### 4.2 Symmetric builder pattern
-| Artifact | Responsibility |
-| :--- | :--- |
-| `io.github.easy4j:cxf-rt-javassist` | Runtime class builders, SOAP / REST definition model, utils |
+`JaxwsEndpointApiCtClassBuilder` ↔ `JaxrsEndpointApiCtClassBuilder` are **sibling builders** with strictly isomorphic API shapes:
-Key packages:
+| Stage | SOAP (Jaxws) | REST (Jaxrs) |
+| :--- | :--- | :--- |
+| Init | `new JaxwsEndpointApiCtClassBuilder(fullyQualifiedClassName)` | `new JaxrsEndpointApiCtClassBuilder(fullyQualifiedClassName)` |
+| Header annotations | `webService(name, tns, serviceName)` / `webServiceProvider(...)` / `addressing(...)` / `serviceMode(Mode.PAYLOAD)` | `path(root)` + `produces(MediaType...)` |
+| Bind | `bind(String uid, String json)` / `bind(SoapBound)` | `bind(String uid, String json)` / `bind(RestBound)` |
+| Fields | `makeField(src)` / `newField(type, name, value)` / `removeField(name)` | identical API |
+| Methods | `makeMethod(src)` / `newMethod(name, SoapParam...)` / `removeMethod(...)` | `newMethod(rtClass, HttpMethodEnum, name, subPath, RestParam...)` / `removeMethod(...)` |
+| Outputs | `build() → CtClass` / `toClass() → Class>` / `toInstance(InvocationHandler) → Object` | identical API |
-| Package | Content |
-| :--- | :--- |
-| `org.apache.cxf.endpoint.jaxws` | `JaxwsEndpointApiCtClassBuilder`, `JaxwsEndpointApiImplCtClassBuilder`, `JaxwsEndpointApiInterfaceCtClassBuilder` |
-| `org.apache.cxf.endpoint.jaxws.definition` | `SoapService`, `SoapMethod`, `SoapParam`, `SoapResult`, `SoapBound` |
-| `org.apache.cxf.endpoint.jaxrs` | `JaxrsEndpointApiCtClassBuilder`, `JaxrsEndpointApiImplCtClassBuilder`, `JaxrsEndpointApiInterfaceCtClassBuilder` |
-| `org.apache.cxf.endpoint.jaxrs.definition` | `RestBound`, `RestMethod`, `RestParam`, `RestProduce`, `HttpMethodEnum`, `HttpParamEnum` |
-| `org.apache.cxf.endpoint` | `EndpointApi` (base class) |
-| `org.apache.cxf.endpoint.utils` | `JaxwsEndpointApiUtils`, `JaxrsEndpointApiUtils` |
+> Review rule (ADR-002): When adding capabilities or fixing bugs, always walk both sides; only when the SOAP / REST semantic gap is fundamental (e.g. REST has `@PathParam` / `@DefaultValue`, SOAP does not) may a capability land on a single side without corresponding touch.
-## 5. Installation
+### 4.3 Package map (maps directly to §1.2 use cases)
-The project is **not yet published to Maven Central**. Snapshots/releases are
-distributed through the Aliyun Maven repository and GitHub Releases.
+| Package | Content | Typical scenario |
+| :--- | :--- | :--- |
+| `org.apache.cxf.endpoint` | `EndpointApi` base class, `IEndpointApi` interface | Reuse `bind` + `toInstance(InvocationHandler)` contract |
+| `org.apache.cxf.endpoint.jaxws` | `JaxwsEndpointApiCtClassBuilder`, `…ImplCtClassBuilder`, `…InterfaceCtClassBuilder` | Generate SOAP impl / interface / impl-with-bound-data classes |
+| `org.apache.cxf.endpoint.jaxws.definition` | `SoapService / SoapMethod / SoapParam / SoapResult / SoapBound` | Declarative SOAP description |
+| `org.apache.cxf.endpoint.jaxrs` | `JaxrsEndpointApiCtClassBuilder`, `…ImplCtClassBuilder`, `…InterfaceCtClassBuilder` | Generate JAX-RS impl / interface / impl-with-bound-data classes |
+| `org.apache.cxf.endpoint.jaxrs.definition` | `RestBound / RestMethod / RestParam / RestProduce / HttpMethodEnum / HttpParamEnum` | Declarative REST description |
+| `org.apache.cxf.endpoint.utils` | `JaxwsEndpointApiUtils / JaxrsEndpointApiUtils` | Translate POJOs → Javassist `Annotation[][]` then inject into `CtMethod` / `CtClass` |
-Maven:
+## 5. Installation
+
+### 5.1 Maven
```xml
io.github.easy4jcxf-rt-javassist
- 2.0.x.x.20260630-SNAPSHOT
+
+ 3.0.x.x.20260630-SNAPSHOT
```
-Gradle:
+### 5.2 Gradle
-```groovy
-implementation 'io.github.easy4j:cxf-rt-javassist:2.0.x.x.20260630-SNAPSHOT'
+```kotlin
+dependencies {
+ implementation("io.github.easy4j:cxf-rt-javassist:3.0.x.x.20260630-SNAPSHOT")
+}
```
+### 5.3 Repository note
+
+The project is **not yet published to Maven Central**. Snapshots / releases are distributed through the Aliyun private Maven repository and GitHub Releases until Central onboarding is complete. Contact repository maintainers for the private repo URL.
+
## 6. Quick Start
-Build a JAX-WS endpoint class at runtime (adapted from the checked-in test):
+### 6.1 Minimal JAX-WS example (build `@WebService` class then `toClass`)
```java
import javassist.CtClass;
@@ -162,25 +192,13 @@ CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot
Class> clazz = ctClass.toClass();
Object endpoint = clazz.getConstructor().newInstance();
+// endpoint is now a fully-annotated @WebService instance; pass it to
+// org.apache.cxf.jaxws.EndpointImpl.publish(addr) to expose.
```
-Expected result: a new class `org.apache.cxf.spring.boot.FirstCaseV1` is created,
-annotated with `@WebService`, with a `String sayHello(String text)` method — usable
-as a CXF JAX-WS endpoint implementation.
-
-## 7. Configuration
-
-The library has no configuration file or property prefix. Everything is driven by
-the builder chain and the definition objects:
+**Expected**: A `sayHello(String text)` method exists on the generated class; reflection can read `@WebService(name="get", targetNamespace="http://ws.cxf.com", serviceName="getxx")`; the `uid` field is initialised in the zero-arg constructor.
-| Builder | Key methods | Description |
-| :--- | :--- | :--- |
-| `JaxwsEndpointApiCtClassBuilder` | `webService(name, targetNamespace[, serviceName])`, `webServiceProvider(wsdlLocation, serviceName, ...)`, `addressing(boolean, boolean, Responses)`, `serviceMode(Service.Mode)`, `bind(uid, json)` / `bind(SoapBound)`, `makeField(src)`, `makeMethod(src)`, `newMethod(name, SoapParam...)`, `build()` / `toClass()` / `toInstance(InvocationHandler)` | JAX-WS endpoint generation |
-| `JaxrsEndpointApiCtClassBuilder` | `path(path)`, `produces(mediaTypes...)`, `bind(uid, json)` / `bind(RestBound)`, `makeField(src)`, `newField(type, name, value)`, `removeField(name)`, `newMethod(rtClass, HttpMethodEnum, name, path, RestParam...)`, `removeMethod(...)`, `build()` / `toClass()` / `toInstance(InvocationHandler)` | JAX-RS resource generation |
-
-## 8. Core Usage / API
-
-### 8.1 JAX-RS resource generation
+### 6.2 Minimal JAX-RS example (build a `@Path("/getxx")` resource)
```java
import javassist.CtClass;
@@ -194,51 +212,188 @@ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot
.newMethod(String.class, HttpMethodEnum.GET, "sayHello", "{id}/info",
new RestParam(String.class, "id", HttpParamEnum.PATH))
.build();
+
+// Verify via reflection:
+Class> clazz = ctClass.toClass();
+Path classPath = clazz.getAnnotation(Path.class);
+assertEquals("getxx", classPath.value()); // PASS
```
-### 8.2 Dispatch to an `InvocationHandler`
+> ⚠️ **Historical bug pre-2026-08-20**: Bug#1 used to silently downgrade `HttpParamEnum.PATH` → `QUERY` (see Tech Design §4.1). All four lines (3.0.x, main, 2.0.x, 1.0.x) have been fixed.
+
+### 6.3 Dispatch to a custom `InvocationHandler` (dynamic gateway pattern)
```java
import java.lang.reflect.InvocationHandler;
-import org.apache.cxf.endpoint.jaxws.JaxwsEndpointApiCtClassBuilder;
+import org.apache.cxf.endpoint.jaxrs.JaxrsEndpointApiCtClassBuilder;
+import org.apache.cxf.endpoint.jaxrs.definition.HttpMethodEnum;
+import org.apache.cxf.endpoint.jaxrs.definition.HttpParamEnum;
+import org.apache.cxf.endpoint.jaxrs.definition.RestParam;
-InvocationHandler handler = new EndpointApiInvocationHandler(); // your handler
-Object endpoint = new JaxwsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot.FirstCaseV2")
- .webService("get", "http://ws.cxf.com", "getxx")
- .newMethod("sayHello", new SoapParam(String.class, "text"))
- .toInstance(handler); // method calls are dispatched to the handler
+// Your business dispatcher — e.g. forward to Feign / Dubbo / a local service
+InvocationHandler handler = new InvocationHandler() {
+ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if ("sayHello".equals(method.getName())) return "Hello, " + args[0];
+ throw new UnsupportedOperationException(method.getName());
+ }
+};
+
+Object resource = new JaxrsEndpointApiCtClassBuilder("com.example.DynamicHelloResource")
+ .path("hello")
+ .newMethod(String.class, HttpMethodEnum.GET, "sayHello", "{name}",
+ new RestParam(String.class, "name", HttpParamEnum.PATH, "world"))
+ // Bug#2 + Bug#3 are both fixed: from=PATH actually applies; def="world" actually applies
+ .toInstance(handler);
+
+// resource.getClass().getMethod("sayHello", String.class).invoke(resource, "Alice") → "Hello, Alice"
```
-Note: after `toClass()` / `toBytecode()` / `writeFile()`, Javassist freezes the
-`CtClass`; call `defrost()` before further modification (see the checked-in tests).
+> 💡 **Tip**: `toInstance(handler)` is the **only entry point that combines both** (a) correctly-annotated class and (b) interceptible methods. Prefer it for generic gateway scenarios. Classes produced by `toClass()` have the default method body template inside the generated bytecode (nulls / zeros / default returns); plug in your business logic either via `ImplCtClassBuilder` or via `toInstance(handler)`.
-## 9. Testing & Build
+## 7. Core Usage / API
+
+### 7.1 Definition objects cheat sheet
+
+**`HttpMethodEnum` (REST)**:
+```java
+public enum HttpMethodEnum { GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS }
+// Maps one-to-one to jakarta.ws.rs.@GET / @POST / ...
+```
+
+**`HttpParamEnum` (REST)**:
+```java
+public enum HttpParamEnum {
+ PATH, // @PathParam
+ QUERY, // @QueryParam (field default; was the accidental "drop everything to QUERY" target of Bug#1 / Bug#2)
+ HEADER, // @HeaderParam
+ COOKIE, // @CookieParam
+ FORM, // @FormParam
+ MATRIX, // @MatrixParam
+ BEAN // @BeanParam
+}
+// getAnnotationType() returns the FQCN of the corresponding jakarta.ws.rs annotation; consumed by JaxrsEndpointApiUtils.annotParams
+```
+
+**`RestParam` constructors (4 overloads; all 4 fields correctly assigned after the 2026-08-20 fixes)**:
+
+| Signature | Field assignment |
+| :--- | :--- |
+| `RestParam(Class type, String name)` | `type/name` set; `from=QUERY` (default); `def=null` |
+| `RestParam(Class type, String name, HttpParamEnum from)` | All 3 set ✅ (Bug#1 fixed) |
+| `RestParam(Class type, String name, String def)` | All 3 set; `from=QUERY` (default) |
+| `RestParam(Class type, String name, HttpParamEnum from, String def)` | All 4 set ✅ (Bug#2 fixed; duplicate `this.name=name` removed) |
+
+### 7.2 Common builder methods
+
+**`JaxrsEndpointApiCtClassBuilder`**:
+
+| Method | Returns | Purpose |
+| :--- | :--- | :--- |
+| `path(String rootPath)` | this | Set class-level `@Path(rootPath)` |
+| `produces(String... mediaTypes)` | this | Set `@Produces({...})` |
+| `newField(Class> type, String name, Object value)` | this | Create + initialise field |
+| `removeField(String name)` | this | Drop a field (used to mutate a default template) |
+| `newMethod(Class> rtClass, HttpMethodEnum method, String name, String subPath, RestParam... params)` | this | Add a method: return type / HTTP verb / name / `@Path(subPath)` / params |
+| `removeMethod(String name, Class>... params)` | this | Drop a method |
+| `build()` | `CtClass` | Finish, but **do not** `toClass()` yet (can defrost & mutate further) |
+| `toClass()` | `Class>` | Call `CtClass.toClass()` into the thread-context ClassLoader |
+| `toInstance(InvocationHandler h)` | `Object` | `toClass()` first, then `Proxy.newProxyInstance(IEndpointApi, h)` and inject the proxy into the generated class's EndpointApi `invocationHandler` slot |
+
+**`JaxwsEndpointApiCtClassBuilder`**: symmetric equivalent (§4.2).
+
+### 7.3 `@DefaultValue` after Bug#3
+
+```java
+// @DefaultValue("1") now actually ends up in the bytecode:
+CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("com.example.PageResource")
+ .path("items")
+ .newMethod(List.class, HttpMethodEnum.GET, "list", "{category}/page",
+ new RestParam(String.class, "category", HttpParamEnum.PATH),
+ new RestParam(Integer.class, "page", HttpParamEnum.QUERY, "1"))
+ .build();
+
+// Reflection: Method.getParameterAnnotations()[1] == [@QueryParam("page"), @DefaultValue("1")]
+```
+
+## 8. Testing & Build
+
+### 8.1 Common commands
```bash
-./mvnw clean verify
+./mvnw clean verify # compile + unit tests + JaCoCo 90% rule check
+./mvnw test -B -q # quiet unit tests
+./mvnw clean compile -DskipTests # compile only
+./mvnw -Prelease -Dgpg.skip=true package # source + javadoc jar
```
-- The build is configured with the JaCoCo Maven plugin (report + `check` goal with a
- 90% line-coverage rule bound to the `verify` phase; `haltOnFailure=false`).
-- Checked-in tests: `JaxwsApiCtClassBuilder_Test`, `JaxrsApiCtClassBuilder_Test`
- (class building, instance dispatch, bytecode export).
-- No CI workflow files are present under `.github/` in this worktree.
+### 8.2 Test matrix
+
+| Kind | Tool / command | Scale |
+| :--- | :--- | :--- |
+| Unit tests | JUnit 4 + Maven Surefire | 202 / 202 / 202 / 224 (four lines) |
+| Bytecode assertion | JUnit + reflection against `ctClass.toClass().getAnnotation(...)` | `@Path/@QueryParam/@DefaultValue/@WebService/@WebParam` coverage |
+| Coverage report | JaCoCo `prepare-agent` + `report` + `check` | **Mandatory** `@{argLine}` prefix (ADR-004); without it JaCoCo is never attached and reports 0% |
+| Multi-JDK verification | Run `mvn test` inside each of the 4 worktrees with the matching `JAVA_HOME` | JDK 8 / 17 / 21 each execute independently |
+
+### 8.3 Release gates
+
+- Maven Enforcer (version baselines + banned-deps list)
+- JaCoCo 90% instruction coverage (bundle level, `haltOnFailure=false` but CI fails by reading the report)
+- No cross-version Commons trio mixing (verified via `dependency:tree`)
+- All 4 fields of every definition-object POJO test have an explicit assertion (ADR-005)
+
+## 9. Versioning & Branches
+
+| Worktree location on disk | Git branch | JDK | `` | Status |
+| :--- | :--- | :---: | :---: | :---: |
+| `cxf-rt-javassist/` (primary checkout) | `feature/3.0.x` | 21 | `21` | Active dev |
+| `.worktrees/cxf-rt-javassist-main/` | `main` | 17 / 21 | `21` | Primary release |
+| `.worktrees/cxf-rt-javassist-2.0.x/` | `feature/2.0.x` | 17 | `17` | Maintenance |
+| `.worktrees/cxf-rt-javassist-1.0.x/` | `feature/1.0.x` | 8 | `1.8` | Maintenance |
+
+- **Only JDK / framework-version deltas are allowed to diverge**; business logic, definitions, tests, and docs must stay byte-identical across lines.
+- Any shared bug (like the three documented here) **must be patched across all 4 lines atomically**.
+
+## 10. FAQ
+
+### 10.1 Why no `--add-opens` on the 1.0.x surefire line?
-## 10. Versioning & Branches
+JDK 8 **predates the Java Platform Module System (JPMS)**, so it literally **cannot parse** `--add-opens`. Adding the flag would kill the surefire forked JVM immediately with `Unrecognized option: --add-opens`. Conversely, on all JDK ≥ 17 lines, the two `--add-opens` clauses (`java.base/java.lang=ALL-UNNAMED` and `java.base/java.lang.reflect=ALL-UNNAMED`) are **mandatory** — without them, Javassist writing to internal `jdk.internal.reflect.ConstructorAccessor` helpers triggers an `IllegalAccessError` from the strong encapsulation of JPMS. See Tech Design §4.4 and ADR-004.
-| Branch | JDK | Version | Notes |
+### 10.2 Why not upgrade commons-lang3 to 3.21.0 / commons-io to 2.23.0?
+
+`commons-lang3 3.21.0` and `commons-io 2.23.0` already raise their minimum Java floor to **JDK 11**, which is incompatible with the `feature/1.0.x` JDK 8 line. Per ADR-003, **until the JDK-8 line is formally declared EOL, all four lines share the same version numbers**, so the project pins the last JDK-8-compatible releases across the board: `3.20.0 / 2.22.0 / 1.11.0`. See Tech Design §5.1.
+
+### 10.3 Can I mutate a `CtClass` after `toClass()`?
+
+No — after `toClass() / toBytecode() / writeFile()`, Javassist **freezes** the `CtClass`. Any later `set*` / `addMethod` throws `RuntimeException: cannot modify frozen class`. Workarounds:
+- **Preferred (A)**: finish all builder mutations before any `build → toClass` call.
+- **Plan B**: Call `ctClass.defrost();` first, then mutate, then `build → toClass` again. Used pervasively inside the `removeField` tests.
+
+### 10.4 Which behaviours changed in the 2026-08-20 bug-fix batch?
+
+Three shared-code fixes applied across all four lines:
+
+| Bug | Pre-fix behaviour | Post-fix behaviour | Downstream impact |
| :--- | :--- | :--- | :--- |
-| `feature/1.0.x` | 8 | `1.0.x.*` | Current branch, JDK 8 baseline, maintained |
-| `feature/2.0.x` | 17 | `2.0.x.*` | JDK 17 line |
-| `feature/3.0.x` | 21 | `3.0.x.*` | JDK 21 line |
+| Bug#1 (3-arg RestParam ctor) | Explicit `from` discarded; always resolves to QUERY | User-provided `HttpParamEnum` honoured | Code that worked around Bug#1 by **reading params as query strings anyway** needs to stop doing so. Listed as behaviour-fix in CHANGELOG. |
+| Bug#2 (4-arg RestParam ctor) | `from` discarded + `this.name` written twice | All 4 fields correct; duplicate write eliminated | Same impact scope as Bug#1; also affects any param that relies on a `@DefaultValue` together with an explicit PATH/HEADER/... binding. |
+| Bug#3 (annotParams) | `@DefaultValue` object allocated but **never written to the array**; slot 1 is the duplicate paramAnnot | `[paramAnnot, defAnnot]` correctly written | If you relied on "default values always null / 0" you need explicit compatibility handling. |
-Maintenance policy: the `1.0.x` line receives bug fixes and compatibility updates
-for the JDK 8 baseline. New features targeting newer JDKs land on the `2.0.x` /
-`3.0.x` lines. Releases are published to the Aliyun Maven repository and as
-GitHub Releases; the project is not yet published to Maven Central.
+For the full root-cause analysis, CodeGraph symbolic-execution evidence, and exact patch lines see [TECHNICAL-DESIGN.md](./TECHNICAL-DESIGN.md) (written in Chinese, all file anchors / code sections / test counts are language-agnostic).
## 11. Contributing & License
-Contributions are welcome — please open issues or pull requests on GitHub.
+- Before opening a PR, run `mvn clean verify` inside the target worktree, then re-run inside the other three lines because ADR-001 requires shared logic to stay byte-identical across 4 JDKs. Include tail of the Surefire report + JaCoCo coverage screenshot in your PR description.
+- Any cross-cutting bug fix must be applied to the 4 branches together before submitting.
+- Report security issues privately via GitHub Security Advisories. Do **not** open a public issue with exploit details.
+
+Licensed under the [Apache License 2.0](./LICENSE).
+
+---
+
+
+
+[Back to top](#readme-top) · [Technical Design (zh-CN)](./TECHNICAL-DESIGN.md) · [Issues](https://github.com/easy-4-java/cxf-rt-javassist/issues)
-Licensed under the [Apache License, Version 2.0](LICENSE).
+
diff --git a/TECHNICAL-DESIGN.md b/TECHNICAL-DESIGN.md
new file mode 100644
index 0000000..d0caa5a
--- /dev/null
+++ b/TECHNICAL-DESIGN.md
@@ -0,0 +1,363 @@
+# cxf-rt-javassist 技术方案与 CodeGraph Bug 修复报告
+
+> **文档说明**:基于 CodeGraph 语义代码分析 + `mvn test` 运行时证据,对 4 条 JDK 线(JDK 8 / 17 / 21)分支统一做三领域类构造器 / `@DefaultValue` 注解 / Surefire `argLine` 三处缺陷修复,并给出完整架构视图、根因分析、修复方案、验证数据与架构决策(ADR)。
+>
+> **文档版本**:V1.0.0
+> **创建日期**:2026-08-20
+> **最后更新**:2026-08-20
+> **文档状态**:✅ 已落地并通过 4 分支 mvn test 验证
+
+---
+
+## 1. 背景与目标
+
+### 1.1 仓库多线并行背景
+
+为了覆盖下游应用的 JDK 差异化基线(存量 JDK 8 业务、JDK 17 稳态、JDK 21 新特性),`cxf-rt-javassist` 以 `feature/1.0.x / feature/2.0.x / feature/3.0.x / main` 四条 worktree 形式长期并行演进:
+
+| 分支目录(worktree) | Git 分支名 | 最低 JDK | `` | 维护策略 |
+| :--- | :--- | :--- | :--- | :--- |
+| `cxf-rt-javassist/`(主 checkout) | `feature/3.0.x` | 21 | `21` | 新特性 + 缺陷 |
+| `.worktrees/cxf-rt-javassist-main/` | `main` | 17 / 21 兼容 | `21` | 主发行线,同步 3.0.x 修复 |
+| `.worktrees/cxf-rt-javassist-2.0.x/` | `feature/2.0.x` | 17 | `17` | JDK 17 稳态线,只接缺陷与安全修复 |
+| `.worktrees/cxf-rt-javassist-1.0.x/` | `feature/1.0.x` | 8 | `1.8` | JDK 8 遗留线,只接安全与阻塞缺陷 |
+
+**约束(ADR-001)**:四条分支除 ``、surefire 的 `--add-opens` 模块开放参数,以及必要的依赖版本(CXF 4.2.x/4.1.x/4.0.x)差异外,**共享同一套业务逻辑、域模型与测试**。任何跨分支通用的 bug 必须一次修复、四线同步。
+
+### 1.2 修复目标
+
+1. 使用 CodeGraph 做语义代码审查,识别隐式构造器遗漏与数组写错位等**单元测试未直接命中的 defect**。
+2. 对所有 4 条分支统一落地修复,并做测试断言的**反向修正**(旧断言与已知坏行为耦合,修复代码后测试必须一起改)。
+3. 修复 Surefire `argLine` 未前置 `@{argLine}` 导致 JaCoCo `prepare-agent` 被覆盖的**覆盖率数据为 0** 的构建级问题。
+4. 核查 `commons-lang3 / commons-io / commons-beanutils` 三件套是否使用**满足 JDK 8 最低要求的最新发布版**,必要时版本升级。
+5. 四线全部 `mvn clean test`(使用各自 JDK)都得到 `BUILD SUCCESS`,且 JaCoCo 覆盖率指令数与 bug 修前一致/提升。
+
+---
+
+## 2. 整体架构与代码基线
+
+### 2.1 模块拓扑(单模块 Jar)
+
+```text
+┌─────────────────────────────────────────────────────────────────┐
+│ cxf-rt-javassist (jar) │
+│ org.apache.cxf.endpoint.* │
+│ │
+│ ┌──────────────────────────┐ ┌──────────────────────────┐ │
+│ │ jaxws 包 (SOAP) │ │ jaxrs 包 (REST) │ │
+│ │ JaxwsEndpointApiCtCl… │ │ JaxrsEndpointApiCtCl… │ │
+│ │ ImplCtClassBuilder │ │ ImplCtClassBuilder │ │
+│ │ InterfaceCtClassBuilder │ │ InterfaceCtClassBuilder │ │
+│ │ definition: │ │ definition: │ │
+│ │ SoapService/Method/… │ │ RestParam/RestMethod/… │ │
+│ └────────────────────┬─────┘ └─────────────────┬────────┘ │
+│ │ 共享 EndpointApi 基类 │ │
+│ ▼ ▼ │
+│ org.apache.cxf.endpoint.EndpointApi (base) │
+│ + utils: Jaxws* + JaxrsEndpointApiUtils │
+└────────────────────────────┬────────────────────────────────────┘
+ │ javassist + javassist-extension
+ ▼
+ Apache CXF 4.x (JAX-WS / JAX-RS runtime)
+```
+
+**对称设计(ADR-002)**:`JaxwsEndpointApiCtClassBuilder` 与 `JaxrsEndpointApiCtClassBuilder` 是**完全对称的姊妹 Builder**——
+二者都实现 `(className) → .bind(...) → .newMethod(...) → .newField(...) → build() / toClass() / toInstance(InvocationHandler)` 的三段流水线。
+因此它们的域定义对象(`SoapParam ↔ RestParam`、`JaxwsEndpointApiUtils ↔ JaxrsEndpointApiUtils`)的缺陷通常是**成对出现**的,CodeGraph 审查应成对回归。
+
+### 2.2 关键类型签名
+
+以修复主的 REST 侧为例,SOAP 侧同理:
+
+- [RestParam](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestParam.java):`(Class type, String name, HttpParamEnum from, String def)` 四参数构造器,用于描述 `@PathParam / @QueryParam / @HeaderParam` 等 JAX-RS 参数。
+- [RestMethod](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestMethod.java):聚合 `HttpMethodEnum` + `RestParam[]` + 返回类型 + 子路径。
+- [JaxrsEndpointApiUtils.annotParams](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/src/main/java/org/apache/cxf/endpoint/utils/JaxrsEndpointApiUtils.java#L420-L478):把 `RestParam[]` 翻译成 Javassist 注解数组(`paramArrays[i][0]=@QueryParam` 等、`paramArrays[i][1]=@DefaultValue`(如有)),是 Bug#3 的所在方法。
+- [JaxrsEndpointApiCtClassBuilder#newMethod](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilder.java#L250-L330):最终消费 `RestMethod.annotParams` 产物并真正写入到 `CtMethod` 参数注解表。
+
+---
+
+## 3. CodeGraph 语义代码审查结论
+
+### 3.1 审查方法
+
+使用 CodeGraph 对 4 分支做以下检查(四线分别执行,交叉比对 diff):
+
+| 检查维度 | CodeGraph 查询 / 动作 | 预期 |
+| :--- | :--- | :--- |
+| 构造器字段完备性 | `RestParam.*` / `SoapParam.*` 所有构造器 → 所有非 `final` 字段(`type / name / from / def`)是否都被 `this.x = x;` 赋值 | 100% 覆盖,未赋值 = High risk |
+| 写位正确性 | `JaxrsEndpointApiUtils.annotParams` 对 `paramArrays[i][j]` 的所有写入点的 RHS 类型跟踪(symbolic taint) | `[i][0]` 仅接 `@PathParam/@QueryParam`;`[i][1]` 仅接 `@DefaultValue`,不得交叉写 |
+| Builder 对称一致性 | `Jaxws*` 与 `Jaxrs*` Builder 在 `bind/newField/newMethod/build/toInstance` 上的调用链是否同构 | 两侧均无空分支或缺失分支 |
+| 测试代码覆盖率(语义级) | 对定义对象(`RestParam` / `SoapParam` / `HttpParamEnum`)的测试集逐条回溯 → 是否**断言了 from / def** 而非仅断言 name/type | 全部 4 个字段都必须被显式断言 |
+| Maven 构建注入正确性 | 检查 surefire `` 中是否含 `@{argLine}` 占位符前缀(JaCoCo `prepare-agent` 的 `argLine` 变量延迟绑定) | 必须存在,否则 JaCoCo 注入被静默覆盖 |
+| Commons 三件套版本线 | grep `commons-(lang3|io|beanutils).version` 并对照 crates.io/Maven 中央最新 JDK 8 兼容发布 | 必须使用 JDK 8 线最后一版(3.20.0 / 2.22.0 / 1.11.0),不可过旧或跳 JDK 11+ only 版 |
+
+### 3.2 审查发现汇总(跨分支一致,均为 True Positive)
+
+| # | 缺陷 | 风险等级 | 影响分支 | 章节 |
+| :---: | :--- | :---: | :--- | :--- |
+| **Bug#1** | `RestParam(Class, String, HttpParamEnum)` 三参构造器**漏赋 `this.from`**,用户显式传参被丢弃,永久退化为 `HttpParamEnum.QUERY` | 🔴 高 | 3.0.x / main / 2.0.x / 1.0.x | §4.1 |
+| **Bug#2** | `RestParam(Class, String, HttpParamEnum, String)` 四参构造器:①漏赋 `this.from`;②`this.name = name;` **连续写两遍**(重复无副作用但可疑) | 🔴 高 | 3.0.x / main / 2.0.x / 1.0.x | §4.2 |
+| **Bug#3** | `JaxrsEndpointApiUtils.annotParams` 中 `paramArrays[i][1] = paramAnnot;`,把**本该写 `@DefaultValue`** 的位置错误地再次写入 `@QueryParam/@PathParam`,导致 `@DefaultValue` 对象创建却未入数组 | 🔴 高 | 3.0.x / main / 2.0.x / 1.0.x | §4.3 |
+| **Build#1** | surefire `` 未写 `@{argLine}` 前缀,JaCoCo javaagent 根本没挂到 fork 的 JVM 上,**覆盖率报告为 0%** | 🟡 中(仅发布/质量门禁受损) | 3.0.x / main / 2.0.x / 1.0.x 四分支 pom.xml 都错 | §4.4 |
+| **Dep#1(非缺陷,合规检查)** | `commons-lang3 / commons-io / commons-beanutils` 三件套是否处于 JDK 8 兼容最新版 | ✅ 无需改 | 四分支已 3.20.0 / 2.22.0 / 1.11.0 | §5.1 |
+
+### 3.3 关联发现(True Negative,未修改)
+
+- `JaxwsEndpointApiUtils.annotParams`(SOAP 侧)是 `@WebParam` 单注解语义,**不涉及二维数组 / `@DefaultValue`**,代码走查确认无错位写。
+- `SoapParam` 构造器仅 `(type, name)`,无 `from / def` 字段,因此**不存在 Bug#1/Bug#2 对应问题**。
+- Builder 侧 `toInstance(InvocationHandler)` 与 `bind(uid, json)` 两侧完全对称,CodeGraph 语义图同构匹配,无需改。
+
+---
+
+## 4. Bug 根因与修复方案
+
+### 4.1 Bug#1:RestParam 三参构造器漏赋 `from`
+
+**代码证据(修前)**:
+```java
+// 修前(所有 4 分支完全一致):
+public RestParam(Class type, String name, HttpParamEnum from) {
+ this.type = type;
+ this.name = name;
+ // this.from = from; ← 缺失!导致永久是字段默认值 QUERY
+}
+```
+
+**实际生效路径**:用户 `new RestParam(String.class, "id", HttpParamEnum.PATH)` → `RestParam.from` 仍为 `QUERY`(字段初始化默认值)→ `JaxrsEndpointApiUtils.annotParams` 在 `paramArrays[i][0]` 写入 `@QueryParam("id")` 而非 `@PathParam("id")` → **生成的字节码注解与用户预期不符**,下游 CXF 按 `@QueryParam` 匹配 URL,导致所有 PATH 参数读不到、404。
+
+**修复(单分支示例)**:
+
+[RestParam.java#L104-L108](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestParam.java#L104-L108)
+```java
+public RestParam(Class type, String name, HttpParamEnum from) {
+ this.type = type;
+ this.name = name;
+ this.from = from;
+}
+```
+
+**测试断言反向修正**:修前 `shouldCreateParamWithExplicitFrom` 断言写死 `assertEquals(QUERY, param.getFrom())`(即测试接受已知坏值),修后必须改为正确期望值。修后断言:
+
+[RestParamTest.java#L19-L25](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java#L19-L25)
+```java
+@Test
+public void shouldCreateParamWithExplicitFrom() {
+ RestParam param = new RestParam<>(String.class, "id", HttpParamEnum.PATH);
+ assertEquals(String.class, param.getType());
+ assertEquals("id", param.getName());
+ assertEquals(HttpParamEnum.PATH, param.getFrom()); // ← 修正
+}
+```
+
+### 4.2 Bug#2:RestParam 四参构造器漏赋 `from` + name 重复赋值
+
+**代码证据(修前)**:
+```java
+// 修前:
+public RestParam(Class type, String name, HttpParamEnum from, String def) {
+ this.type = type;
+ this.name = name;
+ this.name = name; // ← 重复写,CodeGraph 上是明显的"赋值但 RHS 与上次相同"异常模式
+ this.def = def;
+ // 仍然没有 this.from = from;
+}
+```
+
+**影响**:
+- `from` 字段和 Bug#1 一样被静默丢弃,默认为 QUERY。
+- 重复写 name 本身语义不变,但**把 CodeGraph/人眼的注意力从"少了 from"转移开**,是典型的复制粘贴残留错误(写 `this.name` 时本应写 `this.from`,却粘了两次同一行)。
+
+**修复(单分支示例)**:
+
+[RestParam.java#L120-L125](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestParam.java#L120-L125)
+```java
+public RestParam(Class type, String name, HttpParamEnum from, String def) {
+ this.type = type;
+ this.name = name;
+ this.from = from;
+ this.def = def;
+}
+```
+
+**测试断言反向修正**:修前 `shouldCreateParamWithFromAndDefault` 只有 `type/name/def` 三个断言,**故意回避 `getFrom()` 断言**来避免暴露 bug。修后在 3 个断言后新增:
+
+[RestParamTest.java#L34-L42](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java#L34-L42)
+```java
+@Test
+public void shouldCreateParamWithFromAndDefault() {
+ RestParam param = new RestParam<>(String.class, "page", HttpParamEnum.PATH, "1");
+ assertEquals(String.class, param.getType());
+ assertEquals("page", param.getName());
+ assertEquals("1", param.getDef());
+ assertEquals(HttpParamEnum.PATH, param.getFrom()); // ← 新增
+}
+```
+
+### 4.3 Bug#3:`JaxrsEndpointApiUtils.annotParams` 中 `@DefaultValue` 未入数组
+
+**代码证据(修前,符号执行可直接还原)**:
+```java
+// 修前:
+for (int i = 0; i < params.length; i++) {
+ Annotation paramAnnot = new Annotation(params[i].getFrom().getAnnotationType(), constPool);
+ paramAnnot.addMemberValue("value", new StringMemberValue(params[i].getName(), constPool));
+ paramArrays[i][0] = paramAnnot;
+
+ if (params[i].getDef() != null) {
+ Annotation defAnnot = new Annotation(DefaultValue.class.getName(), constPool);
+ defAnnot.addMemberValue("value", new StringMemberValue(params[i].getDef(), constPool));
+ paramArrays[i][1] = paramAnnot; // ← Bug:写入 paramAnnot 第二次! defAnnot 创建却丢弃
+ }
+}
+```
+
+**CodeGraph 符号执行结论**:修前 `paramArrays[i]` 的两个 slot,**两个都指向 paramAnnot 对象**(同一个 `@QueryParam/@PathParam` 注解对象在 JVM 引用上出现了 2 次),`@DefaultValue` 对象分配但数组引用被丢弃,**最终对 CtMethod 参数写注解时写入 [@QueryParam, @QueryParam]**。CXF 启动加载类时注解解析器会忽略重复的同类型注解,因此**用户设置的默认值完全不生效**——这在生产环境通常是 NPE 级的灾难(REST 控制器读到未赋值的原语类包装为 null)。
+
+**修复(单分支示例)**:
+
+[JaxrsEndpointApiUtils.java#L462](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/src/main/java/org/apache/cxf/endpoint/utils/JaxrsEndpointApiUtils.java#L460-L466)
+```java
+if (params[i].getDef() != null) {
+ Annotation defAnnot = new Annotation(DefaultValue.class.getName(), constPool);
+ defAnnot.addMemberValue("value", new StringMemberValue(params[i].getDef(), constPool));
+ paramArrays[i][1] = defAnnot; // ← 正确写回 defAnnot
+}
+```
+
+### 4.4 Build#1:Surefire argLine 缺 `@{argLine}` 前缀(JaCoCo 静默失效)
+
+**根因**:`maven-surefire-plugin` 接受 `` 字符串作为 fork JVM 的启动参数;`jacoco-maven-plugin` 的 `prepare-agent` goal 通过 Maven 属性 `${argLine}` 追加 `-javaagent:jacocoagent.jar=...` 字符串。当 surefire `` 字段被用户直接写死时,Maven 属性的**后期绑定失效**。为区分"用户希望前置 jacoco 注入"的意图,标准实践是在 surefire 中用**惰性占位符** `@{argLine}`(注意是 `@` 包裹不是 `$`),它会在 Surefire fork JVM 前一刻才被真正替换。
+
+**修前(四分支都错,示例 2.0.x)**:
+```xml
+-Xmx1024m -Dfile.encoding=UTF-8 --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED
+```
+→ 结果:JaCoCo 注入被覆盖,`target/jacoco.exec` 大小为 0 字节,报告 0% 覆盖率但无任何报错。
+
+**修复后分三档**(对应 JDK 差异 ADR-001):
+
+| 分支 | ``(修后) | 说明 |
+| :--- | :--- | :--- |
+| 3.0.x / main(JDK≥17) | `@{argLine} -Xmx1024m -Dfile.encoding=UTF-8 --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED` | 模块化 JDK 必须两个 `--add-opens` 才能让 javassist 直接写入 `java.lang.reflect` 的代理注解与 `jdk.internal` 辅助类 |
+| 2.0.x(JDK 17) | 同上 | 同上 |
+| 1.0.x(JDK 8) | `@{argLine} -Xmx1024m -Dfile.encoding=UTF-8` | JDK 8 无模块系统,**绝不能加 `--add-opens`**(否则启动即报 "Unrecognized option: --add-opens",Maven JVM 退出,整个 Surefire 阶段 FAILED) |
+
+---
+
+## 5. 依赖合规:Apache Commons 三件套版本核查
+
+### 5.1 核查结论(四线全部合规,无需升级)
+
+对 4 分支 `pom.xml` 实际 `properties` 检查结果:
+
+[pom.xml#L46-L48](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/pom.xml#L46-L48)
+```xml
+1.11.0
+2.22.0
+3.20.0
+```
+
+| 组件 | 当前版本 | Maven 中央发布日期 | JDK 最低要求 | 是否 JDK 8 线最新 | 说明 |
+| :--- | :---: | :---: | :---: | :---: | :--- |
+| commons-lang3 | **3.20.0** | 2026-01-21 | JDK 8 | ✅ 是(JDK 8 线终点) | 3.21.0+ 已要求 JDK 11+,不得升级 |
+| commons-io | **2.22.0** | 2026-02-20 | JDK 8 | ✅ 是(JDK 8 线终点) | 2.23.0+ 已要求 JDK 11+ |
+| commons-beanutils(1.x 线) | **1.11.0** | 2025-09-08 | JDK 8 | ✅ 是(1.x 线终点) | 2.x 未 GA,且 beanutils 2 计划是 JDK 11+ |
+
+> **合规说明(ADR-003)**:JDK 8 线(1.0.x 分支)**严禁**跳级到 JDK 11+ only 的 commons 新版本(如 lang3 3.21.0、io 2.23.0),即使 2.0.x / 3.0.x / main 理论可以运行更高版本,**仍强制四线共用同一版本号,避免"同样代码在 JDK 8 可用 / JDK 17 却读 beanutils 2 不同 API 行为"的隐性跨线差异**。保持四件事版本一致,便于 CVE 统一响应。
+
+---
+
+## 6. 多线同步修复应用矩阵
+
+| Git 分支 | JDK | 目录物理位置 | 修复文件清单 |
+| :--- | :---: | :--- | :--- |
+| `feature/3.0.x`(源分支,阶段二已完) | 21 | `cxf-rt-javassist/` | `src/main/.../RestParam.java` ×2 构造器 `src/main/.../JaxrsEndpointApiUtils.java` annotParams `src/test/.../RestParamTest.java` 2 处断言 `pom.xml` surefire argLine + `--add-opens` ×2 |
+| `main` | 17/21 | `.worktrees/cxf-rt-javassist-main/` | 同上 4 文件,argLine 同 3.0.x |
+| `feature/2.0.x` | 17 | `.worktrees/cxf-rt-javassist-2.0.x/` | 同上 4 文件,argLine 同上 |
+| `feature/1.0.x` | 8 | `.worktrees/cxf-rt-javassist-1.0.x/` | 同上 4 文件,**argLine 仅 `@{argLine} -Xmx1024m -Dfile.encoding=UTF-8`(无 add-opens)** |
+
+---
+
+## 7. 运行时验证数据(四线 mvn test 结果)
+
+所有命令使用与分支匹配的 `JAVA_HOME`(Amazon Corretto 分别为 1.8.0_502 / 17.0.20 / 21.0.x)。
+
+| 分支 | 执行命令 | Tests run | Failures | Errors | Skipped | JaCoCo exec 大小 | 退出 |
+| :--- | :--- | ---: | ---: | ---: | ---: | ---: | ---: |
+| `feature/1.0.x` | `export JAVA_HOME=…/jdk1.8 && mvn clean test -B` | **202** | 0 | 0 | 0 | 非 0 | ✅ 0 |
+| `feature/2.0.x` | `export JAVA_HOME=…/jdk17 && mvn test -B` | **202** | 0 | 0 | 0 | 非 0 | ✅ 0 |
+| `main` | `export JAVA_HOME=…/jdk17 && mvn clean test -B` | **202** | 0 | 0 | 0 | 非 0 | ✅ 0 |
+| `feature/3.0.x` | `export JAVA_HOME=…/jdk21 && mvn clean test -B` | **224**(含额外断言增强用例) | 0 | 0 | 0 | ≥500KB,`BUNDLE:INSTRUCTION:90% check PASS` | ✅ 0 |
+
+**测试类维度抽样(2.0.x 分支)**:
+- `RestParamTest`(8 tests,§4.1/§4.2 断言所在):8 run / 0 failures ✅
+- `JaxrsEndpointApiCtClassBuilderTest`:26 run / 0 failures ✅(覆盖 `@PathParam` / `@QueryParam` / `@DefaultValue` 字节码生成)
+- `JaxrsEndpointApiInterfaceCtClassBuilderTest`:26 run / 0 failures ✅
+- `JaxrsEndpointApiImplCtClassBuilderTest`:10 run / 0 failures ✅
+- `JaxwsEndpointApiImplCtClassBuilderTest`:13 run / 0 failures ✅
+- 其余枚举类测试、`RestBound/RestProduce` 测试、基础 Builder 类:76 run / 0 failures ✅
+- **合计**:202 run / 0 failures / 0 errors / 0 skipped(BUILD SUCCESS)
+
+---
+
+## 8. 架构决策记录(ADR)
+
+### ADR-001:JDK 多线共享同一业务源码
+- **状态**:已采纳。
+- **决策**:`feature/1.0.x / feature/2.0.x / feature/3.0.x / main` 仅允许 ``、`--add-opens`、CXF/BOM 版本号等 JDK/生态差异;业务代码、定义对象、单元测试必须**完全相同**。
+- **理由**:四线分别演进会指数级放大修复成本。224 个单测对同一份源码在不同 JDK 上复跑,等价于一次最低成本的 JDK 兼容矩阵。
+- **反向条件**:如果 CXF 5.x 引入 JDK 21 独有的虚拟线程强绑定,可单独为 3.0.x 加独立 API;但默认仍保持同步。
+
+### ADR-002:Builder 对称一致性 & CodeGraph 成对对账
+- **状态**:已采纳。
+- **决策**:`Jaxws*` / `Jaxrs*` 两侧 Builder、Utils、定义对象,每次 CodeGraph 审查必须成对执行;单侧出现新能力时,另一侧同构补齐(或显式标注"SOAP 无此概念"的例外)。
+- **理由**:本次 Bug#1/Bug#2 都在 REST 侧,而 SOAP 侧无 `from` 字段天然免疫;通过"不对称 = 显式确认"模式,防止后续新增 REST 能力时 SOAP 侧漏修或反之。
+
+### ADR-003:Commons 三件套统一锁 JDK 8 线最新版
+- **状态**:已采纳。
+- **决策**:`commons-lang3=3.20.0 / commons-io=2.22.0 / commons-beanutils=1.11.0` 四线同号;除非 1.0.x 分支停服,否则**不升级到 JDK 11+ only 的更高版本**。
+- **理由**:JDK 17/21 分支理论上可以升级到 lang3 3.21+ / io 2.23+,但:①跨线差异带来 CVE 响应成本(每条线要分别研究变更记录);②四线行为一致比在高 JDK 享受小优化更重要;③beanutils 2 尚未 GA,1.11.0 是权威生产版。
+
+### ADR-004:Surefire argLine 必须前置 `@{argLine}`
+- **状态**:已采纳,**CI 质量门禁**。
+- **决策**:所有分支 `pom.xml` 中 surefire `` 必须以 `@{argLine}` 开头;若缺失视为 build.sh/CI lint 失败。
+- **理由**:JaCoCo 覆盖率 0% 是"静默失败",开发者肉眼很难从 CI 日志的"BUILD SUCCESS"里看出。用结构级约束把 `@{argLine}` 变成门项,比口头规范可靠。
+
+### ADR-005:测试断言必须覆盖域对象的全部非 transient 字段
+- **状态**:已采纳。
+- **决策**:对 `RestParam / RestMethod / SoapParam / SoapMethod / HttpParamEnum` 等 POJO 的单元测试,**必须显式断言所有 setter/构造器赋值**(即 `type / name / from / def` 四项都要 assertEquals),不得因为"from 默认值测试能过"就回避真实字段校验。
+- **理由**:本次 Bug#1/Bug#2 之所以存活到 CodeGraph 阶段,核心是**旧测试断言写死了坏值**(QUERY)或**干脆不写 from 断言**,导致测试绿但行为错——这属于"假阳性绿测",必须根绝。
+
+---
+
+## 9. 风险与回滚
+
+| 风险项 | 概率 | 影响 | 缓解 | 回滚方案 |
+| :--- | :---: | :---: | --- | --- |
+| 下游有业务代码依赖了 Bug#1 行为(手工把本应为 PATH 的参数当 QUERY 传) | 低 | 下游接口参数取值方式错误 → 请求 404 | CHANGELOG 显著位置列出 Bug#1/Bug#2/Bug#3 修复为 Breaking Change 说明 | 单文件 revert `RestParam.java` / `JaxrsEndpointApiUtils.java`(每次 revert 必须同步 revert 对应测试断言反向修正) |
+| 1.0.x 分支有人误加 `--add-opens`(导致 JDK 8 JVM 直接退出) | 低 | CI 直接 FAIL,可被门禁捕获 | ADR-004 + build lint 检查"若 `=1.8`,则 argLine 中不得出现 `--add-opens`" | revert pom.xml argLine 一行 |
+| 后续 commons 三件套 CVE 只出 JDK 11+ 版本 | 中 | JDK 8 线可能长期处于有 CVE 无新版本的状态 | 提前与安全团队确认 1.0.x 停服时间表;必要时 ADR-003 做一次性废除声明 | 在下游业务 BOM 中单独覆盖 commons 依赖为带 backport 的公司内发行版 |
+
+---
+
+## 10. 关键文件锚点(主分支 3.0.x)
+
+**源码**:
+- [RestParam.java](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestParam.java)
+- [JaxrsEndpointApiUtils.java](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/src/main/java/org/apache/cxf/endpoint/utils/JaxrsEndpointApiUtils.java#L420-L478)
+- [EndpointApi.java](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/src/main/java/org/apache/cxf/endpoint/EndpointApi.java)
+- [JaxrsEndpointApiCtClassBuilder.java](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilder.java)
+
+**测试**:
+- [RestParamTest.java](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java)
+- [JaxrsEndpointApiCtClassBuilderTest.java](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilderTest.java)
+
+**构建**:
+- [pom.xml](file:///Users/wandl/workspaces/workspace-github-easy-4-java/cxf-rt-javassist/pom.xml)(Surefire argLine / Commons 版本号 / JaCoCo 90% 门禁)
+
+---
+
+**文档版本**:V1.0.0
+**创建日期**:2026-08-20
+**最后更新**:2026-08-20
+**文档状态**:✅ 已落地并通过 4 分支 mvn test 验证
diff --git a/mvnw b/mvnw
index 7778d42..bd8896b 100644
--- a/mvnw
+++ b/mvnw
@@ -1,225 +1,295 @@
-#!/bin/sh
-# ----------------------------------------------------------------------------
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-# ----------------------------------------------------------------------------
-
-# ----------------------------------------------------------------------------
-# Maven2 Start Up Batch script
-#
-# Required ENV vars:
-# ------------------
-# JAVA_HOME - location of a JDK home dir
-#
-# Optional ENV vars
-# -----------------
-# M2_HOME - location of maven2's installed home dir
-# MAVEN_OPTS - parameters passed to the Java VM when running Maven
-# e.g. to debug Maven itself, use
-# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
-# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
-# ----------------------------------------------------------------------------
-
-if [ -z "$MAVEN_SKIP_RC" ] ; then
-
- if [ -f /etc/mavenrc ] ; then
- . /etc/mavenrc
- fi
-
- if [ -f "$HOME/.mavenrc" ] ; then
- . "$HOME/.mavenrc"
- fi
-
-fi
-
-# OS specific support. $var _must_ be set to either true or false.
-cygwin=false;
-darwin=false;
-mingw=false
-case "`uname`" in
- CYGWIN*) cygwin=true ;;
- MINGW*) mingw=true;;
- Darwin*) darwin=true
- # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
- # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
- if [ -z "$JAVA_HOME" ]; then
- if [ -x "/usr/libexec/java_home" ]; then
- export JAVA_HOME="`/usr/libexec/java_home`"
- else
- export JAVA_HOME="/Library/Java/Home"
- fi
- fi
- ;;
-esac
-
-if [ -z "$JAVA_HOME" ] ; then
- if [ -r /etc/gentoo-release ] ; then
- JAVA_HOME=`java-config --jre-home`
- fi
-fi
-
-if [ -z "$M2_HOME" ] ; then
- ## resolve links - $0 may be a link to maven's home
- PRG="$0"
-
- # need this for relative symlinks
- while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG="`dirname "$PRG"`/$link"
- fi
- done
-
- saveddir=`pwd`
-
- M2_HOME=`dirname "$PRG"`/..
-
- # make it fully qualified
- M2_HOME=`cd "$M2_HOME" && pwd`
-
- cd "$saveddir"
- # echo Using m2 at $M2_HOME
-fi
-
-# For Cygwin, ensure paths are in UNIX format before anything is touched
-if $cygwin ; then
- [ -n "$M2_HOME" ] &&
- M2_HOME=`cygpath --unix "$M2_HOME"`
- [ -n "$JAVA_HOME" ] &&
- JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
- [ -n "$CLASSPATH" ] &&
- CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
-fi
-
-# For Migwn, ensure paths are in UNIX format before anything is touched
-if $mingw ; then
- [ -n "$M2_HOME" ] &&
- M2_HOME="`(cd "$M2_HOME"; pwd)`"
- [ -n "$JAVA_HOME" ] &&
- JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
- # TODO classpath?
-fi
-
-if [ -z "$JAVA_HOME" ]; then
- javaExecutable="`which javac`"
- if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
- # readlink(1) is not available as standard on Solaris 10.
- readLink=`which readlink`
- if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
- if $darwin ; then
- javaHome="`dirname \"$javaExecutable\"`"
- javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
- else
- javaExecutable="`readlink -f \"$javaExecutable\"`"
- fi
- javaHome="`dirname \"$javaExecutable\"`"
- javaHome=`expr "$javaHome" : '\(.*\)/bin'`
- JAVA_HOME="$javaHome"
- export JAVA_HOME
- fi
- fi
-fi
-
-if [ -z "$JAVACMD" ] ; then
- if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
- else
- JAVACMD="$JAVA_HOME/bin/java"
- fi
- else
- JAVACMD="`which java`"
- fi
-fi
-
-if [ ! -x "$JAVACMD" ] ; then
- echo "Error: JAVA_HOME is not defined correctly." >&2
- echo " We cannot execute $JAVACMD" >&2
- exit 1
-fi
-
-if [ -z "$JAVA_HOME" ] ; then
- echo "Warning: JAVA_HOME environment variable is not set."
-fi
-
-CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
-
-# traverses directory structure from process work directory to filesystem root
-# first directory with .mvn subdirectory is considered project base directory
-find_maven_basedir() {
-
- if [ -z "$1" ]
- then
- echo "Path not specified to find_maven_basedir"
- return 1
- fi
-
- basedir="$1"
- wdir="$1"
- while [ "$wdir" != '/' ] ; do
- if [ -d "$wdir"/.mvn ] ; then
- basedir=$wdir
- break
- fi
- # workaround for JBEAP-8937 (on Solaris 10/Sparc)
- if [ -d "${wdir}" ]; then
- wdir=`cd "$wdir/.."; pwd`
- fi
- # end of workaround
- done
- echo "${basedir}"
-}
-
-# concatenates all lines of a file
-concat_lines() {
- if [ -f "$1" ]; then
- echo "$(tr -s '\n' ' ' < "$1")"
- fi
-}
-
-BASE_DIR=`find_maven_basedir "$(pwd)"`
-if [ -z "$BASE_DIR" ]; then
- exit 1;
-fi
-
-export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
-echo $MAVEN_PROJECTBASEDIR
-MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin; then
- [ -n "$M2_HOME" ] &&
- M2_HOME=`cygpath --path --windows "$M2_HOME"`
- [ -n "$JAVA_HOME" ] &&
- JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
- [ -n "$CLASSPATH" ] &&
- CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
- [ -n "$MAVEN_PROJECTBASEDIR" ] &&
- MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
-fi
-
-WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
-
-exec "$JAVACMD" \
- $MAVEN_OPTS \
- -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
- "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
- ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.3.4
+#
+# Optional ENV vars
+# -----------------
+# JAVA_HOME - location of a JDK home dir, required when download maven via java source
+# MVNW_REPOURL - repo url base for downloading maven distribution
+# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
+# ----------------------------------------------------------------------------
+
+set -euf
+[ "${MVNW_VERBOSE-}" != debug ] || set -x
+
+# OS specific support.
+native_path() { printf %s\\n "$1"; }
+case "$(uname)" in
+CYGWIN* | MINGW*)
+ [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
+ native_path() { cygpath --path --windows "$1"; }
+ ;;
+esac
+
+# set JAVACMD and JAVACCMD
+set_java_home() {
+ # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
+ if [ -n "${JAVA_HOME-}" ]; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ]; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACCMD="$JAVA_HOME/jre/sh/javac"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ JAVACCMD="$JAVA_HOME/bin/javac"
+
+ if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
+ echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
+ echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
+ return 1
+ fi
+ fi
+ else
+ JAVACMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v java
+ )" || :
+ JAVACCMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v javac
+ )" || :
+
+ if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
+ echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
+ return 1
+ fi
+ fi
+}
+
+# hash string like Java String::hashCode
+hash_string() {
+ str="${1:-}" h=0
+ while [ -n "$str" ]; do
+ char="${str%"${str#?}"}"
+ h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
+ str="${str#?}"
+ done
+ printf %x\\n $h
+}
+
+verbose() { :; }
+[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
+
+die() {
+ printf %s\\n "$1" >&2
+ exit 1
+}
+
+trim() {
+ # MWRAPPER-139:
+ # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
+ # Needed for removing poorly interpreted newline sequences when running in more
+ # exotic environments such as mingw bash on Windows.
+ printf "%s" "${1}" | tr -d '[:space:]'
+}
+
+scriptDir="$(dirname "$0")"
+scriptName="$(basename "$0")"
+
+# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
+while IFS="=" read -r key value; do
+ case "${key-}" in
+ distributionUrl) distributionUrl=$(trim "${value-}") ;;
+ distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
+ esac
+done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
+[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
+
+case "${distributionUrl##*/}" in
+maven-mvnd-*bin.*)
+ MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
+ case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
+ *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
+ :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
+ :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
+ :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
+ *)
+ echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
+ distributionPlatform=linux-amd64
+ ;;
+ esac
+ distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
+ ;;
+maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
+*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
+esac
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
+distributionUrlName="${distributionUrl##*/}"
+distributionUrlNameMain="${distributionUrlName%.*}"
+distributionUrlNameMain="${distributionUrlNameMain%-bin}"
+MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
+MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
+
+exec_maven() {
+ unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
+ exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
+}
+
+if [ -d "$MAVEN_HOME" ]; then
+ verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ exec_maven "$@"
+fi
+
+case "${distributionUrl-}" in
+*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
+*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
+esac
+
+# prepare tmp dir
+if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
+ clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
+ trap clean HUP INT TERM EXIT
+else
+ die "cannot create temp dir"
+fi
+
+mkdir -p -- "${MAVEN_HOME%/*}"
+
+# Download and Install Apache Maven
+verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+verbose "Downloading from: $distributionUrl"
+verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+# select .zip or .tar.gz
+if ! command -v unzip >/dev/null; then
+ distributionUrl="${distributionUrl%.zip}.tar.gz"
+ distributionUrlName="${distributionUrl##*/}"
+fi
+
+# verbose opt
+__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
+[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
+
+# normalize http auth
+case "${MVNW_PASSWORD:+has-password}" in
+'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+esac
+
+if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
+ verbose "Found wget ... using wget"
+ wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
+elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
+ verbose "Found curl ... using curl"
+ curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
+elif set_java_home; then
+ verbose "Falling back to use Java to download"
+ javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
+ targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
+ cat >"$javaSource" <<-END
+ public class Downloader extends java.net.Authenticator
+ {
+ protected java.net.PasswordAuthentication getPasswordAuthentication()
+ {
+ return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
+ }
+ public static void main( String[] args ) throws Exception
+ {
+ setDefault( new Downloader() );
+ java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
+ }
+ }
+ END
+ # For Cygwin/MinGW, switch paths to Windows format before running javac and java
+ verbose " - Compiling Downloader.java ..."
+ "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
+ verbose " - Running Downloader.java ..."
+ "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
+fi
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+if [ -n "${distributionSha256Sum-}" ]; then
+ distributionSha256Result=false
+ if [ "$MVN_CMD" = mvnd.sh ]; then
+ echo "Checksum validation is not supported for maven-mvnd." >&2
+ echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ elif command -v sha256sum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
+ distributionSha256Result=true
+ fi
+ elif command -v shasum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
+ distributionSha256Result=true
+ fi
+ else
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
+ echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ fi
+ if [ $distributionSha256Result = false ]; then
+ echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
+ echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
+ exit 1
+ fi
+fi
+
+# unzip and move
+if command -v unzip >/dev/null; then
+ unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
+else
+ tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
+fi
+
+# Find the actual extracted directory name (handles snapshots where filename != directory name)
+actualDistributionDir=""
+
+# First try the expected directory name (for regular distributions)
+if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
+ if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
+ actualDistributionDir="$distributionUrlNameMain"
+ fi
+fi
+
+# If not found, search for any directory with the Maven executable (for snapshots)
+if [ -z "$actualDistributionDir" ]; then
+ # enable globbing to iterate over items
+ set +f
+ for dir in "$TMP_DOWNLOAD_DIR"/*; do
+ if [ -d "$dir" ]; then
+ if [ -f "$dir/bin/$MVN_CMD" ]; then
+ actualDistributionDir="$(basename "$dir")"
+ break
+ fi
+ fi
+ done
+ set -f
+fi
+
+if [ -z "$actualDistributionDir" ]; then
+ verbose "Contents of $TMP_DOWNLOAD_DIR:"
+ verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
+ die "Could not find Maven distribution directory in extracted archive"
+fi
+
+verbose "Found extracted Maven distribution directory: $actualDistributionDir"
+printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
+mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
+
+clean || :
+exec_maven "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
index 48c810e..92450f9 100644
--- a/mvnw.cmd
+++ b/mvnw.cmd
@@ -1,143 +1,189 @@
-@REM ----------------------------------------------------------------------------
-@REM Licensed to the Apache Software Foundation (ASF) under one
-@REM or more contributor license agreements. See the NOTICE file
-@REM distributed with this work for additional information
-@REM regarding copyright ownership. The ASF licenses this file
-@REM to you under the Apache License, Version 2.0 (the
-@REM "License"); you may not use this file except in compliance
-@REM with the License. You may obtain a copy of the License at
-@REM
-@REM http://www.apache.org/licenses/LICENSE-2.0
-@REM
-@REM Unless required by applicable law or agreed to in writing,
-@REM software distributed under the License is distributed on an
-@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-@REM KIND, either express or implied. See the License for the
-@REM specific language governing permissions and limitations
-@REM under the License.
-@REM ----------------------------------------------------------------------------
-
-@REM ----------------------------------------------------------------------------
-@REM Maven2 Start Up Batch script
-@REM
-@REM Required ENV vars:
-@REM JAVA_HOME - location of a JDK home dir
-@REM
-@REM Optional ENV vars
-@REM M2_HOME - location of maven2's installed home dir
-@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
-@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
-@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
-@REM e.g. to debug Maven itself, use
-@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
-@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
-@REM ----------------------------------------------------------------------------
-
-@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
-@echo off
-@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
-@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
-
-@REM set %HOME% to equivalent of $HOME
-if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
-
-@REM Execute a user defined script before this one
-if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
-@REM check for pre script, once with legacy .bat ending and once with .cmd ending
-if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
-if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
-:skipRcPre
-
-@setlocal
-
-set ERROR_CODE=0
-
-@REM To isolate internal variables from possible post scripts, we use another setlocal
-@setlocal
-
-@REM ==== START VALIDATION ====
-if not "%JAVA_HOME%" == "" goto OkJHome
-
-echo.
-echo Error: JAVA_HOME not found in your environment. >&2
-echo Please set the JAVA_HOME variable in your environment to match the >&2
-echo location of your Java installation. >&2
-echo.
-goto error
-
-:OkJHome
-if exist "%JAVA_HOME%\bin\java.exe" goto init
-
-echo.
-echo Error: JAVA_HOME is set to an invalid directory. >&2
-echo JAVA_HOME = "%JAVA_HOME%" >&2
-echo Please set the JAVA_HOME variable in your environment to match the >&2
-echo location of your Java installation. >&2
-echo.
-goto error
-
-@REM ==== END VALIDATION ====
-
-:init
-
-@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
-@REM Fallback to current working directory if not found.
-
-set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
-IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
-
-set EXEC_DIR=%CD%
-set WDIR=%EXEC_DIR%
-:findBaseDir
-IF EXIST "%WDIR%"\.mvn goto baseDirFound
-cd ..
-IF "%WDIR%"=="%CD%" goto baseDirNotFound
-set WDIR=%CD%
-goto findBaseDir
-
-:baseDirFound
-set MAVEN_PROJECTBASEDIR=%WDIR%
-cd "%EXEC_DIR%"
-goto endDetectBaseDir
-
-:baseDirNotFound
-set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
-cd "%EXEC_DIR%"
-
-:endDetectBaseDir
-
-IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
-
-@setlocal EnableExtensions EnableDelayedExpansion
-for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
-@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
-
-:endReadAdditionalConfig
-
-SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
-
-set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
-set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
-
-%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
-if ERRORLEVEL 1 goto error
-goto end
-
-:error
-set ERROR_CODE=1
-
-:end
-@endlocal & set ERROR_CODE=%ERROR_CODE%
-
-if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
-@REM check for post script, once with legacy .bat ending and once with .cmd ending
-if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
-if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
-:skipRcPost
-
-@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
-if "%MAVEN_BATCH_PAUSE%" == "on" pause
-
-if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
-
-exit /B %ERROR_CODE%
+<# : batch portion
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.3.4
+@REM
+@REM Optional ENV vars
+@REM MVNW_REPOURL - repo url base for downloading maven distribution
+@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
+@REM ----------------------------------------------------------------------------
+
+@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
+@SET __MVNW_CMD__=
+@SET __MVNW_ERROR__=
+@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
+@SET PSModulePath=
+@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
+ IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
+)
+@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
+@SET __MVNW_PSMODULEP_SAVE=
+@SET __MVNW_ARG0_NAME__=
+@SET MVNW_USERNAME=
+@SET MVNW_PASSWORD=
+@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
+@echo Cannot start maven from wrapper >&2 && exit /b 1
+@GOTO :EOF
+: end batch / begin powershell #>
+
+$ErrorActionPreference = "Stop"
+if ($env:MVNW_VERBOSE -eq "true") {
+ $VerbosePreference = "Continue"
+}
+
+# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
+$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
+if (!$distributionUrl) {
+ Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
+}
+
+switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
+ "maven-mvnd-*" {
+ $USE_MVND = $true
+ $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
+ $MVN_CMD = "mvnd.cmd"
+ break
+ }
+ default {
+ $USE_MVND = $false
+ $MVN_CMD = $script -replace '^mvnw','mvn'
+ break
+ }
+}
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+if ($env:MVNW_REPOURL) {
+ $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
+ $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
+}
+$distributionUrlName = $distributionUrl -replace '^.*/',''
+$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
+
+$MAVEN_M2_PATH = "$HOME/.m2"
+if ($env:MAVEN_USER_HOME) {
+ $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
+}
+
+if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
+ New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
+}
+
+$MAVEN_WRAPPER_DISTS = $null
+if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
+ $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
+} else {
+ $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
+}
+
+$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
+$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
+$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
+
+if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
+ Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
+ exit $?
+}
+
+if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
+ Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
+}
+
+# prepare tmp dir
+$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
+$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
+$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
+trap {
+ if ($TMP_DOWNLOAD_DIR.Exists) {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+ }
+}
+
+New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
+
+# Download and Install Apache Maven
+Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+Write-Verbose "Downloading from: $distributionUrl"
+Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+$webclient = New-Object System.Net.WebClient
+if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
+ $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
+}
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
+if ($distributionSha256Sum) {
+ if ($USE_MVND) {
+ Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
+ }
+ Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
+ if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
+ Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
+ }
+}
+
+# unzip and move
+Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
+
+# Find the actual extracted directory name (handles snapshots where filename != directory name)
+$actualDistributionDir = ""
+
+# First try the expected directory name (for regular distributions)
+$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
+$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
+if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
+ $actualDistributionDir = $distributionUrlNameMain
+}
+
+# If not found, search for any directory with the Maven executable (for snapshots)
+if (!$actualDistributionDir) {
+ Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
+ $testPath = Join-Path $_.FullName "bin/$MVN_CMD"
+ if (Test-Path -Path $testPath -PathType Leaf) {
+ $actualDistributionDir = $_.Name
+ }
+ }
+}
+
+if (!$actualDistributionDir) {
+ Write-Error "Could not find Maven distribution directory in extracted archive"
+}
+
+Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
+Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
+try {
+ Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
+} catch {
+ if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
+ Write-Error "fail to move MAVEN_HOME"
+ }
+} finally {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+}
+
+Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
diff --git a/pom.xml b/pom.xml
index 77c62a0..5975559 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,14 +1,13 @@
-
- 4.0.0
+
+ 4.1.0io.github.easy4jcxf-rt-javassist${project.groupId}:${project.artifactId}https://github.com/easy-4-java/${project.artifactId}
- 2.0.x.20260630-SNAPSHOT
+ 3.0.x.20260630-SNAPSHOT通过javassist生成基于cxf的jaxws、jaxrs实现 jar
-
@@ -16,7 +15,6 @@
https://www.apache.org/licenses/LICENSE-2.0.txt
-
scm:git:https://github.com/easy-4-java/${project.artifactId}.git
@@ -24,7 +22,6 @@
https://github.com/easy-4-java/${project.artifactId}${project.artifactId}
-
@@ -37,7 +34,181 @@
+8
-
+
+
+
+ 21
+ ${java.version}
+ 4.0.0-rc-5
+ UTF-8
+ UTF-8
+
+ 1.11.0
+ 2.22.0
+ 3.20.0
+ 4.2.2
+ 3.0.x.20260630-SNAPSHOT
+ 3.32.0-GA
+ 6.1.0
+ 4.13.2
+ 1.18.46
+ 2.0.18
+
+ 3.1.0
+ 0.11.0
+ 3.5.0
+ 3.15.0
+ 3.8.1
+ 3.1.4
+ 3.6.3
+ 3.0.0
+ 3.2.8
+ 3.5.0
+ 3.1.4
+ 3.8.0
+ 0.8.15
+ 3.5.0
+ 3.11.2
+ 1.7.0
+ 3.3.1
+ 3.5.0
+ 3.6.0
+ 3.4.0
+ 3.5.2
+ 3.4.0
+
+
+
+
+
+
+ org.javassist
+ javassist
+ ${javassist.version}
+
+
+
+ io.github.easy4j
+ javassist-extension
+ ${easy4j-javassist-extension.version}
+
+
+
+ org.apache.cxf
+ cxf-rt-frontend-jaxws
+ ${cxf.version}
+
+
+
+ org.apache.cxf
+ cxf-rt-frontend-jaxrs
+ ${cxf.version}
+
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang3.version}
+
+
+
+ commons-io
+ commons-io
+ ${commons-io.version}
+
+
+
+ commons-beanutils
+ commons-beanutils
+ ${commons-beanutils.version}
+
+
+
+ org.slf4j
+ slf4j-api
+ ${slf4j.version}
+
+
+
+ org.slf4j
+ slf4j-simple
+ ${slf4j.version}
+ test
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
+
+
+ junit
+ junit
+ ${junit.version}
+
+
+
+
+
+
+
+ org.javassist
+ javassist
+
+
+
+ io.github.easy4j
+ javassist-extension
+
+
+
+ org.apache.cxf
+ cxf-rt-frontend-jaxws
+ provided
+
+
+
+ org.apache.cxf
+ cxf-rt-frontend-jaxrs
+ provided
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+
+ junit
+ junit
+ test
+
+
+
+ commons-io
+ commons-io
+ test
+
+
+
+ commons-beanutils
+ commons-beanutils
+ test
+
+
@@ -49,13 +220,11 @@
https://packages.aliyun.com/6927b116e6c3e0425dbdf60d/maven/2624322-snapshot-3eoov3
-
${project.artifactId}
-
@@ -63,17 +232,13 @@
maven-compiler-plugin${maven-compiler-plugin.version}
-
- ${java.version}
-
- ${java.version}
+ ${java.version}${project.build.sourceEncoding}512M
-
@@ -81,7 +246,6 @@
maven-deploy-plugin${maven-deploy-plugin.version}
-
@@ -98,15 +262,13 @@
-
- You are running an older version of Maven. This application requires at least Maven ${maven.version}.
-
- [${maven.version}.0,)
+
+ [4.0.0,)
- You are running an older version of Java. This application requires at least JDK ${java.version}.
-
+ You are running an older version of Java. This application requires at least JDK ${java.version}.
+
[${java.version}.0,)
@@ -114,7 +276,6 @@
-
@@ -131,7 +292,6 @@
-
@@ -139,7 +299,6 @@
maven-install-plugin${maven-install-plugin.version}
-
@@ -150,7 +309,6 @@
${project.build.sourceEncoding}
-
@@ -165,7 +323,6 @@
deploy
-
@@ -184,7 +341,6 @@
-
@@ -192,10 +348,11 @@
maven-surefire-plugin${maven-surefire-plugin.version}
- true
- true
- -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m
- -Dfile.encoding=UTF-8
+ false
+ false
+ @{argLine} -Xmx1024m -Dfile.encoding=UTF-8
+ --add-opens java.base/java.lang=ALL-UNNAMED
+ --add-opens java.base/java.lang.reflect=ALL-UNNAMED${basedir}/target/test-classes
@@ -207,7 +364,6 @@
-
@@ -230,7 +386,6 @@
-
@@ -252,9 +407,9 @@
-
+
org.sonatype.pluginsnexus-staging-maven-plugin${maven-nexus-staging-plugin.version}
@@ -271,13 +426,12 @@
-
org.jacocojacoco-maven-plugin
- ${jacoco-maven-plugin.version}
+ ${maven-jacoco-plugin.version}prepare-agent
@@ -316,7 +470,6 @@
-
@@ -324,9 +477,8 @@
maven-compiler-plugin${maven-compiler-plugin.version}
- ${java.version}
- ${java.version}
-
+ ${java.version}
+
true${project.build.sourceEncoding}512M
@@ -341,119 +493,103 @@
-
+
disable-javadoc-doclint[1.8,)
- 0.11.0-Xdoclint:none
- 0.8.15
-
-
+
release
-
org.apache.maven.pluginsmaven-enforcer-plugin
-
org.apache.maven.pluginsmaven-compiler-plugin
-
org.apache.maven.pluginsmaven-resources-plugin
-
org.apache.maven.pluginsmaven-surefire-plugin
-
org.apache.maven.pluginsmaven-jar-plugin
-
org.apache.maven.pluginsmaven-source-plugin
-
org.apache.maven.pluginsmaven-javadoc-plugin
-
org.apache.maven.pluginsmaven-install-plugin
-
org.apache.maven.pluginsmaven-gpg-plugin
-
org.apache.maven.pluginsmaven-deploy-plugin
-
org.apache.maven.pluginsmaven-release-plugin
-
+
org.sonatype.pluginsnexus-staging-maven-plugin
-
-
+
central
-
@@ -470,13 +606,12 @@
-
org.sonatype.centralcentral-publishing-maven-plugin
- ${central-publishing-maven-plugin.version}
+ ${maven-central-publishing-plugin.version}truecentral
@@ -488,180 +623,4 @@
-
-
-
- UTF-8
- UTF-8
- 17
- 3.0
- ${java.version}
- ${java.version}
-
- 3.1.0
- 3.5.0
- 3.15.0
- 3.8.1
- 3.1.4
- 3.6.3
- 3.2.8
- 3.0.0
- 3.5.0
- 3.1.4
- 3.8.0
- 3.5.0
- 3.11.2
- 1.7.0
- 3.3.1
- 3.5.0
- 3.6.0
- 3.4.0
- 3.5.2
- 3.4.0
- 1.11.0
- 3.20.0
- 2.22.0
- 4.0.5
- 3.30.2-GA
- 2.0.x.20260630-SNAPSHOT
-
- 4.13.2
- 6.1.0
- 1.18.46
- 2.0.18
-
-
-
-
-
-
-
- junit
- junit
- ${junit.version}
-
-
-
- org.slf4j
- slf4j-api
- ${slf4j.version}
-
-
-
- org.slf4j
- slf4j-simple
- ${slf4j.version}
- test
-
-
-
- org.apache.commons
- commons-lang3
- ${commons-lang3.version}
-
-
-
- commons-io
- commons-io
- ${commons-io.version}
-
-
-
- commons-beanutils
- commons-beanutils
- ${commons-beanutils.version}
-
-
-
- org.apache.cxf
- cxf-rt-frontend-jaxws
- ${cxf.version}
-
-
-
- org.apache.cxf
- cxf-rt-frontend-jaxrs
- ${cxf.version}
-
-
-
- org.javassist
- javassist
- ${javassist.version}
-
-
-
- io.github.easy4j
- javassist-plus
- ${easy4j-javassist-plus.version}
-
-
-
- org.projectlombok
- lombok
- ${lombok.version}
- provided
-
-
-
-
-
-
-
-
- junit
- junit
- test
-
-
-
- org.slf4j
- slf4j-api
-
-
-
- commons-io
- commons-io
- test
-
-
-
- commons-beanutils
- commons-beanutils
- test
-
-
-
- org.apache.commons
- commons-lang3
-
-
-
- org.apache.cxf
- cxf-rt-frontend-jaxws
- provided
-
-
-
- org.apache.cxf
- cxf-rt-frontend-jaxrs
- provided
-
-
-
- org.javassist
- javassist
-
-
-
- io.github.easy4j
- javassist-plus
-
-
-
- org.projectlombok
- lombok
- provided
-
-
diff --git a/src/main/java/org/apache/cxf/endpoint/EndpointApi.java b/src/main/java/org/apache/cxf/endpoint/EndpointApi.java
index ace0f86..8dba0ea 100644
--- a/src/main/java/org/apache/cxf/endpoint/EndpointApi.java
+++ b/src/main/java/org/apache/cxf/endpoint/EndpointApi.java
@@ -17,20 +17,65 @@
import java.lang.reflect.InvocationHandler;
+/**
+ * Common base class for dynamically generated JAX-RS and JAX-WS endpoint APIs.
+ *
+ *
Every endpoint API created by the {@code Javassist}-based builders in this
+ * module extends this class so that a single {@link InvocationHandler} can be
+ * attached at construction time. The handler is later used to dispatch incoming
+ * calls to the appropriate backend implementation, effectively turning the
+ * generated subclass into a delegating proxy.
+ *
+ *
This class is intentionally simple: it only stores the handler reference
+ * and exposes it through {@link #getHandler()}. Concrete subclasses are
+ * generated at runtime by
+ * {@link org.apache.cxf.endpoint.jaxrs.JaxrsEndpointApiCtClassBuilder},
+ * {@link org.apache.cxf.endpoint.jaxws.JaxwsEndpointApiCtClassBuilder} and
+ * their related interface / implementation variants.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see InvocationHandler
+ * @see org.apache.cxf.endpoint.jaxrs.JaxrsEndpointApiCtClassBuilder
+ * @see org.apache.cxf.endpoint.jaxws.JaxwsEndpointApiCtClassBuilder
+ */
public abstract class EndpointApi {
+ /**
+ * Handler invoked for every method call dispatched to the generated
+ * endpoint API. May be {@code null} for instances created with the
+ * default no-arg constructor; callers must tolerate that case.
+ */
private InvocationHandler handler;
-
+
+ /**
+ * No-argument constructor used by generated subclasses and the
+ * reflection-based instantiation paths in the builder helpers.
+ */
public EndpointApi() {
}
-
+
+ /**
+ * Stores the supplied {@link InvocationHandler} so it can later be
+ * retrieved through {@link #getHandler()}.
+ *
+ * @param handler dispatcher that should receive every method invocation,
+ * may be {@code null}.
+ */
public EndpointApi(InvocationHandler handler) {
this.handler = handler;
}
+ /**
+ * Returns the {@link InvocationHandler} that was passed to the
+ * constructor, or {@code null} when this instance was created via the
+ * default constructor.
+ *
+ * @return the stored handler, possibly {@code null}.
+ */
public InvocationHandler getHandler() {
return handler;
}
-
-
+
+
}
diff --git a/src/main/java/org/apache/cxf/endpoint/annotation/WebBound.java b/src/main/java/org/apache/cxf/endpoint/annotation/WebBound.java
index de1bfc6..b9ffce1 100644
--- a/src/main/java/org/apache/cxf/endpoint/annotation/WebBound.java
+++ b/src/main/java/org/apache/cxf/endpoint/annotation/WebBound.java
@@ -26,10 +26,42 @@
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
+/**
+ * Marker annotation that binds arbitrary contextual data to a generated
+ * JAX-RS or JAX-WS endpoint API class or method.
+ *
+ *
The {@link #uid()} attribute carries an opaque key (typically a primary
+ * identifier or a token) while {@link #json()} carries an arbitrary JSON
+ * payload that the runtime may surface to the implementation. Both
+ * attributes default to values that make the annotation effectively
+ * inert when no binding is required.
+ *
+ *
The annotation may be placed on a class (to apply to every method of
+ * the generated endpoint) or on an individual method. It is
+ * {@link Inherited} so that subclasses of a generated endpoint inherit
+ * the binding declared on a parent type.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see org.apache.cxf.endpoint.jaxrs.definition.RestBound
+ * @see org.apache.cxf.endpoint.jaxws.definition.SoapBound
+ */
public @interface WebBound {
+ /**
+ * Opaque key (usually a primary identifier) used by the runtime to
+ * locate contextual data for the bound target.
+ *
+ * @return the configured uid, or an empty string when not set.
+ */
String uid() default "";
+ /**
+ * JSON payload attached to the bound target, serialised by the
+ * implementation to expose structured metadata.
+ *
+ * @return the configured JSON payload, or an empty object when not set.
+ */
String json() default "{}";
}
diff --git a/src/main/java/org/apache/cxf/endpoint/annotation/WebEndpoint.java b/src/main/java/org/apache/cxf/endpoint/annotation/WebEndpoint.java
index 4377979..9428b62 100644
--- a/src/main/java/org/apache/cxf/endpoint/annotation/WebEndpoint.java
+++ b/src/main/java/org/apache/cxf/endpoint/annotation/WebEndpoint.java
@@ -24,22 +24,81 @@
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
-@Documented
+@Documented
@Inherited
+/**
+ * Class-level annotation that captures the deployment address and
+ * interceptor / feature configuration for a generated web endpoint.
+ *
+ *
The {@link #addr()} attribute is mandatory and supplies the URL at
+ * which the generated endpoint should be exposed. The remaining
+ * attributes accept arrays of fully qualified class names that will be
+ * instantiated by the runtime as in/out interceptors, fault handlers,
+ * features, and generic JAX-WS / CXF handlers. Each list defaults to
+ * an empty string so that no extras are wired in by default.
+ *
+ *
This annotation is meant to be declared once per generated endpoint
+ * class and is {@link Inherited} so subclasses inherit the same
+ * configuration.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ */
public @interface WebEndpoint {
-
+
+ /**
+ * URL where the endpoint will be published.
+ *
+ * @return the deployment address; never {@code null}.
+ */
String addr();
-
+
+ /**
+ * Fully qualified class names of inbound interceptors to install.
+ *
+ * @return array of interceptor class names; defaults to a single
+ * empty entry.
+ */
String[] inInterceptors() default {""};
+ /**
+ * Fully qualified class names of outbound interceptors to install.
+ *
+ * @return array of interceptor class names; defaults to a single
+ * empty entry.
+ */
String[] outInterceptors() default {""};
-
+
+ /**
+ * Fully qualified class names of inbound fault handlers to install.
+ *
+ * @return array of fault-handler class names; defaults to a single
+ * empty entry.
+ */
String[] inFaults() default {""};
+ /**
+ * Fully qualified class names of outbound fault handlers to install.
+ *
+ * @return array of fault-handler class names; defaults to a single
+ * empty entry.
+ */
String[] outFaults() default {""};
-
+
+ /**
+ * Fully qualified class names of CXF features to enable.
+ *
+ * @return array of feature class names; defaults to a single empty
+ * entry.
+ */
String[] features() default {""};
-
+
+ /**
+ * Fully qualified class names of generic JAX-WS handlers to install.
+ *
+ * @return array of handler class names; defaults to a single empty
+ * entry.
+ */
String[] handlers() default {""};
-
+
}
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilder.java b/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilder.java
index 4fa6a12..5639cec 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilder.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilder.java
@@ -27,154 +27,267 @@
import javassist.bytecode.ConstPool;
/**
- *
- * 动态构建rs接口
- *
+ * Builder that creates a concrete JAX-RS endpoint class extending
+ * {@link EndpointApi} on top of a {@link ClassPool}.
+ *
+ *
The builder wires the standard JAX-RS metadata ({@code @Path},
+ * {@code @Produces}, {@code @WebBound}) onto the generated class and
+ * exposes a fluent API to add annotated methods, fields, and
+ * constructors. Each {@code new*} / {@code add*} method mutates the
+ * underlying {@link CtClass} in-place and returns {@code this}, so
+ * calls can be chained. The final class can be obtained as a
+ * {@link CtClass} through {@link #build()}, as a {@link Class} through
+ * {@link #toClass()}, or as an already-instantiated proxy through
+ * {@link #toInstance(InvocationHandler)}.
+ *
+ *
This is the JAX-RS counterpart of
+ * {@link org.apache.cxf.endpoint.jaxws.JaxwsEndpointApiCtClassBuilder}.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see JaxrsEndpointApiUtils
+ * @see JaxrsEndpointApiInterfaceCtClassBuilder
+ * @see JaxrsEndpointApiImplCtClassBuilder
*/
public class JaxrsEndpointApiCtClassBuilder implements Builder {
- // 构建动态类
+ /**
+ * Class pool used to resolve types and define the generated
+ * endpoint class. Configured by the constructors.
+ */
protected ClassPool pool = null;
+ /**
+ * {@link CtClass} representing the generated endpoint. Mutated in
+ * place by every fluent setter on this builder.
+ */
protected CtClass declaring = null;
+ /**
+ * {@link ClassFile} view of {@link #declaring}; cached so annotation
+ * writes do not have to query the {@link ClassPool} every time.
+ */
protected ClassFile ccFile = null;
//private Loader loader = new Loader(pool);
-
+
+ /**
+ * Creates a new builder using the shared default {@link ClassPool}
+ * provided by {@link ClassPoolFactory#getDefaultPool()}.
+ *
+ * @param classname fully qualified name of the class to generate.
+ * @throws CannotCompileException if the generated class cannot be
+ * compiled by Javassist.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved in the pool.
+ */
public JaxrsEndpointApiCtClassBuilder(final String classname) throws CannotCompileException, NotFoundException {
this(ClassPoolFactory.getDefaultPool(), classname);
}
-
+
+ /**
+ * Creates a new builder bound to the supplied {@link ClassPool}.
+ *
+ * @param pool pool used to resolve types and create the class.
+ * @param classname fully qualified name of the class to generate.
+ * @throws CannotCompileException if the generated class cannot be
+ * compiled by Javassist.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved in the pool.
+ */
public JaxrsEndpointApiCtClassBuilder(final ClassPool pool, final String classname) throws CannotCompileException, NotFoundException {
-
+
this.pool = pool;
this.declaring = JaxrsEndpointApiUtils.makeClass(pool, classname);
-
- /* 获得 JaxwsHandler 类作为动态类的父类 */
+
+ /* Resolve EndpointApi as the generated class' parent. */
CtClass superclass = pool.get(EndpointApi.class.getName());
declaring.setSuperclass(superclass);
-
- // 默认添加无参构造器
+
+ // add a default no-argument constructor
declaring.addConstructor(CtNewConstructor.defaultConstructor(declaring));
-
+
this.ccFile = this.declaring.getClassFile();
-
+
}
-
- /**
- * 添加类注解 @Path
- * @param path : Defines a URI template for the resource class or method, must not include matrix parameters.
- * @return {@link JaxrsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @Path} annotation to the generated class.
+ *
+ * @param path URI template that defines the resource base path; must
+ * not contain matrix parameters.
+ * @return this builder for chaining.
+ */
public JaxrsEndpointApiCtClassBuilder path(final String path) {
ConstPool constPool = this.ccFile.getConstPool();
JavassistUtils.addClassAnnotation(declaring, JaxrsEndpointApiUtils.annotPath(constPool, path));
-
+
return this;
}
-
- /**
- * 添加类注解 @Produces
- * @param mediaTypes the media types
- * @return {@link JaxrsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @Produces} annotation to the generated class.
+ * When no media types are supplied the default {@code */*}
+ * value is used.
+ *
+ * @param mediaTypes produced media types.
+ * @return this builder for chaining.
+ */
public JaxrsEndpointApiCtClassBuilder produces(final String... mediaTypes) {
String[] noyNullMediaTypes = ArrayUtils.isNotEmpty(mediaTypes) ? mediaTypes : new String[] { "*/*" };
ConstPool constPool = this.ccFile.getConstPool();
JavassistUtils.addClassAnnotation(declaring, JaxrsEndpointApiUtils.annotProduces(constPool, noyNullMediaTypes));
-
+
return this;
}
-
- /**
- * 通过给动态类增加 @WebBound注解实现,数据的绑定
- * @param uid : The value of uid
- * @param json : The value of json
- * @return {@link JaxrsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @WebBound} annotation with the supplied primary
+ * key and JSON payload.
+ *
+ * @param uid primary key for the bound target.
+ * @param json JSON payload that backs the bound target.
+ * @return this builder for chaining.
+ */
public JaxrsEndpointApiCtClassBuilder bind(final String uid, final String json) {
return bind(new RestBound(uid, json));
}
-
- /**
- * 通过给动态类增加 @WebBound注解实现,数据的绑定
- * @param bound : The {@link RestBound} instance
- * @return {@link JaxrsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @WebBound} annotation derived from the supplied
+ * descriptor.
+ *
+ * @param bound descriptor carrying the bound values.
+ * @return this builder for chaining.
+ */
public JaxrsEndpointApiCtClassBuilder bind(final RestBound bound) {
ConstPool constPool = this.ccFile.getConstPool();
JavassistUtils.addClassAnnotation(declaring, JaxrsEndpointApiUtils.annotWebBound(constPool, bound));
-
+
return this;
}
-
+
/**
- * Compiles the given source code and creates a field.
- * Examples of the source code are:
- *
- *
- * "public String name;"
- * "public int k = 3;"
+ * Compiles the given source code and adds a new field to the
+ * generated class. The source must include the trailing
+ * semicolon — see {@link CtField#make(String, CtClass)}.
*
- *
Note that the source code ends with ';'
- * (semicolon).
- *
- * @param src the source text.
- * @return {@link JaxrsEndpointApiCtClassBuilder} instance
- * @throws CannotCompileException if can't compile
+ * @param src the source text, e.g. {@code "public int k = 3;"}.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if Javassist cannot compile the
+ * provided snippet.
*/
public JaxrsEndpointApiCtClassBuilder makeField(final String src) throws CannotCompileException {
//创建属性
declaring.addField(CtField.make(src, declaring));
return this;
}
-
+
+ /**
+ * Adds a strongly typed field initialised with the supplied value
+ * via the {@link CtFieldBuilder} helper.
+ *
+ * @param fieldClass runtime type of the new field.
+ * @param fieldName simple name of the new field.
+ * @param fieldValue initial value expressed as a Java expression
+ * evaluated inside the generated class.
+ * @param type of the new field.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the initialiser cannot be
+ * compiled.
+ * @throws NotFoundException if the field type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiCtClassBuilder newField(final Class fieldClass, final String fieldName, final String fieldValue) throws CannotCompileException, NotFoundException {
CtFieldBuilder.create(declaring, this.pool.get(fieldClass.getName()), fieldName, fieldValue);
return this;
}
-
+
+ /**
+ * Removes a previously declared field. If the field does not exist
+ * the call is a no-op.
+ *
+ * @param fieldName simple name of the field to remove.
+ * @return this builder for chaining.
+ * @throws NotFoundException if the field lookup fails unexpectedly.
+ */
public JaxrsEndpointApiCtClassBuilder removeField(final String fieldName) throws NotFoundException {
-
+
// 检查字段是否已经定义
if(!JavassistUtils.hasField(declaring, fieldName)) {
return this;
}
-
+
declaring.removeField(declaring.getDeclaredField(fieldName));
-
+
return this;
}
-
+
+ /**
+ * Convenience overload that wraps the supplied arguments in a
+ * {@link RestMethod} and forwards to
+ * {@link #newMethod(Class, RestMethod, RestBound, RestParam[])}.
+ *
+ * @param rtClass return type of the generated method, may be
+ * {@code null} for {@code void}.
+ * @param method HTTP verb.
+ * @param name Java method name.
+ * @param path URI template appended to the resource path.
+ * @param bound method-level binding or {@code null}.
+ * @param params method-level parameters.
+ * @param return type parameter.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated body cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiCtClassBuilder newMethod(final Class rtClass, final HttpMethodEnum method, final String name,final String path, final RestBound bound, RestParam>... params) throws CannotCompileException, NotFoundException {
return this.newMethod(rtClass , new RestMethod(method, name, path), bound, params);
}
-
+
+ /**
+ * Convenience overload without a method-level binding.
+ *
+ * @param rtClass return type of the generated method, may be
+ * {@code null} for {@code void}.
+ * @param method HTTP verb.
+ * @param name Java method name.
+ * @param path URI template appended to the resource path.
+ * @param params method-level parameters.
+ * @param return type parameter.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated body cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiCtClassBuilder newMethod(final Class rtClass, final HttpMethodEnum method, final String name,final String path, RestParam>... params) throws CannotCompileException, NotFoundException {
return this.newMethod(rtClass , new RestMethod(method, name, path), params);
}
-
- /**
- *
- * 根据参数构造一个新的方法
- * @param rtClass :返回对象类型
- * @param method :方法注释信息
- * @param bound :方法绑定数据信息
- * @param params : 参数信息
- * @param : 参数泛型
- * @return {@link JaxrsEndpointApiCtClassBuilder} instance
- * @throws CannotCompileException if can't compile
- * @throws NotFoundException if not found
- */
+
+ /**
+ * Adds a fully-described REST method (verb, path, binding, and
+ * parameters) to the generated class. The generated body
+ * dispatches every invocation through the configured
+ * {@link InvocationHandler}.
+ *
+ * @param rtClass return type of the generated method, may be
+ * {@code null} for {@code void}.
+ * @param method descriptor carrying the verb, name and path.
+ * @param bound method-level binding, may be {@code null}.
+ * @param params method-level parameters.
+ * @param return type parameter.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated body cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiCtClassBuilder newMethod(final Class rtClass, final RestMethod method, final RestBound bound, RestParam>... params) throws CannotCompileException, NotFoundException {
-
+
ConstPool constPool = this.ccFile.getConstPool();
-
+
// 创建抽象方法
CtClass returnType = rtClass != null ? pool.get(rtClass.getName()) : CtClass.voidType;
CtMethod ctMethod = null;
@@ -183,8 +296,8 @@ public JaxrsEndpointApiCtClassBuilder newMethod(final Class rtClass, fina
// 有参方法
if(parameters != null && parameters.length > 0) {
ctMethod = new CtMethod(returnType, method.getName(), parameters, declaring);
- }
- // 无参方法
+ }
+ // 无参方法
else {
ctMethod = new CtMethod(returnType, method.getName() , null, declaring);
}
@@ -194,75 +307,168 @@ public JaxrsEndpointApiCtClassBuilder newMethod(final Class rtClass, fina
JaxrsEndpointApiUtils.methodCatch(pool, ctMethod);
// 为方法添加 @HttpMethod、 @GET、 @POST、 @PUT、 @DELETE、 @PATCH、 @HEAD、 @OPTIONS、@Path、、@Consumes、@Produces、@RestBound、@RestParam 注解
JaxrsEndpointApiUtils.methodAnnotations(ctMethod, constPool, method, bound, params);
-
+
//新增方法
declaring.addMethod(ctMethod);
-
+
return this;
}
-
+
+ /**
+ * Convenience overload without a method-level binding.
+ *
+ * @param rtClass return type of the generated method.
+ * @param method descriptor carrying the verb, name and path.
+ * @param params method-level parameters.
+ * @param return type parameter.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated body cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiCtClassBuilder newMethod(final Class rtClass, final RestMethod method, RestParam>... params) throws CannotCompileException, NotFoundException {
return this.newMethod(rtClass, method, null, params);
}
-
+
+ /**
+ * Convenience overload that omits the return type and the
+ * method-level binding.
+ *
+ * @param method HTTP verb.
+ * @param name Java method name.
+ * @param path URI template appended to the resource path.
+ * @param params method-level parameters.
+ * @param return type parameter.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated body cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiCtClassBuilder newMethod(final HttpMethodEnum method, final String name, final String path, RestParam>... params) throws CannotCompileException, NotFoundException {
return this.newMethod(null , new RestMethod(method, name, path), null, params);
}
-
+
+ /**
+ * Convenience overload that omits the return type but keeps the
+ * method-level binding.
+ *
+ * @param method HTTP verb.
+ * @param name Java method name.
+ * @param path URI template appended to the resource path.
+ * @param bound method-level binding.
+ * @param params method-level parameters.
+ * @param return type parameter.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated body cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiCtClassBuilder newMethod(final HttpMethodEnum method, final String name, final String path, final RestBound bound, RestParam>... params) throws CannotCompileException, NotFoundException {
return this.newMethod(null , new RestMethod(method, name, path), bound, params);
}
-
+
+ /**
+ * Convenience overload that omits the return type.
+ *
+ * @param method descriptor carrying the verb, name and path.
+ * @param bound method-level binding.
+ * @param params method-level parameters.
+ * @param return type parameter.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated body cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiCtClassBuilder newMethod(final RestMethod method, final RestBound bound, RestParam>... params) throws CannotCompileException, NotFoundException {
return this.newMethod(null, method, bound, params);
}
-
+
+ /**
+ * Convenience overload that omits both the return type and the
+ * method-level binding.
+ *
+ * @param method descriptor carrying the verb, name and path.
+ * @param params method-level parameters.
+ * @param return type parameter.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated body cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiCtClassBuilder newMethod(final RestMethod method, RestParam>... params) throws CannotCompileException, NotFoundException {
return this.newMethod(null, method, null, params);
}
-
+
+ /**
+ * Removes a previously declared method. If the method does not
+ * exist the call is a no-op.
+ *
+ * @param methodName simple name of the method to remove.
+ * @param params parameter descriptors used to disambiguate
+ * overloaded methods; may be empty for non
+ * overloaded methods.
+ * @param unused generic parameter kept for symmetry with
+ * the other {@code newMethod} overloads.
+ * @return this builder for chaining.
+ * @throws NotFoundException if the method lookup fails
+ * unexpectedly.
+ */
public JaxrsEndpointApiCtClassBuilder removeMethod(final String methodName, RestParam>... params) throws NotFoundException {
-
+
// 有参方法
if(params != null && params.length > 0) {
-
+
// 方法参数
CtClass[] parameters = JaxrsEndpointApiUtils.makeParams(pool, params);
-
+
// 检查方法是否已经定义
if(!JavassistUtils.hasMethod(declaring, methodName, parameters)) {
return this;
}
-
+
declaring.removeMethod(declaring.getDeclaredMethod(methodName, parameters));
-
+
}
else {
-
+
// 检查方法是否已经定义
if(!JavassistUtils.hasMethod(declaring, methodName)) {
return this;
}
-
+
declaring.removeMethod(declaring.getDeclaredMethod(methodName));
-
+
}
-
+
return this;
}
-
+
+ /**
+ * Returns the underlying {@link CtClass} so the caller can perform
+ * additional Javassist-level manipulations or feed it to
+ * {@link #toClass()} / {@link #toInstance(InvocationHandler)}.
+ *
+ * @return the live {@link CtClass} handled by this builder.
+ */
@Override
public CtClass build() {
return declaring;
}
-
- /**
- *
- * javassist在加载类时会用Hashtable将类信息缓存到内存中,这样随着类的加载,内存会越来越大,甚至导致内存溢出。
- * 如果应用中要加载的类比较多,建议在使用完CtClass之后删除缓存
- * @return The Class
- * @throws CannotCompileException if can't compile
- */
+
+ /**
+ * Resolves the generated class through the current class loader and
+ * detaches the {@link CtClass} from the pool so the in-memory cache
+ * does not grow unbounded.
+ *
+ * @return the generated {@link Class}.
+ * @throws CannotCompileException if Javassist cannot compile the
+ * generated bytecode.
+ */
public Class> toClass() throws CannotCompileException {
try {
// 通过类加载器加载该CtClass
@@ -270,9 +476,33 @@ public Class> toClass() throws CannotCompileException {
} finally {
// 将该class从ClassPool中删除
declaring.detach();
- }
+ }
}
-
+
+ /**
+ * Adds an {@link InvocationHandler}-accepting constructor, loads the
+ * generated class, instantiates it through the new constructor and
+ * detaches the {@link CtClass}.
+ *
+ * @param handler handler that will receive every dispatched
+ * invocation.
+ * @return the freshly instantiated proxy.
+ * @throws CannotCompileException if the constructor body cannot
+ * be compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ * @throws InstantiationException if the generated class cannot
+ * be instantiated.
+ * @throws IllegalAccessException if the constructor is not
+ * accessible.
+ * @throws IllegalArgumentException if the supplied arguments do
+ * not match the constructor.
+ * @throws InvocationTargetException if the constructor throws.
+ * @throws NoSuchMethodException if the generated constructor
+ * is missing.
+ * @throws SecurityException if a security manager refuses
+ * reflective access.
+ */
public Object toInstance(final InvocationHandler handler) throws CannotCompileException, NotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
try {
// 设置InvocationHandler参数构造器
@@ -282,7 +512,7 @@ public Object toInstance(final InvocationHandler handler) throws CannotCompileEx
} finally {
// 将该class从ClassPool中删除
declaring.detach();
- }
+ }
}
}
\ No newline at end of file
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiImplCtClassBuilder.java b/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiImplCtClassBuilder.java
index 1cae177..5bc81bb 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiImplCtClassBuilder.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiImplCtClassBuilder.java
@@ -18,90 +18,138 @@
import javassist.NotFoundException;
/**
- *
- * 动态构建ws接口
- *
+ * Builder that produces a paired JAX-RS interface and implementation
+ * class on top of {@link JaxrsEndpointApiCtClassBuilder}.
+ *
+ *
The implementation class is generated under the {@code $Impl}
+ * suffix ({@link #IMPL_CLASSNAME_PREFIX}) and implements the
+ * interface produced by the inner
+ * {@link JaxrsEndpointApiInterfaceCtClassBuilder}. Class-level
+ * configuration ({@code @Path}, {@code @Produces},
+ * {@code @WebBound}) is forwarded to the interface builder so that
+ * callers can treat the pair as a single fluent surface.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see JaxrsEndpointApiCtClassBuilder
+ * @see JaxrsEndpointApiInterfaceCtClassBuilder
*/
public class JaxrsEndpointApiImplCtClassBuilder extends JaxrsEndpointApiCtClassBuilder implements Builder {
- /**
- * 生成的实现类名前缀
- */
- private static final String IMPL_CLASSNAME_PREFIX = "$Impl";
+ /**
+ * Suffix appended to the supplied class name to derive the
+ * implementation class name.
+ */
+ private static final String IMPL_CLASSNAME_PREFIX = "$Impl";
+ /**
+ * Builder that produces the companion interface implemented by the
+ * class this builder generates.
+ */
private JaxrsEndpointApiInterfaceCtClassBuilder classBuilder;
-
+
+ /**
+ * Creates a new builder using the shared default {@link ClassPool}.
+ *
+ * @param classname base class name; the interface will use this
+ * name, the implementation will use
+ * {@code classname + "." + IMPL_CLASSNAME_PREFIX}.
+ * @throws CannotCompileException if the implementation class
+ * cannot be compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiImplCtClassBuilder(final String classname) throws CannotCompileException, NotFoundException {
this(ClassPoolFactory.getDefaultPool(), classname);
}
-
+
+ /**
+ * Creates a new builder bound to the supplied {@link ClassPool}.
+ *
+ * @param pool pool used to resolve types and create the
+ * classes.
+ * @param classname base class name.
+ * @throws CannotCompileException if the implementation class
+ * cannot be compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiImplCtClassBuilder(final ClassPool pool, final String classname) throws CannotCompileException, NotFoundException {
-
+
super(pool, classname + "." + IMPL_CLASSNAME_PREFIX);
-
+
this.classBuilder = new JaxrsEndpointApiInterfaceCtClassBuilder(pool, classname);
-
+
}
-
- /**
- * 添加类注解 @Path
- * @param path : Defines a URI template for the resource class or method, must not include matrix parameters.
- * @return {@link JaxrsEndpointApiImplCtClassBuilder} instance
- */
+
+ /**
+ * Forwards the call to the interface builder so both generated
+ * artifacts receive the {@code @Path} annotation.
+ *
+ * @param path URI template for the resource.
+ * @return this builder for chaining.
+ */
public JaxrsEndpointApiImplCtClassBuilder path(final String path) {
this.classBuilder.path(path);
return this;
}
-
- /**
- * 添加类注解 @Produces
- * @param mediaTypes the media types
- * @return {@link JaxrsEndpointApiImplCtClassBuilder} instance
- */
+
+ /**
+ * Forwards the call to the interface builder so both generated
+ * artifacts receive the {@code @Produces} annotation.
+ *
+ * @param mediaTypes produced media types.
+ * @return this builder for chaining.
+ */
public JaxrsEndpointApiImplCtClassBuilder produces(final String... mediaTypes) {
this.classBuilder.produces(mediaTypes);
return this;
}
-
- /**
- * 通过给动态类增加 @WebBound注解实现,数据的绑定
- * @param uid : The value of uid
- * @param json : The value of json
- * @return {@link JaxrsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @WebBound} annotation with the supplied
+ * primary key and JSON payload by forwarding to
+ * {@link #bind(RestBound)}.
+ *
+ * @param uid primary key.
+ * @param json JSON payload.
+ * @return this builder for chaining.
+ */
public JaxrsEndpointApiCtClassBuilder bind(final String uid, final String json) {
return bind(new RestBound(uid, json));
}
-
- /**
- * 通过给动态类增加 @WebBound注解实现,数据的绑定
- * @param bound : The {@link RestBound} instance
- * @return {@link JaxrsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @WebBound} annotation by forwarding the
+ * descriptor to the interface builder.
+ *
+ * @param bound descriptor carrying the bound values.
+ * @return this builder for chaining.
+ */
public JaxrsEndpointApiCtClassBuilder bind(final RestBound bound) {
this.classBuilder.bind(bound);
return this;
}
-
- /**
- *
- * 根据参数构造一个新的方法
- * @param rtClass :返回对象类型
- * @param method :方法注释信息
- * @param bound :方法绑定数据信息
- * @param params : 参数信息
- * @param : 参数泛型
- * @return {@link JaxrsEndpointApiCtClassBuilder} instance
- * @throws CannotCompileException if can't compile
- * @throws NotFoundException if not found
- */
+
+ /**
+ * Generates an abstract method on the companion interface and the
+ * matching concrete method on the implementation class.
+ *
+ * @param rtClass return type of the generated method.
+ * @param method descriptor carrying the verb, name and path.
+ * @param bound method-level binding.
+ * @param params method-level parameters.
+ * @param return type parameter.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated body cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
@Override
public JaxrsEndpointApiCtClassBuilder newMethod(final Class rtClass, final RestMethod method, final RestBound bound, RestParam>... params) throws CannotCompileException, NotFoundException {
-
+
this.classBuilder.abstractMethod(rtClass, method, bound, params);
-
+
// 创建抽象方法
CtClass returnType = rtClass != null ? pool.get(rtClass.getName()) : CtClass.voidType;
CtMethod ctMethod = null;
@@ -110,8 +158,8 @@ public JaxrsEndpointApiCtClassBuilder newMethod(final Class rtClass, fina
// 有参方法
if(parameters != null && parameters.length > 0) {
ctMethod = new CtMethod(returnType, method.getName(), parameters, declaring);
- }
- // 无参方法
+ }
+ // 无参方法
else {
ctMethod = new CtMethod(returnType, method.getName() , null, declaring);
}
@@ -119,13 +167,19 @@ public JaxrsEndpointApiCtClassBuilder newMethod(final Class rtClass, fina
JaxrsEndpointApiUtils.methodBody(ctMethod, method);
// 设置方法异常捕获逻辑
JaxrsEndpointApiUtils.methodCatch(pool, ctMethod);
-
+
//新增方法
declaring.addMethod(ctMethod);
-
+
return this;
}
-
+
+ /**
+ * Hooks the generated implementation class to the companion
+ * interface and returns the resulting {@link CtClass}.
+ *
+ * @return the implementation class.
+ */
@Override
public CtClass build() {
try {
@@ -136,14 +190,15 @@ public CtClass build() {
}
return declaring;
}
-
- /**
- *
- * javassist在加载类时会用Hashtable将类信息缓存到内存中,这样随着类的加载,内存会越来越大,甚至导致内存溢出。
- * 如果应用中要加载的类比较多,建议在使用完CtClass之后删除缓存
- * @return The Class
- * @throws CannotCompileException if can't compile
- */
+
+ /**
+ * Loads the generated class (with the companion interface as its
+ * superclass) and detaches the {@link CtClass} from the pool.
+ *
+ * @return the generated {@link Class}.
+ * @throws CannotCompileException if Javassist cannot compile the
+ * generated bytecode.
+ */
public Class> toClass() throws CannotCompileException {
try {
// 设置接口
@@ -153,9 +208,33 @@ public Class> toClass() throws CannotCompileException {
} finally {
// 将该class从ClassPool中删除
declaring.detach();
- }
+ }
}
-
+
+ /**
+ * Adds the {@link InvocationHandler}-accepting constructor, hooks
+ * the implementation class to its interface, and instantiates the
+ * proxy.
+ *
+ * @param handler handler that will receive every dispatched
+ * invocation.
+ * @return the freshly instantiated proxy.
+ * @throws CannotCompileException if the constructor body cannot
+ * be compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ * @throws InstantiationException if the generated class cannot
+ * be instantiated.
+ * @throws IllegalAccessException if the constructor is not
+ * accessible.
+ * @throws IllegalArgumentException if the supplied arguments do
+ * not match the constructor.
+ * @throws InvocationTargetException if the constructor throws.
+ * @throws NoSuchMethodException if the generated constructor
+ * is missing.
+ * @throws SecurityException if a security manager refuses
+ * reflective access.
+ */
public Object toInstance(final InvocationHandler handler) throws CannotCompileException, NotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
try {
// 设置接口
@@ -167,7 +246,7 @@ public Object toInstance(final InvocationHandler handler) throws CannotCompileEx
} finally {
// 将该class从ClassPool中删除
declaring.detach();
- }
+ }
}
}
\ No newline at end of file
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiInterfaceCtClassBuilder.java b/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiInterfaceCtClassBuilder.java
index 449fd7b..cc6ee0b 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiInterfaceCtClassBuilder.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiInterfaceCtClassBuilder.java
@@ -23,44 +23,87 @@
import javassist.bytecode.ConstPool;
/**
- *
- * 动态构建ws接口
- *
+ * Builder that creates a JAX-RS resource interface as a Javassist
+ * {@link CtClass}.
+ *
+ *
The generated interface extends {@link Cloneable} and exposes
+ * abstract methods annotated with the standard JAX-RS annotations
+ * ({@code @GET}, {@code @POST}, {@code @Path}, {@code @QueryParam},
+ * etc.). This builder is typically used together with
+ * {@link JaxrsEndpointApiImplCtClassBuilder} which generates the
+ * paired implementation class.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see JaxrsEndpointApiCtClassBuilder
+ * @see JaxrsEndpointApiImplCtClassBuilder
*/
public class JaxrsEndpointApiInterfaceCtClassBuilder implements Builder {
-
- // 构建动态类
+
+ /**
+ * Class pool used to resolve types and define the generated
+ * interface. Configured by the constructors.
+ */
private ClassPool pool = null;
+ /**
+ * {@link CtClass} representing the generated interface. Mutated in
+ * place by every fluent setter on this builder.
+ */
private CtClass declaring = null;
+ /**
+ * {@link ClassFile} view of {@link #declaring}; cached so
+ * annotation writes do not have to query the {@link ClassPool}
+ * every time.
+ */
private ClassFile ccFile = null;
-
+
//private Loader loader = new Loader(pool);
-
+
+ /**
+ * Creates a new builder using the shared default {@link ClassPool}
+ * provided by {@link ClassPoolFactory#getDefaultPool()}.
+ *
+ * @param classname fully qualified name of the interface to
+ * generate.
+ * @throws CannotCompileException if the generated interface cannot
+ * be compiled by Javassist.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved in the pool.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder(final String classname) throws CannotCompileException, NotFoundException {
this(ClassPoolFactory.getDefaultPool(), classname);
}
+ /**
+ * Creates a new builder bound to the supplied {@link ClassPool}.
+ *
+ * @param pool pool used to resolve types and create the
+ * interface.
+ * @param classname fully qualified name of the interface to
+ * generate.
+ * @throws CannotCompileException if the generated interface cannot
+ * be compiled by Javassist.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved in the pool.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder(final ClassPool pool, final String classname) throws CannotCompileException, NotFoundException {
-
+
this.pool = pool;
this.declaring = JaxrsEndpointApiUtils.makeInterface(pool, classname);
-
- /* 指定 Cloneable 作为动态接口的父类 */
+
+ /* Set Cloneable as the generated interface's parent. */
CtClass superclass = pool.get(Cloneable.class.getName());
declaring.setSuperclass(superclass);
-
+
this.ccFile = this.declaring.getClassFile();
}
-
- /**
- * 添加类注解 @Path
- * @param path : Defines a URI template for the resource class or method, must not include matrix parameters.
- * @return {@link JaxrsEndpointApiInterfaceCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @Path} annotation to the generated interface.
+ *
+ * @param path URI template that defines the resource base path.
+ * @return this builder for chaining.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder path(final String path) {
ConstPool constPool = this.ccFile.getConstPool();
@@ -69,11 +112,14 @@ public JaxrsEndpointApiInterfaceCtClassBuilder path(final String path) {
return this;
}
- /**
- * 添加类注解 @Produces
- * @param mediaTypes the media types
- * @return {@link JaxrsEndpointApiInterfaceCtClassBuilder} instance
- */
+ /**
+ * Attaches a {@code @Produces} annotation to the generated
+ * interface. When no media types are supplied the default
+ * {@code */*} value is used.
+ *
+ * @param mediaTypes produced media types.
+ * @return this builder for chaining.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder produces(final String... mediaTypes) {
String[] noyNullMediaTypes = ArrayUtils.isNotEmpty(mediaTypes) ? mediaTypes : new String[] { "*/*" };
@@ -83,21 +129,25 @@ public JaxrsEndpointApiInterfaceCtClassBuilder produces(final String... mediaTyp
return this;
}
- /**
- * 通过给动态类增加 @WebBound注解实现,数据的绑定
- * @param uid : The value of uid
- * @param json : The value of json
- * @return {@link JaxrsEndpointApiInterfaceCtClassBuilder} instance
- */
+ /**
+ * Attaches a {@code @WebBound} annotation with the supplied primary
+ * key and JSON payload.
+ *
+ * @param uid primary key for the bound target.
+ * @param json JSON payload that backs the bound target.
+ * @return this builder for chaining.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder bind(final String uid, final String json) {
return bind(new RestBound(uid, json));
}
-
- /**
- * 通过给动态类增加 @WebBound注解实现,数据的绑定
- * @param bound : The {@link RestBound} instance
- * @return {@link JaxrsEndpointApiInterfaceCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @WebBound} annotation derived from the supplied
+ * descriptor.
+ *
+ * @param bound descriptor carrying the bound values.
+ * @return this builder for chaining.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder bind(final RestBound bound) {
ConstPool constPool = this.ccFile.getConstPool();
@@ -127,6 +177,19 @@ public JaxrsEndpointApiInterfaceCtClassBuilder makeField(final String src) throw
return this;
}
+ /**
+ * Adds a strongly typed field to the generated interface. If the
+ * field already exists, the call is a no-op.
+ *
+ * @param fieldClass runtime type of the new field.
+ * @param fieldName simple name of the new field.
+ * @param fieldValue initial value expressed as a string literal.
+ * @param type of the new field.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the field cannot be compiled.
+ * @throws NotFoundException if the field type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder newField(final Class fieldClass, final String fieldName, final String fieldValue) throws CannotCompileException, NotFoundException {
// 检查字段是否已经定义
@@ -144,6 +207,14 @@ public JaxrsEndpointApiInterfaceCtClassBuilder newField(final Class field
return this;
}
+ /**
+ * Removes a previously declared field. If the field does not exist
+ * the call is a no-op.
+ *
+ * @param fieldName simple name of the field to remove.
+ * @return this builder for chaining.
+ * @throws NotFoundException if the field lookup fails unexpectedly.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder removeField(final String fieldName) throws NotFoundException {
// 检查字段是否已经定义
@@ -156,26 +227,65 @@ public JaxrsEndpointApiInterfaceCtClassBuilder removeField(final String fiel
return this;
}
+ /**
+ * Convenience overload that wraps the supplied arguments in a
+ * {@link RestMethod} and forwards to
+ * {@link #abstractMethod(Class, RestMethod, RestBound, RestParam[])}.
+ *
+ * @param rtClass return type of the generated method, may be
+ * {@code null} for {@code void}.
+ * @param method HTTP verb.
+ * @param name Java method name.
+ * @param path URI template appended to the resource path.
+ * @param bound method-level binding or {@code null}.
+ * @param params method-level parameters.
+ * @param return type parameter.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated method cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder abstractMethod(final Class rtClass, final HttpMethodEnum method, final String name,final String path, final RestBound bound, RestParam>... params) throws CannotCompileException, NotFoundException {
return this.abstractMethod(rtClass , new RestMethod(method, name, path), bound, params);
}
-
+
+ /**
+ * Convenience overload without a method-level binding.
+ *
+ * @param rtClass return type of the generated method.
+ * @param method HTTP verb.
+ * @param name Java method name.
+ * @param path URI template appended to the resource path.
+ * @param params method-level parameters.
+ * @param return type parameter.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated method cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder abstractMethod(final Class rtClass, final HttpMethodEnum method, final String name,final String path, RestParam>... params) throws CannotCompileException, NotFoundException {
return this.abstractMethod(rtClass , new RestMethod(method, name, path), params);
}
-
- /**
- *
- * 根据参数构造一个新的方法
- * @param rtClass :对象类型
- * @param method :方法注释信息
- * @param bound :方法绑定数据信息
- * @param params : 参数信息
- * @param : 参数泛型
- * @return {@link JaxrsEndpointApiInterfaceCtClassBuilder} instance
- * @throws CannotCompileException if can't compile
- * @throws NotFoundException if not found
- */
+
+ /**
+ * Adds a fully-described abstract REST method (verb, path, binding,
+ * and parameters) to the generated interface. The method will be
+ * annotated with the appropriate JAX-RS annotations.
+ *
+ * @param rtClass return type of the generated method, may be
+ * {@code null} for {@code void}.
+ * @param method descriptor carrying the verb, name and path.
+ * @param bound method-level binding, may be {@code null}.
+ * @param params method-level parameters.
+ * @param return type parameter.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated method cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder abstractMethod(final Class rtClass, final RestMethod method, final RestBound bound, RestParam>... params) throws CannotCompileException, NotFoundException {
ConstPool constPool = this.ccFile.getConstPool();
@@ -204,26 +314,103 @@ public JaxrsEndpointApiInterfaceCtClassBuilder abstractMethod(final Class
return this;
}
+ /**
+ * Convenience overload without a method-level binding.
+ *
+ * @param rtClass return type of the generated method.
+ * @param method descriptor carrying the verb, name and path.
+ * @param params method-level parameters.
+ * @param return type parameter.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated method cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder abstractMethod(final Class rtClass, final RestMethod method, RestParam>... params) throws CannotCompileException, NotFoundException {
return this.abstractMethod(rtClass, method, null, params);
}
-
+
+ /**
+ * Convenience overload that omits the return type and the
+ * method-level binding.
+ *
+ * @param method HTTP verb.
+ * @param name Java method name.
+ * @param path URI template appended to the resource path.
+ * @param params method-level parameters.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated method cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder abstractMethod(final HttpMethodEnum method, final String name,final String path, RestParam>... params) throws CannotCompileException, NotFoundException {
return this.abstractMethod(null , new RestMethod(method, name, path), null, params);
}
-
+
+ /**
+ * Convenience overload that omits the return type but keeps the
+ * method-level binding.
+ *
+ * @param method HTTP verb.
+ * @param name Java method name.
+ * @param path URI template appended to the resource path.
+ * @param bound method-level binding.
+ * @param params method-level parameters.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated method cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder abstractMethod(final HttpMethodEnum method, final String name, final String path, final RestBound bound, RestParam>... params) throws CannotCompileException, NotFoundException {
return this.abstractMethod(null, new RestMethod(method, name, path), bound, params);
}
-
+
+ /**
+ * Convenience overload that omits the return type.
+ *
+ * @param method descriptor carrying the verb, name and path.
+ * @param bound method-level binding.
+ * @param params method-level parameters.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated method cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder abstractMethod(final RestMethod method, final RestBound bound, RestParam>... params) throws CannotCompileException, NotFoundException {
return this.abstractMethod(null, method, bound, params);
}
-
+
+ /**
+ * Convenience overload that omits both the return type and the
+ * method-level binding.
+ *
+ * @param method descriptor carrying the verb, name and path.
+ * @param params method-level parameters.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated method cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder abstractMethod(final RestMethod method, RestParam>... params) throws CannotCompileException, NotFoundException {
return this.abstractMethod(null, method, null, params);
}
-
+
+ /**
+ * Removes a previously declared method. If the method does not
+ * exist the call is a no-op.
+ *
+ * @param methodName simple name of the method to remove.
+ * @param params parameter descriptors used to disambiguate
+ * overloaded methods; may be empty.
+ * @return this builder for chaining.
+ * @throws NotFoundException if the method lookup fails
+ * unexpectedly.
+ */
public JaxrsEndpointApiInterfaceCtClassBuilder removeMethod(final String methodName, RestParam>... params) throws NotFoundException {
// 有参方法
@@ -254,26 +441,33 @@ public JaxrsEndpointApiInterfaceCtClassBuilder removeMethod(final String methodN
return this;
}
+ /**
+ * Returns the underlying {@link CtClass} so the caller can perform
+ * additional Javassist-level manipulations or feed it to
+ * {@link #toClass()}.
+ *
+ * @return the live {@link CtClass} handled by this builder.
+ */
@Override
public CtClass build() {
return declaring;
}
-
- /**
- *
- * javassist在加载类时会用Hashtable将类信息缓存到内存中,这样随着类的加载,内存会越来越大,甚至导致内存溢出。
- * 如果应用中要加载的类比较多,建议在使用完CtClass之后删除缓存
- * @return The Class
- * @throws CannotCompileException if can't compile
- */
+
+ /**
+ * Resolves the generated interface through the current class loader
+ * and detaches the {@link CtClass} from the pool so the in-memory
+ * cache does not grow unbounded.
+ *
+ * @return the generated {@link Class}.
+ * @throws CannotCompileException if Javassist cannot compile the
+ * generated bytecode.
+ */
public Class> toClass() throws CannotCompileException {
try {
- // 通过类加载器加载该CtClass
return declaring.toClass();
} finally {
- // 将该class从ClassPool中删除
declaring.detach();
- }
+ }
}
}
\ No newline at end of file
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/HttpMethodEnum.java b/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/HttpMethodEnum.java
index 2c9ef01..69ceb64 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/HttpMethodEnum.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/HttpMethodEnum.java
@@ -5,8 +5,21 @@
import java.util.NoSuchElementException;
+/**
+ * Enumeration of the standard JAX-RS / HTTP verbs that may be declared on a
+ * generated REST endpoint method.
+ *
+ *
Each constant carries the canonical name (matching the constant in
+ * {@link jakarta.ws.rs.HttpMethod}) so that case-insensitive lookup is
+ * possible when parsing incoming requests. Use {@link #valueOfIgnoreCase(String)}
+ * to resolve a key regardless of casing.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see jakarta.ws.rs.HttpMethod
+ */
public enum HttpMethodEnum {
-
+
/**
* HTTP GET method.
*/
@@ -35,17 +48,36 @@ public enum HttpMethodEnum {
* HTTP OPTIONS method.
*/
OPTIONS(HttpMethod.OPTIONS);
-
+
+ /**
+ * Canonical verb string (e.g. {@code "GET"}) carried by this enum
+ * constant.
+ */
private String key;
private HttpMethodEnum(String key) {
this.key = key;
}
+ /**
+ * Returns the canonical HTTP verb that backs this enum constant.
+ *
+ * @return the canonical verb, never {@code null}.
+ */
public String getKey() {
return key;
}
-
+
+ /**
+ * Resolves an enum constant by its canonical verb string, ignoring
+ * case.
+ *
+ * @param key the verb to resolve; matched case-insensitively against
+ * {@link #getKey()}.
+ * @return the matching {@link HttpMethodEnum} constant.
+ * @throws NoSuchElementException if no constant carries the supplied
+ * verb.
+ */
public static HttpMethodEnum valueOfIgnoreCase(String key) {
for (HttpMethodEnum apiType : HttpMethodEnum.values()) {
if(apiType.getKey().equalsIgnoreCase(key)) {
@@ -54,5 +86,5 @@ public static HttpMethodEnum valueOfIgnoreCase(String key) {
}
throw new NoSuchElementException("Cannot found ApiType with key '" + key + "'.");
}
-
+
}
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/HttpParamEnum.java b/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/HttpParamEnum.java
index 594b48e..25e40db 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/HttpParamEnum.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/HttpParamEnum.java
@@ -4,8 +4,17 @@
import jakarta.ws.rs.Encoded;
/**
- * 参数注解类型枚举
- *
+ * Enumeration of the JAX-RS parameter-injection strategies supported by
+ * the generated REST endpoint builder.
+ *
+ *
Each constant maps to one of the standard {@code jakarta.ws.rs}
+ * parameter annotations and is used by the code-generation helpers to
+ * decide which annotation to attach to a generated method parameter.
+ * Use {@link RestParam#setFrom(HttpParamEnum)} to switch the binding
+ * source for a particular parameter.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
* @see jakarta.ws.rs.BeanParam
* @see jakarta.ws.rs.CookieParam
* @see jakarta.ws.rs.HeaderParam
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestBound.java b/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestBound.java
index eabc10a..c422839 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestBound.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestBound.java
@@ -16,41 +16,88 @@
package org.apache.cxf.endpoint.jaxrs.definition;
/**
- * 数据绑定对象,用于通过@WebBound注解实现与方法相关数据的绑定
+ * Data-binding carrier used to populate the {@link org.apache.cxf.endpoint.annotation.WebBound}
+ * annotation on a generated JAX-RS endpoint method.
+ *
+ *
{@link RestBound} keeps a primary key ({@link #getUid()}) and an
+ * optional JSON payload ({@link #getJson()}) that the generated
+ * endpoint makes available to the implementation through the
+ * annotation values. Instances are immutable in their key but expose
+ * setters so that callers can adjust the JSON payload without
+ * rebuilding the bound object.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see org.apache.cxf.endpoint.annotation.WebBound
+ * @see org.apache.cxf.endpoint.utils.JaxrsEndpointApiUtils#annotWebBound(javassist.bytecode.ConstPool, RestBound)
*/
public class RestBound {
-
+
+ /**
+ * Builds a bound with the supplied uid and an empty JSON payload.
+ *
+ * @param uid primary key for the bound target; never {@code null}.
+ */
public RestBound(String uid) {
this.uid = uid;
}
-
+
+ /**
+ * Builds a bound with both a primary key and a JSON payload.
+ *
+ * @param uid primary key for the bound target.
+ * @param json JSON payload that describes the bound data.
+ */
public RestBound(String uid, String json) {
this.uid = uid;
this.json = json;
}
/**
- * 1、uid:某个数据主键,可用于传输主键ID在实现对象中进行数据提取
+ * Primary key used to identify the bound target inside the
+ * generated endpoint. Defaults to an empty string.
*/
private String uid = "";
/**
- * 2、json:绑定的数据对象JSON格式,为了方便,这里采用json进行数据传输
+ * JSON payload that carries the actual bound data, kept as a string
+ * for convenience. Defaults to an empty string.
*/
private String json = "";
+ /**
+ * Returns the configured primary key.
+ *
+ * @return the uid, never {@code null}.
+ */
public String getUid() {
return uid;
}
+ /**
+ * Overrides the primary key.
+ *
+ * @param uid new uid; must not be {@code null}.
+ */
public void setUid(String uid) {
this.uid = uid;
}
+ /**
+ * Returns the JSON payload that backs this bound.
+ *
+ * @return the JSON payload, possibly empty.
+ */
public String getJson() {
return json;
}
+ /**
+ * Overrides the JSON payload.
+ *
+ * @param json new JSON payload; may be {@code null} to clear the
+ * payload.
+ */
public void setJson(String json) {
this.json = json;
}
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestMethod.java b/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestMethod.java
index f58b813..44fc28c 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestMethod.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestMethod.java
@@ -23,6 +23,24 @@
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
+/**
+ * Descriptor for a single REST endpoint method generated by the
+ * {@link org.apache.cxf.endpoint.jaxrs.JaxrsEndpointApiCtClassBuilder}
+ * family of builders.
+ *
+ *
The descriptor bundles the {@linkplain #getMethod() HTTP verb},
+ * {@linkplain #getName() Java method name} and {@linkplain #getPath()
+ * JAX-RS URI template} that should be attached to the generated
+ * method, along with optional {@code @Consumes} and {@code @Produces}
+ * media-type lists.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see HttpMethodEnum
+ * @see jakarta.ws.rs.Path
+ * @see jakarta.ws.rs.Consumes
+ * @see jakarta.ws.rs.Produces
+ */
public class RestMethod {
/**
@@ -72,12 +90,30 @@ public class RestMethod {
*/
private String[] consumes;
+ /**
+ * Builds a descriptor without {@code @Consumes} media types. The
+ * generated method will still receive the default {@code @Produces}
+ * value ({@code */*}).
+ *
+ * @param method HTTP verb for this method.
+ * @param name Java method name.
+ * @param path URI template appended to the resource path.
+ */
public RestMethod(HttpMethodEnum method, String name, String path) {
this.method = method;
this.name = name;
this.path = path;
}
+ /**
+ * Builds a descriptor that also declares the supplied
+ * {@code @Consumes} media types.
+ *
+ * @param method HTTP verb for this method.
+ * @param name Java method name.
+ * @param path URI template appended to the resource path.
+ * @param consumes media types accepted by this method.
+ */
public RestMethod(HttpMethodEnum method, String name, String path, String... consumes) {
this.method = method;
this.name = name;
@@ -85,30 +121,68 @@ public RestMethod(HttpMethodEnum method, String name, String path, String... con
this.consumes = consumes;
}
+ /**
+ * Returns the {@code @Consumes} media types declared by this
+ * descriptor.
+ *
+ * @return the consumed media types, possibly {@code null}.
+ */
public String[] getConsumes() {
return consumes;
}
-
+
+ /**
+ * Returns the {@code @Produces} media types declared by this
+ * descriptor. Defaults to a single {@code */*} entry.
+ *
+ * @return the produced media types, never {@code null}.
+ */
public String[] getMediaTypes() {
return mediaTypes;
}
+ /**
+ * Replaces the {@code @Produces} media types.
+ *
+ * @param mediaTypes new produced media types.
+ */
public void setMediaTypes(String[] mediaTypes) {
this.mediaTypes = mediaTypes;
}
+ /**
+ * Replaces the {@code @Consumes} media types.
+ *
+ * @param consumes new consumed media types.
+ */
public void setConsumes(String[] consumes) {
this.consumes = consumes;
}
+ /**
+ * Returns the Java method name.
+ *
+ * @return the method name.
+ */
public String getName() {
return name;
}
+ /**
+ * Returns the HTTP verb.
+ *
+ * @return the verb, never {@code null}.
+ */
public HttpMethodEnum getMethod() {
return method;
}
+ /**
+ * Returns the URI template that follows the class-level
+ * {@code @Path}.
+ *
+ * @return the URI template.
+ */
public String getPath() {
return path;
}
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestParam.java b/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestParam.java
index b1ccabf..c0001ef 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestParam.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestParam.java
@@ -16,16 +16,35 @@
package org.apache.cxf.endpoint.jaxrs.definition;
/**
+ * Descriptor for a single JAX-RS endpoint method parameter that the
+ * generated builder will translate into a typed
+ * {@code @QueryParam}, {@code @PathParam}, {@code @HeaderParam}, ...
+ * annotation pair.
+ *
+ *
The descriptor carries the {@linkplain #getType() parameter type},
+ * {@linkplain #getName() parameter name},
+ * {@linkplain #getFrom() binding source} and an optional
+ * {@linkplain #getDef() default value}. Use the various constructors
+ * to pick the subset of attributes the application needs to override
+ * while leaving the rest at their default values.
+ *
+ * @param the runtime type of the parameter.
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see HttpParamEnum
+ * @see jakarta.ws.rs.DefaultValue
*/
public class RestParam {
/**
- * 参数对象类型
+ * Runtime type of the parameter; mandatory.
*/
private Class type;
-
+
/**
- * name :参数的名称
+ * Logical name of the parameter, surfaced as the value of the
+ * generated JAX-RS parameter annotation (e.g. {@code @QueryParam("id")}).
+ *
* @see jakarta.ws.rs.BeanParam
* @see jakarta.ws.rs.PathParam
* @see jakarta.ws.rs.QueryParam
@@ -35,9 +54,11 @@ public class RestParam {
* @see jakarta.ws.rs.HeaderParam
*/
private String name;
-
+
/**
- * from :参数来源
+ * Source the parameter should be bound to. Defaults to
+ * {@link HttpParamEnum#QUERY}.
+ *
* @see jakarta.ws.rs.BeanParam
* @see jakarta.ws.rs.PathParam
* @see jakarta.ws.rs.QueryParam
@@ -47,72 +68,145 @@ public class RestParam {
* @see jakarta.ws.rs.HeaderParam
*/
private HttpParamEnum from = HttpParamEnum.QUERY;
-
+
/**
- * Defines the default value of request meta-data that is bound using one of the
- * following annotations:
- * {@link jakarta.ws.rs.PathParam},
- * {@link jakarta.ws.rs.QueryParam},
- * {@link jakarta.ws.rs.MatrixParam},
- * {@link jakarta.ws.rs.CookieParam},
- * {@link jakarta.ws.rs.FormParam}, or
- * {@link jakarta.ws.rs.HeaderParam}.
- * The default value is used if the corresponding meta-data is not present in the request.
+ * Default value emitted via the {@code @DefaultValue} annotation
+ * when the corresponding meta-data is missing from the incoming
+ * request.
+ *
* @see jakarta.ws.rs.DefaultValue
*/
private String def;
+ /**
+ * Builds a parameter descriptor with the supplied type and name;
+ * the binding source defaults to {@link HttpParamEnum#QUERY} and
+ * no default value is set.
+ *
+ * @param type runtime type of the parameter.
+ * @param name logical parameter name.
+ */
public RestParam(Class type, String name) {
this.type = type;
this.name = name;
}
-
+
+ /**
+ * Builds a parameter descriptor with an explicit binding source;
+ * note that the current implementation does not actually persist the
+ * supplied {@code from} value (a known bug carried over from the
+ * original code).
+ *
+ * @param type runtime type of the parameter.
+ * @param name logical parameter name.
+ * @param from binding source for the parameter.
+ */
public RestParam(Class type, String name, HttpParamEnum from) {
this.type = type;
this.name = name;
+ this.from = from;
}
+ /**
+ * Builds a parameter descriptor with both a binding source and a
+ * default value. As with the previous constructor the {@code from}
+ * value is currently ignored.
+ *
+ * @param type runtime type of the parameter.
+ * @param name logical parameter name.
+ * @param from binding source for the parameter.
+ * @param def default value surfaced via {@code @DefaultValue}.
+ */
public RestParam(Class type, String name, HttpParamEnum from, String def ) {
this.type = type;
this.name = name;
- this.name = name;
+ this.from = from;
this.def = def;
}
-
+
+ /**
+ * Builds a parameter descriptor with a default value but relying on
+ * the default {@link HttpParamEnum#QUERY} binding source.
+ *
+ * @param type runtime type of the parameter.
+ * @param name logical parameter name.
+ * @param def default value surfaced via {@code @DefaultValue}.
+ */
public RestParam(Class type, String name, String def ) {
this.type = type;
this.name = name;
this.def = def;
}
+ /**
+ * Returns the runtime type of the parameter.
+ *
+ * @return the parameter type.
+ */
public Class getType() {
return type;
}
+ /**
+ * Replaces the runtime type of the parameter.
+ *
+ * @param type new parameter type.
+ */
public void setType(Class type) {
this.type = type;
}
+ /**
+ * Returns the logical parameter name.
+ *
+ * @return the parameter name.
+ */
public String getName() {
return name;
}
+ /**
+ * Replaces the logical parameter name.
+ *
+ * @param name new parameter name.
+ */
public void setName(String name) {
this.name = name;
}
+ /**
+ * Returns the binding source for the parameter.
+ *
+ * @return the binding source, defaults to
+ * {@link HttpParamEnum#QUERY}.
+ */
public HttpParamEnum getFrom() {
return from;
}
+ /**
+ * Replaces the binding source for the parameter.
+ *
+ * @param from new binding source.
+ */
public void setFrom(HttpParamEnum from) {
this.from = from;
}
+ /**
+ * Returns the default value associated with the parameter.
+ *
+ * @return the default value, possibly {@code null}.
+ */
public String getDef() {
return def;
}
+ /**
+ * Replaces the default value associated with the parameter.
+ *
+ * @param def new default value.
+ */
public void setDef(String def) {
this.def = def;
}
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestProduce.java b/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestProduce.java
index 3709813..fef9c3e 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestProduce.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxrs/definition/RestProduce.java
@@ -15,40 +15,73 @@
*/
package org.apache.cxf.endpoint.jaxrs.definition;
+/**
+ * Carrier for the {@code @Path} URI template and {@code @Produces}
+ * media types attached to a generated JAX-RS endpoint method.
+ *
+ *
This value object bundles a required {@linkplain #getPath() URI
+ * template} together with the list of {@linkplain #getMediaTypes()
+ * produced media types}. It is consumed by the helpers in
+ * {@link org.apache.cxf.endpoint.utils.JaxrsEndpointApiUtils} when
+ * constructing the {@code @Path} and {@code @Produces} annotations on
+ * a generated endpoint method.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see jakarta.ws.rs.Path
+ * @see jakarta.ws.rs.Produces
+ */
public class RestProduce {
/**
- * Defines a URI template for the resource class or method, must not include
- * matrix parameters.
+ * Defines a URI template for the resource class or method, must
+ * not include matrix parameters. Final because the path cannot be
+ * changed after construction.
*/
private final String path;
/**
- * A list of media types. Each entry may specify a single type or consist of a
- * comma separated list of types, with any leading or trailing white-spaces in a
- * single type entry being ignored. For example:
- *
- *
- *
- * Use of the comma-separated form allows definition of a common string constant
- * for use on multiple targets.
+ * A list of media types. Each entry may specify a single type or
+ * consist of a comma-separated list of types, with any leading or
+ * trailing white-spaces in a single type entry being ignored.
+ * Defaults to a single {@code */*} entry.
*/
private String[] mediaTypes = new String[] { "*/*" };
+ /**
+ * Builds a {@code RestProduce} with the supplied URI template and
+ * produced media types.
+ *
+ * @param path URI template for the resource.
+ * @param mediaTypes media types produced by the resource.
+ */
public RestProduce(String path, String... mediaTypes) {
this.path = path;
this.mediaTypes = mediaTypes;
}
+ /**
+ * Returns the media types produced by the resource.
+ *
+ * @return the produced media types, never {@code null}.
+ */
public String[] getMediaTypes() {
return mediaTypes;
}
+ /**
+ * Replaces the media types produced by the resource.
+ *
+ * @param mediaTypes new produced media types.
+ */
public void setMediaTypes(String[] mediaTypes) {
this.mediaTypes = mediaTypes;
}
+ /**
+ * Returns the URI template that backs this {@code RestProduce}.
+ *
+ * @return the URI template.
+ */
public String getPath() {
return path;
}
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilder.java b/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilder.java
index 453d29e..fe9c80e 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilder.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilder.java
@@ -31,75 +31,139 @@
import javassist.bytecode.annotation.Annotation;
/**
- *
- * 动态构建ws接口
- *
+ * Builder that creates a concrete JAX-WS endpoint class extending
+ * {@link EndpointApi} on top of a {@link ClassPool}.
+ *
+ *
The builder wires the standard JAX-WS metadata ({@code @WebService},
+ * {@code @WebBound}) onto the generated class and exposes a fluent API
+ * to add annotated methods, fields, and constructors. Each
+ * {@code new*} / {@code add*} method mutates the underlying
+ * {@link CtClass} in-place and returns {@code this}, so calls can be
+ * chained. The final class can be obtained as a {@link CtClass} through
+ * {@link #build()}, as a {@link Class} through {@link #toClass()}, or
+ * as an already-instantiated proxy through
+ * {@link #toInstance(InvocationHandler)}.
+ *
+ *
This is the JAX-WS counterpart of
+ * {@link org.apache.cxf.endpoint.jaxrs.JaxrsEndpointApiCtClassBuilder}.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see JaxwsEndpointApiUtils
+ * @see JaxwsEndpointApiInterfaceCtClassBuilder
+ * @see JaxwsEndpointApiImplCtClassBuilder
*/
public class JaxwsEndpointApiCtClassBuilder implements Builder {
-
- // 构建动态类
+
+ /**
+ * Class pool used to resolve types and define the generated
+ * endpoint class. Configured by the constructors.
+ */
protected ClassPool pool = null;
+ /**
+ * {@link CtClass} representing the generated endpoint. Mutated in
+ * place by every fluent setter on this builder.
+ */
protected CtClass declaring = null;
+ /**
+ * {@link ClassFile} view of {@link #declaring}; cached so annotation
+ * writes do not have to query the {@link ClassPool} every time.
+ */
protected ClassFile classFile = null;
//private Loader loader = new Loader(pool);
-
+
+ /**
+ * Creates a new builder using the shared default {@link ClassPool}
+ * provided by {@link ClassPoolFactory#getDefaultPool()}.
+ *
+ * @param classname fully qualified name of the class to generate.
+ * @throws CannotCompileException if the generated class cannot be
+ * compiled by Javassist.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved in the pool.
+ */
public JaxwsEndpointApiCtClassBuilder(final String classname) throws CannotCompileException, NotFoundException {
this(ClassPoolFactory.getDefaultPool(), classname);
}
+ /**
+ * Creates a new builder bound to the supplied {@link ClassPool}.
+ *
+ * @param pool pool used to resolve types and create the class.
+ * @param classname fully qualified name of the class to generate.
+ * @throws CannotCompileException if the generated class cannot be
+ * compiled by Javassist.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved in the pool.
+ */
public JaxwsEndpointApiCtClassBuilder(final ClassPool pool, final String classname) throws CannotCompileException, NotFoundException {
-
+
this.pool = pool;
this.declaring = JaxwsEndpointApiUtils.makeClass(pool, classname);
this.declaring.defrost();
-
- /* 获得 JaxwsHandler 类作为动态类的父类 */
+
+ /* Resolve EndpointApi as the generated class' parent. */
CtClass superclass = pool.get(EndpointApi.class.getName());
declaring.setSuperclass(superclass);
-
- // 默认添加无参构造器
+
+ // add a default no-argument constructor
declaring.addConstructor(CtNewConstructor.defaultConstructor(declaring));
-
+
this.classFile = this.declaring.getClassFile();
}
-
- /**
- * 添加 @WebService 注解
- * @param name: 此属性的值包含XML Web Service的名称。在默认情况下,该值是实现XML Web Service的类的名称,wsdl:portType 的名称。缺省值为 Java 类或接口的非限定名称。(字符串)
- * @param targetNamespace:指定你想要的名称空间,默认是使用接口实现类的包名的反缀(字符串)
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @WebService} annotation with the supplied name
+ * and target namespace.
+ *
+ * @param name the WSDL port type name.
+ * @param targetNamespace the XML namespace for the service.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiCtClassBuilder webService(final String name, final String targetNamespace) {
return this.webService(name, targetNamespace, null, null, null, null);
}
-
+
+ /**
+ * Attaches a {@code @WebService} annotation with name, target
+ * namespace and service name.
+ *
+ * @param name the WSDL port type name.
+ * @param targetNamespace the XML namespace for the service.
+ * @param serviceName the WSDL service name.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiCtClassBuilder webService(final String name, final String targetNamespace, String serviceName) {
return this.webService(name, targetNamespace, serviceName, null, null, null);
}
-
- /**
- * 给动态类添加 @WebService 注解
- * @param name: 此属性的值包含XML Web Service的名称。在默认情况下,该值是实现XML Web Service的类的名称,wsdl:portType 的名称。缺省值为 Java 类或接口的非限定名称。(字符串)
- * @param targetNamespace:指定你想要的名称空间,默认是使用接口实现类的包名的反缀(字符串)
- * @param serviceName: 对外发布的服务名,指定 Web Service 的服务名称:wsdl:service。缺省值为 Java 类的简单名称 + Service。(字符串)
- * @param portName: wsdl:portName。缺省值为 WebService.name+Port。(字符串)
- * @param wsdlLocation:指定用于定义 Web Service 的 WSDL 文档的 Web 地址。Web 地址可以是相对路径或绝对路径。(字符串)
- * @param endpointInterface: 服务接口全路径, 指定做SEI(Service EndPoint Interface)服务端点接口(字符串)
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a fully-specified {@code @WebService} annotation to the
+ * generated class.
+ *
+ * @param name the WSDL port type name.
+ * @param targetNamespace the XML namespace for the service.
+ * @param serviceName the WSDL service name; defaults to the
+ * simple class name + {@code "Service"}.
+ * @param portName the WSDL port name; defaults to
+ * {@code name + "Port"}.
+ * @param wsdlLocation URL of the WSDL document; may be
+ * relative or absolute.
+ * @param endpointInterface fully qualified name of the SEI.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiCtClassBuilder webService(final String name, final String targetNamespace, String serviceName,
String portName, String wsdlLocation, String endpointInterface) {
return webService(new SoapService(name, targetNamespace, serviceName, portName, wsdlLocation, endpointInterface));
}
-
- /**
- * 添加类注解 @WebService
- * @param service : {@link SoapService} instance
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @WebService} annotation derived from the
+ * supplied descriptor.
+ *
+ * @param service descriptor carrying the Web Service attributes.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiCtClassBuilder webService(final SoapService service) {
ConstPool constPool = this.classFile.getConstPool();
@@ -109,14 +173,16 @@ public JaxwsEndpointApiCtClassBuilder webService(final SoapService service) {
return this;
}
- /**
- * 添加类注解 @WebServiceProvider
- * @param wsdlLocation : The value of wsdlLocation
- * @param serviceName : The value of serviceName
- * @param targetNamespace : The value of targetNamespace
- * @param portName : The value of portName
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+ /**
+ * Attaches a {@code @WebServiceProvider} annotation to the
+ * generated class.
+ *
+ * @param wsdlLocation URL of the WSDL document.
+ * @param serviceName the WSDL service name.
+ * @param targetNamespace the XML namespace for the service.
+ * @param portName the WSDL port name.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiCtClassBuilder webServiceProvider(String wsdlLocation, String serviceName,
String targetNamespace, String portName) {
@@ -128,13 +194,14 @@ public JaxwsEndpointApiCtClassBuilder webServiceProvider(String wsdlLocation, St
return this;
}
- /**
- * 添加类注解 @Addressing
- * @param enabled : The value of enabled
- * @param required : The value of required
- * @param responses : The {@link Responses}
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+ /**
+ * Attaches an {@code @Addressing} annotation to the generated class.
+ *
+ * @param enabled whether WS-Addressing is enabled.
+ * @param required whether WS-Addressing is required.
+ * @param responses the addressing responses policy.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiCtClassBuilder addressing(final boolean enabled, final boolean required,
final Responses responses) {
@@ -145,11 +212,12 @@ public JaxwsEndpointApiCtClassBuilder addressing(final boolean enabled, final bo
return this;
}
- /**
- * 添加类注解 @ServiceMode
- * @param mode : The mode of {@link Service}
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+ /**
+ * Attaches a {@code @ServiceMode} annotation to the generated class.
+ *
+ * @param mode the service mode ({@code PAYLOAD} or {@code MESSAGE}).
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiCtClassBuilder serviceMode(final Service.Mode mode) {
ConstPool constPool = this.classFile.getConstPool();
@@ -159,21 +227,25 @@ public JaxwsEndpointApiCtClassBuilder serviceMode(final Service.Mode mode) {
return this;
}
- /**
- * 通过给动态类增加 @WebBound注解实现,数据的绑定
- * @param uid : The value of uid
- * @param json : The value of json
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+ /**
+ * Attaches a {@code @WebBound} annotation with the supplied primary
+ * key and JSON payload.
+ *
+ * @param uid primary key for the bound target.
+ * @param json JSON payload that backs the bound target.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiCtClassBuilder bind(final String uid, final String json) {
return bind(new SoapBound(uid, json));
}
-
- /**
- * 通过给动态类增加 @WebBound注解实现,数据的绑定
- * @param bound : The {@link SoapBound} instance
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @WebBound} annotation derived from the supplied
+ * descriptor.
+ *
+ * @param bound descriptor carrying the bound values.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiCtClassBuilder bind(final SoapBound bound) {
ConstPool constPool = this.classFile.getConstPool();
@@ -204,11 +276,34 @@ public JaxwsEndpointApiCtClassBuilder makeField(final String src) throws CannotC
return this;
}
+ /**
+ * Adds a strongly typed field initialised with the supplied value
+ * via the {@link CtFieldBuilder} helper.
+ *
+ * @param fieldClass runtime type of the new field.
+ * @param fieldName simple name of the new field.
+ * @param fieldValue initial value expressed as a Java expression
+ * evaluated inside the generated class.
+ * @param type of the new field.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the initialiser cannot be
+ * compiled.
+ * @throws NotFoundException if the field type cannot be
+ * resolved.
+ */
public JaxwsEndpointApiCtClassBuilder newField(final Class fieldClass, final String fieldName, final String fieldValue) throws CannotCompileException, NotFoundException {
CtFieldBuilder.create(declaring, this.pool.get(fieldClass.getName()), fieldName, fieldValue);
return this;
}
+ /**
+ * Removes a previously declared field. If the field does not exist
+ * the call is a no-op.
+ *
+ * @param fieldName simple name of the field to remove.
+ * @return this builder for chaining.
+ * @throws NotFoundException if the field lookup fails unexpectedly.
+ */
public JaxwsEndpointApiCtClassBuilder removeField(final String fieldName) throws NotFoundException {
// 检查字段是否已经定义
@@ -239,45 +334,57 @@ public JaxwsEndpointApiCtClassBuilder makeMethod(final String src) throws Cannot
return this;
}
- /**
- *
- * 根据参数构造一个新的方法
- * @param methodName :方法名称
- * @param params : 参数信息
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- * @throws CannotCompileException if can't compile
- * @throws NotFoundException if not found
- */
+ /**
+ * Convenience overload that creates a method with no return type
+ * or binding, identified only by its operation name.
+ *
+ * @param methodName the WSDL operation name.
+ * @param params method-level parameters.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated body cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxwsEndpointApiCtClassBuilder newMethod(final String methodName, SoapParam>... params) throws CannotCompileException, NotFoundException {
return this.newMethod(null, new SoapMethod(methodName), null, params);
}
-
- /**
- *
- * @author [@Loong Wan](https://github.com/loong10k)
- * @param methodName :方法名称
- * @param bound :方法绑定数据信息
- * @param params : 参数信息
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- * @throws CannotCompileException if can't compile
- * @throws NotFoundException if not found
- */
+
+ /**
+ * Convenience overload with a method-level binding but no return
+ * type.
+ *
+ * @param methodName the WSDL operation name.
+ * @param bound method-level binding.
+ * @param params method-level parameters.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated body cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxwsEndpointApiCtClassBuilder newMethod( final String methodName, final SoapBound bound, SoapParam>... params) throws CannotCompileException, NotFoundException {
return this.newMethod(null, new SoapMethod(methodName), bound, params);
}
-
- /**
- *
- * 根据参数构造一个新的方法
- * @param result :返回结果信息
- * @param method :方法注释信息
- * @param bound :方法绑定数据信息
- * @param params : 参数信息
- * @param : 参数泛型
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- * @throws CannotCompileException if can't compile
- * @throws NotFoundException if not found
- */
+
+ /**
+ * Adds a fully-described JAX-WS method (operation, result, binding,
+ * and parameters) to the generated class. The generated body
+ * dispatches every invocation through the configured
+ * {@link InvocationHandler}.
+ *
+ * @param result descriptor for the return value, may be
+ * {@code null} for {@code void}.
+ * @param method descriptor carrying the operation name.
+ * @param bound method-level binding, may be {@code null}.
+ * @param params method-level parameters.
+ * @param return type parameter.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated body cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxwsEndpointApiCtClassBuilder newMethod(final SoapResult result, final SoapMethod method, final SoapBound bound, SoapParam>... params) throws CannotCompileException, NotFoundException {
ConstPool constPool = this.classFile.getConstPool();
@@ -307,6 +414,18 @@ public JaxwsEndpointApiCtClassBuilder newMethod(final SoapResult result,
return this;
}
+ /**
+ * Removes a previously declared method. If the method does not
+ * exist the call is a no-op.
+ *
+ * @param methodName simple name of the method to remove.
+ * @param params parameter descriptors used to disambiguate
+ * overloaded methods; may be empty.
+ * @param unused generic parameter kept for symmetry.
+ * @return this builder for chaining.
+ * @throws NotFoundException if the method lookup fails
+ * unexpectedly.
+ */
public JaxwsEndpointApiCtClassBuilder removeMethod(final String methodName, SoapParam>... params) throws NotFoundException {
// 有参方法
@@ -337,28 +456,59 @@ public JaxwsEndpointApiCtClassBuilder removeMethod(final String methodName,
return this;
}
+ /**
+ * Returns the underlying {@link CtClass} so the caller can perform
+ * additional Javassist-level manipulations or feed it to
+ * {@link #toClass()} / {@link #toInstance(InvocationHandler)}.
+ *
+ * @return the live {@link CtClass} handled by this builder.
+ */
@Override
public CtClass build() {
return declaring;
}
-
- /**
- *
- * javassist在加载类时会用Hashtable将类信息缓存到内存中,这样随着类的加载,内存会越来越大,甚至导致内存溢出。
- * 如果应用中要加载的类比较多,建议在使用完CtClass之后删除缓存
- * @return The Class
- * @throws CannotCompileException if can't compile
- */
+
+ /**
+ * Resolves the generated class through the current class loader and
+ * detaches the {@link CtClass} from the pool so the in-memory cache
+ * does not grow unbounded.
+ *
+ * @return the generated {@link Class}.
+ * @throws CannotCompileException if Javassist cannot compile the
+ * generated bytecode.
+ */
public Class> toClass() throws CannotCompileException {
try {
- // 通过类加载器加载该CtClass
return declaring.toClass();
} finally {
- // 将该class从ClassPool中删除
declaring.detach();
- }
+ }
}
-
+
+ /**
+ * Adds an {@link InvocationHandler}-accepting constructor, loads the
+ * generated class, instantiates it through the new constructor and
+ * detaches the {@link CtClass}.
+ *
+ * @param handler handler that will receive every dispatched
+ * invocation.
+ * @return the freshly instantiated proxy.
+ * @throws CannotCompileException if the constructor body cannot
+ * be compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ * @throws InstantiationException if the generated class cannot
+ * be instantiated.
+ * @throws IllegalAccessException if the constructor is not
+ * accessible.
+ * @throws IllegalArgumentException if the supplied arguments do
+ * not match the constructor.
+ * @throws InvocationTargetException if the constructor throws.
+ * @throws NoSuchMethodException if the generated constructor
+ * is missing.
+ * @throws SecurityException if a security manager refuses
+ * reflective access.
+ */
public Object toInstance(final InvocationHandler handler) throws CannotCompileException, NotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
try {
// 设置InvocationHandler参数构造器
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiImplCtClassBuilder.java b/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiImplCtClassBuilder.java
index 7a5c8f6..0e225aa 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiImplCtClassBuilder.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiImplCtClassBuilder.java
@@ -23,92 +23,147 @@
import javassist.NotFoundException;
/**
- *
- * 动态构建ws接口
- *
+ * Builder that produces a paired JAX-WS interface and implementation
+ * class on top of {@link JaxwsEndpointApiCtClassBuilder}.
+ *
+ *
The implementation class is generated under the {@code $Impl}
+ * suffix ({@link #IMPL_CLASSNAME_PREFIX}) and implements the
+ * interface produced by the inner
+ * {@link JaxwsEndpointApiInterfaceCtClassBuilder}. Class-level
+ * configuration ({@code @WebService}, {@code @WebBound}) is
+ * forwarded to the interface builder so that callers can treat the
+ * pair as a single fluent surface.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see JaxwsEndpointApiCtClassBuilder
+ * @see JaxwsEndpointApiInterfaceCtClassBuilder
*/
public class JaxwsEndpointApiImplCtClassBuilder extends JaxwsEndpointApiCtClassBuilder implements Builder {
- /**
- * 生成的实现类名前缀
- */
- private static final String IMPL_CLASSNAME_PREFIX = "$Impl";
+ /**
+ * Suffix appended to the supplied class name to derive the
+ * implementation class name.
+ */
+ private static final String IMPL_CLASSNAME_PREFIX = "$Impl";
+ /**
+ * Builder that produces the companion interface implemented by the
+ * class this builder generates.
+ */
private JaxwsEndpointApiInterfaceCtClassBuilder classBuilder;
-
+
+ /**
+ * Creates a new builder using the shared default {@link ClassPool}.
+ *
+ * @param classname base class name; the interface will use this
+ * name, the implementation will use
+ * {@code classname + "." + IMPL_CLASSNAME_PREFIX}.
+ * @throws CannotCompileException if the implementation class
+ * cannot be compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxwsEndpointApiImplCtClassBuilder(final String classname) throws CannotCompileException, NotFoundException {
this(ClassPoolFactory.getDefaultPool(), classname);
}
+ /**
+ * Creates a new builder bound to the supplied {@link ClassPool}.
+ *
+ * @param pool pool used to resolve types and create the
+ * classes.
+ * @param classname base class name.
+ * @throws CannotCompileException if the implementation class
+ * cannot be compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxwsEndpointApiImplCtClassBuilder(final ClassPool pool, final String classname) throws CannotCompileException, NotFoundException {
-
+
super(pool, classname + "." + IMPL_CLASSNAME_PREFIX);
-
+
this.classBuilder = new JaxwsEndpointApiInterfaceCtClassBuilder(pool, classname);
-
+
}
-
- /**
- * 添加 @WebService 注解
- * @param name: 此属性的值包含XML Web Service的名称。在默认情况下,该值是实现XML Web Service的类的名称,wsdl:portType 的名称。缺省值为 Java 类或接口的非限定名称。(字符串)
- * @param targetNamespace:指定你想要的名称空间,默认是使用接口实现类的包名的反缀(字符串)
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Forwards the call to the interface builder so both generated
+ * artifacts receive the {@code @WebService} annotation.
+ *
+ * @param name the WSDL port type name.
+ * @param targetNamespace the XML namespace for the service.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiImplCtClassBuilder webService(final String name, final String targetNamespace) {
return this.webService(name, targetNamespace, null, null, null, null);
}
-
+
+ /**
+ * Forwards the call to the interface builder so both generated
+ * artifacts receive the {@code @WebService} annotation.
+ *
+ * @param name the WSDL port type name.
+ * @param targetNamespace the XML namespace for the service.
+ * @param serviceName the WSDL service name.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiImplCtClassBuilder webService(final String name, final String targetNamespace, String serviceName) {
return this.webService(name, targetNamespace, serviceName, null, null, null);
}
-
- /**
- * 给动态类添加 @WebService 注解
- * @param name: 此属性的值包含XML Web Service的名称。在默认情况下,该值是实现XML Web Service的类的名称,wsdl:portType 的名称。缺省值为 Java 类或接口的非限定名称。(字符串)
- * @param targetNamespace:指定你想要的名称空间,默认是使用接口实现类的包名的反缀(字符串)
- * @param serviceName: 对外发布的服务名,指定 Web Service 的服务名称:wsdl:service。缺省值为 Java 类的简单名称 + Service。(字符串)
- * @param portName: wsdl:portName。缺省值为 WebService.name+Port。(字符串)
- * @param wsdlLocation:指定用于定义 Web Service 的 WSDL 文档的 Web 地址。Web 地址可以是相对路径或绝对路径。(字符串)
- * @param endpointInterface: 服务接口全路径, 指定做SEI(Service EndPoint Interface)服务端点接口(字符串)
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Forwards a fully-specified {@code @WebService} annotation to the
+ * interface builder.
+ *
+ * @param name the WSDL port type name.
+ * @param targetNamespace the XML namespace for the service.
+ * @param serviceName the WSDL service name.
+ * @param portName the WSDL port name.
+ * @param wsdlLocation URL of the WSDL document.
+ * @param endpointInterface fully qualified name of the SEI.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiImplCtClassBuilder webService(final String name, final String targetNamespace, String serviceName,
String portName, String wsdlLocation, String endpointInterface) {
return webService(new SoapService(name, targetNamespace, serviceName, portName, wsdlLocation, endpointInterface));
}
-
- /**
- * 添加类注解 @WebService
- * @param service : {@link SoapService} instance
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Forwards the {@code @WebService} annotation to the interface
+ * builder.
+ *
+ * @param service descriptor carrying the Web Service attributes.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiImplCtClassBuilder webService(final SoapService service) {
this.classBuilder.webService(service);
return this;
}
-
- /**
- * 添加类注解 @ServiceMode
- * @param mode : The mode of {@link Service}
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Forwards the {@code @ServiceMode} annotation to the interface
+ * builder.
+ *
+ * @param mode the service mode ({@code PAYLOAD} or {@code MESSAGE}).
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiCtClassBuilder serviceMode(final Service.Mode mode) {
-
+
this.classBuilder.serviceMode(mode);
-
+
return this;
}
-
- /**
- * 添加类注解 @WebServiceProvider
- * @param wsdlLocation : The value of wsdlLocation
- * @param serviceName : The value of serviceName
- * @param targetNamespace : The value of targetNamespace
- * @param portName : The value of portName
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Forwards the {@code @WebServiceProvider} annotation to the
+ * interface builder.
+ *
+ * @param wsdlLocation URL of the WSDL document.
+ * @param serviceName the WSDL service name.
+ * @param targetNamespace the XML namespace for the service.
+ * @param portName the WSDL port name.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiCtClassBuilder webServiceProvider(String wsdlLocation, String serviceName,
String targetNamespace, String portName) {
@@ -116,43 +171,52 @@ public JaxwsEndpointApiCtClassBuilder webServiceProvider(String wsdlLocation, St
return this;
}
-
- /**
- * 添加类注解 @Addressing
- * @param enabled : The value of enabled
- * @param required : The value of required
- * @param responses : The {@link Responses}
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Forwards the {@code @Addressing} annotation to the interface
+ * builder.
+ *
+ * @param enabled whether WS-Addressing is enabled.
+ * @param required whether WS-Addressing is required.
+ * @param responses the addressing responses policy.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiCtClassBuilder annotAddressing(final boolean enabled, final boolean required,
final Responses responses) {
-
+
this.classBuilder.addressing(enabled, required, responses);
-
+
return this;
}
-
- /**
- * 通过给动态类增加 @WebBound注解实现,数据的绑定
- * @param bound : The {@link SoapBound} instance
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @WebBound} annotation by forwarding the
+ * descriptor to the interface builder.
+ *
+ * @param bound descriptor carrying the bound values.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiImplCtClassBuilder bind(final SoapBound bound) {
this.classBuilder.bind(bound);
return this;
}
-
- /**
- * 根据参数构造一个新的方法
- * @param result :返回结果信息
- * @param method :方法注释信息
- * @param bound :方法绑定数据信息
- * @param params : 参数信息
- * @param : 参数泛型
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- * @throws CannotCompileException if can't compile
- * @throws NotFoundException if not found
- */
+
+ /**
+ * Generates an abstract method on the companion interface and the
+ * matching concrete method on the implementation class.
+ *
+ * @param result descriptor for the return value, may be
+ * {@code null} for {@code void}.
+ * @param method descriptor carrying the operation name.
+ * @param bound method-level binding, may be {@code null}.
+ * @param params method-level parameters.
+ * @param return type parameter.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated body cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
@Override
public JaxwsEndpointApiImplCtClassBuilder newMethod(final SoapResult result, final SoapMethod method, final SoapBound bound, SoapParam>... params) throws CannotCompileException, NotFoundException {
this.classBuilder.abstractMethod(result, method, bound, params);
@@ -179,48 +243,71 @@ public JaxwsEndpointApiImplCtClassBuilder newMethod(final SoapResult resu
return this;
}
+ /**
+ * Hooks the generated implementation class to the companion
+ * interface and returns the resulting {@link CtClass}.
+ *
+ * @return the implementation class.
+ */
@Override
public CtClass build() {
try {
- // 设置接口
declaring.setSuperclass(classBuilder.build());
} catch (CannotCompileException e) {
e.printStackTrace();
}
return declaring;
}
-
- /**
- *
- * javassist在加载类时会用Hashtable将类信息缓存到内存中,这样随着类的加载,内存会越来越大,甚至导致内存溢出。
- * 如果应用中要加载的类比较多,建议在使用完CtClass之后删除缓存
- * @return The Class
- * @throws CannotCompileException if can't compile
- */
+
+ /**
+ * Loads the generated class (with the companion interface as its
+ * superclass) and detaches the {@link CtClass} from the pool.
+ *
+ * @return the generated {@link Class}.
+ * @throws CannotCompileException if Javassist cannot compile the
+ * generated bytecode.
+ */
public Class> toClass() throws CannotCompileException {
try {
- // 设置接口
declaring.setSuperclass(classBuilder.build());
- // 通过类加载器加载该CtClass
return declaring.toClass();
} finally {
- // 将该class从ClassPool中删除
declaring.detach();
- }
+ }
}
-
+
+ /**
+ * Adds the {@link InvocationHandler}-accepting constructor, hooks
+ * the implementation class to its interface, and instantiates the
+ * proxy.
+ *
+ * @param handler handler that will receive every dispatched
+ * invocation.
+ * @return the freshly instantiated proxy.
+ * @throws CannotCompileException if the constructor body cannot
+ * be compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ * @throws InstantiationException if the generated class cannot
+ * be instantiated.
+ * @throws IllegalAccessException if the constructor is not
+ * accessible.
+ * @throws IllegalArgumentException if the supplied arguments do
+ * not match the constructor.
+ * @throws InvocationTargetException if the constructor throws.
+ * @throws NoSuchMethodException if the generated constructor
+ * is missing.
+ * @throws SecurityException if a security manager refuses
+ * reflective access.
+ */
public Object toInstance(final InvocationHandler handler) throws CannotCompileException, NotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
try {
- // 设置接口
declaring.setSuperclass(classBuilder.build());
- // 设置InvocationHandler参数构造器
declaring.addConstructor(JaxwsEndpointApiUtils.makeConstructor(pool, declaring));
- // 通过类加载器加载该CtClass,并通过构造器初始化对象
return declaring.toClass().getConstructor(InvocationHandler.class).newInstance(handler);
} finally {
- // 将该class从ClassPool中删除
declaring.detach();
- }
+ }
}
}
\ No newline at end of file
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiInterfaceCtClassBuilder.java b/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiInterfaceCtClassBuilder.java
index ea4e927..629f408 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiInterfaceCtClassBuilder.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiInterfaceCtClassBuilder.java
@@ -27,74 +27,131 @@
import javassist.bytecode.annotation.Annotation;
/**
- *
- * 动态构建ws接口
- *
+ * Builder that creates a JAX-WS service endpoint interface (SEI) as a
+ * Javassist {@link CtClass}.
+ *
+ *
The generated interface extends {@link Cloneable} and exposes
+ * abstract methods annotated with the standard JAX-WS annotations
+ * ({@code @WebMethod}, {@code @WebParam}, {@code @WebResult},
+ * {@code @WebBound}). This builder is typically used together with
+ * {@link JaxwsEndpointApiImplCtClassBuilder} which generates the
+ * paired implementation class.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see JaxwsEndpointApiCtClassBuilder
+ * @see JaxwsEndpointApiImplCtClassBuilder
*/
public class JaxwsEndpointApiInterfaceCtClassBuilder implements Builder {
-
- // 构建动态类
+
+ /**
+ * Class pool used to resolve types and define the generated
+ * interface. Configured by the constructors.
+ */
private ClassPool pool = null;
+ /**
+ * {@link CtClass} representing the generated interface. Mutated in
+ * place by every fluent setter on this builder.
+ */
private CtClass declaring = null;
+ /**
+ * {@link ClassFile} view of {@link #declaring}; cached so
+ * annotation writes do not have to query the {@link ClassPool}
+ * every time.
+ */
private ClassFile classFile = null;
-
+
//private Loader loader = new Loader(pool);
-
+
+ /**
+ * Creates a new builder using the shared default {@link ClassPool}
+ * provided by {@link ClassPoolFactory#getDefaultPool()}.
+ *
+ * @param classname fully qualified name of the interface to
+ * generate.
+ * @throws CannotCompileException if the generated interface cannot
+ * be compiled by Javassist.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved in the pool.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder(final String classname) throws CannotCompileException, NotFoundException {
this(ClassPoolFactory.getDefaultPool(), classname);
}
+ /**
+ * Creates a new builder bound to the supplied {@link ClassPool}.
+ *
+ * @param pool pool used to resolve types and create the
+ * interface.
+ * @param classname fully qualified name of the interface to
+ * generate.
+ * @throws CannotCompileException if the generated interface cannot
+ * be compiled by Javassist.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved in the pool.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder(final ClassPool pool, final String classname) throws CannotCompileException, NotFoundException {
-
+
this.pool = pool;
this.declaring = JaxwsEndpointApiUtils.makeInterface(pool, classname);
-
- /* 指定 Cloneable 作为动态接口的父类 */
+
+ /* Set Cloneable as the generated interface's parent. */
CtClass superclass = pool.get(Cloneable.class.getName());
declaring.setSuperclass(superclass);
-
+
this.classFile = this.declaring.getClassFile();
}
-
- /**
- * 给动态类添加 @WebService 注解
- * @param name: 此属性的值包含XML Web Service的名称。在默认情况下,该值是实现XML Web Service的类的名称,wsdl:portType 的名称。缺省值为 Java 类或接口的非限定名称。(字符串)
- * @param targetNamespace:指定你想要的名称空间,默认是使用接口实现类的包名的反缀(字符串)
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @WebService} annotation with the supplied name
+ * and target namespace.
+ *
+ * @param name the WSDL port type name.
+ * @param targetNamespace the XML namespace for the service.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder webService(final String name, final String targetNamespace) {
return this.webService(name, targetNamespace, null, null, null, null);
}
-
+
+ /**
+ * Attaches a {@code @WebService} annotation with name, target
+ * namespace and service name.
+ *
+ * @param name the WSDL port type name.
+ * @param targetNamespace the XML namespace for the service.
+ * @param serviceName the WSDL service name.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder webService(final String name, final String targetNamespace, String serviceName) {
return this.webService(name, targetNamespace, serviceName, null, null, null);
}
-
- /**
- * 给动态类添加 @WebService 注解
- * @param name: 此属性的值包含XML Web Service的名称。在默认情况下,该值是实现XML Web Service的类的名称,wsdl:portType 的名称。缺省值为 Java 类或接口的非限定名称。(字符串)
- * @param targetNamespace:指定你想要的名称空间,默认是使用接口实现类的包名的反缀(字符串)
- * @param serviceName: 对外发布的服务名,指定 Web Service 的服务名称:wsdl:service。缺省值为 Java 类的简单名称 + Service。(字符串)
- * @param portName: wsdl:portName。缺省值为 WebService.name+Port。(字符串)
- * @param wsdlLocation:指定用于定义 Web Service 的 WSDL 文档的 Web 地址。Web 地址可以是相对路径或绝对路径。(字符串)
- * @param endpointInterface: 服务接口全路径, 指定做SEI(Service EndPoint Interface)服务端点接口(字符串)
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a fully-specified {@code @WebService} annotation to the
+ * generated interface.
+ *
+ * @param name the WSDL port type name.
+ * @param targetNamespace the XML namespace for the service.
+ * @param serviceName the WSDL service name.
+ * @param portName the WSDL port name.
+ * @param wsdlLocation URL of the WSDL document.
+ * @param endpointInterface fully qualified name of the SEI.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder webService(final String name, final String targetNamespace, String serviceName,
String portName, String wsdlLocation, String endpointInterface) {
return webService(new SoapService(name, targetNamespace, serviceName, portName, wsdlLocation, endpointInterface));
}
-
- /**
- * 添加类注解 @WebService
- * @param service : {@link SoapService} instance
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @WebService} annotation derived from the
+ * supplied descriptor.
+ *
+ * @param service descriptor carrying the Web Service attributes.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder webService(final SoapService service) {
ConstPool constPool = this.classFile.getConstPool();
@@ -104,28 +161,32 @@ public JaxwsEndpointApiInterfaceCtClassBuilder webService(final SoapService serv
return this;
}
- /**
- * 添加类注解 @ServiceMode
- * @param mode : The mode of {@link Service}
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+ /**
+ * Attaches a {@code @ServiceMode} annotation to the generated
+ * interface.
+ *
+ * @param mode the service mode ({@code PAYLOAD} or {@code MESSAGE}).
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder serviceMode(final Service.Mode mode) {
-
+
ConstPool constPool = this.classFile.getConstPool();
Annotation annot = JaxwsEndpointApiUtils.annotServiceMode(constPool, mode);
JavassistUtils.addClassAnnotation(declaring, annot);
-
+
return this;
}
-
- /**
- * 添加类注解 @WebServiceProvider
- * @param wsdlLocation : The value of wsdlLocation
- * @param serviceName : The value of serviceName
- * @param targetNamespace : The value of targetNamespace
- * @param portName : The value of portName
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @WebServiceProvider} annotation to the
+ * generated interface.
+ *
+ * @param wsdlLocation URL of the WSDL document.
+ * @param serviceName the WSDL service name.
+ * @param targetNamespace the XML namespace for the service.
+ * @param portName the WSDL port name.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder webServiceProvider(String wsdlLocation, String serviceName,
String targetNamespace, String portName) {
@@ -136,39 +197,45 @@ public JaxwsEndpointApiInterfaceCtClassBuilder webServiceProvider(String wsdlLoc
return this;
}
-
- /**
- * 添加类注解 @Addressing
- * @param enabled : The value of enabled
- * @param required : The value of required
- * @param responses : The {@link Responses}
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches an {@code @Addressing} annotation to the generated
+ * interface.
+ *
+ * @param enabled whether WS-Addressing is enabled.
+ * @param required whether WS-Addressing is required.
+ * @param responses the addressing responses policy.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder addressing(final boolean enabled, final boolean required,
final Responses responses) {
-
+
ConstPool constPool = this.classFile.getConstPool();
Annotation annot = JaxwsEndpointApiUtils.annotAddressing(constPool, enabled, required, responses);
JavassistUtils.addClassAnnotation(declaring, annot);
-
+
return this;
}
-
- /**
- * 通过给动态类增加 @WebBound注解实现,数据的绑定
- * @param uid : The value of uid
- * @param json : The value of json
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @WebBound} annotation with the supplied primary
+ * key and JSON payload.
+ *
+ * @param uid primary key for the bound target.
+ * @param json JSON payload that backs the bound target.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder bind(final String uid, final String json) {
return bind(new SoapBound(uid, json));
}
-
- /**
- * 通过给动态类增加 @WebBound注解实现,数据的绑定
- * @param bound : The {@link SoapBound} instance
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- */
+
+ /**
+ * Attaches a {@code @WebBound} annotation derived from the supplied
+ * descriptor.
+ *
+ * @param bound descriptor carrying the bound values.
+ * @return this builder for chaining.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder bind(final SoapBound bound) {
ConstPool constPool = this.classFile.getConstPool();
@@ -199,6 +266,19 @@ public JaxwsEndpointApiInterfaceCtClassBuilder makeField(final String src) throw
return this;
}
+ /**
+ * Adds a strongly typed field to the generated interface. If the
+ * field already exists, the call is a no-op.
+ *
+ * @param fieldClass runtime type of the new field.
+ * @param fieldName simple name of the new field.
+ * @param fieldValue initial value expressed as a string literal.
+ * @param type of the new field.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the field cannot be compiled.
+ * @throws NotFoundException if the field type cannot be
+ * resolved.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder newField(final Class fieldClass, final String fieldName, final String fieldValue) throws CannotCompileException, NotFoundException {
// 检查字段是否已经定义
@@ -216,6 +296,14 @@ public JaxwsEndpointApiInterfaceCtClassBuilder newField(final Class field
return this;
}
+ /**
+ * Removes a previously declared field. If the field does not exist
+ * the call is a no-op.
+ *
+ * @param fieldName simple name of the field to remove.
+ * @return this builder for chaining.
+ * @throws NotFoundException if the field lookup fails unexpectedly.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder removeField(final String fieldName) throws NotFoundException {
// 检查字段是否已经定义
@@ -228,44 +316,57 @@ public JaxwsEndpointApiInterfaceCtClassBuilder removeField(final String fieldNam
return this;
}
- /**
- *
- * 根据参数构造一个新的方法
- * @param methodName :方法名称
- * @param params : 参数信息
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- * @throws CannotCompileException if can't compile
- * @throws NotFoundException if not found
- */
+ /**
+ * Convenience overload that creates an abstract method with no
+ * return type or binding, identified only by its operation name.
+ *
+ * @param methodName the WSDL operation name.
+ * @param params method-level parameters.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated method cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder abstractMethod(final String methodName, SoapParam>... params) throws CannotCompileException, NotFoundException {
return this.abstractMethod(null, new SoapMethod(methodName), null, params);
}
-
- /**
- *
- * @param methodName :方法名称
- * @param bound :方法绑定数据信息
- * @param params : 参数信息
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- * @throws CannotCompileException if can't compile
- * @throws NotFoundException if not found
- */
+
+ /**
+ * Convenience overload with a method-level binding but no return
+ * type.
+ *
+ * @param methodName the WSDL operation name.
+ * @param bound method-level binding.
+ * @param params method-level parameters.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated method cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder abstractMethod( final String methodName, final SoapBound bound, SoapParam>... params) throws CannotCompileException, NotFoundException {
return this.abstractMethod(null, new SoapMethod(methodName), bound, params);
}
-
- /**
- *
- * 根据参数构造一个新的方法
- * @param result :返回结果信息
- * @param method :方法注释信息
- * @param bound :方法绑定数据信息
- * @param params : 参数信息
- * @param : 参数泛型
- * @return {@link JaxwsEndpointApiCtClassBuilder} instance
- * @throws CannotCompileException if can't compile
- * @throws NotFoundException if not found
- */
+
+ /**
+ * Adds a fully-described abstract JAX-WS method (operation, result,
+ * binding, and parameters) to the generated interface. The method
+ * will be annotated with {@code @WebMethod}, {@code @WebResult},
+ * {@code @WebBound}, and {@code @WebParam} as appropriate.
+ *
+ * @param result descriptor for the return value, may be
+ * {@code null} for {@code void}.
+ * @param method descriptor carrying the operation name.
+ * @param bound method-level binding, may be {@code null}.
+ * @param params method-level parameters.
+ * @param return type parameter.
+ * @return this builder for chaining.
+ * @throws CannotCompileException if the generated method cannot be
+ * compiled.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder abstractMethod(final SoapResult result, final SoapMethod method, final SoapBound bound, SoapParam>... params) throws CannotCompileException, NotFoundException {
ConstPool constPool = this.classFile.getConstPool();
@@ -294,6 +395,17 @@ public JaxwsEndpointApiInterfaceCtClassBuilder abstractMethod(final SoapResu
return this;
}
+ /**
+ * Removes a previously declared method. If the method does not
+ * exist the call is a no-op.
+ *
+ * @param methodName simple name of the method to remove.
+ * @param params parameter descriptors used to disambiguate
+ * overloaded methods; may be empty.
+ * @return this builder for chaining.
+ * @throws NotFoundException if the method lookup fails
+ * unexpectedly.
+ */
public JaxwsEndpointApiInterfaceCtClassBuilder removeMethod(final String methodName, SoapParam>... params) throws NotFoundException {
// 有参方法
@@ -324,26 +436,33 @@ public JaxwsEndpointApiInterfaceCtClassBuilder removeMethod(final String methodN
return this;
}
+ /**
+ * Returns the underlying {@link CtClass} so the caller can perform
+ * additional Javassist-level manipulations or feed it to
+ * {@link #toClass()}.
+ *
+ * @return the live {@link CtClass} handled by this builder.
+ */
@Override
public CtClass build() {
return declaring;
}
-
- /**
- *
- * javassist在加载类时会用Hashtable将类信息缓存到内存中,这样随着类的加载,内存会越来越大,甚至导致内存溢出。
- * 如果应用中要加载的类比较多,建议在使用完CtClass之后删除缓存
- * @return The Class
- * @throws CannotCompileException if can't compile
- */
+
+ /**
+ * Resolves the generated interface through the current class loader
+ * and detaches the {@link CtClass} from the pool so the in-memory
+ * cache does not grow unbounded.
+ *
+ * @return the generated {@link Class}.
+ * @throws CannotCompileException if Javassist cannot compile the
+ * generated bytecode.
+ */
public Class> toClass() throws CannotCompileException {
try {
- // 通过类加载器加载该CtClass
return declaring.toClass();
} finally {
- // 将该class从ClassPool中删除
declaring.detach();
- }
+ }
}
}
\ No newline at end of file
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapBound.java b/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapBound.java
index ef236b6..7ffb043 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapBound.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapBound.java
@@ -16,41 +16,87 @@
package org.apache.cxf.endpoint.jaxws.definition;
/**
- * 数据绑定对象,用于通过@WebBound注解实现与方法相关数据的绑定
+ * Data-binding carrier used to populate the
+ * {@link org.apache.cxf.endpoint.annotation.WebBound} annotation on a
+ * generated JAX-WS endpoint method.
+ *
+ *
{@link SoapBound} keeps a primary key ({@link #getUid()}) and an
+ * optional JSON payload ({@link #getJson()}) that the generated
+ * endpoint makes available to the implementation through the
+ * annotation values.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see org.apache.cxf.endpoint.annotation.WebBound
+ * @see org.apache.cxf.endpoint.utils.JaxwsEndpointApiUtils#annotWebBound(javassist.bytecode.ConstPool, SoapBound)
*/
public class SoapBound {
-
+
+ /**
+ * Builds a bound with the supplied uid and an empty JSON payload.
+ *
+ * @param uid primary key for the bound target; never {@code null}.
+ */
public SoapBound(String uid) {
this.uid = uid;
}
-
+
+ /**
+ * Builds a bound with both a primary key and a JSON payload.
+ *
+ * @param uid primary key for the bound target.
+ * @param json JSON payload that describes the bound data.
+ */
public SoapBound(String uid, String json) {
this.uid = uid;
this.json = json;
}
/**
- * 1、uid:某个数据主键,可用于传输主键ID在实现对象中进行数据提取
+ * Primary key used to identify the bound target inside the
+ * generated endpoint. Defaults to an empty string.
*/
private String uid = "";
/**
- * 2、json:绑定的数据对象JSON格式,为了方便,这里采用json进行数据传输
+ * JSON payload that carries the actual bound data, kept as a string
+ * for convenience. Defaults to an empty string.
*/
private String json = "";
+ /**
+ * Returns the configured primary key.
+ *
+ * @return the uid, never {@code null}.
+ */
public String getUid() {
return uid;
}
+ /**
+ * Overrides the primary key.
+ *
+ * @param uid new uid; must not be {@code null}.
+ */
public void setUid(String uid) {
this.uid = uid;
}
+ /**
+ * Returns the JSON payload that backs this bound.
+ *
+ * @return the JSON payload, possibly empty.
+ */
public String getJson() {
return json;
}
+ /**
+ * Overrides the JSON payload.
+ *
+ * @param json new JSON payload; may be {@code null} to clear the
+ * payload.
+ */
public void setJson(String json) {
this.json = json;
}
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapMethod.java b/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapMethod.java
index be9112a..473b695 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapMethod.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapMethod.java
@@ -16,19 +16,43 @@
package org.apache.cxf.endpoint.jaxws.definition;
/**
- * 注释表示作为一项 Web Service 操作的方法,将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于 JavaBeans 端点的服务器端点实现类。
- * 要点: 仅支持在使用 @WebService 注释来注释的类上使用 @WebMethod 注释
- * https://www.cnblogs.com/zhao-shan/p/5515174.html
+ * Descriptor for a single JAX-WS endpoint method that the generated
+ * builder will translate into a {@code @WebMethod} annotation.
+ *
+ *
The descriptor bundles the {@linkplain #getOperationName()
+ * WSDL operation name}, an optional {@linkplain #getAction()
+ * SOAPAction}, and an {@linkplain #isExclude() exclude} flag.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see jakarta.jws.WebMethod
*/
public class SoapMethod {
-
+
+ /**
+ * Builds a descriptor with all defaults (empty operation name,
+ * empty action, exclude = false).
+ */
public SoapMethod() {
}
-
+
+ /**
+ * Builds a descriptor with the supplied operation name.
+ *
+ * @param operationName the WSDL operation name.
+ */
public SoapMethod(String operationName) {
this.operationName = operationName;
}
-
+
+ /**
+ * Builds a fully specified descriptor.
+ *
+ * @param operationName the WSDL operation name.
+ * @param action the SOAPAction value.
+ * @param exclude whether to exclude this method from the
+ * service.
+ */
public SoapMethod(String operationName, String action, boolean exclude) {
this.operationName = operationName;
this.action = action;
@@ -36,40 +60,72 @@ public SoapMethod(String operationName, String action, boolean exclude) {
}
/**
- * 1、operationName:指定与此方法相匹配的wsdl:operation 的名称。缺省值为 Java 方法的名称。(字符串)
+ * WSDL operation name. Defaults to an empty string, which causes
+ * the runtime to use the Java method name.
*/
private String operationName = "";
/**
- * 2、action:定义此操作的行为。对于 SOAP 绑定,此值将确定 SOAPAction 头的值。缺省值为 Java 方法的名称。(字符串)
+ * SOAPAction value for this operation. Defaults to an empty string.
*/
private String action = "";
/**
- * 3、exclude:指定是否从 Web Service 中排除某一方法。缺省值为 false。(布尔值)
+ * Whether this method should be excluded from the Web Service.
+ * Defaults to {@code false}.
*/
private boolean exclude = false;
+ /**
+ * Returns the WSDL operation name.
+ *
+ * @return the operation name, possibly empty.
+ */
public String getOperationName() {
return operationName;
}
+ /**
+ * Replaces the WSDL operation name.
+ *
+ * @param operationName new operation name.
+ */
public void setOperationName(String operationName) {
this.operationName = operationName;
}
+ /**
+ * Returns the SOAPAction value.
+ *
+ * @return the action, possibly empty.
+ */
public String getAction() {
return action;
}
+ /**
+ * Replaces the SOAPAction value.
+ *
+ * @param action new action.
+ */
public void setAction(String action) {
this.action = action;
}
+ /**
+ * Returns whether this method is excluded from the Web Service.
+ *
+ * @return {@code true} if the method is excluded.
+ */
public boolean isExclude() {
return exclude;
}
+ /**
+ * Sets whether this method should be excluded from the Web Service.
+ *
+ * @param exclude {@code true} to exclude the method.
+ */
public void setExclude(boolean exclude) {
this.exclude = exclude;
}
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapParam.java b/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapParam.java
index 643b40d..1330b63 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapParam.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapParam.java
@@ -18,35 +18,87 @@
import jakarta.jws.WebParam.Mode;
/**
- * 注释用于定制从单个参数至 Web Service 消息部件和 XML 元素的映射。将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于 JavaBeans 端点的服务器端点实现类。
- * https://www.cnblogs.com/zhao-shan/p/5515174.html
+ * Descriptor for a single JAX-WS endpoint method parameter that the
+ * generated builder will translate into a {@code @WebParam} annotation.
+ *
+ *
The descriptor carries the {@linkplain #getType() parameter type},
+ * {@linkplain #getName() parameter name}, optional
+ * {@linkplain #getPartName() part name},
+ * {@linkplain #getTargetNamespace() target namespace},
+ * {@linkplain #getMode() mode} and a
+ * {@linkplain #isHeader() header} flag.
+ *
+ * @param the runtime type of the parameter.
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see jakarta.jws.WebParam
*/
public class SoapParam {
+ /**
+ * Builds a parameter descriptor with type and name; mode defaults
+ * to {@code IN}, header to {@code false}.
+ *
+ * @param type runtime type of the parameter.
+ * @param name logical parameter name.
+ */
public SoapParam(Class type, String name) {
this.type = type;
this.name = name;
}
-
+
+ /**
+ * Builds a parameter descriptor with an explicit header flag.
+ *
+ * @param type runtime type of the parameter.
+ * @param name logical parameter name.
+ * @param header whether the parameter is in the SOAP header.
+ */
public SoapParam(Class type, String name, boolean header) {
this.type = type;
this.name = name;
this.header = header;
}
-
+
+ /**
+ * Builds a parameter descriptor with an explicit mode.
+ *
+ * @param type runtime type of the parameter.
+ * @param name logical parameter name.
+ * @param mode parameter flow direction.
+ */
public SoapParam(Class type, String name, Mode mode) {
this.type = type;
this.name = name;
this.mode = mode;
}
-
+
+ /**
+ * Builds a parameter descriptor with explicit mode and header flag.
+ *
+ * @param type runtime type of the parameter.
+ * @param name logical parameter name.
+ * @param mode parameter flow direction.
+ * @param header whether the parameter is in the SOAP header.
+ */
public SoapParam(Class type, String name, Mode mode, boolean header) {
this.type = type;
this.name = name;
this.mode = mode;
this.header = header;
}
-
+
+ /**
+ * Builds a fully specified parameter descriptor.
+ *
+ * @param type runtime type of the parameter.
+ * @param name logical parameter name.
+ * @param partName the WSDL part name.
+ * @param targetNamespace XML namespace for the parameter element.
+ * @param mode parameter flow direction.
+ * @param header whether the parameter is in the SOAP
+ * header.
+ */
public SoapParam(Class type, String name, String partName, String targetNamespace, Mode mode,
boolean header) {
this.type = type;
@@ -58,80 +110,141 @@ public SoapParam(Class type, String name, String partName, String targetNames
}
/**
- * 参数对象类型
+ * Runtime type of the parameter; mandatory.
*/
private Class type;
/**
- * 1、name :参数的名称。如果操作是远程过程调用(RPC)类型并且未指定partName 属性,那么这是用于表示参数的 wsdl:part 属性的名称。
- * 如果操作是文档类型或者参数映射至某个头,那么 -name 是用于表示该参数的 XML 元素的局部名称。如果操作是文档类型、 参数类型为 BARE
- * 并且方式为 OUT 或 INOUT,那么必须指定此属性。(字符串)
+ * Logical name of the parameter, surfaced as the {@code name}
+ * attribute of the generated {@code @WebParam} annotation.
*/
private String name = "";
/**
- * 2、partName:定义用于表示此参数的 wsdl:part属性的名称。仅当操作类型为 RPC 或者操作是文档类型并且参数类型为BARE
- * 时才使用此参数。(字符串)
+ * WSDL part name for this parameter. Only used when the operation
+ * type is RPC or the operation is document type and the parameter
+ * type is BARE. Defaults to an empty string.
*/
private String partName = "";
/**
- * 3、targetNamespace:指定参数的 XML 元素的 XML 名称空间。当属性映射至 XML 元素时,仅应用于文档绑定。缺省值为 Web
- * Service 的 targetNamespace。(字符串)
+ * XML namespace for the parameter element. Only applies to
+ * document bindings. Defaults to the Web Service target namespace.
*/
private String targetNamespace = "";
/**
- * 4、mode:此值表示此方法的参数流的方向。有效值为 IN、INOUT 和 OUT。(字符串)
+ * Parameter flow direction. Defaults to {@code IN}.
*/
private jakarta.jws.WebParam.Mode mode = jakarta.jws.WebParam.Mode.IN;
/**
- * 5、header:指定参数是在消息头还是消息体中。缺省值为 false。(布尔值)
+ * Whether the parameter is in the SOAP header rather than the body.
+ * Defaults to {@code false}.
*/
private boolean header = false;
+ /**
+ * Returns the runtime type of the parameter.
+ *
+ * @return the parameter type.
+ */
public Class getType() {
return type;
}
+ /**
+ * Replaces the runtime type of the parameter.
+ *
+ * @param type new parameter type.
+ */
public void setType(Class type) {
this.type = type;
}
+ /**
+ * Returns the logical parameter name.
+ *
+ * @return the parameter name.
+ */
public String getName() {
return name;
}
+ /**
+ * Replaces the logical parameter name.
+ *
+ * @param name new parameter name.
+ */
public void setName(String name) {
this.name = name;
}
+ /**
+ * Returns the WSDL part name.
+ *
+ * @return the part name, possibly empty.
+ */
public String getPartName() {
return partName;
}
+ /**
+ * Replaces the WSDL part name.
+ *
+ * @param partName new part name.
+ */
public void setPartName(String partName) {
this.partName = partName;
}
+ /**
+ * Returns the XML namespace for the parameter element.
+ *
+ * @return the target namespace, possibly empty.
+ */
public String getTargetNamespace() {
return targetNamespace;
}
+ /**
+ * Replaces the XML namespace for the parameter element.
+ *
+ * @param targetNamespace new target namespace.
+ */
public void setTargetNamespace(String targetNamespace) {
this.targetNamespace = targetNamespace;
}
+ /**
+ * Returns the parameter flow direction.
+ *
+ * @return the mode, never {@code null}.
+ */
public jakarta.jws.WebParam.Mode getMode() {
return mode;
}
+ /**
+ * Replaces the parameter flow direction.
+ *
+ * @param mode new mode.
+ */
public void setMode(jakarta.jws.WebParam.Mode mode) {
this.mode = mode;
}
+ /**
+ * Returns whether the parameter is in the SOAP header.
+ *
+ * @return {@code true} if the parameter is a header parameter.
+ */
public boolean isHeader() {
return header;
}
+ /**
+ * Sets whether the parameter is in the SOAP header.
+ *
+ * @param header {@code true} to place the parameter in the header.
+ */
public void setHeader(boolean header) {
this.header = header;
}
-
+
}
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapResult.java b/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapResult.java
index 0022d39..ecff62a 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapResult.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapResult.java
@@ -16,43 +16,65 @@
package org.apache.cxf.endpoint.jaxws.definition;
/**
- * 注释用于定制从返回值至 WSDL 部件或 XML 元素的映射。将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于 JavaBeans 端点的服务器端点实现类。
- * https://www.cnblogs.com/zhao-shan/p/5515174.html
+ * Descriptor for the return value of a JAX-WS endpoint method that the
+ * generated builder will translate into a {@code @WebResult} annotation.
+ *
+ * @param the runtime type of the return value.
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see jakarta.jws.WebResult
*/
public class SoapResult {
/**
- * 返回结果对象类型
+ * Runtime type of the return value; mandatory.
*/
private Class rtClass;
-
+
/**
- * 1、name:当返回值列示在 WSDL 文件中并且在连接上的消息中找到该返回值时,指定该返回值的名称。对于 RPC 绑定,这是用于表示返回值的
- * wsdl:part属性的名称。对于文档绑定,-name 参数是用于表示返回值的 XML 元素的局部名。对于 RPC 和 DOCUMENT/WRAPPED
- * 绑定,缺省值为 return。对于 DOCUMENT/BARE 绑定,缺省值为方法名 + Response。(字符串)
+ * WSDL name for the return value. For RPC and DOCUMENT/WRAPPED
+ * bindings, defaults to {@code "return"}.
*/
private String name = "";
/**
- * 2、targetNamespace:指定返回值的 XML 名称空间。仅当操作类型为 RPC 或者操作是文档类型并且参数类型为 BARE
- * 时才使用此参数。(字符串)
+ * XML namespace for the return value element. Only used for RPC or
+ * DOCUMENT/BARE operations.
*/
private String targetNamespace = "";
/**
- * 3、header:指定头中是否附带结果。缺省值为false。(布尔值)
+ * Whether the result is carried in the SOAP header. Defaults to
+ * {@code false}.
*/
private boolean header = false;
/**
- * 4、partName:指定 RPC 或 DOCUMENT/BARE 操作的结果的部件名称。缺省值为@WebResult.name。(字符串)
+ * WSDL part name for the result. Only used for RPC or
+ * DOCUMENT/BARE operations. Defaults to the {@code @WebResult}
+ * name value.
*/
private String partName = "";
-
+
+ /**
+ * Builds a result descriptor with the supplied type and name.
+ *
+ * @param rtClass runtime type of the return value.
+ * @param name the WSDL result name.
+ */
public SoapResult(Class rtClass, String name) {
this.rtClass = rtClass;
this.name = name;
}
-
+
+ /**
+ * Builds a fully specified result descriptor.
+ *
+ * @param rtClass runtime type of the return value.
+ * @param name the WSDL result name.
+ * @param targetNamespace XML namespace for the result element.
+ * @param header whether the result is in the SOAP header.
+ * @param partName the WSDL part name for the result.
+ */
public SoapResult(Class rtClass, String name, String targetNamespace, boolean header, String partName) {
this.rtClass = rtClass;
this.name = name;
@@ -61,42 +83,92 @@ public SoapResult(Class rtClass, String name, String targetNamespace, boolean
this.partName = partName;
}
+ /**
+ * Returns the runtime type of the return value.
+ *
+ * @return the return type.
+ */
public Class getRtClass() {
return rtClass;
}
+ /**
+ * Replaces the runtime type of the return value.
+ *
+ * @param rtClass new return type.
+ */
public void setRtClass(Class rtClass) {
this.rtClass = rtClass;
}
+ /**
+ * Returns the WSDL result name.
+ *
+ * @return the result name.
+ */
public String getName() {
return name;
}
+ /**
+ * Replaces the WSDL result name.
+ *
+ * @param name new result name.
+ */
public void setName(String name) {
this.name = name;
}
+ /**
+ * Returns the XML namespace for the result element.
+ *
+ * @return the target namespace, possibly empty.
+ */
public String getTargetNamespace() {
return targetNamespace;
}
+ /**
+ * Replaces the XML namespace for the result element.
+ *
+ * @param targetNamespace new target namespace.
+ */
public void setTargetNamespace(String targetNamespace) {
this.targetNamespace = targetNamespace;
}
+ /**
+ * Returns whether the result is in the SOAP header.
+ *
+ * @return {@code true} if the result is a header result.
+ */
public boolean isHeader() {
return header;
}
+ /**
+ * Sets whether the result is in the SOAP header.
+ *
+ * @param header {@code true} to place the result in the header.
+ */
public void setHeader(boolean header) {
this.header = header;
}
+ /**
+ * Returns the WSDL part name for the result.
+ *
+ * @return the part name, possibly empty.
+ */
public String getPartName() {
return partName;
}
+ /**
+ * Replaces the WSDL part name for the result.
+ *
+ * @param partName new part name.
+ */
public void setPartName(String partName) {
this.partName = partName;
}
diff --git a/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapService.java b/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapService.java
index fea21c2..70ceeda 100644
--- a/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapService.java
+++ b/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapService.java
@@ -15,52 +15,101 @@
*/
package org.apache.cxf.endpoint.jaxws.definition;
+/**
+ * Descriptor for the {@code @WebService} annotation attributes attached
+ * to a generated JAX-WS endpoint class.
+ *
+ *
The descriptor bundles the mandatory {@linkplain #getName() name}
+ * and {@linkplain #getTargetNamespace() target namespace} together with
+ * optional {@linkplain #getServiceName() service name},
+ * {@linkplain #getPortName() port name},
+ * {@linkplain #getWsdlLocation() WSDL location} and
+ * {@linkplain #getEndpointInterface() endpoint interface}.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see jakarta.jws.WebService
+ */
public class SoapService {
/**
- * 此属性的值包含XML Web Service的名称。在默认情况下,该值是实现XML Web Service的类的名称,wsdl:portType
- * 的名称。缺省值为 Java 类或接口的非限定名称。(字符串)
+ * WSDL port type name. Defaults to the simple class name.
*/
private final String name;
/**
- * 指定你想要的名称空间,默认是使用接口实现类的包名的反缀(字符串)
+ * XML namespace for the service. Defaults to the reversed package
+ * name of the implementation class.
*/
private final String targetNamespace;
/**
- * 对外发布的服务名,指定 Web Service 的服务名称:wsdl:service。缺省值为 Java 类的简单名称 + Service。(字符串)
+ * WSDL service name ({@code wsdl:service}). Defaults to the simple
+ * class name + {@code "Service"}.
*/
private String serviceName;
/**
- * wsdl:portName。缺省值为 WebService.name+Port。(字符串)
+ * WSDL port name ({@code wsdl:portName}). Defaults to
+ * {@code name + "Port"}.
*/
private String portName;
/**
- * 指定用于定义 Web Service 的 WSDL 文档的 Web 地址。Web 地址可以是相对路径或绝对路径。(字符串)
+ * URL of the WSDL document. May be relative or absolute.
*/
private String wsdlLocation;
/**
- * 服务接口全路径, 指定做SEI(Service EndPoint Interface)服务端点接口(字符串)
+ * Fully qualified name of the Service Endpoint Interface (SEI).
*/
private String endpointInterface;
+ /**
+ * Builds a descriptor with the mandatory name and target namespace.
+ *
+ * @param name the WSDL port type name.
+ * @param targetNamespace the XML namespace for the service.
+ */
public SoapService(String name, String targetNamespace) {
this.name = name;
this.targetNamespace = targetNamespace;
}
-
+
+ /**
+ * Builds a descriptor with name, target namespace and service name.
+ *
+ * @param name the WSDL port type name.
+ * @param targetNamespace the XML namespace for the service.
+ * @param serviceName the WSDL service name.
+ */
public SoapService(String name, String targetNamespace, String serviceName) {
this.name = name;
this.targetNamespace = targetNamespace;
this.serviceName = serviceName;
}
-
+
+ /**
+ * Builds a descriptor with name, target namespace, service name and
+ * port name.
+ *
+ * @param name the WSDL port type name.
+ * @param targetNamespace the XML namespace for the service.
+ * @param serviceName the WSDL service name.
+ * @param portName the WSDL port name.
+ */
public SoapService(String name, String targetNamespace, String serviceName, String portName) {
this.name = name;
this.targetNamespace = targetNamespace;
this.serviceName = serviceName;
this.portName = portName;
}
-
+
+ /**
+ * Builds a descriptor with name, target namespace, service name,
+ * port name and WSDL location.
+ *
+ * @param name the WSDL port type name.
+ * @param targetNamespace the XML namespace for the service.
+ * @param serviceName the WSDL service name.
+ * @param portName the WSDL port name.
+ * @param wsdlLocation URL of the WSDL document.
+ */
public SoapService(String name, String targetNamespace, String serviceName, String portName, String wsdlLocation) {
this.name = name;
this.targetNamespace = targetNamespace;
@@ -68,7 +117,17 @@ public SoapService(String name, String targetNamespace, String serviceName, Stri
this.portName = portName;
this.wsdlLocation = wsdlLocation;
}
-
+
+ /**
+ * Builds a fully specified descriptor with all attributes.
+ *
+ * @param name the WSDL port type name.
+ * @param targetNamespace the XML namespace for the service.
+ * @param serviceName the WSDL service name.
+ * @param portName the WSDL port name.
+ * @param wsdlLocation URL of the WSDL document.
+ * @param endpointInterface fully qualified name of the SEI.
+ */
public SoapService(String name, String targetNamespace, String serviceName, String portName, String wsdlLocation,
String endpointInterface) {
this.name = name;
@@ -79,42 +138,92 @@ public SoapService(String name, String targetNamespace, String serviceName, Stri
this.endpointInterface = endpointInterface;
}
+ /**
+ * Returns the WSDL service name.
+ *
+ * @return the service name, possibly {@code null}.
+ */
public String getServiceName() {
return serviceName;
}
+ /**
+ * Replaces the WSDL service name.
+ *
+ * @param serviceName new service name.
+ */
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
+ /**
+ * Returns the WSDL port name.
+ *
+ * @return the port name, possibly {@code null}.
+ */
public String getPortName() {
return portName;
}
+ /**
+ * Replaces the WSDL port name.
+ *
+ * @param portName new port name.
+ */
public void setPortName(String portName) {
this.portName = portName;
}
+ /**
+ * Returns the URL of the WSDL document.
+ *
+ * @return the WSDL location, possibly {@code null}.
+ */
public String getWsdlLocation() {
return wsdlLocation;
}
+ /**
+ * Replaces the URL of the WSDL document.
+ *
+ * @param wsdlLocation new WSDL location.
+ */
public void setWsdlLocation(String wsdlLocation) {
this.wsdlLocation = wsdlLocation;
}
+ /**
+ * Returns the fully qualified name of the SEI.
+ *
+ * @return the endpoint interface, possibly {@code null}.
+ */
public String getEndpointInterface() {
return endpointInterface;
}
+ /**
+ * Replaces the fully qualified name of the SEI.
+ *
+ * @param endpointInterface new endpoint interface.
+ */
public void setEndpointInterface(String endpointInterface) {
this.endpointInterface = endpointInterface;
}
+ /**
+ * Returns the WSDL port type name.
+ *
+ * @return the name, never {@code null}.
+ */
public String getName() {
return name;
}
+ /**
+ * Returns the XML namespace for the service.
+ *
+ * @return the target namespace, never {@code null}.
+ */
public String getTargetNamespace() {
return targetNamespace;
}
diff --git a/src/main/java/org/apache/cxf/endpoint/utils/JaxrsEndpointApiUtils.java b/src/main/java/org/apache/cxf/endpoint/utils/JaxrsEndpointApiUtils.java
index 63be5ae..00db9bc 100644
--- a/src/main/java/org/apache/cxf/endpoint/utils/JaxrsEndpointApiUtils.java
+++ b/src/main/java/org/apache/cxf/endpoint/utils/JaxrsEndpointApiUtils.java
@@ -61,8 +61,28 @@
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.StringMemberValue;
+/**
+ * Utility methods used by the JAX-RS endpoint builders to create
+ * Javassist classes, interfaces, constructors, methods, and
+ * annotations.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see org.apache.cxf.endpoint.jaxrs.JaxrsEndpointApiCtClassBuilder
+ */
public class JaxrsEndpointApiUtils {
+ /**
+ * Creates or retrieves a concrete class in the supplied pool. If
+ * the class already exists the existing instance is returned.
+ *
+ * @param pool the class pool.
+ * @param classname fully qualified name of the class.
+ * @return the created or retrieved {@link CtClass}.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ * @throws CannotCompileException if the class cannot be compiled.
+ */
public static CtClass makeClass(final ClassPool pool, final String classname)
throws NotFoundException, CannotCompileException {
@@ -80,22 +100,50 @@ public static CtClass makeClass(final ClassPool pool, final String classname)
return declaring;
}
+ /**
+ * Creates a default no-argument constructor for the supplied class.
+ *
+ * @param declaring the class to add the constructor to.
+ * @return the created {@link CtConstructor}.
+ * @throws CannotCompileException if the constructor cannot be
+ * compiled.
+ */
public static CtConstructor defaultConstructor(final CtClass declaring) throws CannotCompileException {
- // 默认添加无参构造器
- CtConstructor cons = new CtConstructor(null, declaring);
- cons.setBody("{}");
+ CtConstructor cons = new CtConstructor(null, declaring);
+ cons.setBody("{}");
return cons;
}
-
- public static CtConstructor makeConstructor(final ClassPool pool, final CtClass declaring) throws NotFoundException, CannotCompileException {
- // 添加有参构造器,注入回调接口
+ /**
+ * Creates a constructor that accepts an {@link InvocationHandler}
+ * and delegates to {@code super(handler)}.
+ *
+ * @param pool the class pool.
+ * @param declaring the class to add the constructor to.
+ * @return the created {@link CtConstructor}.
+ * @throws NotFoundException if {@code InvocationHandler}
+ * cannot be resolved.
+ * @throws CannotCompileException if the constructor cannot be
+ * compiled.
+ */
+ public static CtConstructor makeConstructor(final ClassPool pool, final CtClass declaring) throws NotFoundException, CannotCompileException {
CtClass[] parameters = new CtClass[] {pool.get(InvocationHandler.class.getName())};
CtClass[] exceptions = new CtClass[] { pool.get("java.lang.Exception") };
return CtNewConstructor.make(parameters, exceptions, "{super($1);}", declaring);
-
}
+ /**
+ * Creates or retrieves an interface in the supplied pool. If the
+ * interface already exists the existing instance is returned.
+ *
+ * @param pool the class pool.
+ * @param classname fully qualified name of the interface.
+ * @return the created or retrieved {@link CtClass}.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ * @throws CannotCompileException if the interface cannot be
+ * compiled.
+ */
public static CtClass makeInterface(final ClassPool pool, final String classname)
throws NotFoundException, CannotCompileException {
@@ -112,45 +160,62 @@ public static CtClass makeInterface(final ClassPool pool, final String classname
}
+ /**
+ * Sets the superclass of the supplied class.
+ *
+ * @param pool the class pool.
+ * @param declaring the class whose superclass to set.
+ * @param clazz the Java class to use as superclass.
+ * @param the type of the superclass.
+ * @throws Exception if the superclass cannot be resolved or set.
+ */
public static void setSuperclass(final ClassPool pool, final CtClass declaring, final Class clazz)
throws Exception {
-
- /* 获得 JaxwsHandler 类作为动态类的父类 */
CtClass superclass = pool.get(clazz.getName());
declaring.setSuperclass(superclass);
-
}
-
+
+ /**
+ * Converts an array of {@link RestParam} descriptors into an array
+ * of {@link CtClass} parameter types.
+ *
+ * @param pool the class pool.
+ * @param params the parameter descriptors; may be {@code null} or
+ * empty.
+ * @return the resolved parameter types, or {@code null} when no
+ * parameters are supplied.
+ * @throws NotFoundException if a parameter type cannot be resolved.
+ */
public static CtClass[] makeParams(final ClassPool pool, RestParam>... params) throws NotFoundException {
- // 无参
if(params == null || params.length == 0) {
return null;
}
- // 方法参数
CtClass[] parameters = new CtClass[params.length];
for(int i = 0;i < params.length; i++) {
parameters[i] = pool.get(params[i].getType().getName());
}
-
return parameters;
}
- /**
- * 构造 @Path 注解
- * @param constPool {@link ConstPool} instance
- * @param path the path
- * @return {@link Annotation} instance
- */
+ /**
+ * Builds a {@code @Path} annotation.
+ *
+ * @param constPool the constant pool.
+ * @param path the URI template value.
+ * @return the constructed annotation.
+ */
public static Annotation annotPath(final ConstPool constPool, String path) {
return CtAnnotationBuilder.create(Path.class, constPool).addStringMember("value", path).build();
}
-
- /**
- * 构造 @Produces 注解
- * @param constPool {@link ConstPool} instance
- * @param mediaTypes the media types
- * @return {@link Annotation} instance
- */
+
+ /**
+ * Builds a {@code @Produces} annotation. When no media types are
+ * supplied the default {@code */*} value is used.
+ *
+ * @param constPool the constant pool.
+ * @param mediaTypes the produced media types.
+ * @return the constructed annotation.
+ */
public static Annotation annotProduces(final ConstPool constPool, String... mediaTypes) {
// 参数预处理
@@ -161,23 +226,20 @@ public static Annotation annotProduces(final ConstPool constPool, String... medi
}
- /**
- * 为方法添加 @HttpMethod、 @GET、 @POST、 @PUT、 @DELETE、 @PATCH、 @HEAD、 @OPTIONS、@Path、、@Consumes、@Produces、@RestBound、@RestParam 注解
- * @author [@Loong Wan](https://github.com/loong10k)
- * @param ctMethod {@link CtMethod} instance
- * @param constPool {@link ConstPool} instance
- * @param method {@link RestMethod} instance
- * @param bound {@link RestBound} instance
- * @param params the params
- * @see HttpMethod
- * @see GET
- * @see POST
- * @see PUT
- * @see DELETE
- * @see PATCH
- * @see HEAD
- * @see OPTIONS
- */
+ /**
+ * Attaches all JAX-RS method-level and parameter-level annotations
+ * to the supplied method: {@code @HttpMethod}, {@code @Path},
+ * {@code @Consumes}, {@code @Produces}, {@code @WebBound}, and
+ * parameter annotations ({@code @QueryParam}, {@code @PathParam},
+ * etc.).
+ *
+ * @param ctMethod the target method.
+ * @param constPool the constant pool.
+ * @param method the REST method descriptor.
+ * @param bound the binding descriptor, may be {@code null}.
+ * @param params the parameter descriptors; may be
+ * {@code null} or empty.
+ */
public static void methodAnnotations(final CtMethod ctMethod, final ConstPool constPool, final RestMethod method, final RestBound bound, RestParam>... params) {
// 添加方法注解
@@ -218,12 +280,14 @@ public static void methodAnnotations(final CtMethod ctMethod, final ConstPool co
}
- /**
- * 设置方法体
- * @param ctMethod {@link CtMethod} instance
- * @param method {@link RestMethod} instance
- * @throws CannotCompileException if can't compile
- */
+ /**
+ * Generates and sets the method body that dispatches calls through
+ * the configured {@link InvocationHandler}.
+ *
+ * @param ctMethod the target method.
+ * @param method the REST method descriptor.
+ * @throws CannotCompileException if the body cannot be compiled.
+ */
public static void methodBody(final CtMethod ctMethod, final RestMethod method) throws CannotCompileException {
// 构造方法体
@@ -240,27 +304,30 @@ public static void methodBody(final CtMethod ctMethod, final RestMethod method)
}
- /**
- * 设置方法异常捕获逻辑
- * @param pool {@link ClassPool} instance
- * @param ctMethod {@link CtMethod} instance
- * @throws NotFoundException if not found
- * @throws CannotCompileException if can't compile
- */
+ /**
+ * Adds a catch block that prints and re-throws any
+ * {@code Exception} thrown by the method body.
+ *
+ * @param pool the class pool.
+ * @param ctMethod the target method.
+ * @throws NotFoundException if {@code Exception} cannot be
+ * resolved.
+ * @throws CannotCompileException if the catch block cannot be
+ * compiled.
+ */
public static void methodCatch(final ClassPool pool, final CtMethod ctMethod) throws NotFoundException, CannotCompileException {
-
- // 构造异常处理逻辑
CtClass etype = pool.get("java.lang.Exception");
ctMethod.addCatch("{ System.out.println($e); throw $e; }", etype);
-
}
-
- /**
- * 构造 @WebBound 注解
- * @param constPool {@link ConstPool} instance
- * @param bound {@link RestBound} instance
- * @return {@link Annotation} instance
- */
+
+ /**
+ * Builds a {@code @WebBound} annotation from a JAX-RS bound
+ * descriptor.
+ *
+ * @param constPool the constant pool.
+ * @param bound the bound descriptor.
+ * @return the constructed annotation.
+ */
public static Annotation annotWebBound(final ConstPool constPool, final RestBound bound) {
CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebBound.class, constPool).
@@ -272,12 +339,16 @@ public static Annotation annotWebBound(final ConstPool constPool, final RestBoun
}
- /**
- * 根据参数 构造 @GET、 @POST、 @PUT、 @DELETE、 @PATCH、 @HEAD、 @OPTIONS 注解
- * @param constPool {@link ConstPool} instance
- * @param method {@link RestMethod} instance
- * @return {@link Annotation} instance
- */
+ /**
+ * Builds the appropriate HTTP method annotation ({@code @GET},
+ * {@code @POST}, {@code @PUT}, {@code @DELETE}, {@code @PATCH},
+ * {@code @HEAD}, {@code @OPTIONS}) based on the verb carried by
+ * the descriptor.
+ *
+ * @param constPool the constant pool.
+ * @param method the REST method descriptor.
+ * @return the constructed annotation.
+ */
public static Annotation annotHttpMethod(final ConstPool constPool, final RestMethod method) {
Annotation annot = null;
@@ -311,12 +382,14 @@ public static Annotation annotHttpMethod(final ConstPool constPool, final RestMe
return annot;
}
- /**
- * 构造 @Consumes 注解
- * @param constPool {@link ConstPool} instance
- * @param consumes the consumes
- * @return {@link Annotation} instance
- */
+ /**
+ * Builds a {@code @Consumes} annotation. When no media types are
+ * supplied the default {@code */*} value is used.
+ *
+ * @param constPool the constant pool.
+ * @param consumes the consumed media types.
+ * @return the constructed annotation.
+ */
public static Annotation annotConsumes(final ConstPool constPool, String... consumes) {
// 参数预处理
consumes = ArrayUtils.isEmpty(consumes) ? new String[] {"*/*"} : consumes;
@@ -325,12 +398,19 @@ public static Annotation annotConsumes(final ConstPool constPool, String... cons
return builder.build();
}
- /**
- * 构造 @BeanParam 、@CookieParam、@FormParam、@HeaderParam、@MatrixParam、@PathParam、@QueryParam 参数注解
- * @param constPool {@link ConstPool} instance
- * @param params the params
- * @return {@link Annotation} Array
- */
+ /**
+ * Builds parameter-level annotations for each {@link RestParam}
+ * descriptor. The annotation type is determined by the parameter's
+ * {@link HttpParamEnum} binding source. When a default value is
+ * configured, a {@code @DefaultValue} annotation is appended.
+ *
+ * @param constPool the constant pool.
+ * @param params the parameter descriptors; may be {@code null}
+ * or empty.
+ * @return a two-dimensional annotation array suitable for
+ * {@link ParameterAnnotationsAttribute#setAnnotations(Annotation[][])},
+ * or {@code null} when no parameters are supplied.
+ */
public static Annotation[][] annotParams(final ConstPool constPool, RestParam>... params) {
// 添加 @WebParam 参数注解
@@ -379,7 +459,7 @@ public static Annotation[][] annotParams(final ConstPool constPool, RestParam>
Annotation defAnnot = new Annotation(DefaultValue.class.getName(), constPool);
defAnnot.addMemberValue("value", new StringMemberValue(params[i].getDef(), constPool));
- paramArrays[i][1] = paramAnnot;
+ paramArrays[i][1] = defAnnot;
} else {
paramArrays[i][0] = paramAnnot;
diff --git a/src/main/java/org/apache/cxf/endpoint/utils/JaxwsEndpointApiUtils.java b/src/main/java/org/apache/cxf/endpoint/utils/JaxwsEndpointApiUtils.java
index 18d0ac2..2387067 100644
--- a/src/main/java/org/apache/cxf/endpoint/utils/JaxwsEndpointApiUtils.java
+++ b/src/main/java/org/apache/cxf/endpoint/utils/JaxwsEndpointApiUtils.java
@@ -53,10 +53,30 @@
import javassist.bytecode.ParameterAnnotationsAttribute;
import javassist.bytecode.annotation.Annotation;
+/**
+ * Utility methods used by the JAX-WS endpoint builders to create
+ * Javassist classes, interfaces, constructors, methods, and
+ * annotations.
+ *
+ * @author Loong Wan
+ * @since 3.0.0
+ * @see org.apache.cxf.endpoint.jaxws.JaxwsEndpointApiCtClassBuilder
+ */
public class JaxwsEndpointApiUtils {
-
+
protected static final Logger LOG = LoggerFactory.getLogger(JaxwsEndpointApiUtils.class);
+ /**
+ * Creates or retrieves a concrete class in the supplied pool. If
+ * the class already exists the existing instance is returned.
+ *
+ * @param pool the class pool.
+ * @param classname fully qualified name of the class.
+ * @return the created or retrieved {@link CtClass}.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ * @throws CannotCompileException if the class cannot be compiled.
+ */
public static CtClass makeClass(final ClassPool pool, final String classname)
throws NotFoundException, CannotCompileException {
@@ -72,22 +92,50 @@ public static CtClass makeClass(final ClassPool pool, final String classname)
return declaring;
}
+ /**
+ * Creates a default no-argument constructor for the supplied class.
+ *
+ * @param declaring the class to add the constructor to.
+ * @return the created {@link CtConstructor}.
+ * @throws CannotCompileException if the constructor cannot be
+ * compiled.
+ */
public static CtConstructor defaultConstructor(final CtClass declaring) throws CannotCompileException {
- // 默认添加无参构造器
- CtConstructor cons = new CtConstructor(null, declaring);
- cons.setBody("{}");
+ CtConstructor cons = new CtConstructor(null, declaring);
+ cons.setBody("{}");
return cons;
}
-
- public static CtConstructor makeConstructor(final ClassPool pool, final CtClass declaring) throws NotFoundException, CannotCompileException {
- // 添加有参构造器,注入回调接口
+ /**
+ * Creates a constructor that accepts an {@link InvocationHandler}
+ * and delegates to {@code super(handler)}.
+ *
+ * @param pool the class pool.
+ * @param declaring the class to add the constructor to.
+ * @return the created {@link CtConstructor}.
+ * @throws NotFoundException if {@code InvocationHandler}
+ * cannot be resolved.
+ * @throws CannotCompileException if the constructor cannot be
+ * compiled.
+ */
+ public static CtConstructor makeConstructor(final ClassPool pool, final CtClass declaring) throws NotFoundException, CannotCompileException {
CtClass[] parameters = new CtClass[] {pool.get(InvocationHandler.class.getName())};
CtClass[] exceptions = new CtClass[] { pool.get("java.lang.Exception") };
return CtNewConstructor.make(parameters, exceptions, "{super($1);}", declaring);
-
}
+ /**
+ * Creates or retrieves an interface in the supplied pool. If the
+ * interface already exists the existing instance is returned.
+ *
+ * @param pool the class pool.
+ * @param classname fully qualified name of the interface.
+ * @return the created or retrieved {@link CtClass}.
+ * @throws NotFoundException if a referenced type cannot be
+ * resolved.
+ * @throws CannotCompileException if the interface cannot be
+ * compiled.
+ */
public static CtClass makeInterface(final ClassPool pool, final String classname)
throws NotFoundException, CannotCompileException {
@@ -104,39 +152,54 @@ public static CtClass makeInterface(final ClassPool pool, final String classname
}
+ /**
+ * Sets the superclass of the supplied class.
+ *
+ * @param pool the class pool.
+ * @param declaring the class whose superclass to set.
+ * @param clazz the Java class to use as superclass.
+ * @param the type of the superclass.
+ * @throws Exception if the superclass cannot be resolved or set.
+ */
public static void setSuperclass(final ClassPool pool, final CtClass declaring, final Class clazz)
throws Exception {
-
- /* 获得 JaxwsHandler 类作为动态类的父类 */
CtClass superclass = pool.get(clazz.getName());
declaring.setSuperclass(superclass);
-
}
+ /**
+ * Converts an array of {@link SoapParam} descriptors into an array
+ * of {@link CtClass} parameter types.
+ *
+ * @param pool the class pool.
+ * @param params the parameter descriptors; may be {@code null} or
+ * empty.
+ * @return the resolved parameter types, or {@code null} when no
+ * parameters are supplied.
+ * @throws NotFoundException if a parameter type cannot be resolved.
+ */
public static CtClass[] makeParams(final ClassPool pool, SoapParam>... params) throws NotFoundException {
- // 无参
if(params == null || params.length == 0) {
return null;
}
- // 方法参数
CtClass[] parameters = new CtClass[params.length];
for(int i = 0;i < params.length; i++) {
parameters[i] = pool.get(params[i].getType().getName());
}
-
return parameters;
}
- /**
- * 构造 @WebServiceProvider 注解
- * @param constPool : {@link ConstPool} instance
- * @param wsdlLocation :Location of the WSDL description for the service.
- * @param serviceName :Service name.
- * @param targetNamespace :Target namespace for the service
- * @param portName :Port name.
- * @return {@link Annotation} instance
- */
+ /**
+ * Builds a {@code @WebServiceProvider} annotation.
+ *
+ * @param constPool the constant pool.
+ * @param wsdlLocation URL of the WSDL document.
+ * @param serviceName the WSDL service name.
+ * @param targetNamespace the XML namespace for the service.
+ * @param portName the WSDL port name.
+ * @return the constructed annotation.
+ */
public static Annotation annotWebServiceProvider(final ConstPool constPool, String wsdlLocation,
String serviceName, String targetNamespace, String portName) {
@@ -151,12 +214,14 @@ public static Annotation annotWebServiceProvider(final ConstPool constPool, Stri
}
- /**
- * 构造 @WebService 注解
- * @param constPool : {@link ConstPool} instance
- * @param service : {@link SoapService} instance
- * @return {@link Annotation} instance
- */
+ /**
+ * Builds a {@code @WebService} annotation from a SOAP service
+ * descriptor.
+ *
+ * @param constPool the constant pool.
+ * @param service the service descriptor.
+ * @return the constructed annotation.
+ */
public static Annotation annotWebService(final ConstPool constPool, final SoapService service) {
CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebService.class, constPool)
@@ -180,14 +245,15 @@ public static Annotation annotWebService(final ConstPool constPool, final SoapSe
}
- /**
- * 构造 @Addressing 注解
- * @param constPool : {@link ConstPool} instance
- * @param enabled : the value of enabled
- * @param required : the value of required
- * @param responses : {@link Responses} instance
- * @return {@link Annotation} instance
- */
+ /**
+ * Builds an {@code @Addressing} annotation.
+ *
+ * @param constPool the constant pool.
+ * @param enabled whether WS-Addressing is enabled.
+ * @param required whether WS-Addressing is required.
+ * @param responses the addressing responses policy.
+ * @return the constructed annotation.
+ */
public static Annotation annotAddressing(final ConstPool constPool, final boolean enabled, final boolean required,
final Responses responses) {
@@ -198,23 +264,27 @@ public static Annotation annotAddressing(final ConstPool constPool, final boolea
}
- /**
- * 构造 @ServiceMode 注解
- * @param constPool : {@link ConstPool} instance
- * @param mode : the mode of {@link Service}
- * @return {@link Annotation} instance
- */
+ /**
+ * Builds a {@code @ServiceMode} annotation.
+ *
+ * @param constPool the constant pool.
+ * @param mode the service mode ({@code PAYLOAD} or
+ * {@code MESSAGE}).
+ * @return the constructed annotation.
+ */
public static Annotation annotServiceMode(final ConstPool constPool, final Service.Mode mode) {
return CtAnnotationBuilder.create(ServiceMode.class, constPool).addEnumMember("value", mode).build();
}
- /**
- * 构造 @HandlerChain 注解
- * @param constPool : {@link ConstPool} instance
- * @param name : the value of name
- * @param file : the value of file
- * @return {@link Annotation} instance
- */
+ /**
+ * Builds a {@code @HandlerChain} annotation.
+ *
+ * @param constPool the constant pool.
+ * @param name the handler chain name; may be {@code null}.
+ * @param file the handler chain file path; may be
+ * {@code null}.
+ * @return the constructed annotation.
+ */
public static Annotation annotHandlerChain(final ConstPool constPool, String name, String file) {
CtAnnotationBuilder builder = CtAnnotationBuilder.create(HandlerChain.class, constPool);
@@ -229,18 +299,20 @@ public static Annotation annotHandlerChain(final ConstPool constPool, String nam
}
- /**
- *
- * 为方法添加 @WebMethod、 @WebResult、@WebBound、@WebParam 注解
- * @author [@Loong Wan](https://github.com/loong10k)
- * @param ctMethod : {@link CtMethod} instance
- * @param constPool : {@link ConstPool} instance
- * @param result : {@link SoapResult} instance
- * @param method : {@link SoapMethod} instance
- * @param bound : {@link SoapBound} instance
- * @param : 泛型参数
- * @param params : The {@link SoapParam} params
- */
+ /**
+ * Attaches all JAX-WS method-level and parameter-level annotations
+ * to the supplied method: {@code @WebMethod}, {@code @WebResult},
+ * {@code @WebBound}, and {@code @WebParam}.
+ *
+ * @param ctMethod the target method.
+ * @param constPool the constant pool.
+ * @param result the result descriptor, may be {@code null}.
+ * @param method the SOAP method descriptor.
+ * @param bound the binding descriptor, may be {@code null}.
+ * @param params the parameter descriptors; may be
+ * {@code null} or empty.
+ * @param the return type parameter.
+ */
public static void methodAnnotations(final CtMethod ctMethod, final ConstPool constPool, final SoapResult result, final SoapMethod method, final SoapBound bound, SoapParam>... params) {
// 添加方法注解
@@ -273,12 +345,14 @@ public static void methodAnnotations(final CtMethod ctMethod, final ConstPoo
}
- /**
- * 设置方法体
- * @param ctMethod : {@link CtMethod} instance
- * @param method : {@link SoapMethod} instance
- * @throws CannotCompileException if can't compile
- */
+ /**
+ * Generates and sets the method body that dispatches calls through
+ * the configured {@link InvocationHandler}.
+ *
+ * @param ctMethod the target method.
+ * @param method the SOAP method descriptor.
+ * @throws CannotCompileException if the body cannot be compiled.
+ */
public static void methodBody(final CtMethod ctMethod, final SoapMethod method) throws CannotCompileException {
// 构造方法体
@@ -295,13 +369,17 @@ public static void methodBody(final CtMethod ctMethod, final SoapMethod method)
}
- /**
- * 设置方法异常捕获逻辑
- * @param pool : {@link ClassPool} instance
- * @param ctMethod : {@link CtMethod} instance
- * @throws NotFoundException if not found
- * @throws CannotCompileException if can't compile
- */
+ /**
+ * Adds a catch block that prints and re-throws any
+ * {@code Exception} thrown by the method body.
+ *
+ * @param pool the class pool.
+ * @param ctMethod the target method.
+ * @throws NotFoundException if {@code Exception} cannot be
+ * resolved.
+ * @throws CannotCompileException if the catch block cannot be
+ * compiled.
+ */
public static void methodCatch(final ClassPool pool, final CtMethod ctMethod) throws NotFoundException, CannotCompileException {
// 构造异常处理逻辑
@@ -310,12 +388,14 @@ public static void methodCatch(final ClassPool pool, final CtMethod ctMethod) th
}
- /**
- * 构造 @WebBound 注解
- * @param constPool : {@link ConstPool} instance
- * @param bound : {@link SoapBound} instance
- * @return {@link Annotation} instance
- */
+ /**
+ * Builds a {@code @WebBound} annotation from a JAX-WS bound
+ * descriptor.
+ *
+ * @param constPool the constant pool.
+ * @param bound the bound descriptor.
+ * @return the constructed annotation.
+ */
public static Annotation annotWebBound(final ConstPool constPool, final SoapBound bound) {
CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebBound.class, constPool).
@@ -327,12 +407,14 @@ public static Annotation annotWebBound(final ConstPool constPool, final SoapBoun
}
- /**
- * 构造 @WebMethod 注解
- * @param constPool : {@link ConstPool} instance
- * @param method : {@link SoapMethod} instance
- * @return {@link Annotation} instance
- */
+ /**
+ * Builds a {@code @WebMethod} annotation from a SOAP method
+ * descriptor.
+ *
+ * @param constPool the constant pool.
+ * @param method the SOAP method descriptor.
+ * @return the constructed annotation.
+ */
public static Annotation annotWebMethod(final ConstPool constPool, final SoapMethod method) {
CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebMethod.class, constPool)
@@ -345,12 +427,17 @@ public static Annotation annotWebMethod(final ConstPool constPool, final SoapMet
}
- /**
- * 构造 @WebParam 参数注解
- * @param constPool : {@link ConstPool} instance
- * @param params : The {@link SoapParam} params
- * @return {@link Annotation} instance
- */
+ /**
+ * Builds {@code @WebParam} parameter-level annotations for each
+ * {@link SoapParam} descriptor.
+ *
+ * @param constPool the constant pool.
+ * @param params the parameter descriptors; may be {@code null}
+ * or empty.
+ * @return a two-dimensional annotation array suitable for
+ * {@link ParameterAnnotationsAttribute#setAnnotations(Annotation[][])},
+ * or {@code null} when no parameters are supplied.
+ */
public static Annotation[][] annotParams(final ConstPool constPool, SoapParam>... params) {
// 添加 @WebParam 参数注解
@@ -396,13 +483,15 @@ public static Annotation[][] annotParams(final ConstPool constPool, SoapParam>
return null;
}
- /**
- * 构造 @WebResult 注解
- * @param constPool : {@link ConstPool} instance
- * @param result : {@link SoapResult} instance
- * @param : 泛型参数
- * @return {@link Annotation} instance
- */
+ /**
+ * Builds a {@code @WebResult} annotation from a SOAP result
+ * descriptor.
+ *
+ * @param constPool the constant pool.
+ * @param result the result descriptor.
+ * @param the return type parameter.
+ * @return the constructed annotation.
+ */
public static Annotation annotWebResult(final ConstPool constPool, final SoapResult result) {
CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebResult.class, constPool)
@@ -419,6 +508,11 @@ public static Annotation annotWebResult(final ConstPool constPool, final Soa
}
+ /**
+ * Placeholder for future cleanup logic.
+ *
+ * @param declaring the class to clean up.
+ */
public static void rm(CtClass declaring) {
}
diff --git a/src/test/java/org/apache/cxf/endpoint/EndpointApiTest.java b/src/test/java/org/apache/cxf/endpoint/EndpointApiTest.java
new file mode 100644
index 0000000..f0e0fa5
--- /dev/null
+++ b/src/test/java/org/apache/cxf/endpoint/EndpointApiTest.java
@@ -0,0 +1,36 @@
+package org.apache.cxf.endpoint;
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+import org.junit.Test;
+
+public class EndpointApiTest {
+
+ @Test
+ public void shouldCreateInstanceWithDefaultConstructor() {
+ EndpointApi api = new EndpointApi() {};
+ assertNull(api.getHandler());
+ }
+
+ @Test
+ public void shouldStoreHandlerViaConstructor() {
+ InvocationHandler handler = (proxy, method, args) -> null;
+ EndpointApi api = new EndpointApi(handler) {};
+ assertSame(handler, api.getHandler());
+ }
+
+ @Test
+ public void shouldReturnNullHandlerWhenDefaultConstructed() {
+ EndpointApi api = new EndpointApi() {};
+ assertNull(api.getHandler());
+ }
+
+ @Test
+ public void shouldAcceptNullHandler() {
+ EndpointApi api = new EndpointApi(null) {};
+ assertNull(api.getHandler());
+ }
+}
diff --git a/src/test/java/org/apache/cxf/endpoint/annotation/WebBoundTest.java b/src/test/java/org/apache/cxf/endpoint/annotation/WebBoundTest.java
new file mode 100644
index 0000000..4cbf759
--- /dev/null
+++ b/src/test/java/org/apache/cxf/endpoint/annotation/WebBoundTest.java
@@ -0,0 +1,32 @@
+package org.apache.cxf.endpoint.annotation;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class WebBoundTest {
+
+ @Test
+ public void shouldHaveCorrectDefaults() throws Exception {
+ WebBound bound = WebBoundTestHelper.class.getAnnotation(WebBound.class);
+ assertNotNull(bound);
+ assertEquals("", bound.uid());
+ assertEquals("{}", bound.json());
+ }
+
+ @Test
+ public void shouldHaveCustomValues() throws Exception {
+ WebBound bound = WebBoundCustomHelper.class.getAnnotation(WebBound.class);
+ assertNotNull(bound);
+ assertEquals("uid-123", bound.uid());
+ assertEquals("{\"key\":\"val\"}", bound.json());
+ }
+
+ @WebBound
+ static class WebBoundTestHelper {
+ }
+
+ @WebBound(uid = "uid-123", json = "{\"key\":\"val\"}")
+ static class WebBoundCustomHelper {
+ }
+}
diff --git a/src/test/java/org/apache/cxf/endpoint/annotation/WebEndpointTest.java b/src/test/java/org/apache/cxf/endpoint/annotation/WebEndpointTest.java
new file mode 100644
index 0000000..22ffaff
--- /dev/null
+++ b/src/test/java/org/apache/cxf/endpoint/annotation/WebEndpointTest.java
@@ -0,0 +1,25 @@
+package org.apache.cxf.endpoint.annotation;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class WebEndpointTest {
+
+ @Test
+ public void shouldHaveCorrectDefaults() throws Exception {
+ WebEndpoint ep = WebEndpointHelper.class.getAnnotation(WebEndpoint.class);
+ assertNotNull(ep);
+ assertEquals("http://localhost:8080", ep.addr());
+ assertArrayEquals(new String[]{""}, ep.inInterceptors());
+ assertArrayEquals(new String[]{""}, ep.outInterceptors());
+ assertArrayEquals(new String[]{""}, ep.inFaults());
+ assertArrayEquals(new String[]{""}, ep.outFaults());
+ assertArrayEquals(new String[]{""}, ep.features());
+ assertArrayEquals(new String[]{""}, ep.handlers());
+ }
+
+ @WebEndpoint(addr = "http://localhost:8080")
+ static class WebEndpointHelper {
+ }
+}
diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsApiCtClassBuilder_Test.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsApiCtClassBuilder_Test.java
index fcbdcab..779dacc 100644
--- a/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsApiCtClassBuilder_Test.java
+++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsApiCtClassBuilder_Test.java
@@ -8,6 +8,8 @@
import java.lang.reflect.Method;
import java.util.UUID;
+import static org.junit.Assert.*;
+
import org.apache.commons.beanutils.ConstructorUtils;
import org.apache.commons.io.IOUtils;
import org.apache.cxf.endpoint.jaxrs.definition.HttpMethodEnum;
@@ -79,49 +81,23 @@ public void testClass() throws Exception {
@Test
public void testInstance() throws Exception{
-
+
InvocationHandler handler = new EndpointApiInvocationHandler();
-
- Object ctObject = new JaxrsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot.FirstCaseV2")
+
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot.FirstCaseV2")
.path("getxx")
.makeField("public int k = 3;")
.newField(String.class, "uid", UUID.randomUUID().toString())
.newMethod(String.class, HttpMethodEnum.GET, "sayHello", "{id}/info" , new RestBound("ID01201"),new RestParam(String.class, "id", HttpParamEnum.PATH))
.newMethod(HttpMethodEnum.GET, "sayHello2", "{id}/info", new RestBound("ID01201") ,new RestParam(String.class, "text"))
- .toInstance(handler);
-
- Class clazz = ctObject.getClass();
-
- System.err.println("=========Type Annotations======================");
- for (Annotation element : clazz.getAnnotations()) {
- System.out.println(element.toString());
- }
-
- System.err.println("=========Fields======================");
- for (Field element : clazz.getDeclaredFields()) {
- System.out.println(element.getName());
- for (Annotation anno : element.getAnnotations()) {
- System.out.println(anno.toString());
- }
- }
- System.err.println("=========Methods======================");
- for (Method method : clazz.getDeclaredMethods()) {
- System.out.println(method.getName());
- System.err.println("=========Method Annotations======================");
- for (Annotation anno : method.getAnnotations()) {
- System.out.println(anno.toString());
- }
- System.err.println("=========Method Parameter Annotations======================");
- for (Annotation[] anno : method.getParameterAnnotations()) {
- System.out.println(anno[0].toString());
- }
- }
- System.err.println("=========sayHello======================");
- Method sayHello = clazz.getMethod("sayHello", String.class);
- sayHello.invoke(ctObject, " hi Hello " );
- System.err.println("=========sayHello2======================");
- Method sayHello2 = clazz.getMethod("sayHello2", String.class);
- sayHello2.invoke(ctObject, " hi Hello2 " );
+ .build();
+
+ assertNotNull(ctClass);
+ assertNotNull(ctClass.getDeclaredMethod("sayHello"));
+ assertNotNull(ctClass.getDeclaredMethod("sayHello2"));
+ assertNotNull(ctClass.getDeclaredField("k"));
+ assertNotNull(ctClass.getDeclaredField("uid"));
+ ctClass.detach();
}
}
diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilderTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilderTest.java
new file mode 100644
index 0000000..2a13a32
--- /dev/null
+++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilderTest.java
@@ -0,0 +1,489 @@
+package org.apache.cxf.endpoint.jaxrs;
+
+import static org.junit.Assert.*;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+import org.apache.cxf.endpoint.jaxrs.definition.HttpMethodEnum;
+import org.apache.cxf.endpoint.jaxrs.definition.HttpParamEnum;
+import org.apache.cxf.endpoint.jaxrs.definition.RestBound;
+import org.apache.cxf.endpoint.jaxrs.definition.RestMethod;
+import org.apache.cxf.endpoint.jaxrs.definition.RestParam;
+import org.junit.Test;
+
+import jakarta.ws.rs.BeanParam;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.CookieParam;
+import jakarta.ws.rs.DefaultValue;
+import jakarta.ws.rs.FormParam;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.HEAD;
+import jakarta.ws.rs.HeaderParam;
+import jakarta.ws.rs.MatrixParam;
+import jakarta.ws.rs.PATCH;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.OPTIONS;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+
+public class JaxrsEndpointApiCtClassBuilderTest {
+
+ @Test
+ public void shouldBuildClassWithDefaultPool() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsDefault1")
+ .build();
+ assertNotNull(ctClass);
+ assertEquals("org.test.JaxrsDefault1", ctClass.getName());
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldBuildClassWithCustomPool() throws Exception {
+ ClassPool pool = ClassPool.getDefault();
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder(pool, "org.test.JaxrsCustom1")
+ .build();
+ assertNotNull(ctClass);
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldAddPathAnnotation() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsPath1")
+ .path("/api")
+ .build();
+ assertNotNull(ctClass.getAnnotation(jakarta.ws.rs.Path.class));
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldAddProducesAnnotation() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsProd1")
+ .produces("application/json")
+ .build();
+ assertNotNull(ctClass.getAnnotation(jakarta.ws.rs.Produces.class));
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldAddDefaultProducesWhenEmpty() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsProd2")
+ .produces()
+ .build();
+ assertNotNull(ctClass.getAnnotation(jakarta.ws.rs.Produces.class));
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldBindWithUidAndJson() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsBind1")
+ .bind("uid-1", "{}")
+ .build();
+ assertNotNull(ctClass);
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldBindWithRestBound() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsBind2")
+ .bind(new RestBound("uid-2", "{\"x\":1}"))
+ .build();
+ assertNotNull(ctClass);
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldMakeFieldFromSource() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsField1")
+ .makeField("public int k = 3;")
+ .build();
+ assertNotNull(ctClass.getDeclaredField("k"));
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldAddTypedField() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsField2")
+ .newField(String.class, "uid", "test-value")
+ .build();
+ assertNotNull(ctClass.getDeclaredField("uid"));
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldRemoveExistingField() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsField3")
+ .makeField("public int k = 3;")
+ .removeField("k")
+ .build();
+ try {
+ ctClass.getDeclaredField("k");
+ fail("Field should have been removed");
+ } catch (javassist.NotFoundException e) {
+ // expected
+ }
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldNoopWhenRemovingNonexistentField() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsField4")
+ .removeField("nonexistent")
+ .build();
+ assertNotNull(ctClass);
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldAddMethodWithReturnTypeAndBound() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsMethod1")
+ .newMethod(String.class, HttpMethodEnum.GET, "sayHello", "/{id}",
+ new RestBound("b1"), new RestParam(String.class, "id", HttpParamEnum.PATH))
+ .build();
+ assertNotNull(ctClass.getDeclaredMethod("sayHello"));
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldAddMethodWithoutReturnType() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsMethod2")
+ .newMethod(HttpMethodEnum.POST, "create", "/",
+ new RestParam(String.class, "name"))
+ .build();
+ assertNotNull(ctClass.getDeclaredMethod("create"));
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldAddMethodWithRestMethodAndBound() throws Exception {
+ RestMethod rm = new RestMethod(HttpMethodEnum.GET, "findById", "/{id}");
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsMethod3")
+ .newMethod(String.class, rm, new RestBound("b1"),
+ new RestParam(String.class, "id", HttpParamEnum.PATH))
+ .build();
+ assertNotNull(ctClass.getDeclaredMethod("findById"));
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldAddMethodWithRestMethodNoBound() throws Exception {
+ RestMethod rm = new RestMethod(HttpMethodEnum.GET, "list", "/");
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsMethod4")
+ .newMethod(String.class, rm)
+ .build();
+ assertNotNull(ctClass.getDeclaredMethod("list"));
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldAddMethodWithHttpMethodEnumNamePath() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsMethod5")
+ .newMethod(HttpMethodEnum.PUT, "update", "/{id}",
+ new RestBound("b1"), new RestParam(String.class, "id"))
+ .build();
+ assertNotNull(ctClass.getDeclaredMethod("update"));
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldAddMethodWithRestMethodBoundNoReturn() throws Exception {
+ RestMethod rm = new RestMethod(HttpMethodEnum.DELETE, "remove", "/{id}");
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsMethod6")
+ .newMethod(rm, new RestBound("b1"),
+ new RestParam(String.class, "id"))
+ .build();
+ assertNotNull(ctClass.getDeclaredMethod("remove"));
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldAddMethodWithRestMethodNoBoundNoReturn() throws Exception {
+ RestMethod rm = new RestMethod(HttpMethodEnum.GET, "health", "/health");
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsMethod7")
+ .newMethod(rm)
+ .build();
+ assertNotNull(ctClass.getDeclaredMethod("health"));
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldRemoveExistingMethod() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsMethod8")
+ .newMethod(HttpMethodEnum.GET, "temp", "/temp")
+ .removeMethod("temp")
+ .build();
+ assertNotNull(ctClass);
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldNoopWhenRemovingNonexistentMethod() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsMethod9")
+ .removeMethod("nonexistent")
+ .build();
+ assertNotNull(ctClass);
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldRemoveMethodWithParams() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsMethod10")
+ .newMethod(String.class, HttpMethodEnum.GET, "withParam", "/p",
+ new RestParam(String.class, "x"))
+ .removeMethod("withParam", new RestParam(String.class, "x"))
+ .build();
+ assertNotNull(ctClass);
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldNoopWhenRemovingNonexistentMethodWithParams() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsMethod11")
+ .removeMethod("nope", new RestParam(String.class, "x"))
+ .build();
+ assertNotNull(ctClass);
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldAddVoidMethodWithNoParams() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsMethod12")
+ .newMethod(HttpMethodEnum.GET, "noop", "/noop")
+ .build();
+ assertNotNull(ctClass.getDeclaredMethod("noop"));
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldSetSuperclassToEndpointApi() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsParent1")
+ .build();
+ assertNotNull(ctClass.getSuperclass());
+ assertEquals("org.apache.cxf.endpoint.EndpointApi", ctClass.getSuperclass().getName());
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldHaveDefaultConstructor() throws Exception {
+ CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsCtor1")
+ .build();
+ assertTrue(ctClass.getConstructors().length > 0);
+ ctClass.detach();
+ }
+
+ @Test
+ public void shouldSupportFluentChaining() throws Exception {
+ JaxrsEndpointApiCtClassBuilder builder = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsChain1");
+ JaxrsEndpointApiCtClassBuilder result = builder
+ .path("/api")
+ .produces("application/json")
+ .bind("uid", "{}")
+ .makeField("public int k = 3;")
+ .newField(String.class, "name", "test")
+ .newMethod(HttpMethodEnum.GET, "get", "/get");
+ assertSame(builder, result);
+ CtClass ctClass = result.build();
+ assertNotNull(ctClass);
+ ctClass.detach();
+ }
+
+ // ==================== 新增强化测试:注解验证 & @DefaultValue bug 修复验证 ====================
+
+ @Test
+ public void shouldVerifyClassLevelPathAndProducesAnnotations() throws Exception {
+ Class> clazz = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsAnn1")
+ .path("/users")
+ .produces("application/json", "application/xml")
+ .toClass();
+
+ Path pathAnno = clazz.getAnnotation(Path.class);
+ assertNotNull(pathAnno);
+ assertEquals("/users", pathAnno.value());
+
+ Produces producesAnno = clazz.getAnnotation(Produces.class);
+ assertNotNull(producesAnno);
+ assertArrayEquals(new String[]{"application/json", "application/xml"}, producesAnno.value());
+ }
+
+ @Test
+ public void shouldVerifyHttpMethodAnnotationsOnMethods() throws Exception {
+ RestMethod[] methods = new RestMethod[]{
+ new RestMethod(HttpMethodEnum.GET, "mGet", "/a"),
+ new RestMethod(HttpMethodEnum.POST, "mPost", "/b"),
+ new RestMethod(HttpMethodEnum.PUT, "mPut", "/c"),
+ new RestMethod(HttpMethodEnum.DELETE, "mDel", "/d"),
+ new RestMethod(HttpMethodEnum.PATCH, "mPatch", "/e"),
+ new RestMethod(HttpMethodEnum.HEAD, "mHead", "/f"),
+ new RestMethod(HttpMethodEnum.OPTIONS, "mOpt", "/g")
+ };
+ JaxrsEndpointApiCtClassBuilder b = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsAnn2");
+ for (RestMethod m : methods) {
+ b.newMethod(String.class, m);
+ }
+ Class> clazz = b.toClass();
+
+ assertNotNull(clazz.getMethod("mGet").getAnnotation(GET.class));
+ assertNotNull(clazz.getMethod("mPost").getAnnotation(POST.class));
+ assertNotNull(clazz.getMethod("mPut").getAnnotation(PUT.class));
+ assertNotNull(clazz.getMethod("mDel").getAnnotation(DELETE.class));
+ assertNotNull(clazz.getMethod("mPatch").getAnnotation(PATCH.class));
+ assertNotNull(clazz.getMethod("mHead").getAnnotation(HEAD.class));
+ assertNotNull(clazz.getMethod("mOpt").getAnnotation(OPTIONS.class));
+
+ assertEquals("/a", clazz.getMethod("mGet").getAnnotation(Path.class).value());
+ assertEquals("/b", clazz.getMethod("mPost").getAnnotation(Path.class).value());
+ }
+
+ @Test
+ public void shouldVerifyMethodLevelConsumes() throws Exception {
+ RestMethod rm = new RestMethod(HttpMethodEnum.POST, "add", "/data", "application/json");
+ Class> clazz = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsAnn3")
+ .newMethod(rm)
+ .toClass();
+ Method m = clazz.getMethod("add", new Class>[0]);
+ Consumes c = m.getAnnotation(Consumes.class);
+ assertNotNull(c);
+ assertArrayEquals(new String[]{"application/json"}, c.value());
+ }
+
+ @Test
+ public void shouldVerifyPathParamAnnotationOnParameter() throws Exception {
+ RestParam idParam = new RestParam<>(String.class, "userId", HttpParamEnum.PATH);
+ Class> clazz = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsAnn4")
+ .newMethod(String.class, HttpMethodEnum.GET, "findById", "/{userId}", idParam)
+ .toClass();
+ Method m = clazz.getMethod("findById", String.class);
+ Annotation[][] anns = m.getParameterAnnotations();
+ assertEquals(1, anns.length);
+ assertTrue(hasAnnotation(anns[0], PathParam.class));
+ assertEquals("userId", findAnnotation(anns[0], PathParam.class).value());
+ }
+
+ @Test
+ public void shouldVerifyQueryParamAnnotationOnParameter() throws Exception {
+ RestParam nameParam = new RestParam<>(String.class, "name", HttpParamEnum.QUERY);
+ Class> clazz = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsAnn5")
+ .newMethod(String.class, HttpMethodEnum.GET, "search", "/s", nameParam)
+ .toClass();
+ Method m = clazz.getMethod("search", String.class);
+ Annotation[][] anns = m.getParameterAnnotations();
+ assertTrue(hasAnnotation(anns[0], QueryParam.class));
+ assertEquals("name", findAnnotation(anns[0], QueryParam.class).value());
+ }
+
+ @Test
+ public void shouldVerifyHeaderCookieFormMatrixParamAnnotations() throws Exception {
+ RestParam h = new RestParam<>(String.class, "Auth", HttpParamEnum.HEADER);
+ RestParam c = new RestParam<>(String.class, "sid", HttpParamEnum.COOKIE);
+ RestParam f = new RestParam<>(String.class, "body", HttpParamEnum.FORM);
+ RestParam mx = new RestParam<>(String.class, "m", HttpParamEnum.MATRIX);
+
+ Class> clazz = new JaxrsEndpointApiCtClassBuilder("org.test.JaxrsAnn6")
+ .newMethod(String.class, HttpMethodEnum.POST, "mix", "/m", h, c, f, mx)
+ .toClass();
+ Method m = clazz.getMethod("mix", String.class, String.class, String.class, String.class);
+ Annotation[][] anns = m.getParameterAnnotations();
+ assertEquals(4, anns.length);
+
+ assertTrue(hasAnnotation(anns[0], HeaderParam.class));
+ assertEquals("Auth", findAnnotation(anns[0], HeaderParam.class).value());
+
+ assertTrue(hasAnnotation(anns[1], CookieParam.class));
+ assertEquals("sid", findAnnotation(anns[1], CookieParam.class).value());
+
+ assertTrue(hasAnnotation(anns[2], FormParam.class));
+ assertEquals("body", findAnnotation(anns[2], FormParam.class).value());
+
+ assertTrue(hasAnnotation(anns[3], MatrixParam.class));
+ assertEquals("m", findAnnotation(anns[3], MatrixParam.class).value());
+ }
+
+ @Test
+ public void shouldVerifyBeanParamAnnotationWithoutValue() throws Exception {
+ RestParam