From 5290abe71f2d6faf3f0547ee82fe04091eb42311 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Thu, 9 Jul 2026 16:20:20 +0800 Subject: [PATCH 01/56] =?UTF-8?q?build:=E5=AF=B9=E9=BD=90=201.0.x=20?= =?UTF-8?q?=E5=88=86=E6=94=AF=20pom.xml=20=E9=80=9A=E7=94=A8=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=20(JDK=201.8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index f60cd62..55dd32b 100644 --- a/pom.xml +++ b/pom.xml @@ -371,30 +371,30 @@ UTF-8 UTF-8 - 17 + 1.8 3.9.9 ${java.version} ${java.version} 3.1.0 - 3.4.0 - 3.13.0 + 3.5.0 + 3.15.0 3.8.0 - 3.1.3 - 3.5.0 - 3.2.6 + 3.1.4 + 3.6.3 + 3.2.8 3.0.0 3.5.0 - 3.1.3 + 3.1.4 3.8.0 - 3.4.2 - 3.8.0 + 3.5.0 + 3.12.0 1.7.0 - 3.1.1 - 3.3.1 + 3.3.1 + 3.5.0 3.6.0 - 3.3.1 - 3.5.0 + 3.4.0 + 3.5.6 3.4.0 1.9.4 @@ -405,8 +405,8 @@ 1.0.4.RELEASE 4.13.2 5.10.3 - 1.18.20 - 2.0.16 + 1.18.46 + 2.0.18 From bf6e0fa14714118306736c5c5902f59b2ba47d2b Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Thu, 9 Jul 2026 16:42:34 +0800 Subject: [PATCH 02/56] =?UTF-8?q?build:=E4=BF=AE=E6=AD=A3=201.0.x=20?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=8F=B7=20(JDK=201.8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 55dd32b..9813b85 100644 --- a/pom.xml +++ b/pom.xml @@ -394,7 +394,7 @@ 3.5.0 3.6.0 3.4.0 - 3.5.6 + 3.5.0 3.4.0 1.9.4 From 7657b5933c7045f9b44e5184bb0b39de53c06c9c Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Thu, 9 Jul 2026 16:55:52 +0800 Subject: [PATCH 03/56] =?UTF-8?q?build:=E4=BF=AE=E6=AD=A3=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=8F=B7=E4=B8=BA=201.0.x.20260630-SNAPSHOT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9813b85..f3d46ea 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 io.github.hiwepy cxf-rt-javassist - 4.0.x.20241003.RELEASE + 1.0.x.20260630-SNAPSHOT 通过javassist生成基于cxf的jaxws、jaxrs实现 ${project.groupId}:${project.artifactId} https://github.com/hiwepy/${project.artifactId} @@ -536,4 +536,4 @@ - \ No newline at end of file + From 051c971b79910d5cb2a7fcb5d0836341f20073b2 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Mon, 13 Jul 2026 01:28:22 +0800 Subject: [PATCH 04/56] chore: unify maven wrapper (mvnw, mvnw.cmd, .mvn, maven) and .gitignore across hiwepy projects --- .gitignore | 180 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 102 insertions(+), 78 deletions(-) diff --git a/.gitignore b/.gitignore index f4b6243..8a6aaa8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,111 +1,135 @@ -target/ -bin/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ -.flattened-pom.xml -.fastRequest -Cargo.lock -*.swp -*.swo - -### STS ### -.apt_generated +### IDE ### +*.iml +*.ipr +*.iws +.idea/ .classpath .factorypath .project .settings .springBeans +.apt_generated .sts4-cache +.metadata +.recommenders rebel.xml +.vscode/ -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr +### Gradle ### +.gradle +!gradle/wrapper/gradle-wrapper.jar + +### Maven ### +target/ +dependency-reduced-pom.xml +pom.xml.versionsBackup +.flattened-pom.xml +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ ### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ +nbproject/private/ +nbbuild/ +nbdist/ +.nb-gradle/ +dist/ + +### Build ### build/ !**/src/main/**/build/ !**/src/test/**/build/ +build.log +bin/ +lib/ +codes/ +/code +gen_code -### VS Code ### -.vscode/ -.claude/ - -### Mac OS ### -.DS_Store - -**/dependency-reduced-pom.xml - -### Python -__pycache__/ -*.py[cod] -*$py.class -*.egg-info/ +### Logs ### +logs/ +*.log +log.*/ +transaction-logs -### Docs -docs/_build -.venv/ +### Archives ### +*.jar +*.war +*.ear +*.tar +*.gz +!/libs/*.jar +### Temporary files ### +*# +*.sw? +*~ +.#* +.*.md.html +*.cache +*.diff +*.patch +*.tmp +*.java~ +*.properties~ +*.xml~ -# vibe coding -.claude -openspec -AGENTS.md -CLAUDE.md +### System files ### +.DS_Store +Thumbs.db +Servers -##Log -logs/ +### Application specific ### +MANIFEST.MF +_site/ +activemq-data +dump.rdb +interpolated*.xml +manifest.yml +overridedb.* +secrets.yml +upload -## Frontend (all frontend/ directories across examples and extensions) -# Dependencies -**/node_modules/ -# Build output -**/dist/ -**/build/ +### Node / Frontend ### +node_modules/ **/.output/ -# Vite / bundler caches **/.vite/ **/.cache/ **/.parcel-cache/ -# Type-check / tsc output **/*.tsbuildinfo -# Env files (may contain secrets) **/.env.local **/.env.*.local -# Coverage **/coverage/ -# Storybook **/storybook-static/ -# boba-tea-shop: generated static assets served by the supervisor-agent **/boba-tea-shop/supervisor-agent/**/static/ -##agentscope -.agentscope/ +### Python ### +__pycache__/ +*.py[cod] +*$py.class +*.egg-info/ +.venv/ + +### Docs ### +docs/_build + +### Rust ### +Cargo.lock + +### Server ### +/server/nacos-server/data +/server/nacos-server/logs -## harness db +### AI / vibe coding ### +.claude +openspec +AGENTS.md +CLAUDE.md +.agentscope/ **/*.db -### Apache CXF (from cxf-rt-javassist) ### -.metadata -.recommenders -/code -MANIFEST.MF -_site/ -activemq-data -build.log -lib/ -manifest.yml -overridedb.* -settings.xml -transaction-logs -secrets.yml -dump.rdb -interpolated*.xml +### Development tools ### +.fastRequest +*.http +*.json +qodana.yaml From 966822e766fdb5bda34c4480b98532fad312241c Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Fri, 31 Jul 2026 20:12:33 +0800 Subject: [PATCH 05/56] =?UTF-8?q?refactor:=20groupId=20=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=20io.github.easy4j=EF=BC=8Curl/scm=20=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E4=B8=BA=20easy-4-java?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index f3d46ea..e8eb1fe 100644 --- a/pom.xml +++ b/pom.xml @@ -3,12 +3,12 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - io.github.hiwepy + io.github.easy4j cxf-rt-javassist 1.0.x.20260630-SNAPSHOT 通过javassist生成基于cxf的jaxws、jaxrs实现 ${project.groupId}:${project.artifactId} - https://github.com/hiwepy/${project.artifactId} + https://github.com/easy-4-java/${project.artifactId} jar @@ -19,9 +19,9 @@ - scm:git:https:github.com/hiwepy/${project.artifactId}.git - scm:git:https:github.com/hiwepy/${project.artifactId}.git - https:github.com/hiwepy/${project.artifactId} + scm:git:https:github.com/easy-4-java/${project.artifactId}.git + scm:git:https:github.com/easy-4-java/${project.artifactId}.git + https:github.com/easy-4-java/${project.artifactId} ${project.artifactId} @@ -474,7 +474,7 @@ - io.github.hiwepy + io.github.easy4j javassist-plus ${javassist-plus.version} @@ -530,7 +530,7 @@ javassist - io.github.hiwepy + io.github.easy4j javassist-plus From afa8444a1cc3e928b92f04a9818ad410c63aae68 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Fri, 31 Jul 2026 20:37:39 +0800 Subject: [PATCH 06/56] =?UTF-8?q?refactor:=20=E5=8C=85=E5=90=8D=20io.githu?= =?UTF-8?q?b.hiwepy=20=E6=94=B9=E4=B8=BA=20io.github.easy4j?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilder.java | 6 +++--- .../endpoint/jaxrs/JaxrsEndpointApiImplCtClassBuilder.java | 2 +- .../jaxrs/JaxrsEndpointApiInterfaceCtClassBuilder.java | 4 ++-- .../cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilder.java | 6 +++--- .../endpoint/jaxws/JaxwsEndpointApiImplCtClassBuilder.java | 2 +- .../jaxws/JaxwsEndpointApiInterfaceCtClassBuilder.java | 4 ++-- .../apache/cxf/endpoint/utils/JaxrsEndpointApiUtils.java | 4 ++-- .../apache/cxf/endpoint/utils/JaxwsEndpointApiUtils.java | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) 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 58deb0b..4fa6a12 100644 --- a/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilder.java +++ b/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilder.java @@ -12,9 +12,9 @@ import org.apache.cxf.endpoint.jaxrs.definition.RestParam; import org.apache.cxf.endpoint.utils.JaxrsEndpointApiUtils; -import io.github.hiwepy.javassist.bytecode.CtFieldBuilder; -import io.github.hiwepy.javassist.utils.ClassPoolFactory; -import io.github.hiwepy.javassist.utils.JavassistUtils; +import io.github.easy4j.javassist.bytecode.CtFieldBuilder; +import io.github.easy4j.javassist.utils.ClassPoolFactory; +import io.github.easy4j.javassist.utils.JavassistUtils; import javassist.CannotCompileException; import javassist.ClassPool; 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 6360f2e..1cae177 100644 --- a/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiImplCtClassBuilder.java +++ b/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiImplCtClassBuilder.java @@ -9,7 +9,7 @@ import org.apache.cxf.endpoint.jaxrs.definition.RestParam; import org.apache.cxf.endpoint.utils.JaxrsEndpointApiUtils; -import io.github.hiwepy.javassist.utils.ClassPoolFactory; +import io.github.easy4j.javassist.utils.ClassPoolFactory; import javassist.CannotCompileException; import javassist.ClassPool; 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 140ef28..449fd7b 100644 --- a/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiInterfaceCtClassBuilder.java +++ b/src/main/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiInterfaceCtClassBuilder.java @@ -8,8 +8,8 @@ import org.apache.cxf.endpoint.jaxrs.definition.RestParam; import org.apache.cxf.endpoint.utils.JaxrsEndpointApiUtils; -import io.github.hiwepy.javassist.utils.ClassPoolFactory; -import io.github.hiwepy.javassist.utils.JavassistUtils; +import io.github.easy4j.javassist.utils.ClassPoolFactory; +import io.github.easy4j.javassist.utils.JavassistUtils; import javassist.CannotCompileException; import javassist.ClassPool; 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 a546a67..4568224 100644 --- a/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilder.java +++ b/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilder.java @@ -15,9 +15,9 @@ import org.apache.cxf.endpoint.jaxws.definition.SoapService; import org.apache.cxf.endpoint.utils.JaxwsEndpointApiUtils; -import io.github.hiwepy.javassist.bytecode.CtFieldBuilder; -import io.github.hiwepy.javassist.utils.ClassPoolFactory; -import io.github.hiwepy.javassist.utils.JavassistUtils; +import io.github.easy4j.javassist.bytecode.CtFieldBuilder; +import io.github.easy4j.javassist.utils.ClassPoolFactory; +import io.github.easy4j.javassist.utils.JavassistUtils; import javassist.CannotCompileException; import javassist.ClassPool; 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 6e5fa58..7a5c8f6 100644 --- a/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiImplCtClassBuilder.java +++ b/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiImplCtClassBuilder.java @@ -14,7 +14,7 @@ import org.apache.cxf.endpoint.jaxws.definition.SoapService; import org.apache.cxf.endpoint.utils.JaxwsEndpointApiUtils; -import io.github.hiwepy.javassist.utils.ClassPoolFactory; +import io.github.easy4j.javassist.utils.ClassPoolFactory; import javassist.CannotCompileException; import javassist.ClassPool; 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 c26677a..ea4e927 100644 --- a/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiInterfaceCtClassBuilder.java +++ b/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiInterfaceCtClassBuilder.java @@ -11,8 +11,8 @@ import org.apache.cxf.endpoint.jaxws.definition.SoapService; import org.apache.cxf.endpoint.utils.JaxwsEndpointApiUtils; -import io.github.hiwepy.javassist.utils.ClassPoolFactory; -import io.github.hiwepy.javassist.utils.JavassistUtils; +import io.github.easy4j.javassist.utils.ClassPoolFactory; +import io.github.easy4j.javassist.utils.JavassistUtils; import javassist.CannotCompileException; import javassist.ClassPool; 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 f696b41..ad9985d 100644 --- a/src/main/java/org/apache/cxf/endpoint/utils/JaxrsEndpointApiUtils.java +++ b/src/main/java/org/apache/cxf/endpoint/utils/JaxrsEndpointApiUtils.java @@ -45,8 +45,8 @@ import org.apache.cxf.endpoint.jaxrs.definition.RestMethod; import org.apache.cxf.endpoint.jaxrs.definition.RestParam; -import io.github.hiwepy.javassist.bytecode.CtAnnotationBuilder; -import io.github.hiwepy.javassist.utils.JavassistUtils; +import io.github.easy4j.javassist.bytecode.CtAnnotationBuilder; +import io.github.easy4j.javassist.utils.JavassistUtils; import javassist.CannotCompileException; import javassist.ClassPool; 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 145b290..8f07d4b 100644 --- a/src/main/java/org/apache/cxf/endpoint/utils/JaxwsEndpointApiUtils.java +++ b/src/main/java/org/apache/cxf/endpoint/utils/JaxwsEndpointApiUtils.java @@ -38,8 +38,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.github.hiwepy.javassist.bytecode.CtAnnotationBuilder; -import io.github.hiwepy.javassist.utils.JavassistUtils; +import io.github.easy4j.javassist.bytecode.CtAnnotationBuilder; +import io.github.easy4j.javassist.utils.JavassistUtils; import javassist.CannotCompileException; import javassist.ClassPool; From 055baad92b038fa40c998f41091f50a05932dd19 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Sat, 1 Aug 2026 01:33:13 +0800 Subject: [PATCH 07/56] =?UTF-8?q?build:=20distributionManagement=20?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=8C=87=E5=90=91=E9=98=BF=E9=87=8C=E4=BA=91?= =?UTF-8?q?=E4=BB=93=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 1074 +++++++++++++++++++++++++++---------------------------- 1 file changed, 536 insertions(+), 538 deletions(-) diff --git a/pom.xml b/pom.xml index e8eb1fe..368c28c 100644 --- a/pom.xml +++ b/pom.xml @@ -1,539 +1,537 @@ - - - 4.0.0 - io.github.easy4j - cxf-rt-javassist - 1.0.x.20260630-SNAPSHOT - 通过javassist生成基于cxf的jaxws、jaxrs实现 - ${project.groupId}:${project.artifactId} - https://github.com/easy-4-java/${project.artifactId} - jar - - - - The Apache Software License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt - - - - - scm:git:https:github.com/easy-4-java/${project.artifactId}.git - scm:git:https:github.com/easy-4-java/${project.artifactId}.git - https:github.com/easy-4-java/${project.artifactId} - ${project.artifactId} - - - - - wandl - hiwepy@gmail.com - - developer - - +8 - - - - - - ossrh - Maven Snapshots Repository - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - Maven Central Staging Repository - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - ${project.artifactId} - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - ${java.version} - ${java.version} - ${project.build.sourceEncoding} - 512M - - - - - org.apache.maven.plugins - maven-deploy-plugin - ${maven-deploy-plugin.version} - - - - org.apache.maven.plugins - maven-enforcer-plugin - ${maven-enforcer-plugin.version} - - - default-cli - - enforce - - validate - - - - - - - [${maven.version}.0,) - - - - - - [${java.version}.0,) - - - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin.version} - - - sign-artifacts - verify - - sign - - - - - - - org.apache.maven.plugins - maven-install-plugin - ${maven-install-plugin.version} - - - - org.apache.maven.plugins - maven-resources-plugin - ${maven-resources-plugin.version} - - ${project.build.sourceEncoding} - - - - - org.apache.maven.plugins - maven-release-plugin - ${maven-release-plugin.version} - - v@{project.version} - true - false - release - deploy - - - - - org.apache.maven.plugins - maven-source-plugin - ${maven-source-plugin.version} - - true - - - - attach-sources - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin.version} - - - true - true - - - -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m - -Dfile.encoding=UTF-8 - - ${basedir}/target/test-classes - - - **/*Test.java - - - **/TestBean.java - - - - - - org.apache.maven.plugins - maven-jar-plugin - ${maven-jar-plugin.version} - - true - - true - - true - true - true - - - true - io.github.hiwepy.javassist.MyAgent2 - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - ${maven-javadoc-plugin.version} - - ${project.build.sourceEncoding} - ${project.build.sourceEncoding} - ${project.build.sourceEncoding} - - - - attach-javadocs - package - - jar - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - ${maven-nexus-staging-plugin.version} - true - - ossrh - https://oss.sonatype.org/ - false - - 60 - 20 - true - - - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - org.apache.maven.plugins - maven-resources-plugin - - - - org.apache.maven.plugins - maven-surefire-plugin - - - - org.apache.maven.plugins - maven-jar-plugin - - - - org.apache.maven.plugins - maven-source-plugin - - - - org.apache.maven.plugins - maven-install-plugin - - - - org.apache.maven.plugins - maven-deploy-plugin - - - - - - - disable-javadoc-doclint - - [1.8,) - - - -Xdoclint:none - - - - release - - - - - org.apache.maven.plugins - maven-enforcer-plugin - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - org.apache.maven.plugins - maven-resources-plugin - - - - org.apache.maven.plugins - maven-surefire-plugin - - - - org.apache.maven.plugins - maven-jar-plugin - - - - org.apache.maven.plugins - maven-source-plugin - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - - org.apache.maven.plugins - maven-install-plugin - - - - org.apache.maven.plugins - maven-gpg-plugin - - - - org.apache.maven.plugins - maven-deploy-plugin - - - - org.apache.maven.plugins - maven-release-plugin - - - org.sonatype.plugins - nexus-staging-maven-plugin - - - - - - - - UTF-8 - UTF-8 - 1.8 - - 3.9.9 - ${java.version} - ${java.version} - 3.1.0 - 3.5.0 - 3.15.0 - 3.8.0 - 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.12.0 - 1.7.0 - 3.3.1 - 3.5.0 - 3.6.0 - 3.4.0 - 3.5.0 - 3.4.0 - - 1.9.4 - 3.17.0 - 2.17.0 - 4.0.5 - 3.30.2-GA - 1.0.4.RELEASE - 4.13.2 - 5.10.3 - 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 - ${javassist-plus.version} - - - - - - - - - junit - junit - test - - - org.slf4j - slf4j-api - - - org.slf4j - slf4j-simple - test - - - 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 - - - - + + + 4.0.0 + io.github.easy4j + cxf-rt-javassist + 1.0.x.20260630-SNAPSHOT + 通过javassist生成基于cxf的jaxws、jaxrs实现 + ${project.groupId}:${project.artifactId} + https://github.com/easy-4-java/${project.artifactId} + jar + + + + The Apache Software License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + + + + + scm:git:https:github.com/easy-4-java/${project.artifactId}.git + scm:git:https:github.com/easy-4-java/${project.artifactId}.git + https:github.com/easy-4-java/${project.artifactId} + ${project.artifactId} + + + + + wandl + hiwepy@gmail.com + + developer + + +8 + + + + + + 2624322-release-6F6h6R + https://packages.aliyun.com/6927b116e6c3e0425dbdf60d/maven/2624322-release-6f6h6r + + + 2624322-snapshot-3EoOv3 + https://packages.aliyun.com/6927b116e6c3e0425dbdf60d/maven/2624322-snapshot-3eoov3 + + + + + ${project.artifactId} + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + 512M + + + + + org.apache.maven.plugins + maven-deploy-plugin + ${maven-deploy-plugin.version} + + + + org.apache.maven.plugins + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + default-cli + + enforce + + validate + + + + + + + [${maven.version}.0,) + + + + + + [${java.version}.0,) + + + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + + sign-artifacts + verify + + sign + + + + + + + org.apache.maven.plugins + maven-install-plugin + ${maven-install-plugin.version} + + + + org.apache.maven.plugins + maven-resources-plugin + ${maven-resources-plugin.version} + + ${project.build.sourceEncoding} + + + + + org.apache.maven.plugins + maven-release-plugin + ${maven-release-plugin.version} + + v@{project.version} + true + false + release + deploy + + + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + true + + + + attach-sources + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + true + true + + + -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m + -Dfile.encoding=UTF-8 + + ${basedir}/target/test-classes + + + **/*Test.java + + + **/TestBean.java + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + true + + true + + true + true + true + + + true + io.github.hiwepy.javassist.MyAgent2 + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + + + + attach-javadocs + package + + jar + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + ${maven-nexus-staging-plugin.version} + true + + ossrh + https://oss.sonatype.org/ + false + + 60 + 20 + true + + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.apache.maven.plugins + maven-resources-plugin + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + org.apache.maven.plugins + maven-jar-plugin + + + + org.apache.maven.plugins + maven-source-plugin + + + + org.apache.maven.plugins + maven-install-plugin + + + + org.apache.maven.plugins + maven-deploy-plugin + + + + + + + disable-javadoc-doclint + + [1.8,) + + + -Xdoclint:none + + + + release + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.apache.maven.plugins + maven-resources-plugin + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + org.apache.maven.plugins + maven-jar-plugin + + + + org.apache.maven.plugins + maven-source-plugin + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + org.apache.maven.plugins + maven-install-plugin + + + + org.apache.maven.plugins + maven-gpg-plugin + + + + org.apache.maven.plugins + maven-deploy-plugin + + + + org.apache.maven.plugins + maven-release-plugin + + + org.sonatype.plugins + nexus-staging-maven-plugin + + + + + + + + UTF-8 + UTF-8 + 1.8 + + 3.9.9 + ${java.version} + ${java.version} + 3.1.0 + 3.5.0 + 3.15.0 + 3.8.0 + 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.12.0 + 1.7.0 + 3.3.1 + 3.5.0 + 3.6.0 + 3.4.0 + 3.5.0 + 3.4.0 + + 1.9.4 + 3.17.0 + 2.17.0 + 4.0.5 + 3.30.2-GA + 1.0.4.RELEASE + 4.13.2 + 5.10.3 + 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 + ${javassist-plus.version} + + + + + + + + + junit + junit + test + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-simple + test + + + 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 + + + + From 4ac6c4acac76d07208dd8b108d127fad1fe3fb89 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Thu, 6 Aug 2026 01:43:42 +0800 Subject: [PATCH 08/56] =?UTF-8?q?fix:=20javassist-plus=20=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=E7=89=88=E6=9C=AC=E6=94=B9=E4=B8=BA=E5=88=86=E6=94=AF?= =?UTF-8?q?=E7=89=88=E6=9C=AC=201.0.x.20260630-SNAPSHOT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 368c28c..99bcfd6 100644 --- a/pom.xml +++ b/pom.xml @@ -400,7 +400,7 @@ 2.17.0 4.0.5 3.30.2-GA - 1.0.4.RELEASE + 1.0.x.20260630-SNAPSHOT 4.13.2 5.10.3 1.18.46 From 1a82b8c6a8a19a605f3c9670516bc900989e3764 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Thu, 6 Aug 2026 12:08:19 +0800 Subject: [PATCH 09/56] build: refactor pom.xml to dependencyManagement + canonical versions (1.0.x) --- pom.xml | 203 +++++++++++++++----------------------------------------- 1 file changed, 52 insertions(+), 151 deletions(-) diff --git a/pom.xml b/pom.xml index 99bcfd6..c758a4e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,5 @@ - + + 4.0.0 io.github.easy4j @@ -51,7 +50,7 @@ ${project.artifactId} - + org.apache.maven.plugins maven-compiler-plugin @@ -63,13 +62,13 @@ 512M - + org.apache.maven.plugins maven-deploy-plugin ${maven-deploy-plugin.version} - + org.apache.maven.plugins maven-enforcer-plugin @@ -85,13 +84,13 @@ - + You are running an older version of Maven. This application requires at least Maven ${maven.version}. [${maven.version}.0,) - + You are running an older version of Java. This application requires at least JDK ${java.version}. [${java.version}.0,) @@ -100,7 +99,7 @@ - + org.apache.maven.plugins maven-gpg-plugin @@ -115,13 +114,13 @@ - + org.apache.maven.plugins maven-install-plugin ${maven-install-plugin.version} - + org.apache.maven.plugins maven-resources-plugin @@ -130,7 +129,7 @@ ${project.build.sourceEncoding} - + org.apache.maven.plugins maven-release-plugin @@ -143,7 +142,7 @@ deploy - + org.apache.maven.plugins maven-source-plugin @@ -160,19 +159,17 @@ - + org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin.version} - + true true - - + + -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m -Dfile.encoding=UTF-8 @@ -186,7 +183,7 @@ - + org.apache.maven.plugins maven-jar-plugin @@ -207,7 +204,7 @@ - + org.apache.maven.plugins maven-javadoc-plugin @@ -236,7 +233,7 @@ ossrh https://oss.sonatype.org/ false - + 60 20 true @@ -244,49 +241,7 @@ - - - - org.apache.maven.plugins - maven-enforcer-plugin - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - org.apache.maven.plugins - maven-resources-plugin - - - - org.apache.maven.plugins - maven-surefire-plugin - - - - org.apache.maven.plugins - maven-jar-plugin - - - - org.apache.maven.plugins - maven-source-plugin - - - - org.apache.maven.plugins - maven-install-plugin - - - - org.apache.maven.plugins - maven-deploy-plugin - - - + @@ -302,57 +257,57 @@ release - + org.apache.maven.plugins maven-enforcer-plugin - + org.apache.maven.plugins maven-compiler-plugin - + org.apache.maven.plugins maven-resources-plugin - + org.apache.maven.plugins maven-surefire-plugin - + org.apache.maven.plugins maven-jar-plugin - + org.apache.maven.plugins maven-source-plugin - + org.apache.maven.plugins maven-javadoc-plugin - + org.apache.maven.plugins maven-install-plugin - + org.apache.maven.plugins maven-gpg-plugin - + org.apache.maven.plugins maven-deploy-plugin - + org.apache.maven.plugins maven-release-plugin @@ -370,14 +325,14 @@ UTF-8 UTF-8 1.8 - - 3.9.9 + + 3.0 ${java.version} ${java.version} 3.1.0 3.5.0 3.15.0 - 3.8.0 + 3.8.1 3.1.4 3.6.3 3.2.8 @@ -386,18 +341,18 @@ 3.1.4 3.8.0 3.5.0 - 3.12.0 + 3.11.2 1.7.0 3.3.1 3.5.0 3.6.0 3.4.0 - 3.5.0 + 3.5.2 3.4.0 - - 1.9.4 - 3.17.0 - 2.17.0 + + 1.11.0 + 3.20.0 + 2.22.0 4.0.5 3.30.2-GA 1.0.x.20260630-SNAPSHOT @@ -410,16 +365,16 @@ - + - + junit junit ${junit.version} - + org.slf4j slf4j-api @@ -432,106 +387,52 @@ 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 ${javassist-plus.version} - + org.projectlomboklombok${lombok.version}provided - - - - junit - junit - test - - - org.slf4j - slf4j-api - - - org.slf4j - slf4j-simple - test - - - 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 - - - - - + junitjunittestorg.slf4jslf4j-apicommons-iocommons-iotestcommons-beanutilscommons-beanutilstestorg.apache.commonscommons-lang3org.apache.cxfcxf-rt-frontend-jaxwsprovidedorg.apache.cxfcxf-rt-frontend-jaxrsprovidedorg.javassistjavassistio.github.easy4jjavassist-plusorg.projectlomboklombokprovided \ No newline at end of file From c8798958b2af69d1a57613c270bc9eabbbcd6434 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Thu, 6 Aug 2026 14:40:12 +0800 Subject: [PATCH 10/56] =?UTF-8?q?chore:=20developers=20=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E4=B8=BA=20Loong=20Wan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index c758a4e..8d8b85d 100644 --- a/pom.xml +++ b/pom.xml @@ -24,16 +24,17 @@ ${project.artifactId} - - - wandl - hiwepy@gmail.com - - developer - - +8 - - + + + Loong Wan + hiwepy@gmail.com + https://github.com/loong10k + + developer + + +8 + + From 2a62dbf571b8aa70136776cee99e80851e508931 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Thu, 6 Aug 2026 14:41:23 +0800 Subject: [PATCH 11/56] =?UTF-8?q?chore:=20developers=20=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E4=B8=BA=20Loong=20Wan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8d8b85d..56a0b01 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ ${project.artifactId} - + Loong Wan hiwepy@gmail.com From 3588ab1d97299c5eeea50c955f138cf1a54781c4 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Thu, 6 Aug 2026 14:59:05 +0800 Subject: [PATCH 12/56] =?UTF-8?q?chore:=20=E6=B3=A8=E9=87=8A=E4=BD=9C?= =?UTF-8?q?=E8=80=85=E4=BF=A1=E6=81=AF=E7=BB=9F=E4=B8=80=E4=B8=BA=20@Loong?= =?UTF-8?q?=20Wan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/apache/cxf/endpoint/EndpointApi.java | 72 +- .../cxf/endpoint/annotation/WebBound.java | 70 +- .../cxf/endpoint/annotation/WebEndpoint.java | 90 +- .../endpoint/jaxrs/definition/RestBound.java | 118 +-- .../endpoint/jaxrs/definition/RestMethod.java | 232 ++--- .../endpoint/jaxrs/definition/RestParam.java | 240 ++--- .../jaxrs/definition/RestProduce.java | 112 +-- .../jaxws/JaxwsEndpointApiCtClassBuilder.java | 746 +++++++-------- .../endpoint/jaxws/definition/SoapBound.java | 118 +-- .../endpoint/jaxws/definition/SoapMethod.java | 154 ++-- .../endpoint/jaxws/definition/SoapParam.java | 274 +++--- .../endpoint/jaxws/definition/SoapResult.java | 206 ++--- .../jaxws/definition/SoapService.java | 244 ++--- .../endpoint/utils/JaxrsEndpointApiUtils.java | 792 ++++++++-------- .../endpoint/utils/JaxwsEndpointApiUtils.java | 852 +++++++++--------- .../apache/cxf/endpoint/jaxrs/Customer.java | 96 +- .../cxf/endpoint/jaxrs/EndpointApiSample.java | 174 ++-- .../cxf/endpoint/jaxws/EndpointApiSample.java | 136 +-- 18 files changed, 2363 insertions(+), 2363 deletions(-) diff --git a/src/main/java/org/apache/cxf/endpoint/EndpointApi.java b/src/main/java/org/apache/cxf/endpoint/EndpointApi.java index 02acee9..ace0f86 100644 --- a/src/main/java/org/apache/cxf/endpoint/EndpointApi.java +++ b/src/main/java/org/apache/cxf/endpoint/EndpointApi.java @@ -1,36 +1,36 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint; - -import java.lang.reflect.InvocationHandler; - -public abstract class EndpointApi { - - private InvocationHandler handler; - - public EndpointApi() { - } - - public EndpointApi(InvocationHandler handler) { - this.handler = handler; - } - - public InvocationHandler getHandler() { - return handler; - } - - -} +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint; + +import java.lang.reflect.InvocationHandler; + +public abstract class EndpointApi { + + private InvocationHandler handler; + + public EndpointApi() { + } + + public EndpointApi(InvocationHandler handler) { + this.handler = handler; + } + + 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 aa5d4a0..de1bfc6 100644 --- a/src/main/java/org/apache/cxf/endpoint/annotation/WebBound.java +++ b/src/main/java/org/apache/cxf/endpoint/annotation/WebBound.java @@ -1,35 +1,35 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ ElementType.TYPE, ElementType.METHOD }) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Inherited -public @interface WebBound { - - String uid() default ""; - - String json() default "{}"; - -} +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface WebBound { + + String uid() default ""; + + 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 7dc024d..4377979 100644 --- a/src/main/java/org/apache/cxf/endpoint/annotation/WebEndpoint.java +++ b/src/main/java/org/apache/cxf/endpoint/annotation/WebEndpoint.java @@ -1,45 +1,45 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Inherited -public @interface WebEndpoint { - - String addr(); - - String[] inInterceptors() default {""}; - - String[] outInterceptors() default {""}; - - String[] inFaults() default {""}; - - String[] outFaults() default {""}; - - String[] features() default {""}; - - String[] handlers() default {""}; - -} +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface WebEndpoint { + + String addr(); + + String[] inInterceptors() default {""}; + + String[] outInterceptors() default {""}; + + String[] inFaults() default {""}; + + String[] outFaults() default {""}; + + String[] features() default {""}; + + String[] handlers() default {""}; + +} 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 ae140e3..eabc10a 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 @@ -1,59 +1,59 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint.jaxrs.definition; - -/** - * 数据绑定对象,用于通过@WebBound注解实现与方法相关数据的绑定 - */ -public class RestBound { - - public RestBound(String uid) { - this.uid = uid; - } - - public RestBound(String uid, String json) { - this.uid = uid; - this.json = json; - } - - /** - * 1、uid:某个数据主键,可用于传输主键ID在实现对象中进行数据提取 - */ - private String uid = ""; - - /** - * 2、json:绑定的数据对象JSON格式,为了方便,这里采用json进行数据传输 - */ - private String json = ""; - - public String getUid() { - return uid; - } - - public void setUid(String uid) { - this.uid = uid; - } - - public String getJson() { - return json; - } - - public void setJson(String json) { - this.json = json; - } - -} - +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint.jaxrs.definition; + +/** + * 数据绑定对象,用于通过@WebBound注解实现与方法相关数据的绑定 + */ +public class RestBound { + + public RestBound(String uid) { + this.uid = uid; + } + + public RestBound(String uid, String json) { + this.uid = uid; + this.json = json; + } + + /** + * 1、uid:某个数据主键,可用于传输主键ID在实现对象中进行数据提取 + */ + private String uid = ""; + + /** + * 2、json:绑定的数据对象JSON格式,为了方便,这里采用json进行数据传输 + */ + private String json = ""; + + public String getUid() { + return uid; + } + + public void setUid(String uid) { + this.uid = uid; + } + + public String getJson() { + return json; + } + + 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 cc3b4d1..f58b813 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 @@ -1,116 +1,116 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint.jaxrs.definition; - -import jakarta.ws.rs.DELETE; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.HEAD; -import jakarta.ws.rs.OPTIONS; -import jakarta.ws.rs.PATCH; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.PUT; - -public class RestMethod { - - /** - * Java 方法的名称 - */ - private final String name; - - /** - * Associates the name of a HTTP method with an annotation. - * @see GET - * @see POST - * @see PUT - * @see DELETE - * @see PATCH - * @see HEAD - * @see OPTIONS - */ - private final HttpMethodEnum method; - - /** - * Identifies the URI path that a resource class or class method will serve - * requests for. - * - * @see jakarta.ws.rs.Path - */ - 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: - * - *
-	 * { "image/jpeg, image/gif ", " image/png" }
-	 * 
- * - * Use of the comma-separated form allows definition of a common string constant - * for use on multiple targets. - */ - private String[] mediaTypes = new String[] { "*/*" }; - - /** - * Defines the media types that the methods of a resource class or - * {@link jakarta.ws.rs.ext.MessageBodyReader} can accept. - * - * @see jakarta.ws.rs.Consumes - */ - private String[] consumes; - - public RestMethod(HttpMethodEnum method, String name, String path) { - this.method = method; - this.name = name; - this.path = path; - } - - public RestMethod(HttpMethodEnum method, String name, String path, String... consumes) { - this.method = method; - this.name = name; - this.path = path; - this.consumes = consumes; - } - - public String[] getConsumes() { - return consumes; - } - - public String[] getMediaTypes() { - return mediaTypes; - } - - public void setMediaTypes(String[] mediaTypes) { - this.mediaTypes = mediaTypes; - } - - public void setConsumes(String[] consumes) { - this.consumes = consumes; - } - - public String getName() { - return name; - } - - public HttpMethodEnum getMethod() { - return method; - } - - public String getPath() { - return path; - } - -} +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint.jaxrs.definition; + +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HEAD; +import jakarta.ws.rs.OPTIONS; +import jakarta.ws.rs.PATCH; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; + +public class RestMethod { + + /** + * Java 方法的名称 + */ + private final String name; + + /** + * Associates the name of a HTTP method with an annotation. + * @see GET + * @see POST + * @see PUT + * @see DELETE + * @see PATCH + * @see HEAD + * @see OPTIONS + */ + private final HttpMethodEnum method; + + /** + * Identifies the URI path that a resource class or class method will serve + * requests for. + * + * @see jakarta.ws.rs.Path + */ + 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: + * + *
+	 * { "image/jpeg, image/gif ", " image/png" }
+	 * 
+ * + * Use of the comma-separated form allows definition of a common string constant + * for use on multiple targets. + */ + private String[] mediaTypes = new String[] { "*/*" }; + + /** + * Defines the media types that the methods of a resource class or + * {@link jakarta.ws.rs.ext.MessageBodyReader} can accept. + * + * @see jakarta.ws.rs.Consumes + */ + private String[] consumes; + + public RestMethod(HttpMethodEnum method, String name, String path) { + this.method = method; + this.name = name; + this.path = path; + } + + public RestMethod(HttpMethodEnum method, String name, String path, String... consumes) { + this.method = method; + this.name = name; + this.path = path; + this.consumes = consumes; + } + + public String[] getConsumes() { + return consumes; + } + + public String[] getMediaTypes() { + return mediaTypes; + } + + public void setMediaTypes(String[] mediaTypes) { + this.mediaTypes = mediaTypes; + } + + public void setConsumes(String[] consumes) { + this.consumes = consumes; + } + + public String getName() { + return name; + } + + public HttpMethodEnum getMethod() { + return method; + } + + 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 4c865a7..b1ccabf 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 @@ -1,120 +1,120 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint.jaxrs.definition; - -/** - */ -public class RestParam { - - /** - * 参数对象类型 - */ - private Class type; - - /** - * name :参数的名称 - * @see jakarta.ws.rs.BeanParam - * @see jakarta.ws.rs.PathParam - * @see jakarta.ws.rs.QueryParam - * @see jakarta.ws.rs.MatrixParam - * @see jakarta.ws.rs.CookieParam - * @see jakarta.ws.rs.FormParam - * @see jakarta.ws.rs.HeaderParam - */ - private String name; - - /** - * from :参数来源 - * @see jakarta.ws.rs.BeanParam - * @see jakarta.ws.rs.PathParam - * @see jakarta.ws.rs.QueryParam - * @see jakarta.ws.rs.MatrixParam - * @see jakarta.ws.rs.CookieParam - * @see jakarta.ws.rs.FormParam - * @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. - * @see jakarta.ws.rs.DefaultValue - */ - private String def; - - public RestParam(Class type, String name) { - this.type = type; - this.name = name; - } - - public RestParam(Class type, String name, HttpParamEnum from) { - this.type = type; - this.name = name; - } - - public RestParam(Class type, String name, HttpParamEnum from, String def ) { - this.type = type; - this.name = name; - this.name = name; - this.def = def; - } - - public RestParam(Class type, String name, String def ) { - this.type = type; - this.name = name; - this.def = def; - } - - public Class getType() { - return type; - } - - public void setType(Class type) { - this.type = type; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public HttpParamEnum getFrom() { - return from; - } - - public void setFrom(HttpParamEnum from) { - this.from = from; - } - - public String getDef() { - return def; - } - - public void setDef(String def) { - this.def = def; - } - -} +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint.jaxrs.definition; + +/** + */ +public class RestParam { + + /** + * 参数对象类型 + */ + private Class type; + + /** + * name :参数的名称 + * @see jakarta.ws.rs.BeanParam + * @see jakarta.ws.rs.PathParam + * @see jakarta.ws.rs.QueryParam + * @see jakarta.ws.rs.MatrixParam + * @see jakarta.ws.rs.CookieParam + * @see jakarta.ws.rs.FormParam + * @see jakarta.ws.rs.HeaderParam + */ + private String name; + + /** + * from :参数来源 + * @see jakarta.ws.rs.BeanParam + * @see jakarta.ws.rs.PathParam + * @see jakarta.ws.rs.QueryParam + * @see jakarta.ws.rs.MatrixParam + * @see jakarta.ws.rs.CookieParam + * @see jakarta.ws.rs.FormParam + * @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. + * @see jakarta.ws.rs.DefaultValue + */ + private String def; + + public RestParam(Class type, String name) { + this.type = type; + this.name = name; + } + + public RestParam(Class type, String name, HttpParamEnum from) { + this.type = type; + this.name = name; + } + + public RestParam(Class type, String name, HttpParamEnum from, String def ) { + this.type = type; + this.name = name; + this.name = name; + this.def = def; + } + + public RestParam(Class type, String name, String def ) { + this.type = type; + this.name = name; + this.def = def; + } + + public Class getType() { + return type; + } + + public void setType(Class type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public HttpParamEnum getFrom() { + return from; + } + + public void setFrom(HttpParamEnum from) { + this.from = from; + } + + public String getDef() { + return def; + } + + 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 dd53125..3709813 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 @@ -1,56 +1,56 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint.jaxrs.definition; - -public class RestProduce { - - /** - * Defines a URI template for the resource class or method, must not include - * matrix parameters. - */ - 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: - * - *
-	 * { "image/jpeg, image/gif ", " image/png" }
-	 * 
- * - * Use of the comma-separated form allows definition of a common string constant - * for use on multiple targets. - */ - private String[] mediaTypes = new String[] { "*/*" }; - - public RestProduce(String path, String... mediaTypes) { - this.path = path; - this.mediaTypes = mediaTypes; - } - - public String[] getMediaTypes() { - return mediaTypes; - } - - public void setMediaTypes(String[] mediaTypes) { - this.mediaTypes = mediaTypes; - } - - public String getPath() { - return path; - } - -} +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint.jaxrs.definition; + +public class RestProduce { + + /** + * Defines a URI template for the resource class or method, must not include + * matrix parameters. + */ + 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: + * + *
+	 * { "image/jpeg, image/gif ", " image/png" }
+	 * 
+ * + * Use of the comma-separated form allows definition of a common string constant + * for use on multiple targets. + */ + private String[] mediaTypes = new String[] { "*/*" }; + + public RestProduce(String path, String... mediaTypes) { + this.path = path; + this.mediaTypes = mediaTypes; + } + + public String[] getMediaTypes() { + return mediaTypes; + } + + public void setMediaTypes(String[] mediaTypes) { + this.mediaTypes = mediaTypes; + } + + 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 4568224..453d29e 100644 --- a/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilder.java +++ b/src/main/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilder.java @@ -1,374 +1,374 @@ -package org.apache.cxf.endpoint.jaxws; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; - -import jakarta.xml.ws.Service; -import jakarta.xml.ws.soap.AddressingFeature.Responses; - -import org.apache.commons.lang3.builder.Builder; -import org.apache.cxf.endpoint.EndpointApi; -import org.apache.cxf.endpoint.jaxws.definition.SoapBound; -import org.apache.cxf.endpoint.jaxws.definition.SoapMethod; -import org.apache.cxf.endpoint.jaxws.definition.SoapParam; -import org.apache.cxf.endpoint.jaxws.definition.SoapResult; -import org.apache.cxf.endpoint.jaxws.definition.SoapService; -import org.apache.cxf.endpoint.utils.JaxwsEndpointApiUtils; - -import io.github.easy4j.javassist.bytecode.CtFieldBuilder; -import io.github.easy4j.javassist.utils.ClassPoolFactory; -import io.github.easy4j.javassist.utils.JavassistUtils; - -import javassist.CannotCompileException; -import javassist.ClassPool; -import javassist.CtClass; -import javassist.CtField; -import javassist.CtMethod; -import javassist.CtNewConstructor; -import javassist.NotFoundException; -import javassist.bytecode.ClassFile; -import javassist.bytecode.ConstPool; -import javassist.bytecode.annotation.Annotation; - -/** - * - * 动态构建ws接口 - *

http://www.cnblogs.com/sunfie/p/5154246.html

- *

http://blog.csdn.net/youaremoon/article/details/50766972

- *

https://my.oschina.net/GameKing/blog/794580

- *

http://wsmajunfeng.iteye.com/blog/1912983

- */ -public class JaxwsEndpointApiCtClassBuilder implements Builder { - - // 构建动态类 - protected ClassPool pool = null; - protected CtClass declaring = null; - protected ClassFile classFile = null; - //private Loader loader = new Loader(pool); - - public JaxwsEndpointApiCtClassBuilder(final String classname) throws CannotCompileException, NotFoundException { - this(ClassPoolFactory.getDefaultPool(), classname); - } - - public JaxwsEndpointApiCtClassBuilder(final ClassPool pool, final String classname) throws CannotCompileException, NotFoundException { - - this.pool = pool; - this.declaring = JaxwsEndpointApiUtils.makeClass(pool, classname); - this.declaring.defrost(); - - /* 获得 JaxwsHandler 类作为动态类的父类 */ - CtClass superclass = pool.get(EndpointApi.class.getName()); - declaring.setSuperclass(superclass); - - // 默认添加无参构造器 - 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 - */ - public JaxwsEndpointApiCtClassBuilder webService(final String name, final String targetNamespace) { - return this.webService(name, targetNamespace, null, null, null, null); - } - - 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 - */ - 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 - */ - public JaxwsEndpointApiCtClassBuilder webService(final SoapService service) { - - ConstPool constPool = this.classFile.getConstPool(); - Annotation annot = JaxwsEndpointApiUtils.annotWebService(constPool, service); - 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 - */ - public JaxwsEndpointApiCtClassBuilder webServiceProvider(String wsdlLocation, String serviceName, - String targetNamespace, String portName) { - - ConstPool constPool = this.classFile.getConstPool(); - Annotation annot = JaxwsEndpointApiUtils.annotWebServiceProvider(constPool, wsdlLocation, serviceName, - targetNamespace, portName); - JavassistUtils.addClassAnnotation(declaring, annot); - - return this; - } - - /** - * 添加类注解 @Addressing - * @param enabled : The value of enabled - * @param required : The value of required - * @param responses : The {@link Responses} - * @return {@link JaxwsEndpointApiCtClassBuilder} instance - */ - public JaxwsEndpointApiCtClassBuilder 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; - } - - /** - * 添加类注解 @ServiceMode - * @param mode : The mode of {@link Service} - * @return {@link JaxwsEndpointApiCtClassBuilder} instance - */ - public JaxwsEndpointApiCtClassBuilder serviceMode(final Service.Mode mode) { - - ConstPool constPool = this.classFile.getConstPool(); - Annotation annot = JaxwsEndpointApiUtils.annotServiceMode(constPool, mode); - JavassistUtils.addClassAnnotation(declaring, annot); - - return this; - } - - /** - * 通过给动态类增加 @WebBound注解实现,数据的绑定 - * @param uid : The value of uid - * @param json : The value of json - * @return {@link JaxwsEndpointApiCtClassBuilder} instance - */ - 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 - */ - public JaxwsEndpointApiCtClassBuilder bind(final SoapBound bound) { - - ConstPool constPool = this.classFile.getConstPool(); - Annotation annot = JaxwsEndpointApiUtils.annotWebBound(constPool, bound); - JavassistUtils.addClassAnnotation(declaring, annot); - - return this; - } - - /** - * Compiles the given source code and creates a field. - * Examples of the source code are: - * - *
-     * "public String name;"
-     * "public int k = 3;"
- * - *

Note that the source code ends with ';' - * (semicolon). - * - * @param src the source text. - * @return {@link JaxwsEndpointApiCtClassBuilder} instance - * @throws CannotCompileException if can't compile - */ - public JaxwsEndpointApiCtClassBuilder makeField(final String src) throws CannotCompileException { - //创建属性 - declaring.addField(CtField.make(src, declaring)); - return this; - } - - 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; - } - - public JaxwsEndpointApiCtClassBuilder removeField(final String fieldName) throws NotFoundException { - - // 检查字段是否已经定义 - if(!JavassistUtils.hasField(declaring, fieldName)) { - return this; - } - - declaring.removeField(declaring.getDeclaredField(fieldName)); - - return this; - } - - - /** - * Compiles the given source code and creates a method. - * The source code must include not only the method body - * but the whole declaration, for example, - * - *

"public Object id(Object obj) { return obj; }"
- * - * @param src the source text. - * @return {@link JaxwsEndpointApiCtClassBuilder} instance - * @throws CannotCompileException if can't compile - */ - public JaxwsEndpointApiCtClassBuilder makeMethod(final String src) throws CannotCompileException { - //创建方法 - declaring.addMethod(CtMethod.make(src, declaring)); - return this; - } - - /** - * - * 根据参数构造一个新的方法 - * @param methodName :方法名称 - * @param params : 参数信息 - * @return {@link JaxwsEndpointApiCtClassBuilder} instance - * @throws CannotCompileException if can't compile - * @throws NotFoundException if not found - */ - public JaxwsEndpointApiCtClassBuilder newMethod(final String methodName, SoapParam... params) throws CannotCompileException, NotFoundException { - return this.newMethod(null, new SoapMethod(methodName), null, params); - } - - /** - * - * @author : hiwepy - * @param methodName :方法名称 - * @param bound :方法绑定数据信息 - * @param params : 参数信息 - * @return {@link JaxwsEndpointApiCtClassBuilder} instance - * @throws CannotCompileException if can't compile - * @throws NotFoundException if not found - */ - 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 - */ - public JaxwsEndpointApiCtClassBuilder newMethod(final SoapResult result, final SoapMethod method, final SoapBound bound, SoapParam... params) throws CannotCompileException, NotFoundException { - - ConstPool constPool = this.classFile.getConstPool(); - - CtClass returnType = result != null ? pool.get(result.getRtClass().getName()) : CtClass.voidType; - CtMethod ctMethod = null; - // 方法参数 - CtClass[] parameters = JaxwsEndpointApiUtils.makeParams(pool, params); - // 有参方法 - if(parameters != null && parameters.length > 0) { - ctMethod = new CtMethod(returnType, method.getOperationName(), parameters, declaring); - } - // 无参方法 - else { - ctMethod = new CtMethod(returnType, method.getOperationName() , null, declaring); - } - // 设置方法体 - JaxwsEndpointApiUtils.methodBody(ctMethod, method); - // 设置方法异常捕获逻辑 - JaxwsEndpointApiUtils.methodCatch(pool, ctMethod); - // 为方法添加 @WebMethod、 @WebResult、@WebBound、@WebParam 注解 - JaxwsEndpointApiUtils.methodAnnotations(ctMethod, constPool, result, method, bound, params); - - //新增方法 - declaring.addMethod(ctMethod); - - return this; - } - - public JaxwsEndpointApiCtClassBuilder removeMethod(final String methodName, SoapParam... params) throws NotFoundException { - - // 有参方法 - if(params != null && params.length > 0) { - - // 方法参数 - CtClass[] parameters = JaxwsEndpointApiUtils.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; - } - - @Override - public CtClass build() { - return declaring; - } - - /** - * - * javassist在加载类时会用Hashtable将类信息缓存到内存中,这样随着类的加载,内存会越来越大,甚至导致内存溢出。 - * 如果应用中要加载的类比较多,建议在使用完CtClass之后删除缓存 - * @return The Class - * @throws CannotCompileException if can't compile - */ - public Class toClass() throws CannotCompileException { - try { - // 通过类加载器加载该CtClass - return declaring.toClass(); - } finally { - // 将该class从ClassPool中删除 - declaring.detach(); - } - } - - public Object toInstance(final InvocationHandler handler) throws CannotCompileException, NotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { - try { - // 设置InvocationHandler参数构造器 - declaring.addConstructor(JaxwsEndpointApiUtils.makeConstructor(pool, declaring)); - // 通过类加载器加载该CtClass,并通过构造器初始化对象 - return declaring.toClass().getConstructor(InvocationHandler.class).newInstance(handler); - } finally { - // 将该class从ClassPool中删除 - declaring.detach(); - } - } - +package org.apache.cxf.endpoint.jaxws; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; + +import jakarta.xml.ws.Service; +import jakarta.xml.ws.soap.AddressingFeature.Responses; + +import org.apache.commons.lang3.builder.Builder; +import org.apache.cxf.endpoint.EndpointApi; +import org.apache.cxf.endpoint.jaxws.definition.SoapBound; +import org.apache.cxf.endpoint.jaxws.definition.SoapMethod; +import org.apache.cxf.endpoint.jaxws.definition.SoapParam; +import org.apache.cxf.endpoint.jaxws.definition.SoapResult; +import org.apache.cxf.endpoint.jaxws.definition.SoapService; +import org.apache.cxf.endpoint.utils.JaxwsEndpointApiUtils; + +import io.github.easy4j.javassist.bytecode.CtFieldBuilder; +import io.github.easy4j.javassist.utils.ClassPoolFactory; +import io.github.easy4j.javassist.utils.JavassistUtils; + +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtField; +import javassist.CtMethod; +import javassist.CtNewConstructor; +import javassist.NotFoundException; +import javassist.bytecode.ClassFile; +import javassist.bytecode.ConstPool; +import javassist.bytecode.annotation.Annotation; + +/** + * + * 动态构建ws接口 + *

http://www.cnblogs.com/sunfie/p/5154246.html

+ *

http://blog.csdn.net/youaremoon/article/details/50766972

+ *

https://my.oschina.net/GameKing/blog/794580

+ *

http://wsmajunfeng.iteye.com/blog/1912983

+ */ +public class JaxwsEndpointApiCtClassBuilder implements Builder { + + // 构建动态类 + protected ClassPool pool = null; + protected CtClass declaring = null; + protected ClassFile classFile = null; + //private Loader loader = new Loader(pool); + + public JaxwsEndpointApiCtClassBuilder(final String classname) throws CannotCompileException, NotFoundException { + this(ClassPoolFactory.getDefaultPool(), classname); + } + + public JaxwsEndpointApiCtClassBuilder(final ClassPool pool, final String classname) throws CannotCompileException, NotFoundException { + + this.pool = pool; + this.declaring = JaxwsEndpointApiUtils.makeClass(pool, classname); + this.declaring.defrost(); + + /* 获得 JaxwsHandler 类作为动态类的父类 */ + CtClass superclass = pool.get(EndpointApi.class.getName()); + declaring.setSuperclass(superclass); + + // 默认添加无参构造器 + 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 + */ + public JaxwsEndpointApiCtClassBuilder webService(final String name, final String targetNamespace) { + return this.webService(name, targetNamespace, null, null, null, null); + } + + 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 + */ + 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 + */ + public JaxwsEndpointApiCtClassBuilder webService(final SoapService service) { + + ConstPool constPool = this.classFile.getConstPool(); + Annotation annot = JaxwsEndpointApiUtils.annotWebService(constPool, service); + 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 + */ + public JaxwsEndpointApiCtClassBuilder webServiceProvider(String wsdlLocation, String serviceName, + String targetNamespace, String portName) { + + ConstPool constPool = this.classFile.getConstPool(); + Annotation annot = JaxwsEndpointApiUtils.annotWebServiceProvider(constPool, wsdlLocation, serviceName, + targetNamespace, portName); + JavassistUtils.addClassAnnotation(declaring, annot); + + return this; + } + + /** + * 添加类注解 @Addressing + * @param enabled : The value of enabled + * @param required : The value of required + * @param responses : The {@link Responses} + * @return {@link JaxwsEndpointApiCtClassBuilder} instance + */ + public JaxwsEndpointApiCtClassBuilder 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; + } + + /** + * 添加类注解 @ServiceMode + * @param mode : The mode of {@link Service} + * @return {@link JaxwsEndpointApiCtClassBuilder} instance + */ + public JaxwsEndpointApiCtClassBuilder serviceMode(final Service.Mode mode) { + + ConstPool constPool = this.classFile.getConstPool(); + Annotation annot = JaxwsEndpointApiUtils.annotServiceMode(constPool, mode); + JavassistUtils.addClassAnnotation(declaring, annot); + + return this; + } + + /** + * 通过给动态类增加 @WebBound注解实现,数据的绑定 + * @param uid : The value of uid + * @param json : The value of json + * @return {@link JaxwsEndpointApiCtClassBuilder} instance + */ + 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 + */ + public JaxwsEndpointApiCtClassBuilder bind(final SoapBound bound) { + + ConstPool constPool = this.classFile.getConstPool(); + Annotation annot = JaxwsEndpointApiUtils.annotWebBound(constPool, bound); + JavassistUtils.addClassAnnotation(declaring, annot); + + return this; + } + + /** + * Compiles the given source code and creates a field. + * Examples of the source code are: + * + *
+     * "public String name;"
+     * "public int k = 3;"
+ * + *

Note that the source code ends with ';' + * (semicolon). + * + * @param src the source text. + * @return {@link JaxwsEndpointApiCtClassBuilder} instance + * @throws CannotCompileException if can't compile + */ + public JaxwsEndpointApiCtClassBuilder makeField(final String src) throws CannotCompileException { + //创建属性 + declaring.addField(CtField.make(src, declaring)); + return this; + } + + 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; + } + + public JaxwsEndpointApiCtClassBuilder removeField(final String fieldName) throws NotFoundException { + + // 检查字段是否已经定义 + if(!JavassistUtils.hasField(declaring, fieldName)) { + return this; + } + + declaring.removeField(declaring.getDeclaredField(fieldName)); + + return this; + } + + + /** + * Compiles the given source code and creates a method. + * The source code must include not only the method body + * but the whole declaration, for example, + * + *

"public Object id(Object obj) { return obj; }"
+ * + * @param src the source text. + * @return {@link JaxwsEndpointApiCtClassBuilder} instance + * @throws CannotCompileException if can't compile + */ + public JaxwsEndpointApiCtClassBuilder makeMethod(final String src) throws CannotCompileException { + //创建方法 + declaring.addMethod(CtMethod.make(src, declaring)); + return this; + } + + /** + * + * 根据参数构造一个新的方法 + * @param methodName :方法名称 + * @param params : 参数信息 + * @return {@link JaxwsEndpointApiCtClassBuilder} instance + * @throws CannotCompileException if can't compile + * @throws NotFoundException if not found + */ + 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 + */ + 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 + */ + public JaxwsEndpointApiCtClassBuilder newMethod(final SoapResult result, final SoapMethod method, final SoapBound bound, SoapParam... params) throws CannotCompileException, NotFoundException { + + ConstPool constPool = this.classFile.getConstPool(); + + CtClass returnType = result != null ? pool.get(result.getRtClass().getName()) : CtClass.voidType; + CtMethod ctMethod = null; + // 方法参数 + CtClass[] parameters = JaxwsEndpointApiUtils.makeParams(pool, params); + // 有参方法 + if(parameters != null && parameters.length > 0) { + ctMethod = new CtMethod(returnType, method.getOperationName(), parameters, declaring); + } + // 无参方法 + else { + ctMethod = new CtMethod(returnType, method.getOperationName() , null, declaring); + } + // 设置方法体 + JaxwsEndpointApiUtils.methodBody(ctMethod, method); + // 设置方法异常捕获逻辑 + JaxwsEndpointApiUtils.methodCatch(pool, ctMethod); + // 为方法添加 @WebMethod、 @WebResult、@WebBound、@WebParam 注解 + JaxwsEndpointApiUtils.methodAnnotations(ctMethod, constPool, result, method, bound, params); + + //新增方法 + declaring.addMethod(ctMethod); + + return this; + } + + public JaxwsEndpointApiCtClassBuilder removeMethod(final String methodName, SoapParam... params) throws NotFoundException { + + // 有参方法 + if(params != null && params.length > 0) { + + // 方法参数 + CtClass[] parameters = JaxwsEndpointApiUtils.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; + } + + @Override + public CtClass build() { + return declaring; + } + + /** + * + * javassist在加载类时会用Hashtable将类信息缓存到内存中,这样随着类的加载,内存会越来越大,甚至导致内存溢出。 + * 如果应用中要加载的类比较多,建议在使用完CtClass之后删除缓存 + * @return The Class + * @throws CannotCompileException if can't compile + */ + public Class toClass() throws CannotCompileException { + try { + // 通过类加载器加载该CtClass + return declaring.toClass(); + } finally { + // 将该class从ClassPool中删除 + declaring.detach(); + } + } + + public Object toInstance(final InvocationHandler handler) throws CannotCompileException, NotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { + try { + // 设置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/definition/SoapBound.java b/src/main/java/org/apache/cxf/endpoint/jaxws/definition/SoapBound.java index 2cecb5e..ef236b6 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 @@ -1,59 +1,59 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint.jaxws.definition; - -/** - * 数据绑定对象,用于通过@WebBound注解实现与方法相关数据的绑定 - */ -public class SoapBound { - - public SoapBound(String uid) { - this.uid = uid; - } - - public SoapBound(String uid, String json) { - this.uid = uid; - this.json = json; - } - - /** - * 1、uid:某个数据主键,可用于传输主键ID在实现对象中进行数据提取 - */ - private String uid = ""; - - /** - * 2、json:绑定的数据对象JSON格式,为了方便,这里采用json进行数据传输 - */ - private String json = ""; - - public String getUid() { - return uid; - } - - public void setUid(String uid) { - this.uid = uid; - } - - public String getJson() { - return json; - } - - public void setJson(String json) { - this.json = json; - } - -} - +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint.jaxws.definition; + +/** + * 数据绑定对象,用于通过@WebBound注解实现与方法相关数据的绑定 + */ +public class SoapBound { + + public SoapBound(String uid) { + this.uid = uid; + } + + public SoapBound(String uid, String json) { + this.uid = uid; + this.json = json; + } + + /** + * 1、uid:某个数据主键,可用于传输主键ID在实现对象中进行数据提取 + */ + private String uid = ""; + + /** + * 2、json:绑定的数据对象JSON格式,为了方便,这里采用json进行数据传输 + */ + private String json = ""; + + public String getUid() { + return uid; + } + + public void setUid(String uid) { + this.uid = uid; + } + + public String getJson() { + return json; + } + + 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 7e26792..be9112a 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 @@ -1,77 +1,77 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint.jaxws.definition; - -/** - * 注释表示作为一项 Web Service 操作的方法,将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于 JavaBeans 端点的服务器端点实现类。 - * 要点: 仅支持在使用 @WebService 注释来注释的类上使用 @WebMethod 注释 - * https://www.cnblogs.com/zhao-shan/p/5515174.html - */ -public class SoapMethod { - - public SoapMethod() { - } - - public SoapMethod(String operationName) { - this.operationName = operationName; - } - - public SoapMethod(String operationName, String action, boolean exclude) { - this.operationName = operationName; - this.action = action; - this.exclude = exclude; - } - - /** - * 1、operationName:指定与此方法相匹配的wsdl:operation 的名称。缺省值为 Java 方法的名称。(字符串) - */ - private String operationName = ""; - - /** - * 2、action:定义此操作的行为。对于 SOAP 绑定,此值将确定 SOAPAction 头的值。缺省值为 Java 方法的名称。(字符串) - */ - private String action = ""; - - /** - * 3、exclude:指定是否从 Web Service 中排除某一方法。缺省值为 false。(布尔值) - */ - private boolean exclude = false; - - public String getOperationName() { - return operationName; - } - - public void setOperationName(String operationName) { - this.operationName = operationName; - } - - public String getAction() { - return action; - } - - public void setAction(String action) { - this.action = action; - } - - public boolean isExclude() { - return exclude; - } - - public void setExclude(boolean exclude) { - this.exclude = exclude; - } - -} +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint.jaxws.definition; + +/** + * 注释表示作为一项 Web Service 操作的方法,将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于 JavaBeans 端点的服务器端点实现类。 + * 要点: 仅支持在使用 @WebService 注释来注释的类上使用 @WebMethod 注释 + * https://www.cnblogs.com/zhao-shan/p/5515174.html + */ +public class SoapMethod { + + public SoapMethod() { + } + + public SoapMethod(String operationName) { + this.operationName = operationName; + } + + public SoapMethod(String operationName, String action, boolean exclude) { + this.operationName = operationName; + this.action = action; + this.exclude = exclude; + } + + /** + * 1、operationName:指定与此方法相匹配的wsdl:operation 的名称。缺省值为 Java 方法的名称。(字符串) + */ + private String operationName = ""; + + /** + * 2、action:定义此操作的行为。对于 SOAP 绑定,此值将确定 SOAPAction 头的值。缺省值为 Java 方法的名称。(字符串) + */ + private String action = ""; + + /** + * 3、exclude:指定是否从 Web Service 中排除某一方法。缺省值为 false。(布尔值) + */ + private boolean exclude = false; + + public String getOperationName() { + return operationName; + } + + public void setOperationName(String operationName) { + this.operationName = operationName; + } + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public boolean isExclude() { + return exclude; + } + + 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 ec2f9b9..643b40d 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 @@ -1,137 +1,137 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint.jaxws.definition; - -import jakarta.jws.WebParam.Mode; - -/** - * 注释用于定制从单个参数至 Web Service 消息部件和 XML 元素的映射。将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于 JavaBeans 端点的服务器端点实现类。 - * https://www.cnblogs.com/zhao-shan/p/5515174.html - */ -public class SoapParam { - - public SoapParam(Class type, String name) { - this.type = type; - this.name = name; - } - - public SoapParam(Class type, String name, boolean header) { - this.type = type; - this.name = name; - this.header = header; - } - - public SoapParam(Class type, String name, Mode mode) { - this.type = type; - this.name = name; - this.mode = mode; - } - - public SoapParam(Class type, String name, Mode mode, boolean header) { - this.type = type; - this.name = name; - this.mode = mode; - this.header = header; - } - - public SoapParam(Class type, String name, String partName, String targetNamespace, Mode mode, - boolean header) { - this.type = type; - this.name = name; - this.partName = partName; - this.targetNamespace = targetNamespace; - this.mode = mode; - this.header = header; - } - - /** - * 参数对象类型 - */ - private Class type; - /** - * 1、name :参数的名称。如果操作是远程过程调用(RPC)类型并且未指定partName 属性,那么这是用于表示参数的 wsdl:part 属性的名称。 - * 如果操作是文档类型或者参数映射至某个头,那么 -name 是用于表示该参数的 XML 元素的局部名称。如果操作是文档类型、 参数类型为 BARE - * 并且方式为 OUT 或 INOUT,那么必须指定此属性。(字符串) - */ - private String name = ""; - /** - * 2、partName:定义用于表示此参数的 wsdl:part属性的名称。仅当操作类型为 RPC 或者操作是文档类型并且参数类型为BARE - * 时才使用此参数。(字符串) - */ - private String partName = ""; - /** - * 3、targetNamespace:指定参数的 XML 元素的 XML 名称空间。当属性映射至 XML 元素时,仅应用于文档绑定。缺省值为 Web - * Service 的 targetNamespace。(字符串) - */ - private String targetNamespace = ""; - /** - * 4、mode:此值表示此方法的参数流的方向。有效值为 IN、INOUT 和 OUT。(字符串) - */ - private jakarta.jws.WebParam.Mode mode = jakarta.jws.WebParam.Mode.IN; - /** - * 5、header:指定参数是在消息头还是消息体中。缺省值为 false。(布尔值) - */ - private boolean header = false; - - public Class getType() { - return type; - } - - public void setType(Class type) { - this.type = type; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getPartName() { - return partName; - } - - public void setPartName(String partName) { - this.partName = partName; - } - - public String getTargetNamespace() { - return targetNamespace; - } - - public void setTargetNamespace(String targetNamespace) { - this.targetNamespace = targetNamespace; - } - - public jakarta.jws.WebParam.Mode getMode() { - return mode; - } - - public void setMode(jakarta.jws.WebParam.Mode mode) { - this.mode = mode; - } - - public boolean isHeader() { - return header; - } - - public void setHeader(boolean header) { - this.header = header; - } - -} +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint.jaxws.definition; + +import jakarta.jws.WebParam.Mode; + +/** + * 注释用于定制从单个参数至 Web Service 消息部件和 XML 元素的映射。将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于 JavaBeans 端点的服务器端点实现类。 + * https://www.cnblogs.com/zhao-shan/p/5515174.html + */ +public class SoapParam { + + public SoapParam(Class type, String name) { + this.type = type; + this.name = name; + } + + public SoapParam(Class type, String name, boolean header) { + this.type = type; + this.name = name; + this.header = header; + } + + public SoapParam(Class type, String name, Mode mode) { + this.type = type; + this.name = name; + this.mode = mode; + } + + public SoapParam(Class type, String name, Mode mode, boolean header) { + this.type = type; + this.name = name; + this.mode = mode; + this.header = header; + } + + public SoapParam(Class type, String name, String partName, String targetNamespace, Mode mode, + boolean header) { + this.type = type; + this.name = name; + this.partName = partName; + this.targetNamespace = targetNamespace; + this.mode = mode; + this.header = header; + } + + /** + * 参数对象类型 + */ + private Class type; + /** + * 1、name :参数的名称。如果操作是远程过程调用(RPC)类型并且未指定partName 属性,那么这是用于表示参数的 wsdl:part 属性的名称。 + * 如果操作是文档类型或者参数映射至某个头,那么 -name 是用于表示该参数的 XML 元素的局部名称。如果操作是文档类型、 参数类型为 BARE + * 并且方式为 OUT 或 INOUT,那么必须指定此属性。(字符串) + */ + private String name = ""; + /** + * 2、partName:定义用于表示此参数的 wsdl:part属性的名称。仅当操作类型为 RPC 或者操作是文档类型并且参数类型为BARE + * 时才使用此参数。(字符串) + */ + private String partName = ""; + /** + * 3、targetNamespace:指定参数的 XML 元素的 XML 名称空间。当属性映射至 XML 元素时,仅应用于文档绑定。缺省值为 Web + * Service 的 targetNamespace。(字符串) + */ + private String targetNamespace = ""; + /** + * 4、mode:此值表示此方法的参数流的方向。有效值为 IN、INOUT 和 OUT。(字符串) + */ + private jakarta.jws.WebParam.Mode mode = jakarta.jws.WebParam.Mode.IN; + /** + * 5、header:指定参数是在消息头还是消息体中。缺省值为 false。(布尔值) + */ + private boolean header = false; + + public Class getType() { + return type; + } + + public void setType(Class type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPartName() { + return partName; + } + + public void setPartName(String partName) { + this.partName = partName; + } + + public String getTargetNamespace() { + return targetNamespace; + } + + public void setTargetNamespace(String targetNamespace) { + this.targetNamespace = targetNamespace; + } + + public jakarta.jws.WebParam.Mode getMode() { + return mode; + } + + public void setMode(jakarta.jws.WebParam.Mode mode) { + this.mode = mode; + } + + public boolean isHeader() { + return 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 d84ff83..0022d39 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 @@ -1,104 +1,104 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint.jaxws.definition; - -/** - * 注释用于定制从返回值至 WSDL 部件或 XML 元素的映射。将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于 JavaBeans 端点的服务器端点实现类。 - * https://www.cnblogs.com/zhao-shan/p/5515174.html - */ -public class SoapResult { - - /** - * 返回结果对象类型 - */ - private Class rtClass; - - /** - * 1、name:当返回值列示在 WSDL 文件中并且在连接上的消息中找到该返回值时,指定该返回值的名称。对于 RPC 绑定,这是用于表示返回值的 - * wsdl:part属性的名称。对于文档绑定,-name 参数是用于表示返回值的 XML 元素的局部名。对于 RPC 和 DOCUMENT/WRAPPED - * 绑定,缺省值为 return。对于 DOCUMENT/BARE 绑定,缺省值为方法名 + Response。(字符串) - */ - private String name = ""; - - /** - * 2、targetNamespace:指定返回值的 XML 名称空间。仅当操作类型为 RPC 或者操作是文档类型并且参数类型为 BARE - * 时才使用此参数。(字符串) - */ - private String targetNamespace = ""; - - /** - * 3、header:指定头中是否附带结果。缺省值为false。(布尔值) - */ - private boolean header = false; - /** - * 4、partName:指定 RPC 或 DOCUMENT/BARE 操作的结果的部件名称。缺省值为@WebResult.name。(字符串) - */ - private String partName = ""; - - public SoapResult(Class rtClass, String name) { - this.rtClass = rtClass; - this.name = name; - } - - public SoapResult(Class rtClass, String name, String targetNamespace, boolean header, String partName) { - this.rtClass = rtClass; - this.name = name; - this.targetNamespace = targetNamespace; - this.header = header; - this.partName = partName; - } - - public Class getRtClass() { - return rtClass; - } - - public void setRtClass(Class rtClass) { - this.rtClass = rtClass; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getTargetNamespace() { - return targetNamespace; - } - - public void setTargetNamespace(String targetNamespace) { - this.targetNamespace = targetNamespace; - } - - public boolean isHeader() { - return header; - } - - public void setHeader(boolean header) { - this.header = header; - } - - public String getPartName() { - return partName; - } - - public void setPartName(String partName) { - this.partName = partName; - } - +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint.jaxws.definition; + +/** + * 注释用于定制从返回值至 WSDL 部件或 XML 元素的映射。将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于 JavaBeans 端点的服务器端点实现类。 + * https://www.cnblogs.com/zhao-shan/p/5515174.html + */ +public class SoapResult { + + /** + * 返回结果对象类型 + */ + private Class rtClass; + + /** + * 1、name:当返回值列示在 WSDL 文件中并且在连接上的消息中找到该返回值时,指定该返回值的名称。对于 RPC 绑定,这是用于表示返回值的 + * wsdl:part属性的名称。对于文档绑定,-name 参数是用于表示返回值的 XML 元素的局部名。对于 RPC 和 DOCUMENT/WRAPPED + * 绑定,缺省值为 return。对于 DOCUMENT/BARE 绑定,缺省值为方法名 + Response。(字符串) + */ + private String name = ""; + + /** + * 2、targetNamespace:指定返回值的 XML 名称空间。仅当操作类型为 RPC 或者操作是文档类型并且参数类型为 BARE + * 时才使用此参数。(字符串) + */ + private String targetNamespace = ""; + + /** + * 3、header:指定头中是否附带结果。缺省值为false。(布尔值) + */ + private boolean header = false; + /** + * 4、partName:指定 RPC 或 DOCUMENT/BARE 操作的结果的部件名称。缺省值为@WebResult.name。(字符串) + */ + private String partName = ""; + + public SoapResult(Class rtClass, String name) { + this.rtClass = rtClass; + this.name = name; + } + + public SoapResult(Class rtClass, String name, String targetNamespace, boolean header, String partName) { + this.rtClass = rtClass; + this.name = name; + this.targetNamespace = targetNamespace; + this.header = header; + this.partName = partName; + } + + public Class getRtClass() { + return rtClass; + } + + public void setRtClass(Class rtClass) { + this.rtClass = rtClass; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getTargetNamespace() { + return targetNamespace; + } + + public void setTargetNamespace(String targetNamespace) { + this.targetNamespace = targetNamespace; + } + + public boolean isHeader() { + return header; + } + + public void setHeader(boolean header) { + this.header = header; + } + + public String getPartName() { + return partName; + } + + public void setPartName(String partName) { + this.partName = partName; + } + } \ No newline at end of file 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 1b0b5c6..fea21c2 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 @@ -1,122 +1,122 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint.jaxws.definition; - -public class SoapService { - - /** - * 此属性的值包含XML Web Service的名称。在默认情况下,该值是实现XML Web Service的类的名称,wsdl:portType - * 的名称。缺省值为 Java 类或接口的非限定名称。(字符串) - */ - private final String name; - /** - * 指定你想要的名称空间,默认是使用接口实现类的包名的反缀(字符串) - */ - private final String targetNamespace; - /** - * 对外发布的服务名,指定 Web Service 的服务名称:wsdl:service。缺省值为 Java 类的简单名称 + Service。(字符串) - */ - private String serviceName; - /** - * wsdl:portName。缺省值为 WebService.name+Port。(字符串) - */ - private String portName; - /** - * 指定用于定义 Web Service 的 WSDL 文档的 Web 地址。Web 地址可以是相对路径或绝对路径。(字符串) - */ - private String wsdlLocation; - /** - * 服务接口全路径, 指定做SEI(Service EndPoint Interface)服务端点接口(字符串) - */ - private String endpointInterface; - - public SoapService(String name, String targetNamespace) { - this.name = name; - this.targetNamespace = targetNamespace; - } - - public SoapService(String name, String targetNamespace, String serviceName) { - this.name = name; - this.targetNamespace = targetNamespace; - this.serviceName = serviceName; - } - - public SoapService(String name, String targetNamespace, String serviceName, String portName) { - this.name = name; - this.targetNamespace = targetNamespace; - this.serviceName = serviceName; - this.portName = portName; - } - - public SoapService(String name, String targetNamespace, String serviceName, String portName, String wsdlLocation) { - this.name = name; - this.targetNamespace = targetNamespace; - this.serviceName = serviceName; - this.portName = portName; - this.wsdlLocation = wsdlLocation; - } - - public SoapService(String name, String targetNamespace, String serviceName, String portName, String wsdlLocation, - String endpointInterface) { - this.name = name; - this.targetNamespace = targetNamespace; - this.serviceName = serviceName; - this.portName = portName; - this.wsdlLocation = wsdlLocation; - this.endpointInterface = endpointInterface; - } - - public String getServiceName() { - return serviceName; - } - - public void setServiceName(String serviceName) { - this.serviceName = serviceName; - } - - public String getPortName() { - return portName; - } - - public void setPortName(String portName) { - this.portName = portName; - } - - public String getWsdlLocation() { - return wsdlLocation; - } - - public void setWsdlLocation(String wsdlLocation) { - this.wsdlLocation = wsdlLocation; - } - - public String getEndpointInterface() { - return endpointInterface; - } - - public void setEndpointInterface(String endpointInterface) { - this.endpointInterface = endpointInterface; - } - - public String getName() { - return name; - } - - public String getTargetNamespace() { - return targetNamespace; - } - -} +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint.jaxws.definition; + +public class SoapService { + + /** + * 此属性的值包含XML Web Service的名称。在默认情况下,该值是实现XML Web Service的类的名称,wsdl:portType + * 的名称。缺省值为 Java 类或接口的非限定名称。(字符串) + */ + private final String name; + /** + * 指定你想要的名称空间,默认是使用接口实现类的包名的反缀(字符串) + */ + private final String targetNamespace; + /** + * 对外发布的服务名,指定 Web Service 的服务名称:wsdl:service。缺省值为 Java 类的简单名称 + Service。(字符串) + */ + private String serviceName; + /** + * wsdl:portName。缺省值为 WebService.name+Port。(字符串) + */ + private String portName; + /** + * 指定用于定义 Web Service 的 WSDL 文档的 Web 地址。Web 地址可以是相对路径或绝对路径。(字符串) + */ + private String wsdlLocation; + /** + * 服务接口全路径, 指定做SEI(Service EndPoint Interface)服务端点接口(字符串) + */ + private String endpointInterface; + + public SoapService(String name, String targetNamespace) { + this.name = name; + this.targetNamespace = targetNamespace; + } + + public SoapService(String name, String targetNamespace, String serviceName) { + this.name = name; + this.targetNamespace = targetNamespace; + this.serviceName = serviceName; + } + + public SoapService(String name, String targetNamespace, String serviceName, String portName) { + this.name = name; + this.targetNamespace = targetNamespace; + this.serviceName = serviceName; + this.portName = portName; + } + + public SoapService(String name, String targetNamespace, String serviceName, String portName, String wsdlLocation) { + this.name = name; + this.targetNamespace = targetNamespace; + this.serviceName = serviceName; + this.portName = portName; + this.wsdlLocation = wsdlLocation; + } + + public SoapService(String name, String targetNamespace, String serviceName, String portName, String wsdlLocation, + String endpointInterface) { + this.name = name; + this.targetNamespace = targetNamespace; + this.serviceName = serviceName; + this.portName = portName; + this.wsdlLocation = wsdlLocation; + this.endpointInterface = endpointInterface; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getPortName() { + return portName; + } + + public void setPortName(String portName) { + this.portName = portName; + } + + public String getWsdlLocation() { + return wsdlLocation; + } + + public void setWsdlLocation(String wsdlLocation) { + this.wsdlLocation = wsdlLocation; + } + + public String getEndpointInterface() { + return endpointInterface; + } + + public void setEndpointInterface(String endpointInterface) { + this.endpointInterface = endpointInterface; + } + + public String getName() { + return name; + } + + 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 ad9985d..63be5ae 100644 --- a/src/main/java/org/apache/cxf/endpoint/utils/JaxrsEndpointApiUtils.java +++ b/src/main/java/org/apache/cxf/endpoint/utils/JaxrsEndpointApiUtils.java @@ -1,396 +1,396 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint.utils; - -import java.lang.reflect.InvocationHandler; - -import jakarta.ws.rs.BeanParam; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.CookieParam; -import jakarta.ws.rs.DELETE; -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.HttpMethod; -import jakarta.ws.rs.MatrixParam; -import jakarta.ws.rs.OPTIONS; -import jakarta.ws.rs.PATCH; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.PUT; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; - -import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.cxf.endpoint.annotation.WebBound; -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 io.github.easy4j.javassist.bytecode.CtAnnotationBuilder; -import io.github.easy4j.javassist.utils.JavassistUtils; - -import javassist.CannotCompileException; -import javassist.ClassPool; -import javassist.CtClass; -import javassist.CtConstructor; -import javassist.CtMethod; -import javassist.CtNewConstructor; -import javassist.NotFoundException; -import javassist.bytecode.AnnotationsAttribute; -import javassist.bytecode.ConstPool; -import javassist.bytecode.ParameterAnnotationsAttribute; -import javassist.bytecode.annotation.Annotation; -import javassist.bytecode.annotation.StringMemberValue; - -public class JaxrsEndpointApiUtils { - - public static CtClass makeClass(final ClassPool pool, final String classname) - throws NotFoundException, CannotCompileException { - - CtClass declaring = pool.getOrNull(classname); - if (null == declaring) { - declaring = pool.makeClass(classname); - } - - /* - * 当 ClassPool.doPruning=true的时候,Javassist 在CtClass object被冻结时,会释放存储在ClassPool对应的数据。 - * 这样做可以减少javassist的内存消耗。默认情况ClassPool.doPruning=false。 - */ - declaring.stopPruning(true); - - return declaring; - } - - public static CtConstructor defaultConstructor(final CtClass declaring) throws CannotCompileException { - // 默认添加无参构造器 - CtConstructor cons = new CtConstructor(null, declaring); - cons.setBody("{}"); - return cons; - } - - 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); - - } - - public static CtClass makeInterface(final ClassPool pool, final String classname) - throws NotFoundException, CannotCompileException { - - CtClass declaring = pool.getOrNull(classname); - if (null == declaring) { - declaring = pool.makeInterface(classname); - } - - // 当 ClassPool.doPruning=true的时候,Javassist 在CtClass - // object被冻结时,会释放存储在ClassPool对应的数据。这样做可以减少javassist的内存消耗。默认情况ClassPool.doPruning=false。 - declaring.stopPruning(true); - - return declaring; - } - - - 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); - - } - - 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 - */ - 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 - */ - public static Annotation annotProduces(final ConstPool constPool, String... mediaTypes) { - - // 参数预处理 - mediaTypes = ArrayUtils.isEmpty(mediaTypes) ? new String[] {"*/*"} : mediaTypes; - CtAnnotationBuilder builder = CtAnnotationBuilder.create(Produces.class, constPool). - addStringMember("value", mediaTypes); - return builder.build(); - - } - - /** - * 为方法添加 @HttpMethod、 @GET、 @POST、 @PUT、 @DELETE、 @PATCH、 @HEAD、 @OPTIONS、@Path、、@Consumes、@Produces、@RestBound、@RestParam 注解 - * @author : hiwepy - * @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 - */ - public static void methodAnnotations(final CtMethod ctMethod, final ConstPool constPool, final RestMethod method, final RestBound bound, RestParam... params) { - - // 添加方法注解 - AnnotationsAttribute methodAttr = JavassistUtils.getAnnotationsAttribute(ctMethod); - - // 添加 @WebBound 注解 - if (bound != null) { - methodAttr.addAnnotation(JaxrsEndpointApiUtils.annotWebBound(constPool, bound)); - } - - // 添加 @GET、 @POST、 @PUT、 @DELETE、 @PATCH、 @HEAD、 @OPTIONS 注解 - methodAttr.addAnnotation(JaxrsEndpointApiUtils.annotHttpMethod(constPool, method)); - - // 添加 @Path 注解 - methodAttr.addAnnotation(JaxrsEndpointApiUtils.annotPath(constPool, method.getPath())); - - // 添加 @Consumes 注解 - if (ArrayUtils.isNotEmpty(method.getConsumes())) { - methodAttr.addAnnotation(JaxrsEndpointApiUtils.annotConsumes(constPool, method.getConsumes())); - } - - // 添加 @Produces 注解 - if (ArrayUtils.isNotEmpty(method.getMediaTypes())) { - methodAttr.addAnnotation(JaxrsEndpointApiUtils.annotProduces(constPool, method.getMediaTypes())); - } - - ctMethod.getMethodInfo().addAttribute(methodAttr); - - // 添加 @WebParam 参数注解 - if(params != null && params.length > 0) { - - ParameterAnnotationsAttribute parameterAtrribute = JavassistUtils.getParameterAnnotationsAttribute(ctMethod); - Annotation[][] paramArrays = JaxrsEndpointApiUtils.annotParams(constPool, params); - parameterAtrribute.setAnnotations(paramArrays); - ctMethod.getMethodInfo().addAttribute(parameterAtrribute); - - } - - } - - /** - * 设置方法体 - * @param ctMethod {@link CtMethod} instance - * @param method {@link RestMethod} instance - * @throws CannotCompileException if can't compile - */ - public static void methodBody(final CtMethod ctMethod, final RestMethod method) throws CannotCompileException { - - // 构造方法体 - StringBuilder body = new StringBuilder(); - body.append("{\n"); - body.append("if(getHandler() != null){\n"); - body.append("Method method = this.getClass().getDeclaredMethod(\"" + method.getName() + "\", $sig);"); - body.append("return ($r)getHandler().invoke($0, method, $args);"); - body.append("}\n"); - body.append("return null;\n"); - body.append("}"); - // 将方法的内容设置为要写入的代码,当方法被 abstract修饰时,该修饰符被移除。 - ctMethod.setBody(body.toString()); - - } - - /** - * 设置方法异常捕获逻辑 - * @param pool {@link ClassPool} instance - * @param ctMethod {@link CtMethod} instance - * @throws NotFoundException if not found - * @throws CannotCompileException if can't compile - */ - 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 - */ - public static Annotation annotWebBound(final ConstPool constPool, final RestBound bound) { - - CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebBound.class, constPool). - addStringMember("uid", bound.getUid()); - if (StringUtils.isNotBlank(bound.getJson())) { - builder.addStringMember("json", bound.getJson()); - } - return builder.build(); - - } - - /** - * 根据参数 构造 @GET、 @POST、 @PUT、 @DELETE、 @PATCH、 @HEAD、 @OPTIONS 注解 - * @param constPool {@link ConstPool} instance - * @param method {@link RestMethod} instance - * @return {@link Annotation} instance - */ - public static Annotation annotHttpMethod(final ConstPool constPool, final RestMethod method) { - - Annotation annot = null; - switch (method.getMethod()) { - case GET:{ - annot = new Annotation(GET.class.getName(), constPool); - };break; - case POST:{ - annot = new Annotation(POST.class.getName(), constPool); - };break; - case PUT:{ - annot = new Annotation(PUT.class.getName(), constPool); - };break; - case DELETE:{ - annot = new Annotation(DELETE.class.getName(), constPool); - };break; - case PATCH:{ - annot = new Annotation(PATCH.class.getName(), constPool); - };break; - case HEAD:{ - annot = new Annotation(HEAD.class.getName(), constPool); - };break; - case OPTIONS:{ - annot = new Annotation(OPTIONS.class.getName(), constPool); - };break; - default:{ - annot = new Annotation(GET.class.getName(), constPool); - };break; - } - - return annot; - } - - /** - * 构造 @Consumes 注解 - * @param constPool {@link ConstPool} instance - * @param consumes the consumes - * @return {@link Annotation} instance - */ - public static Annotation annotConsumes(final ConstPool constPool, String... consumes) { - // 参数预处理 - consumes = ArrayUtils.isEmpty(consumes) ? new String[] {"*/*"} : consumes; - CtAnnotationBuilder builder = CtAnnotationBuilder.create(Consumes.class, constPool). - addStringMember("value", consumes); - return builder.build(); - } - - /** - * 构造 @BeanParam 、@CookieParam、@FormParam、@HeaderParam、@MatrixParam、@PathParam、@QueryParam 参数注解 - * @param constPool {@link ConstPool} instance - * @param params the params - * @return {@link Annotation} Array - */ - public static Annotation[][] annotParams(final ConstPool constPool, RestParam... params) { - - // 添加 @WebParam 参数注解 - if (params != null && params.length > 0) { - - Annotation[][] paramArrays = new Annotation[params.length][1]; - - Annotation paramAnnot = null; - for (int i = 0; i < params.length; i++) { - - switch (params[i].getFrom()) { - case BEAN:{ - paramAnnot = new Annotation(BeanParam.class.getName(), constPool); - };break; - case COOKIE:{ - paramAnnot = new Annotation(CookieParam.class.getName(), constPool); - };break; - case FORM:{ - paramAnnot = new Annotation(FormParam.class.getName(), constPool); - };break; - case HEADER:{ - paramAnnot = new Annotation(HeaderParam.class.getName(), constPool); - };break; - case MATRIX:{ - paramAnnot = new Annotation(MatrixParam.class.getName(), constPool); - };break; - case PATH:{ - paramAnnot = new Annotation(PathParam.class.getName(), constPool); - };break; - case QUERY:{ - paramAnnot = new Annotation(QueryParam.class.getName(), constPool); - };break; - default:{ - paramAnnot = new Annotation(QueryParam.class.getName(), constPool); - };break; - } - if(HttpParamEnum.BEAN.compareTo(params[i].getFrom()) != 0){ - paramAnnot.addMemberValue("value", new StringMemberValue(params[i].getName(), constPool)); - } - - // 有默认值 - if(StringUtils.isNotBlank(params[i].getDef())) { - - paramArrays[i] = new Annotation[2]; - paramArrays[i][0] = paramAnnot; - - Annotation defAnnot = new Annotation(DefaultValue.class.getName(), constPool); - defAnnot.addMemberValue("value", new StringMemberValue(params[i].getDef(), constPool)); - paramArrays[i][1] = paramAnnot; - - } else { - paramArrays[i][0] = paramAnnot; - } - - } - - return paramArrays; - - } - return null; - } - -} +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint.utils; + +import java.lang.reflect.InvocationHandler; + +import jakarta.ws.rs.BeanParam; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.CookieParam; +import jakarta.ws.rs.DELETE; +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.HttpMethod; +import jakarta.ws.rs.MatrixParam; +import jakarta.ws.rs.OPTIONS; +import jakarta.ws.rs.PATCH; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.cxf.endpoint.annotation.WebBound; +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 io.github.easy4j.javassist.bytecode.CtAnnotationBuilder; +import io.github.easy4j.javassist.utils.JavassistUtils; + +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtMethod; +import javassist.CtNewConstructor; +import javassist.NotFoundException; +import javassist.bytecode.AnnotationsAttribute; +import javassist.bytecode.ConstPool; +import javassist.bytecode.ParameterAnnotationsAttribute; +import javassist.bytecode.annotation.Annotation; +import javassist.bytecode.annotation.StringMemberValue; + +public class JaxrsEndpointApiUtils { + + public static CtClass makeClass(final ClassPool pool, final String classname) + throws NotFoundException, CannotCompileException { + + CtClass declaring = pool.getOrNull(classname); + if (null == declaring) { + declaring = pool.makeClass(classname); + } + + /* + * 当 ClassPool.doPruning=true的时候,Javassist 在CtClass object被冻结时,会释放存储在ClassPool对应的数据。 + * 这样做可以减少javassist的内存消耗。默认情况ClassPool.doPruning=false。 + */ + declaring.stopPruning(true); + + return declaring; + } + + public static CtConstructor defaultConstructor(final CtClass declaring) throws CannotCompileException { + // 默认添加无参构造器 + CtConstructor cons = new CtConstructor(null, declaring); + cons.setBody("{}"); + return cons; + } + + 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); + + } + + public static CtClass makeInterface(final ClassPool pool, final String classname) + throws NotFoundException, CannotCompileException { + + CtClass declaring = pool.getOrNull(classname); + if (null == declaring) { + declaring = pool.makeInterface(classname); + } + + // 当 ClassPool.doPruning=true的时候,Javassist 在CtClass + // object被冻结时,会释放存储在ClassPool对应的数据。这样做可以减少javassist的内存消耗。默认情况ClassPool.doPruning=false。 + declaring.stopPruning(true); + + return declaring; + } + + + 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); + + } + + 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 + */ + 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 + */ + public static Annotation annotProduces(final ConstPool constPool, String... mediaTypes) { + + // 参数预处理 + mediaTypes = ArrayUtils.isEmpty(mediaTypes) ? new String[] {"*/*"} : mediaTypes; + CtAnnotationBuilder builder = CtAnnotationBuilder.create(Produces.class, constPool). + addStringMember("value", mediaTypes); + return builder.build(); + + } + + /** + * 为方法添加 @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 + */ + public static void methodAnnotations(final CtMethod ctMethod, final ConstPool constPool, final RestMethod method, final RestBound bound, RestParam... params) { + + // 添加方法注解 + AnnotationsAttribute methodAttr = JavassistUtils.getAnnotationsAttribute(ctMethod); + + // 添加 @WebBound 注解 + if (bound != null) { + methodAttr.addAnnotation(JaxrsEndpointApiUtils.annotWebBound(constPool, bound)); + } + + // 添加 @GET、 @POST、 @PUT、 @DELETE、 @PATCH、 @HEAD、 @OPTIONS 注解 + methodAttr.addAnnotation(JaxrsEndpointApiUtils.annotHttpMethod(constPool, method)); + + // 添加 @Path 注解 + methodAttr.addAnnotation(JaxrsEndpointApiUtils.annotPath(constPool, method.getPath())); + + // 添加 @Consumes 注解 + if (ArrayUtils.isNotEmpty(method.getConsumes())) { + methodAttr.addAnnotation(JaxrsEndpointApiUtils.annotConsumes(constPool, method.getConsumes())); + } + + // 添加 @Produces 注解 + if (ArrayUtils.isNotEmpty(method.getMediaTypes())) { + methodAttr.addAnnotation(JaxrsEndpointApiUtils.annotProduces(constPool, method.getMediaTypes())); + } + + ctMethod.getMethodInfo().addAttribute(methodAttr); + + // 添加 @WebParam 参数注解 + if(params != null && params.length > 0) { + + ParameterAnnotationsAttribute parameterAtrribute = JavassistUtils.getParameterAnnotationsAttribute(ctMethod); + Annotation[][] paramArrays = JaxrsEndpointApiUtils.annotParams(constPool, params); + parameterAtrribute.setAnnotations(paramArrays); + ctMethod.getMethodInfo().addAttribute(parameterAtrribute); + + } + + } + + /** + * 设置方法体 + * @param ctMethod {@link CtMethod} instance + * @param method {@link RestMethod} instance + * @throws CannotCompileException if can't compile + */ + public static void methodBody(final CtMethod ctMethod, final RestMethod method) throws CannotCompileException { + + // 构造方法体 + StringBuilder body = new StringBuilder(); + body.append("{\n"); + body.append("if(getHandler() != null){\n"); + body.append("Method method = this.getClass().getDeclaredMethod(\"" + method.getName() + "\", $sig);"); + body.append("return ($r)getHandler().invoke($0, method, $args);"); + body.append("}\n"); + body.append("return null;\n"); + body.append("}"); + // 将方法的内容设置为要写入的代码,当方法被 abstract修饰时,该修饰符被移除。 + ctMethod.setBody(body.toString()); + + } + + /** + * 设置方法异常捕获逻辑 + * @param pool {@link ClassPool} instance + * @param ctMethod {@link CtMethod} instance + * @throws NotFoundException if not found + * @throws CannotCompileException if can't compile + */ + 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 + */ + public static Annotation annotWebBound(final ConstPool constPool, final RestBound bound) { + + CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebBound.class, constPool). + addStringMember("uid", bound.getUid()); + if (StringUtils.isNotBlank(bound.getJson())) { + builder.addStringMember("json", bound.getJson()); + } + return builder.build(); + + } + + /** + * 根据参数 构造 @GET、 @POST、 @PUT、 @DELETE、 @PATCH、 @HEAD、 @OPTIONS 注解 + * @param constPool {@link ConstPool} instance + * @param method {@link RestMethod} instance + * @return {@link Annotation} instance + */ + public static Annotation annotHttpMethod(final ConstPool constPool, final RestMethod method) { + + Annotation annot = null; + switch (method.getMethod()) { + case GET:{ + annot = new Annotation(GET.class.getName(), constPool); + };break; + case POST:{ + annot = new Annotation(POST.class.getName(), constPool); + };break; + case PUT:{ + annot = new Annotation(PUT.class.getName(), constPool); + };break; + case DELETE:{ + annot = new Annotation(DELETE.class.getName(), constPool); + };break; + case PATCH:{ + annot = new Annotation(PATCH.class.getName(), constPool); + };break; + case HEAD:{ + annot = new Annotation(HEAD.class.getName(), constPool); + };break; + case OPTIONS:{ + annot = new Annotation(OPTIONS.class.getName(), constPool); + };break; + default:{ + annot = new Annotation(GET.class.getName(), constPool); + };break; + } + + return annot; + } + + /** + * 构造 @Consumes 注解 + * @param constPool {@link ConstPool} instance + * @param consumes the consumes + * @return {@link Annotation} instance + */ + public static Annotation annotConsumes(final ConstPool constPool, String... consumes) { + // 参数预处理 + consumes = ArrayUtils.isEmpty(consumes) ? new String[] {"*/*"} : consumes; + CtAnnotationBuilder builder = CtAnnotationBuilder.create(Consumes.class, constPool). + addStringMember("value", consumes); + return builder.build(); + } + + /** + * 构造 @BeanParam 、@CookieParam、@FormParam、@HeaderParam、@MatrixParam、@PathParam、@QueryParam 参数注解 + * @param constPool {@link ConstPool} instance + * @param params the params + * @return {@link Annotation} Array + */ + public static Annotation[][] annotParams(final ConstPool constPool, RestParam... params) { + + // 添加 @WebParam 参数注解 + if (params != null && params.length > 0) { + + Annotation[][] paramArrays = new Annotation[params.length][1]; + + Annotation paramAnnot = null; + for (int i = 0; i < params.length; i++) { + + switch (params[i].getFrom()) { + case BEAN:{ + paramAnnot = new Annotation(BeanParam.class.getName(), constPool); + };break; + case COOKIE:{ + paramAnnot = new Annotation(CookieParam.class.getName(), constPool); + };break; + case FORM:{ + paramAnnot = new Annotation(FormParam.class.getName(), constPool); + };break; + case HEADER:{ + paramAnnot = new Annotation(HeaderParam.class.getName(), constPool); + };break; + case MATRIX:{ + paramAnnot = new Annotation(MatrixParam.class.getName(), constPool); + };break; + case PATH:{ + paramAnnot = new Annotation(PathParam.class.getName(), constPool); + };break; + case QUERY:{ + paramAnnot = new Annotation(QueryParam.class.getName(), constPool); + };break; + default:{ + paramAnnot = new Annotation(QueryParam.class.getName(), constPool); + };break; + } + if(HttpParamEnum.BEAN.compareTo(params[i].getFrom()) != 0){ + paramAnnot.addMemberValue("value", new StringMemberValue(params[i].getName(), constPool)); + } + + // 有默认值 + if(StringUtils.isNotBlank(params[i].getDef())) { + + paramArrays[i] = new Annotation[2]; + paramArrays[i][0] = paramAnnot; + + Annotation defAnnot = new Annotation(DefaultValue.class.getName(), constPool); + defAnnot.addMemberValue("value", new StringMemberValue(params[i].getDef(), constPool)); + paramArrays[i][1] = paramAnnot; + + } else { + paramArrays[i][0] = paramAnnot; + } + + } + + return paramArrays; + + } + return null; + } + +} 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 8f07d4b..18d0ac2 100644 --- a/src/main/java/org/apache/cxf/endpoint/utils/JaxwsEndpointApiUtils.java +++ b/src/main/java/org/apache/cxf/endpoint/utils/JaxwsEndpointApiUtils.java @@ -1,426 +1,426 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint.utils; - -import java.lang.reflect.InvocationHandler; - -import jakarta.jws.HandlerChain; -import jakarta.jws.WebMethod; -import jakarta.jws.WebParam; -import jakarta.jws.WebResult; -import jakarta.jws.WebService; -import jakarta.xml.ws.Service; -import jakarta.xml.ws.ServiceMode; -import jakarta.xml.ws.WebServiceProvider; -import jakarta.xml.ws.soap.Addressing; -import jakarta.xml.ws.soap.AddressingFeature.Responses; - -import org.apache.commons.lang3.StringUtils; -import org.apache.cxf.endpoint.annotation.WebBound; -import org.apache.cxf.endpoint.jaxws.definition.SoapBound; -import org.apache.cxf.endpoint.jaxws.definition.SoapMethod; -import org.apache.cxf.endpoint.jaxws.definition.SoapParam; -import org.apache.cxf.endpoint.jaxws.definition.SoapResult; -import org.apache.cxf.endpoint.jaxws.definition.SoapService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.github.easy4j.javassist.bytecode.CtAnnotationBuilder; -import io.github.easy4j.javassist.utils.JavassistUtils; - -import javassist.CannotCompileException; -import javassist.ClassPool; -import javassist.CtClass; -import javassist.CtConstructor; -import javassist.CtMethod; -import javassist.CtNewConstructor; -import javassist.NotFoundException; -import javassist.bytecode.AnnotationsAttribute; -import javassist.bytecode.ConstPool; -import javassist.bytecode.ParameterAnnotationsAttribute; -import javassist.bytecode.annotation.Annotation; - -public class JaxwsEndpointApiUtils { - - protected static final Logger LOG = LoggerFactory.getLogger(JaxwsEndpointApiUtils.class); - - public static CtClass makeClass(final ClassPool pool, final String classname) - throws NotFoundException, CannotCompileException { - - CtClass declaring = pool.getOrNull(classname); - if (null == declaring) { - declaring = pool.makeClass(classname); - } - - // 当 ClassPool.doPruning=true的时候,Javassist 在CtClass - // object被冻结时,会释放存储在ClassPool对应的数据。这样做可以减少javassist的内存消耗。默认情况ClassPool.doPruning=false。 - declaring.stopPruning(true); - - return declaring; - } - - public static CtConstructor defaultConstructor(final CtClass declaring) throws CannotCompileException { - // 默认添加无参构造器 - CtConstructor cons = new CtConstructor(null, declaring); - cons.setBody("{}"); - return cons; - } - - 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); - - } - - public static CtClass makeInterface(final ClassPool pool, final String classname) - throws NotFoundException, CannotCompileException { - - CtClass declaring = pool.getOrNull(classname); - if (null == declaring) { - declaring = pool.makeInterface(classname); - } - - // 当 ClassPool.doPruning=true的时候,Javassist 在CtClass - // object被冻结时,会释放存储在ClassPool对应的数据。这样做可以减少javassist的内存消耗。默认情况ClassPool.doPruning=false。 - declaring.stopPruning(true); - - return declaring; - } - - - 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); - - } - - 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 - */ - public static Annotation annotWebServiceProvider(final ConstPool constPool, String wsdlLocation, - String serviceName, String targetNamespace, String portName) { - - wsdlLocation = StringUtils.isNotBlank(wsdlLocation) ? wsdlLocation : ""; - serviceName = StringUtils.isNotBlank(serviceName) ? serviceName : ""; - targetNamespace = StringUtils.isNotBlank(targetNamespace) ? targetNamespace : ""; - portName = StringUtils.isNotBlank(portName) ? portName : ""; - - return CtAnnotationBuilder.create(WebServiceProvider.class, constPool) - .addStringMember("wsdlLocation", wsdlLocation).addStringMember("serviceName", serviceName) - .addStringMember("targetNamespace", targetNamespace).addStringMember("portName", portName).build(); - - } - - /** - * 构造 @WebService 注解 - * @param constPool : {@link ConstPool} instance - * @param service : {@link SoapService} instance - * @return {@link Annotation} instance - */ - public static Annotation annotWebService(final ConstPool constPool, final SoapService service) { - - CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebService.class, constPool) - .addStringMember("name", service.getName()) - .addStringMember("targetNamespace", service.getTargetNamespace()); - - if (StringUtils.isNotBlank(service.getServiceName())) { - builder.addStringMember("serviceName", service.getServiceName()); - } - if (StringUtils.isNotBlank(service.getPortName())) { - builder.addStringMember("portName", service.getPortName()); - } - if (StringUtils.isNotBlank(service.getWsdlLocation())) { - builder.addStringMember("wsdlLocation", service.getWsdlLocation()); - } - if (StringUtils.isNotBlank(service.getEndpointInterface())) { - builder.addStringMember("endpointInterface", service.getEndpointInterface()); - } - - return builder.build(); - - } - - /** - * 构造 @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 - */ - public static Annotation annotAddressing(final ConstPool constPool, final boolean enabled, final boolean required, - final Responses responses) { - - return CtAnnotationBuilder.create(Addressing.class, constPool) - .addBooleanMember("enabled", enabled) - .addBooleanMember("required", required) - .addEnumMember("responses", responses).build(); - - } - - /** - * 构造 @ServiceMode 注解 - * @param constPool : {@link ConstPool} instance - * @param mode : the mode of {@link Service} - * @return {@link Annotation} instance - */ - 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 - */ - public static Annotation annotHandlerChain(final ConstPool constPool, String name, String file) { - - CtAnnotationBuilder builder = CtAnnotationBuilder.create(HandlerChain.class, constPool); - if (StringUtils.isNotBlank(name)) { - builder.addStringMember("name", name ); - } - if (StringUtils.isNotBlank(file)) { - builder.addStringMember("file", file); - } - return builder.build(); - - } - - - /** - * - * 为方法添加 @WebMethod、 @WebResult、@WebBound、@WebParam 注解 - * @author : hiwepy - * @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 - */ - public static void methodAnnotations(final CtMethod ctMethod, final ConstPool constPool, final SoapResult result, final SoapMethod method, final SoapBound bound, SoapParam... params) { - - // 添加方法注解 - AnnotationsAttribute methodAttr = JavassistUtils.getAnnotationsAttribute(ctMethod); - - // 添加 @WebBound 注解 - if (bound != null) { - methodAttr.addAnnotation(JaxwsEndpointApiUtils.annotWebBound(constPool, bound)); - } - - // 添加 @WebMethod 注解 - methodAttr.addAnnotation(JaxwsEndpointApiUtils.annotWebMethod(constPool, method)); - - // 添加 @WebResult 注解 - if (null != result ) { - methodAttr.addAnnotation(JaxwsEndpointApiUtils.annotWebResult(constPool, result)); - } - - ctMethod.getMethodInfo().addAttribute(methodAttr); - - // 添加 @WebParam 参数注解 - if(params != null && params.length > 0) { - - ParameterAnnotationsAttribute parameterAtrribute = JavassistUtils.getParameterAnnotationsAttribute(ctMethod); - Annotation[][] paramArrays = JaxwsEndpointApiUtils.annotParams(constPool, params); - parameterAtrribute.setAnnotations(paramArrays); - ctMethod.getMethodInfo().addAttribute(parameterAtrribute); - - } - - } - - /** - * 设置方法体 - * @param ctMethod : {@link CtMethod} instance - * @param method : {@link SoapMethod} instance - * @throws CannotCompileException if can't compile - */ - public static void methodBody(final CtMethod ctMethod, final SoapMethod method) throws CannotCompileException { - - // 构造方法体 - StringBuilder body = new StringBuilder(); - body.append("{\n"); - body.append("if(getHandler() != null){\n"); - body.append("Method method = this.getClass().getDeclaredMethod(\"" + method.getOperationName() + "\", $sig);"); - body.append("return ($r)getHandler().invoke($0, method, $args);"); - body.append("}\n"); - body.append("return null;\n"); - body.append("}"); - // 将方法的内容设置为要写入的代码,当方法被 abstract修饰时,该修饰符被移除。 - ctMethod.setBody(body.toString()); - - } - - /** - * 设置方法异常捕获逻辑 - * @param pool : {@link ClassPool} instance - * @param ctMethod : {@link CtMethod} instance - * @throws NotFoundException if not found - * @throws CannotCompileException if can't compile - */ - 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 SoapBound} instance - * @return {@link Annotation} instance - */ - public static Annotation annotWebBound(final ConstPool constPool, final SoapBound bound) { - - CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebBound.class, constPool). - addStringMember("uid", bound.getUid()); - if (StringUtils.isNotBlank(bound.getJson())) { - builder.addStringMember("json", bound.getJson()); - } - return builder.build(); - - } - - /** - * 构造 @WebMethod 注解 - * @param constPool : {@link ConstPool} instance - * @param method : {@link SoapMethod} instance - * @return {@link Annotation} instance - */ - public static Annotation annotWebMethod(final ConstPool constPool, final SoapMethod method) { - - CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebMethod.class, constPool) - .addStringMember("operationName", method.getOperationName()); - if (StringUtils.isNotBlank(method.getAction())) { - builder.addStringMember("action", method.getAction()); - } - builder.addBooleanMember("exclude", method.isExclude()); - return builder.build(); - - } - - /** - * 构造 @WebParam 参数注解 - * @param constPool : {@link ConstPool} instance - * @param params : The {@link SoapParam} params - * @return {@link Annotation} instance - */ - public static Annotation[][] annotParams(final ConstPool constPool, SoapParam... params) { - - // 添加 @WebParam 参数注解 - if (params != null && params.length > 0) { - - // 参数模式定义 - // Map modeMap = modeMap(constPool, params); - - Annotation[][] paramArrays = new Annotation[params.length][1]; - - for (int i = 0; i < params.length; i++) { - - CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebParam.class, constPool) - .addStringMember("name", params[i].getName()) - .addStringMember("targetNamespace", params[i].getTargetNamespace()) - .addEnumMember("mode", params[i].getMode()) - .addBooleanMember("header", params[i].isHeader()); - if (StringUtils.isNotBlank(params[i].getPartName())) { - builder.addStringMember("partName", params[i].getPartName()); - } - paramArrays[i][0] = builder.build(); - - /* - - Annotation paramAnnot = new Annotation(WebParam.class.getName(), constPool); - paramAnnot.addMemberValue("name", new StringMemberValue(params[i].getName(), constPool)); - if (StringUtils.isNotBlank(params[i].getPartName())) { - paramAnnot.addMemberValue("partName", new StringMemberValue(params[i].getPartName(), constPool)); - } - paramAnnot.addMemberValue("targetNamespace", - new StringMemberValue(params[i].getTargetNamespace(), constPool)); - paramAnnot.addMemberValue("mode", modeMap.get(params[i].getMode().name())); - if (params[i].isHeader()) { - paramAnnot.addMemberValue("header", new BooleanMemberValue(true, constPool)); - } - paramArrays[i][0] = paramAnnot;*/ - - } - - return paramArrays; - - } - return null; - } - - /** - * 构造 @WebResult 注解 - * @param constPool : {@link ConstPool} instance - * @param result : {@link SoapResult} instance - * @param : 泛型参数 - * @return {@link Annotation} instance - */ - public static Annotation annotWebResult(final ConstPool constPool, final SoapResult result) { - - CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebResult.class, constPool) - .addStringMember("name", StringUtils.isNotBlank(result.getName()) ? result.getName() : "") - .addBooleanMember("header", result.isHeader()); - if (StringUtils.isNotBlank(result.getPartName())) { - builder.addStringMember("partName", result.getPartName()); - } - if (StringUtils.isNotBlank(result.getTargetNamespace())) { - builder.addStringMember("targetNamespace", result.getTargetNamespace()); - } - return builder.build(); - - } - - - public static void rm(CtClass declaring) { - - } - -} +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint.utils; + +import java.lang.reflect.InvocationHandler; + +import jakarta.jws.HandlerChain; +import jakarta.jws.WebMethod; +import jakarta.jws.WebParam; +import jakarta.jws.WebResult; +import jakarta.jws.WebService; +import jakarta.xml.ws.Service; +import jakarta.xml.ws.ServiceMode; +import jakarta.xml.ws.WebServiceProvider; +import jakarta.xml.ws.soap.Addressing; +import jakarta.xml.ws.soap.AddressingFeature.Responses; + +import org.apache.commons.lang3.StringUtils; +import org.apache.cxf.endpoint.annotation.WebBound; +import org.apache.cxf.endpoint.jaxws.definition.SoapBound; +import org.apache.cxf.endpoint.jaxws.definition.SoapMethod; +import org.apache.cxf.endpoint.jaxws.definition.SoapParam; +import org.apache.cxf.endpoint.jaxws.definition.SoapResult; +import org.apache.cxf.endpoint.jaxws.definition.SoapService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.github.easy4j.javassist.bytecode.CtAnnotationBuilder; +import io.github.easy4j.javassist.utils.JavassistUtils; + +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtMethod; +import javassist.CtNewConstructor; +import javassist.NotFoundException; +import javassist.bytecode.AnnotationsAttribute; +import javassist.bytecode.ConstPool; +import javassist.bytecode.ParameterAnnotationsAttribute; +import javassist.bytecode.annotation.Annotation; + +public class JaxwsEndpointApiUtils { + + protected static final Logger LOG = LoggerFactory.getLogger(JaxwsEndpointApiUtils.class); + + public static CtClass makeClass(final ClassPool pool, final String classname) + throws NotFoundException, CannotCompileException { + + CtClass declaring = pool.getOrNull(classname); + if (null == declaring) { + declaring = pool.makeClass(classname); + } + + // 当 ClassPool.doPruning=true的时候,Javassist 在CtClass + // object被冻结时,会释放存储在ClassPool对应的数据。这样做可以减少javassist的内存消耗。默认情况ClassPool.doPruning=false。 + declaring.stopPruning(true); + + return declaring; + } + + public static CtConstructor defaultConstructor(final CtClass declaring) throws CannotCompileException { + // 默认添加无参构造器 + CtConstructor cons = new CtConstructor(null, declaring); + cons.setBody("{}"); + return cons; + } + + 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); + + } + + public static CtClass makeInterface(final ClassPool pool, final String classname) + throws NotFoundException, CannotCompileException { + + CtClass declaring = pool.getOrNull(classname); + if (null == declaring) { + declaring = pool.makeInterface(classname); + } + + // 当 ClassPool.doPruning=true的时候,Javassist 在CtClass + // object被冻结时,会释放存储在ClassPool对应的数据。这样做可以减少javassist的内存消耗。默认情况ClassPool.doPruning=false。 + declaring.stopPruning(true); + + return declaring; + } + + + 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); + + } + + 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 + */ + public static Annotation annotWebServiceProvider(final ConstPool constPool, String wsdlLocation, + String serviceName, String targetNamespace, String portName) { + + wsdlLocation = StringUtils.isNotBlank(wsdlLocation) ? wsdlLocation : ""; + serviceName = StringUtils.isNotBlank(serviceName) ? serviceName : ""; + targetNamespace = StringUtils.isNotBlank(targetNamespace) ? targetNamespace : ""; + portName = StringUtils.isNotBlank(portName) ? portName : ""; + + return CtAnnotationBuilder.create(WebServiceProvider.class, constPool) + .addStringMember("wsdlLocation", wsdlLocation).addStringMember("serviceName", serviceName) + .addStringMember("targetNamespace", targetNamespace).addStringMember("portName", portName).build(); + + } + + /** + * 构造 @WebService 注解 + * @param constPool : {@link ConstPool} instance + * @param service : {@link SoapService} instance + * @return {@link Annotation} instance + */ + public static Annotation annotWebService(final ConstPool constPool, final SoapService service) { + + CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebService.class, constPool) + .addStringMember("name", service.getName()) + .addStringMember("targetNamespace", service.getTargetNamespace()); + + if (StringUtils.isNotBlank(service.getServiceName())) { + builder.addStringMember("serviceName", service.getServiceName()); + } + if (StringUtils.isNotBlank(service.getPortName())) { + builder.addStringMember("portName", service.getPortName()); + } + if (StringUtils.isNotBlank(service.getWsdlLocation())) { + builder.addStringMember("wsdlLocation", service.getWsdlLocation()); + } + if (StringUtils.isNotBlank(service.getEndpointInterface())) { + builder.addStringMember("endpointInterface", service.getEndpointInterface()); + } + + return builder.build(); + + } + + /** + * 构造 @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 + */ + public static Annotation annotAddressing(final ConstPool constPool, final boolean enabled, final boolean required, + final Responses responses) { + + return CtAnnotationBuilder.create(Addressing.class, constPool) + .addBooleanMember("enabled", enabled) + .addBooleanMember("required", required) + .addEnumMember("responses", responses).build(); + + } + + /** + * 构造 @ServiceMode 注解 + * @param constPool : {@link ConstPool} instance + * @param mode : the mode of {@link Service} + * @return {@link Annotation} instance + */ + 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 + */ + public static Annotation annotHandlerChain(final ConstPool constPool, String name, String file) { + + CtAnnotationBuilder builder = CtAnnotationBuilder.create(HandlerChain.class, constPool); + if (StringUtils.isNotBlank(name)) { + builder.addStringMember("name", name ); + } + if (StringUtils.isNotBlank(file)) { + builder.addStringMember("file", file); + } + return builder.build(); + + } + + + /** + * + * 为方法添加 @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 + */ + public static void methodAnnotations(final CtMethod ctMethod, final ConstPool constPool, final SoapResult result, final SoapMethod method, final SoapBound bound, SoapParam... params) { + + // 添加方法注解 + AnnotationsAttribute methodAttr = JavassistUtils.getAnnotationsAttribute(ctMethod); + + // 添加 @WebBound 注解 + if (bound != null) { + methodAttr.addAnnotation(JaxwsEndpointApiUtils.annotWebBound(constPool, bound)); + } + + // 添加 @WebMethod 注解 + methodAttr.addAnnotation(JaxwsEndpointApiUtils.annotWebMethod(constPool, method)); + + // 添加 @WebResult 注解 + if (null != result ) { + methodAttr.addAnnotation(JaxwsEndpointApiUtils.annotWebResult(constPool, result)); + } + + ctMethod.getMethodInfo().addAttribute(methodAttr); + + // 添加 @WebParam 参数注解 + if(params != null && params.length > 0) { + + ParameterAnnotationsAttribute parameterAtrribute = JavassistUtils.getParameterAnnotationsAttribute(ctMethod); + Annotation[][] paramArrays = JaxwsEndpointApiUtils.annotParams(constPool, params); + parameterAtrribute.setAnnotations(paramArrays); + ctMethod.getMethodInfo().addAttribute(parameterAtrribute); + + } + + } + + /** + * 设置方法体 + * @param ctMethod : {@link CtMethod} instance + * @param method : {@link SoapMethod} instance + * @throws CannotCompileException if can't compile + */ + public static void methodBody(final CtMethod ctMethod, final SoapMethod method) throws CannotCompileException { + + // 构造方法体 + StringBuilder body = new StringBuilder(); + body.append("{\n"); + body.append("if(getHandler() != null){\n"); + body.append("Method method = this.getClass().getDeclaredMethod(\"" + method.getOperationName() + "\", $sig);"); + body.append("return ($r)getHandler().invoke($0, method, $args);"); + body.append("}\n"); + body.append("return null;\n"); + body.append("}"); + // 将方法的内容设置为要写入的代码,当方法被 abstract修饰时,该修饰符被移除。 + ctMethod.setBody(body.toString()); + + } + + /** + * 设置方法异常捕获逻辑 + * @param pool : {@link ClassPool} instance + * @param ctMethod : {@link CtMethod} instance + * @throws NotFoundException if not found + * @throws CannotCompileException if can't compile + */ + 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 SoapBound} instance + * @return {@link Annotation} instance + */ + public static Annotation annotWebBound(final ConstPool constPool, final SoapBound bound) { + + CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebBound.class, constPool). + addStringMember("uid", bound.getUid()); + if (StringUtils.isNotBlank(bound.getJson())) { + builder.addStringMember("json", bound.getJson()); + } + return builder.build(); + + } + + /** + * 构造 @WebMethod 注解 + * @param constPool : {@link ConstPool} instance + * @param method : {@link SoapMethod} instance + * @return {@link Annotation} instance + */ + public static Annotation annotWebMethod(final ConstPool constPool, final SoapMethod method) { + + CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebMethod.class, constPool) + .addStringMember("operationName", method.getOperationName()); + if (StringUtils.isNotBlank(method.getAction())) { + builder.addStringMember("action", method.getAction()); + } + builder.addBooleanMember("exclude", method.isExclude()); + return builder.build(); + + } + + /** + * 构造 @WebParam 参数注解 + * @param constPool : {@link ConstPool} instance + * @param params : The {@link SoapParam} params + * @return {@link Annotation} instance + */ + public static Annotation[][] annotParams(final ConstPool constPool, SoapParam... params) { + + // 添加 @WebParam 参数注解 + if (params != null && params.length > 0) { + + // 参数模式定义 + // Map modeMap = modeMap(constPool, params); + + Annotation[][] paramArrays = new Annotation[params.length][1]; + + for (int i = 0; i < params.length; i++) { + + CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebParam.class, constPool) + .addStringMember("name", params[i].getName()) + .addStringMember("targetNamespace", params[i].getTargetNamespace()) + .addEnumMember("mode", params[i].getMode()) + .addBooleanMember("header", params[i].isHeader()); + if (StringUtils.isNotBlank(params[i].getPartName())) { + builder.addStringMember("partName", params[i].getPartName()); + } + paramArrays[i][0] = builder.build(); + + /* + + Annotation paramAnnot = new Annotation(WebParam.class.getName(), constPool); + paramAnnot.addMemberValue("name", new StringMemberValue(params[i].getName(), constPool)); + if (StringUtils.isNotBlank(params[i].getPartName())) { + paramAnnot.addMemberValue("partName", new StringMemberValue(params[i].getPartName(), constPool)); + } + paramAnnot.addMemberValue("targetNamespace", + new StringMemberValue(params[i].getTargetNamespace(), constPool)); + paramAnnot.addMemberValue("mode", modeMap.get(params[i].getMode().name())); + if (params[i].isHeader()) { + paramAnnot.addMemberValue("header", new BooleanMemberValue(true, constPool)); + } + paramArrays[i][0] = paramAnnot;*/ + + } + + return paramArrays; + + } + return null; + } + + /** + * 构造 @WebResult 注解 + * @param constPool : {@link ConstPool} instance + * @param result : {@link SoapResult} instance + * @param : 泛型参数 + * @return {@link Annotation} instance + */ + public static Annotation annotWebResult(final ConstPool constPool, final SoapResult result) { + + CtAnnotationBuilder builder = CtAnnotationBuilder.create(WebResult.class, constPool) + .addStringMember("name", StringUtils.isNotBlank(result.getName()) ? result.getName() : "") + .addBooleanMember("header", result.isHeader()); + if (StringUtils.isNotBlank(result.getPartName())) { + builder.addStringMember("partName", result.getPartName()); + } + if (StringUtils.isNotBlank(result.getTargetNamespace())) { + builder.addStringMember("targetNamespace", result.getTargetNamespace()); + } + return builder.build(); + + } + + + public static void rm(CtClass declaring) { + + } + +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/Customer.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/Customer.java index 267f155..ff98f03 100644 --- a/src/test/java/org/apache/cxf/endpoint/jaxrs/Customer.java +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/Customer.java @@ -1,49 +1,49 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint.jaxrs; - - -import java.util.Date; - -import jakarta.xml.bind.annotation.XmlRootElement; - -public class Customer { - private String id; - private String name; - private Date birthday; - public String getId() { - return id; - } - public void setId(String id) { - this.id = id; - } - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - public Date getBirthday() { - return birthday; - } - public void setBirthday(Date birthday) { - this.birthday = birthday; - } - /*@Override - public String toString() { - return org.apache.commons.lang.builder.ToStringBuilder.reflectionToString(this); - } */ +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint.jaxrs; + + +import java.util.Date; + +import jakarta.xml.bind.annotation.XmlRootElement; + +public class Customer { + private String id; + private String name; + private Date birthday; + public String getId() { + return id; + } + public void setId(String id) { + this.id = id; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public Date getBirthday() { + return birthday; + } + public void setBirthday(Date birthday) { + this.birthday = birthday; + } + /*@Override + public String toString() { + return org.apache.commons.lang.builder.ToStringBuilder.reflectionToString(this); + } */ } \ No newline at end of file diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/EndpointApiSample.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/EndpointApiSample.java index aae3ad2..58eb41d 100644 --- a/src/test/java/org/apache/cxf/endpoint/jaxrs/EndpointApiSample.java +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/EndpointApiSample.java @@ -1,88 +1,88 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint.jaxrs; - -import java.lang.reflect.InvocationHandler; -import java.util.Calendar; - -import jakarta.ws.rs.BeanParam; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.DefaultValue; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; -import jakarta.ws.rs.core.Response; - -import org.apache.cxf.endpoint.EndpointApi; - - -@Path(value = "/sample") -@Produces("application/json") -public class EndpointApiSample extends EndpointApi { - - public EndpointApiSample() { - } - - public EndpointApiSample(InvocationHandler handler) { - super(handler); - } - - @POST - @Path("add") - @Consumes - public Response post(@BeanParam Customer myBean) { - return Response.status(200).entity("getUserById is called, id : " + myBean.getId()).build(); - } - - @GET - @Path("/{type}/list") - @Consumes - public Customer findCustomerByType(@PathParam("type") @DefaultValue("a") String type) { - Customer customer = new Customer(); - customer.setId(type); - customer.setName("xiaojing" + type); - customer.setBirthday(Calendar.getInstance().getTime()); - System.out.println(">>>>>>>>>>>>>服务端信息:" + customer); - return customer; - } - - @GET - @Path("/{id}/info") - @Consumes - public Customer findCustomerById(@PathParam("id") String id) { - Customer customer = new Customer(); - customer.setId(id); - customer.setName("xiaojing" + id); - customer.setBirthday(Calendar.getInstance().getTime()); - System.out.println(">>>>>>>>>>>>>服务端信息:" + customer); - return customer; - } - - @GET - @Path(value = "/search") - public Customer findCustomerByName(@QueryParam("name") String name) { - Customer customer = new Customer(); - customer.setId(name); - customer.setName(name); - customer.setBirthday(Calendar.getInstance().getTime()); - System.out.println(">>>>>>>>>>>>>服务端信息:" + customer); - return customer; - } - +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint.jaxrs; + +import java.lang.reflect.InvocationHandler; +import java.util.Calendar; + +import jakarta.ws.rs.BeanParam; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DefaultValue; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.Response; + +import org.apache.cxf.endpoint.EndpointApi; + + +@Path(value = "/sample") +@Produces("application/json") +public class EndpointApiSample extends EndpointApi { + + public EndpointApiSample() { + } + + public EndpointApiSample(InvocationHandler handler) { + super(handler); + } + + @POST + @Path("add") + @Consumes + public Response post(@BeanParam Customer myBean) { + return Response.status(200).entity("getUserById is called, id : " + myBean.getId()).build(); + } + + @GET + @Path("/{type}/list") + @Consumes + public Customer findCustomerByType(@PathParam("type") @DefaultValue("a") String type) { + Customer customer = new Customer(); + customer.setId(type); + customer.setName("xiaojing" + type); + customer.setBirthday(Calendar.getInstance().getTime()); + System.out.println(">>>>>>>>>>>>>服务端信息:" + customer); + return customer; + } + + @GET + @Path("/{id}/info") + @Consumes + public Customer findCustomerById(@PathParam("id") String id) { + Customer customer = new Customer(); + customer.setId(id); + customer.setName("xiaojing" + id); + customer.setBirthday(Calendar.getInstance().getTime()); + System.out.println(">>>>>>>>>>>>>服务端信息:" + customer); + return customer; + } + + @GET + @Path(value = "/search") + public Customer findCustomerByName(@QueryParam("name") String name) { + Customer customer = new Customer(); + customer.setId(name); + customer.setName(name); + customer.setBirthday(Calendar.getInstance().getTime()); + System.out.println(">>>>>>>>>>>>>服务端信息:" + customer); + return customer; + } + } \ No newline at end of file diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/EndpointApiSample.java b/src/test/java/org/apache/cxf/endpoint/jaxws/EndpointApiSample.java index bcb3ead..72c0caf 100644 --- a/src/test/java/org/apache/cxf/endpoint/jaxws/EndpointApiSample.java +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/EndpointApiSample.java @@ -1,69 +1,69 @@ -/* - * Copyright (c) 2018, hiwepy (https://github.com/hiwepy). - * - * Licensed 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. - */ -package org.apache.cxf.endpoint.jaxws; - -import java.lang.reflect.InvocationHandler; - -import jakarta.jws.WebMethod; -import jakarta.jws.WebParam; -import jakarta.jws.WebResult; -import jakarta.jws.WebService; - -import org.apache.cxf.endpoint.EndpointApi; - -@WebService(serviceName = "sample", // 与接口中指定的name一致 - targetNamespace = "http://ws.cxf.com/"// , // 与接口中的命名空间一致,一般是接口的包名倒 -// endpointInterface = "org.apache.cxf.spring.boot.api.JaxwsSample"// 接口地址 -) -public class EndpointApiSample extends EndpointApi { - - public EndpointApiSample() { - } - - public EndpointApiSample(InvocationHandler handler) { - super(handler); - } - - /* - * action 需要填写完成,有发现使用Soap接口调用无法识别 - */ - @WebMethod(operationName = "sayHello", action = "http://ws.cxf.com/sayHello/") - @WebResult(name = "String", targetNamespace = "") - public String sayHello(@WebParam(name = "userName") String name) { - - //getHandler().invoke(this, method, args); - //Method method = this.getClass().getDeclaredMethod(name, $sig); - //getHandler().invoke($0, method, $args); - - return "Hello ," + name; - } - - @WebMethod(operationName = "sayHello2", action = "http://ws.cxf.com/sayHello/") - @WebResult(name = "String", targetNamespace = "") - public String sayHello2(@WebParam(name = "userName") String name) { - return "Hello ," + name; - } - - @WebMethod(operationName = "invoke", action = "http://ws.cxf.com/sayHello/") - @WebResult(name = "String", targetNamespace = "") - public String invoke(@WebParam(name = "userName") String name) { - - //this.getHandler().invoke(this, method, args) - - return "Hello ," + name; - } - +/* + * Copyright (c) 2018, Loong Wan (https://github.com/loong10k). + * + * Licensed 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. + */ +package org.apache.cxf.endpoint.jaxws; + +import java.lang.reflect.InvocationHandler; + +import jakarta.jws.WebMethod; +import jakarta.jws.WebParam; +import jakarta.jws.WebResult; +import jakarta.jws.WebService; + +import org.apache.cxf.endpoint.EndpointApi; + +@WebService(serviceName = "sample", // 与接口中指定的name一致 + targetNamespace = "http://ws.cxf.com/"// , // 与接口中的命名空间一致,一般是接口的包名倒 +// endpointInterface = "org.apache.cxf.spring.boot.api.JaxwsSample"// 接口地址 +) +public class EndpointApiSample extends EndpointApi { + + public EndpointApiSample() { + } + + public EndpointApiSample(InvocationHandler handler) { + super(handler); + } + + /* + * action 需要填写完成,有发现使用Soap接口调用无法识别 + */ + @WebMethod(operationName = "sayHello", action = "http://ws.cxf.com/sayHello/") + @WebResult(name = "String", targetNamespace = "") + public String sayHello(@WebParam(name = "userName") String name) { + + //getHandler().invoke(this, method, args); + //Method method = this.getClass().getDeclaredMethod(name, $sig); + //getHandler().invoke($0, method, $args); + + return "Hello ," + name; + } + + @WebMethod(operationName = "sayHello2", action = "http://ws.cxf.com/sayHello/") + @WebResult(name = "String", targetNamespace = "") + public String sayHello2(@WebParam(name = "userName") String name) { + return "Hello ," + name; + } + + @WebMethod(operationName = "invoke", action = "http://ws.cxf.com/sayHello/") + @WebResult(name = "String", targetNamespace = "") + public String invoke(@WebParam(name = "userName") String name) { + + //this.getHandler().invoke(this, method, args) + + return "Hello ," + name; + } + } \ No newline at end of file From 320663949f355c158edfe8846f279f148e3e2aee Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Thu, 6 Aug 2026 15:29:54 +0800 Subject: [PATCH 13/56] =?UTF-8?q?refactor:=20Premain-Class=20=E5=8C=85?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=20io.github.hiwepy=20=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=E4=B8=BA=20io.github.easy4j?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 56a0b01..fa98a09 100644 --- a/pom.xml +++ b/pom.xml @@ -200,7 +200,7 @@ true - io.github.hiwepy.javassist.MyAgent2 + io.github.easy4j.javassist.MyAgent2 From 4e9507a4883766836be716f439ea61bb0f0cad17 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Thu, 6 Aug 2026 16:15:53 +0800 Subject: [PATCH 14/56] =?UTF-8?q?build:=20=E8=A1=A5=E9=BD=90=20Maven=20Cen?= =?UTF-8?q?tral=20=E5=8F=91=E5=B8=83=E9=85=8D=E7=BD=AE=EF=BC=88licenses/sc?= =?UTF-8?q?m/sources/javadoc/gpg/central-publishing=20=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fa98a09..b101aea 100644 --- a/pom.xml +++ b/pom.xml @@ -251,7 +251,8 @@ [1.8,) - -Xdoclint:none + 0.7.0 + -Xdoclint:none From cb2b4b3c28ddda4c35ba37d8a883a2ebe2196aba Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Thu, 6 Aug 2026 16:31:15 +0800 Subject: [PATCH 15/56] =?UTF-8?q?build:=20=E6=B7=BB=E5=8A=A0=20Maven=20Cen?= =?UTF-8?q?tral=20=E5=8F=91=E5=B8=83=20profile=EF=BC=88gpg=20=E7=AD=BE?= =?UTF-8?q?=E5=90=8D=20+=20central-publishing=20=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b101aea..4dd13ec 100644 --- a/pom.xml +++ b/pom.xml @@ -321,7 +321,39 @@ - + + central + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.central + central-publishing-maven-plugin + ${central-publishing-maven-plugin.version} + true + + central + true + published + + + + + + UTF-8 From ce6553418b21809ee23ca02855d208a1328e051b Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Thu, 6 Aug 2026 18:46:38 +0800 Subject: [PATCH 16/56] =?UTF-8?q?feat(pom):=20=E8=A1=A5=20name/url?= =?UTF-8?q?=EF=BC=8Ccentral-publishing-maven-plugin=20=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=200.11.0=EF=BC=8C=E5=A2=9E=E5=8A=A0=20maven-compiler-plugin=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E4=B8=8E=20jacoco=20=E8=A6=86=E7=9B=96?= =?UTF-8?q?=E7=8E=87=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4dd13ec..4d17ee3 100644 --- a/pom.xml +++ b/pom.xml @@ -4,6 +4,8 @@ 4.0.0 io.github.easy4j cxf-rt-javassist + ${project.groupId}:${project.artifactId} + https://github.com/easy-4-java/${project.artifactId} 1.0.x.20260630-SNAPSHOT 通过javassist生成基于cxf的jaxws、jaxrs实现 ${project.groupId}:${project.artifactId} @@ -242,6 +244,69 @@ + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + prepare-agent + + prepare-agent + + + + report + verify + + report + + + + check + verify + + check + + + false + + + BUNDLE + + + LINE + COVEREDRATIO + 0.90 + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + true + ${project.build.sourceEncoding} + 512M + + + org.projectlombok + lombok + ${lombok.version} + + + + + @@ -251,9 +316,10 @@ [1.8,) - 0.7.0 + 0.11.0 -Xdoclint:none - + 0.8.15 + release From 85a5b930023153fb5be5b1e00a01cb6a69b9155a Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Thu, 6 Aug 2026 19:14:06 +0800 Subject: [PATCH 17/56] =?UTF-8?q?fix(pom):=20=E5=8E=BB=E9=87=8D=20name/url?= =?UTF-8?q?=E3=80=81=E7=BB=9F=E4=B8=80=20XML=20=E7=BC=A9=E8=BF=9B=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=EF=BC=88xmllint=EF=BC=89=E3=80=81=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=20scm=20URL=EF=BC=8C=E7=A7=BB=E9=99=A4=E8=A6=86=E7=9B=96=20par?= =?UTF-8?q?ent=20=E7=89=88=E6=9C=AC=E7=AE=A1=E7=90=86=E7=9A=84=E7=A9=BA?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E4=BE=9D=E8=B5=96=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 1059 +++++++++++++++++++++++++++---------------------------- 1 file changed, 528 insertions(+), 531 deletions(-) diff --git a/pom.xml b/pom.xml index 4d17ee3..da9a9a7 100644 --- a/pom.xml +++ b/pom.xml @@ -1,538 +1,535 @@ - - 4.0.0 - io.github.easy4j - cxf-rt-javassist - ${project.groupId}:${project.artifactId} - https://github.com/easy-4-java/${project.artifactId} - 1.0.x.20260630-SNAPSHOT - 通过javassist生成基于cxf的jaxws、jaxrs实现 - ${project.groupId}:${project.artifactId} - https://github.com/easy-4-java/${project.artifactId} - jar - - - - The Apache Software License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt - - - - - scm:git:https:github.com/easy-4-java/${project.artifactId}.git - scm:git:https:github.com/easy-4-java/${project.artifactId}.git - https:github.com/easy-4-java/${project.artifactId} - ${project.artifactId} - - - - - Loong Wan - hiwepy@gmail.com - https://github.com/loong10k - - developer - - +8 - - - - - - 2624322-release-6F6h6R - https://packages.aliyun.com/6927b116e6c3e0425dbdf60d/maven/2624322-release-6f6h6r - - - 2624322-snapshot-3EoOv3 - https://packages.aliyun.com/6927b116e6c3e0425dbdf60d/maven/2624322-snapshot-3eoov3 - - - - - ${project.artifactId} - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - ${java.version} - ${java.version} - ${project.build.sourceEncoding} - 512M - - - - - org.apache.maven.plugins - maven-deploy-plugin - ${maven-deploy-plugin.version} - - - - org.apache.maven.plugins - maven-enforcer-plugin - ${maven-enforcer-plugin.version} - - - default-cli - - enforce - - validate - - - - + 4.0.0 + io.github.easy4j + cxf-rt-javassist + ${project.groupId}:${project.artifactId} + https://github.com/easy-4-java/${project.artifactId} + 1.0.x.20260630-SNAPSHOT + 通过javassist生成基于cxf的jaxws、jaxrs实现 + jar + + + The Apache Software License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + + + + scm:git:https://github.com/easy-4-java/${project.artifactId}.git + scm:git:https://github.com/easy-4-java/${project.artifactId}.git + https://github.com/easy-4-java/${project.artifactId} + ${project.artifactId} + + + + Loong Wan + hiwepy@gmail.com + https://github.com/loong10k + + developer + + +8 + + + + + 2624322-release-6F6h6R + https://packages.aliyun.com/6927b116e6c3e0425dbdf60d/maven/2624322-release-6f6h6r + + + 2624322-snapshot-3EoOv3 + https://packages.aliyun.com/6927b116e6c3e0425dbdf60d/maven/2624322-snapshot-3eoov3 + + + + ${project.artifactId} + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + 512M + + + + org.apache.maven.plugins + maven-deploy-plugin + ${maven-deploy-plugin.version} + + + org.apache.maven.plugins + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + default-cli + + enforce + + validate + + + + You are running an older version of Maven. This application requires at least Maven ${maven.version}. - [${maven.version}.0,) - - - + [${maven.version}.0,) + + + You are running an older version of Java. This application requires at least JDK ${java.version}. - [${java.version}.0,) - - - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin.version} - - - sign-artifacts - verify - - sign - - - - - - - org.apache.maven.plugins - maven-install-plugin - ${maven-install-plugin.version} - - - - org.apache.maven.plugins - maven-resources-plugin - ${maven-resources-plugin.version} - - ${project.build.sourceEncoding} - - - - - org.apache.maven.plugins - maven-release-plugin - ${maven-release-plugin.version} - - v@{project.version} - true - false - release - deploy - - - - - org.apache.maven.plugins - maven-source-plugin - ${maven-source-plugin.version} - - true - - - - attach-sources - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin.version} - - - true - true - - - -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m + [${java.version}.0,) + + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + + sign-artifacts + verify + + sign + + + + + + org.apache.maven.plugins + maven-install-plugin + ${maven-install-plugin.version} + + + org.apache.maven.plugins + maven-resources-plugin + ${maven-resources-plugin.version} + + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-release-plugin + ${maven-release-plugin.version} + + v@{project.version} + true + false + release + deploy + + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + true + + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + true + true + -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m -Dfile.encoding=UTF-8 - - ${basedir}/target/test-classes - - - **/*Test.java - - - **/TestBean.java - - - - - - org.apache.maven.plugins - maven-jar-plugin - ${maven-jar-plugin.version} - - true - - true - - true - true - true - - - true - io.github.easy4j.javassist.MyAgent2 - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - ${maven-javadoc-plugin.version} - - ${project.build.sourceEncoding} - ${project.build.sourceEncoding} - ${project.build.sourceEncoding} - - - - attach-javadocs - package - - jar - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - ${maven-nexus-staging-plugin.version} - true - - ossrh - https://oss.sonatype.org/ - false - - 60 - 20 - true - - - - - - - org.jacoco - jacoco-maven-plugin - ${jacoco-maven-plugin.version} - - - prepare-agent - - prepare-agent - - - - report - verify - - report - - - - check - verify - - check - - - false - - - BUNDLE - - - LINE - COVEREDRATIO - 0.90 - - - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - ${java.version} - ${java.version} - true - ${project.build.sourceEncoding} - 512M - - - org.projectlombok - lombok - ${lombok.version} - - - - - - - - - - disable-javadoc-doclint - - [1.8,) - - - 0.11.0 - -Xdoclint:none - 0.8.15 - - - - release - - - - - org.apache.maven.plugins - maven-enforcer-plugin - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - org.apache.maven.plugins - maven-resources-plugin - - - - org.apache.maven.plugins - maven-surefire-plugin - - - - org.apache.maven.plugins - maven-jar-plugin - - - - org.apache.maven.plugins - maven-source-plugin - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - - org.apache.maven.plugins - maven-install-plugin - - - - org.apache.maven.plugins - maven-gpg-plugin - - - - org.apache.maven.plugins - maven-deploy-plugin - - - - org.apache.maven.plugins - maven-release-plugin - - - org.sonatype.plugins - nexus-staging-maven-plugin - - - - - - central - - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin.version} - - - sign-artifacts - verify - - sign - - - - - - org.sonatype.central - central-publishing-maven-plugin - ${central-publishing-maven-plugin.version} - true - - central - true - published - - - - - - - - - UTF-8 - UTF-8 - 1.8 - - 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 - 1.0.x.20260630-SNAPSHOT - 4.13.2 - 5.10.3 - 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 - ${javassist-plus.version} - - - org.projectlomboklombok${lombok.version}provided - - - junitjunittestorg.slf4jslf4j-apicommons-iocommons-iotestcommons-beanutilscommons-beanutilstestorg.apache.commonscommons-lang3org.apache.cxfcxf-rt-frontend-jaxwsprovidedorg.apache.cxfcxf-rt-frontend-jaxrsprovidedorg.javassistjavassistio.github.easy4jjavassist-plusorg.projectlomboklombokprovided \ No newline at end of file + + ${basedir}/target/test-classes + + + **/*Test.java + + + **/TestBean.java + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + true + + true + + true + true + true + + + true + io.github.easy4j.javassist.MyAgent2 + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + + + + attach-javadocs + package + + jar + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + ${maven-nexus-staging-plugin.version} + true + + ossrh + https://oss.sonatype.org/ + false + 60 + 20 + true + + + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + prepare-agent + + prepare-agent + + + + report + verify + + report + + + + check + verify + + check + + + false + + + BUNDLE + + + LINE + COVEREDRATIO + 0.90 + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + true + ${project.build.sourceEncoding} + 512M + + + org.projectlombok + lombok + ${lombok.version} + + + + + + + + + disable-javadoc-doclint + + [1.8,) + + + 0.11.0 + -Xdoclint:none + 0.8.15 + + + + release + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-resources-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + org.apache.maven.plugins + maven-install-plugin + + + org.apache.maven.plugins + maven-gpg-plugin + + + org.apache.maven.plugins + maven-deploy-plugin + + + org.apache.maven.plugins + maven-release-plugin + + + org.sonatype.plugins + nexus-staging-maven-plugin + + + + + + central + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.central + central-publishing-maven-plugin + ${central-publishing-maven-plugin.version} + true + + central + true + published + + + + + + + + UTF-8 + UTF-8 + 1.8 + 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 + 1.0.x.20260630-SNAPSHOT + 4.13.2 + 5.10.3 + 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 + ${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 + + +
From 597d05ceeccee68b1f805d4d0cf768c57d704ff9 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Thu, 6 Aug 2026 19:42:12 +0800 Subject: [PATCH 18/56] =?UTF-8?q?style(pom):=20=E6=8C=89=20ddd4j=20?= =?UTF-8?q?=E9=A3=8E=E6=A0=BC=E8=A1=A5=E5=85=85=E4=B8=AD=E6=96=87=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=E4=B8=8E=E6=8E=92=E7=89=88=EF=BC=88=E5=9D=97=E9=97=B4?= =?UTF-8?q?=E7=A9=BA=E8=A1=8C=EF=BC=89=EF=BC=8C=E7=BB=9F=E4=B8=80=E8=A6=86?= =?UTF-8?q?=E7=9B=96=E7=8E=87/=E5=8F=91=E5=B8=83=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E5=90=8D=E4=B8=BA=20maven-jacoco-plugin.vers?= =?UTF-8?q?ion=20=E4=B8=8E=20maven-central-publishing-plugin.version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index da9a9a7..94f9c46 100644 --- a/pom.xml +++ b/pom.xml @@ -8,18 +8,24 @@ 1.0.x.20260630-SNAPSHOT 通过javassist生成基于cxf的jaxws、jaxrs实现 jar + + The Apache Software License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0.txt + + scm:git:https://github.com/easy-4-java/${project.artifactId}.git scm:git:https://github.com/easy-4-java/${project.artifactId}.git https://github.com/easy-4-java/${project.artifactId} ${project.artifactId} + + Loong Wan @@ -31,6 +37,8 @@ +8 + + 2624322-release-6F6h6R @@ -41,10 +49,16 @@ https://packages.aliyun.com/6927b116e6c3e0425dbdf60d/maven/2624322-snapshot-3eoov3 + + ${project.artifactId} + + + + org.apache.maven.plugins maven-compiler-plugin @@ -91,6 +105,8 @@ + + org.apache.maven.plugins maven-gpg-plugin @@ -130,6 +146,8 @@ deploy + + org.apache.maven.plugins maven-source-plugin @@ -186,6 +204,8 @@ + + org.apache.maven.plugins maven-javadoc-plugin @@ -222,10 +242,12 @@ + + org.jacoco jacoco-maven-plugin - ${jacoco-maven-plugin.version} + ${maven-jacoco-plugin.version} prepare-agent @@ -264,6 +286,8 @@ + + org.apache.maven.plugins maven-compiler-plugin @@ -285,26 +309,34 @@ + + disable-javadoc-doclint [1.8,) + + - 0.11.0 + 0.11.0 -Xdoclint:none - 0.8.15 + 0.8.15 release + + org.apache.maven.plugins maven-enforcer-plugin + + org.apache.maven.plugins maven-compiler-plugin @@ -321,10 +353,14 @@ org.apache.maven.plugins maven-jar-plugin + + org.apache.maven.plugins maven-source-plugin + + org.apache.maven.plugins maven-javadoc-plugin @@ -333,6 +369,8 @@ org.apache.maven.plugins maven-install-plugin + + org.apache.maven.plugins maven-gpg-plugin @@ -354,8 +392,12 @@ central + + + + org.apache.maven.plugins maven-gpg-plugin @@ -370,10 +412,12 @@ + + org.sonatype.central central-publishing-maven-plugin - ${central-publishing-maven-plugin.version} + ${maven-central-publishing-plugin.version} true central @@ -385,6 +429,8 @@ + + UTF-8 UTF-8 @@ -423,7 +469,11 @@ 1.18.46 2.0.18 + + + + junit @@ -484,6 +534,8 @@ + + junit From 940814b15a23284d9465cc033872b41cb6257eb7 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Thu, 6 Aug 2026 21:01:59 +0800 Subject: [PATCH 19/56] =?UTF-8?q?style(pom):=20=E6=8C=89=20ddd4j=20?= =?UTF-8?q?=E7=B2=BE=E9=AB=93=E7=BB=9F=E4=B8=80=E4=BC=98=E5=8C=96=E2=80=94?= =?UTF-8?q?=E2=80=944=20=E7=A9=BA=E6=A0=BC=E7=BC=A9=E8=BF=9B=E3=80=81?= =?UTF-8?q?=E4=BE=9D=E8=B5=96(For=20Xxx/mvn=E9=93=BE=E6=8E=A5)=E3=80=81?= =?UTF-8?q?=E6=8F=92=E4=BB=B6(=E9=93=BE=E6=8E=A5+=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E8=AF=B4=E6=98=8E+=E9=85=8D=E7=BD=AE=E9=A1=B9=E6=B3=A8?= =?UTF-8?q?=E9=87=8A)=E3=80=81=E5=B1=9E=E6=80=A7=E5=88=86=E7=BB=84?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=E3=80=81=E5=9D=97=E9=97=B4=E6=8E=92=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 1224 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 652 insertions(+), 572 deletions(-) diff --git a/pom.xml b/pom.xml index 94f9c46..7a6edec 100644 --- a/pom.xml +++ b/pom.xml @@ -1,587 +1,667 @@ - 4.0.0 - io.github.easy4j - cxf-rt-javassist - ${project.groupId}:${project.artifactId} - https://github.com/easy-4-java/${project.artifactId} - 1.0.x.20260630-SNAPSHOT - 通过javassist生成基于cxf的jaxws、jaxrs实现 - jar - - - - - The Apache Software License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt - - - - - - scm:git:https://github.com/easy-4-java/${project.artifactId}.git - scm:git:https://github.com/easy-4-java/${project.artifactId}.git + 4.0.0 + io.github.easy4j + cxf-rt-javassist + ${project.groupId}:${project.artifactId} https://github.com/easy-4-java/${project.artifactId} - ${project.artifactId} - - - - - - Loong Wan - hiwepy@gmail.com - https://github.com/loong10k - - developer - - +8 - - - - - - - 2624322-release-6F6h6R - https://packages.aliyun.com/6927b116e6c3e0425dbdf60d/maven/2624322-release-6f6h6r - - - 2624322-snapshot-3EoOv3 - https://packages.aliyun.com/6927b116e6c3e0425dbdf60d/maven/2624322-snapshot-3eoov3 - - - - - - ${project.artifactId} - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - ${java.version} - ${java.version} - ${project.build.sourceEncoding} - 512M - - - - org.apache.maven.plugins - maven-deploy-plugin - ${maven-deploy-plugin.version} - - - org.apache.maven.plugins - maven-enforcer-plugin - ${maven-enforcer-plugin.version} - - - default-cli - - enforce - - validate - - - - + 1.0.x.20260630-SNAPSHOT + 通过javassist生成基于cxf的jaxws、jaxrs实现 + jar + + + + + The Apache Software License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + + + + + + scm:git:https://github.com/easy-4-java/${project.artifactId}.git + scm:git:https://github.com/easy-4-java/${project.artifactId}.git + https://github.com/easy-4-java/${project.artifactId} + ${project.artifactId} + + + + + + Loong Wan + hiwepy@gmail.com + https://github.com/loong10k + + developer + + +8 + + + + + + + 2624322-release-6F6h6R + https://packages.aliyun.com/6927b116e6c3e0425dbdf60d/maven/2624322-release-6f6h6r + + + 2624322-snapshot-3EoOv3 + https://packages.aliyun.com/6927b116e6c3e0425dbdf60d/maven/2624322-snapshot-3eoov3 + + + + + + ${project.artifactId} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + ${java.version} + + ${java.version} + + ${project.build.sourceEncoding} + + 512M + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + ${maven-deploy-plugin.version} + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + default-cli + + enforce + + validate + + + + You are running an older version of Maven. This application requires at least Maven ${maven.version}. - [${maven.version}.0,) - - - + [${maven.version}.0,) + + + You are running an older version of Java. This application requires at least JDK ${java.version}. - [${java.version}.0,) - - - - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin.version} - - - sign-artifacts - verify - - sign - - - - - - org.apache.maven.plugins - maven-install-plugin - ${maven-install-plugin.version} - - - org.apache.maven.plugins - maven-resources-plugin - ${maven-resources-plugin.version} - - ${project.build.sourceEncoding} - - - - org.apache.maven.plugins - maven-release-plugin - ${maven-release-plugin.version} - - v@{project.version} - true - false - release - deploy - - - - - - org.apache.maven.plugins - maven-source-plugin - ${maven-source-plugin.version} - - true - - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin.version} - - true - true - -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m + [${java.version}.0,) + + + + + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + + sign-artifacts + verify + + sign + + + + + + + + + org.apache.maven.plugins + maven-install-plugin + ${maven-install-plugin.version} + + + + + + org.apache.maven.plugins + maven-resources-plugin + ${maven-resources-plugin.version} + + ${project.build.sourceEncoding} + + + + + + + org.apache.maven.plugins + maven-release-plugin + ${maven-release-plugin.version} + + v@{project.version} + true + false + release + deploy + + + + + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + true + + + + attach-sources + + jar-no-fork + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + true + true + -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m -Dfile.encoding=UTF-8 - - ${basedir}/target/test-classes - - - **/*Test.java - - - **/TestBean.java - - - - - org.apache.maven.plugins - maven-jar-plugin - ${maven-jar-plugin.version} - - true - - true - - true - true - true - - - true - io.github.easy4j.javassist.MyAgent2 - - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - ${maven-javadoc-plugin.version} - - ${project.build.sourceEncoding} - ${project.build.sourceEncoding} - ${project.build.sourceEncoding} - - - - attach-javadocs - package - - jar - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - ${maven-nexus-staging-plugin.version} - true - - ossrh - https://oss.sonatype.org/ - false - 60 - 20 - true - - - - - - - - - org.jacoco - jacoco-maven-plugin - ${maven-jacoco-plugin.version} - - - prepare-agent - - prepare-agent - - - - report - verify - - report - - - - check - verify - - check - - - false - - - BUNDLE - - - LINE - COVEREDRATIO - 0.90 - - - - - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - ${java.version} - ${java.version} - true - ${project.build.sourceEncoding} - 512M - - - org.projectlombok - lombok - ${lombok.version} - - - - - - - - - - - disable-javadoc-doclint - - [1.8,) - - - - - 0.11.0 - -Xdoclint:none - 0.8.15 - - - - release - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - org.apache.maven.plugins - maven-resources-plugin - - - org.apache.maven.plugins - maven-surefire-plugin - - - org.apache.maven.plugins - maven-jar-plugin - - - - - org.apache.maven.plugins - maven-source-plugin - - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - org.apache.maven.plugins - maven-install-plugin - - - - - org.apache.maven.plugins - maven-gpg-plugin - - - org.apache.maven.plugins - maven-deploy-plugin - - - org.apache.maven.plugins - maven-release-plugin - - - org.sonatype.plugins - nexus-staging-maven-plugin - - - - - - central + + ${basedir}/target/test-classes + + + **/*Test.java + + + **/TestBean.java + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + true + + true + + true + true + true + + + true + io.github.easy4j.javassist.MyAgent2 + + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + + + + attach-javadocs + package + + jar + + + + - - + + + org.sonatype.plugins + nexus-staging-maven-plugin + ${maven-nexus-staging-plugin.version} + true + + ossrh + https://oss.sonatype.org/ + false + 60 + 20 + true + + + + - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin.version} - - - sign-artifacts - verify - - sign - - - - - - - - org.sonatype.central - central-publishing-maven-plugin - ${maven-central-publishing-plugin.version} - true - - central - true - published - - + + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + prepare-agent + + prepare-agent + + + + report + verify + + report + + + + check + verify + + check + + + false + + + BUNDLE + + + LINE + COVEREDRATIO + 0.90 + + + + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + + true + ${project.build.sourceEncoding} + 512M + + + org.projectlombok + lombok + ${lombok.version} + + + + - - - - - - - UTF-8 - UTF-8 - 1.8 - 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 - 1.0.x.20260630-SNAPSHOT - 4.13.2 - 5.10.3 - 1.18.46 - 2.0.18 - - - - - - + + + + + + disable-javadoc-doclint + + [1.8,) + + + 0.11.0 + -Xdoclint:none + 0.8.15 + + + + + + release + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + + + org.apache.maven.plugins + maven-source-plugin + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + + + org.apache.maven.plugins + maven-install-plugin + + + + + + org.apache.maven.plugins + maven-gpg-plugin + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + + + + + org.apache.maven.plugins + maven-release-plugin + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + + + + + + + + central + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + + sign-artifacts + verify + + sign + + + + + + + + + org.sonatype.central + central-publishing-maven-plugin + ${central-publishing-maven-plugin.version} + true + + central + true + published + + + + + + + + + + UTF-8 + UTF-8 + 1.8 + 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 + 1.0.x.20260630-SNAPSHOT + + 4.13.2 + 5.10.3 + 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 + ${javassist-plus.version} + + + + org.projectlombok + lombok + ${lombok.version} + provided + + + + + - - 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 - ${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 + - - - - - - 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 - - From ee7ea87293f719cb8d8c7627a0653befe046a777 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Fri, 7 Aug 2026 00:05:50 +0800 Subject: [PATCH 20/56] =?UTF-8?q?chore:=20=E8=B7=A8=E7=BB=84=E4=BB=B6=20ea?= =?UTF-8?q?sy4j=20=E4=BE=9D=E8=B5=96=E7=89=88=E6=9C=AC=E4=B8=8E=20ddd4j-de?= =?UTF-8?q?pendencies=20=E5=AF=B9=E9=BD=90=EF=BC=88easy4j-*=20=E5=B1=9E?= =?UTF-8?q?=E6=80=A7=E5=90=8D=E8=A7=84=E8=8C=83=E5=8C=96=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7a6edec..d89fac5 100644 --- a/pom.xml +++ b/pom.xml @@ -523,7 +523,7 @@ 2.22.0 4.0.5 3.30.2-GA - 1.0.x.20260630-SNAPSHOT + 1.0.x.20260630-SNAPSHOT 4.13.2 5.10.3 @@ -593,7 +593,7 @@ io.github.easy4j javassist-plus - ${javassist-plus.version} + ${easy4j-javassist-plus.version} From 22a40312cf8efe6470f96485d37b821749ff6fdd Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Fri, 7 Aug 2026 00:50:52 +0800 Subject: [PATCH 21/56] =?UTF-8?q?docs:=20=E6=96=B0=E5=A2=9E=E4=B8=AD?= =?UTF-8?q?=E8=8B=B1=E5=8F=8C=E8=AF=AD=20README=EF=BC=88README.md=20+=20RE?= =?UTF-8?q?ADME.zh-CN.md=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 254 +++++++++++++++++++++++++++++++++++++++++++++--- README.zh-CN.md | 237 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 479 insertions(+), 12 deletions(-) create mode 100644 README.zh-CN.md diff --git a/README.md b/README.md index 5b56f6e..6b6c66d 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,242 @@ -# cxf-rt-javassist -通过javassist生成基于cxf的jaxws、jaxrs实现 - -### Maven Dependency - -``` xml - - com.github.hiwepy - cxf-rt-javassist - ${project.version} - -``` +# cxf-rt-javassist + +[![Java](https://img.shields.io/badge/Java-8-orange)] [![License](https://img.shields.io/badge/license-Apache%202.0-green)](LICENSE) + +> Generate Apache CXF JAX-WS / JAX-RS implementations with +> [Javassist](https://www.javassist.org): fluent `CtClass` builders that produce +> `@WebService` / JAX-RS annotated endpoint classes at runtime. + +## Table of Contents + +- [1. Project Overview](#1-project-overview) +- [2. Features & Status](#2-features--status) +- [3. Requirements & Compatibility](#3-requirements--compatibility) +- [4. Architecture & Modules](#4-architecture--modules) +- [5. Installation](#5-installation) +- [6. Quick Start](#6-quick-start) +- [7. Configuration](#7-configuration) +- [8. Core Usage / API](#8-core-usage--api) +- [9. Testing & Build](#9-testing--build) +- [10. Versioning & Branches](#10-versioning--branches) +- [11. Contributing & License](#11-contributing--license) + +## 1. Project Overview + +`cxf-rt-javassist` generates Apache CXF JAX-WS / JAX-RS endpoint implementations at +runtime using [Javassist](https://www.javassist.org). Two fluent builders turn a +declarative description into real, loadable classes: + +- **`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. + +The builders rely on the `io.github.easy4j:javassist-plus` companion library +(`CtFieldBuilder`, `ClassPoolFactory`, `JavassistUtils`). + +What it is **not**: + +- 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. + +Typical scenarios: + +| 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()` | + +## 2. Features & Status + +| Capability | Status | Notes | +| :--- | :--- | :--- | +| 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`) | + +## 3. Requirements & Compatibility + +| Requirement | Version / Notes | +| :--- | :--- | +| JDK | 8+ | +| 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) | + +Version 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.*` | + +## 4. Architecture & Modules + +```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 | + +-------------------------------------------+ +``` + +Single-module Maven project (`packaging: jar`). No child modules. + +| Artifact | Responsibility | +| :--- | :--- | +| `io.github.easy4j:cxf-rt-javassist` | Runtime class builders, SOAP / REST definition model, utils | + +Key packages: + +| 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` | + +## 5. Installation + +The project is **not yet published to Maven Central**. Snapshots/releases are +distributed through the Aliyun Maven repository and GitHub Releases. + +Maven: + +```xml + + io.github.easy4j + cxf-rt-javassist + 1.0.x.20260630-SNAPSHOT + +``` + +Gradle: + +```groovy +implementation 'io.github.easy4j:cxf-rt-javassist:1.0.x.20260630-SNAPSHOT' +``` + +## 6. Quick Start + +Build a JAX-WS endpoint class at runtime (adapted from the checked-in test): + +```java +import javassist.CtClass; +import org.apache.cxf.endpoint.jaxws.JaxwsEndpointApiCtClassBuilder; +import org.apache.cxf.endpoint.jaxws.definition.SoapParam; + +CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot.FirstCaseV1") + .webService("get", "http://ws.cxf.com", "getxx") + .newField(String.class, "uid", java.util.UUID.randomUUID().toString()) + .newMethod("sayHello", new SoapParam(String.class, "text")) + .build(); + +Class clazz = ctClass.toClass(); +Object endpoint = clazz.getConstructor().newInstance(); +``` + +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: + +| 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 + +```java +import javassist.CtClass; +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; + +CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot.FirstCase1") + .path("getxx") + .newMethod(String.class, HttpMethodEnum.GET, "sayHello", "{id}/info", + new RestParam(String.class, "id", HttpParamEnum.PATH)) + .build(); +``` + +### 8.2 Dispatch to an `InvocationHandler` + +```java +import java.lang.reflect.InvocationHandler; +import org.apache.cxf.endpoint.jaxws.JaxwsEndpointApiCtClassBuilder; + +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 +``` + +Note: after `toClass()` / `toBytecode()` / `writeFile()`, Javassist freezes the +`CtClass`; call `defrost()` before further modification (see the checked-in tests). + +## 9. Testing & Build + +```bash +./mvnw clean verify +``` + +- 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. + +## 10. Versioning & Branches + +| Branch | JDK | Version | Notes | +| :--- | :--- | :--- | :--- | +| `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 | + +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. + +## 11. Contributing & License + +Contributions are welcome — please open issues or pull requests on GitHub. + +Licensed under the [Apache License, Version 2.0](LICENSE). diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 0000000..8e35326 --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,237 @@ +# cxf-rt-javassist + +[![Java](https://img.shields.io/badge/Java-8-orange)] [![License](https://img.shields.io/badge/license-Apache%202.0-green)](LICENSE) + +> 使用 [Javassist](https://www.javassist.org) 生成基于 Apache CXF 的 JAX-WS / +> JAX-RS 实现:流式 `CtClass` 构建器在运行时产出带 `@WebService` / JAX-RS 注解的 +> 端点类。 + +## 目录 + +- [1. 项目概述](#1-项目概述) +- [2. 功能与状态](#2-功能与状态) +- [3. 环境要求与兼容性](#3-环境要求与兼容性) +- [4. 架构与模块](#4-架构与模块) +- [5. 安装](#5-安装) +- [6. 快速开始](#6-快速开始) +- [7. 配置](#7-配置) +- [8. 核心用法 / API](#8-核心用法--api) +- [9. 测试与构建](#9-测试与构建) +- [10. 版本与分支](#10-版本与分支) +- [11. 贡献与许可](#11-贡献与许可) + +[English](./README.md) | [简体中文](./README.zh-CN.md) + +## 1. 项目概述 + +`cxf-rt-javassist` 使用 [Javassist](https://www.javassist.org) 在运行时生成 Apache +CXF JAX-WS / JAX-RS 端点实现。两个流式构建器把声明式描述转换为真实、可加载的类: + +- **`JaxwsEndpointApiCtClassBuilder`** — 生成 `@WebService` 端点类(名称、目标命名 + 空间、服务名、WSDL provider 模式、addressing、service mode、绑定数据、字段与 + SOAP 方法)。 +- **`JaxrsEndpointApiCtClassBuilder`** — 生成 JAX-RS 资源类(`@Path`、produces、 + 带 `HttpMethodEnum` / `RestParam` 的 REST 方法、绑定数据、字段)。 + +生成的类继承共享的 `EndpointApi` 基类;通过 `toInstance(InvocationHandler)` 可得到 +方法调用被分发给自定义处理器的实例——非常适合代理 / 可插拔端点逻辑。 + +构建器依赖姊妹库 `io.github.easy4j:javassist-plus`(`CtFieldBuilder`、 +`ClassPoolFactory`、`JavassistUtils`)。 + +它不是: + +- CXF 运行时本身——提供服务生成的端点仍需要 Apache CXF frontend 构件。 +- Spring Boot starter——不提供自动装配。 + +典型场景: + +| 场景 | 使用内容 | +| :--- | :--- | +| 运行时生成 `@WebService` 端点类 | `JaxwsEndpointApiCtClassBuilder` + `webService(...)` / `newMethod(...)` | +| 运行时生成 JAX-RS 资源类 | `JaxrsEndpointApiCtClassBuilder` + `path(...)` / `newMethod(...)` | +| 把生成的方法调用分发给处理器 | `toInstance(InvocationHandler)` | +| 导出生成的字节码离线检查 | `build()` → `CtClass` → `toBytecode()` / `writeFile()` | + +## 2. 功能与状态 + +| 能力 | 状态 | 说明 | +| :--- | :--- | :--- | +| JAX-WS 类构建器 | 稳定 | `webService`、`webServiceProvider`、`addressing`、`serviceMode`、`bind`、`makeField`、`makeMethod`、`newMethod`、`build` / `toClass` / `toInstance` | +| JAX-RS 类构建器 | 稳定 | `path`、`produces`、`bind`、`makeField`、`newField`、`removeField`、`newMethod`(`HttpMethodEnum` / `RestMethod`)、`removeMethod`、`build` / `toClass` / `toInstance` | +| SOAP 定义模型 | 稳定 | `SoapService`、`SoapMethod`、`SoapParam`、`SoapResult`、`SoapBound` | +| REST 定义模型 | 稳定 | `RestBound`、`RestMethod`、`RestParam`、`RestProduce`、`HttpMethodEnum`、`HttpParamEnum` | +| 工具类 | 稳定 | `JaxwsEndpointApiUtils`、`JaxrsEndpointApiUtils` | +| 测试 | 稳定 | `JaxwsApiCtClassBuilder_Test`、`JaxrsApiCtClassBuilder_Test` + 样例(`EndpointApiSample`、`EndpointApiInvocationHandler`、`Customer`) | + +## 3. 环境要求与兼容性 + +| 要求 | 版本 / 说明 | +| :--- | :--- | +| JDK | 8+ | +| Maven | 3.0+(enforcer 强制;项目内置 Maven Wrapper `./mvnw`) | +| Apache CXF | 4.x(`cxf-rt-frontend-jaxws` / `cxf-rt-frontend-jaxrs`,由本 pom 管理) | +| Javassist | 3.30.2-GA(由本 pom 管理) | +| 姊妹库 | `io.github.easy4j:javassist-plus`(同一版本线) | + +版本线: + +| 分支 | JDK | 版本 | +| :--- | :--- | :--- | +| `feature/1.0.x` | 8 | `1.0.x.*` | +| `feature/2.0.x` | 17 | `2.0.x.*` | +| `feature/3.0.x` | 21 | `3.0.x.*` | + +## 4. 架构与模块 + +```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 | + +-------------------------------------------+ +``` + +单模块 Maven 工程(`packaging: jar`),无子模块。 + +| 构件 | 职责 | +| :--- | :--- | +| `io.github.easy4j:cxf-rt-javassist` | 运行时类构建器、SOAP / REST 定义模型、工具类 | + +关键包: + +| 包 | 内容 | +| :--- | :--- | +| `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`(基类) | +| `org.apache.cxf.endpoint.utils` | `JaxwsEndpointApiUtils`、`JaxrsEndpointApiUtils` | + +## 5. 安装 + +项目**尚未发布到 Maven Central**。快照 / 发布版本通过阿里云 Maven 仓库与 GitHub +Releases 分发。 + +Maven: + +```xml + + io.github.easy4j + cxf-rt-javassist + 1.0.x.20260630-SNAPSHOT + +``` + +Gradle: + +```groovy +implementation 'io.github.easy4j:cxf-rt-javassist:1.0.x.20260630-SNAPSHOT' +``` + +## 6. 快速开始 + +运行时构建 JAX-WS 端点类(改编自仓库内已提交的测试): + +```java +import javassist.CtClass; +import org.apache.cxf.endpoint.jaxws.JaxwsEndpointApiCtClassBuilder; +import org.apache.cxf.endpoint.jaxws.definition.SoapParam; + +CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot.FirstCaseV1") + .webService("get", "http://ws.cxf.com", "getxx") + .newField(String.class, "uid", java.util.UUID.randomUUID().toString()) + .newMethod("sayHello", new SoapParam(String.class, "text")) + .build(); + +Class clazz = ctClass.toClass(); +Object endpoint = clazz.getConstructor().newInstance(); +``` + +预期结果:创建新类 `org.apache.cxf.spring.boot.FirstCaseV1`,带 `@WebService` 注解 +与 `String sayHello(String text)` 方法——可直接作为 CXF JAX-WS 端点实现使用。 + +## 7. 配置 + +本库没有配置文件或属性前缀。一切由构建器链与定义对象驱动: + +| 构建器 | 关键方法 | 说明 | +| :--- | :--- | :--- | +| `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 端点生成 | +| `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 资源生成 | + +## 8. 核心用法 / API + +### 8.1 JAX-RS 资源生成 + +```java +import javassist.CtClass; +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; + +CtClass ctClass = new JaxrsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot.FirstCase1") + .path("getxx") + .newMethod(String.class, HttpMethodEnum.GET, "sayHello", "{id}/info", + new RestParam(String.class, "id", HttpParamEnum.PATH)) + .build(); +``` + +### 8.2 分发到 `InvocationHandler` + +```java +import java.lang.reflect.InvocationHandler; +import org.apache.cxf.endpoint.jaxws.JaxwsEndpointApiCtClassBuilder; + +InvocationHandler handler = new EndpointApiInvocationHandler(); // 你的处理器 +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); // 方法调用被分发给该处理器 +``` + +注意:`toClass()` / `toBytecode()` / `writeFile()` 之后 Javassist 会冻结 `CtClass`; +继续修改前需调用 `defrost()`(参见已提交的测试)。 + +## 9. 测试与构建 + +```bash +./mvnw clean verify +``` + +- 构建配置了 JaCoCo Maven 插件(报告 + 绑定在 `verify` 阶段的 `check` 目标, + 行覆盖率规则为 90%;`haltOnFailure=false`)。 +- 已提交测试:`JaxwsApiCtClassBuilder_Test`、`JaxrsApiCtClassBuilder_Test` + (类构建、实例分发、字节码导出)。 +- 本 worktree 的 `.github/` 下无 CI 工作流文件。 + +## 10. 版本与分支 + +| 分支 | JDK | 版本 | 说明 | +| :--- | :--- | :--- | :--- | +| `feature/1.0.x` | 8 | `1.0.x.*` | 当前分支,JDK 8 基线,维护中 | +| `feature/2.0.x` | 17 | `2.0.x.*` | JDK 17 版本线 | +| `feature/3.0.x` | 21 | `3.0.x.*` | JDK 21 版本线 | + +维护策略:`1.0.x` 版本线接收针对 JDK 8 基线的缺陷修复与兼容性更新;面向新 JDK 的 +新特性在 `2.0.x` / `3.0.x` 版本线开发。发布物通过阿里云 Maven 仓库与 GitHub +Releases 分发;项目尚未发布到 Maven Central。 + +## 11. 贡献与许可 + +欢迎通过 GitHub Issue 或 Pull Request 参与贡献。 + +本项目基于 [Apache License, Version 2.0](LICENSE) 许可。 From 03a9d96c3e57355e7e31fd79810c5a4a042073cc Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Fri, 7 Aug 2026 01:07:51 +0800 Subject: [PATCH 22/56] =?UTF-8?q?docs:=20=E7=BB=9F=E4=B8=80=E5=8F=8C?= =?UTF-8?q?=E8=AF=AD=20README=20=E5=A4=B4=E9=83=A8=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E4=B8=8E=E7=89=88=E6=9C=AC=E7=BA=BF=E8=AF=B4=E6=98=8E=EF=BC=88?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E6=8D=A2=E8=A1=8C=E3=80=81JDK=20=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E7=BA=BF=E4=BF=AE=E6=AD=A3=E3=80=81=E9=94=9A=E7=82=B9?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +++- README.zh-CN.md | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6b6c66d..92fb7a2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # cxf-rt-javassist -[![Java](https://img.shields.io/badge/Java-8-orange)] [![License](https://img.shields.io/badge/license-Apache%202.0-green)](LICENSE) +[English](./README.md) | [简体中文](./README.zh-CN.md) + +[![License](https://img.shields.io/badge/license-Apache%202.0-green) > Generate Apache CXF JAX-WS / JAX-RS implementations with > [Javassist](https://www.javassist.org): fluent `CtClass` builders that produce diff --git a/README.zh-CN.md b/README.zh-CN.md index 8e35326..fecd3a9 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,6 +1,8 @@ # cxf-rt-javassist -[![Java](https://img.shields.io/badge/Java-8-orange)] [![License](https://img.shields.io/badge/license-Apache%202.0-green)](LICENSE) +[English](./README.md) | [简体中文](./README.zh-CN.md) + +[![License](https://img.shields.io/badge/license-Apache%202.0-green) > 使用 [Javassist](https://www.javassist.org) 生成基于 Apache CXF 的 JAX-WS / > JAX-RS 实现:流式 `CtClass` 构建器在运行时产出带 `@WebService` / JAX-RS 注解的 @@ -20,8 +22,6 @@ - [10. 版本与分支](#10-版本与分支) - [11. 贡献与许可](#11-贡献与许可) -[English](./README.md) | [简体中文](./README.zh-CN.md) - ## 1. 项目概述 `cxf-rt-javassist` 使用 [Javassist](https://www.javassist.org) 在运行时生成 Apache From 075bcd191a2a3aeb8c95d41c428ad495f7072c84 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Fri, 7 Aug 2026 01:33:09 +0800 Subject: [PATCH 23/56] =?UTF-8?q?docs:=20=E4=BF=AE=E6=AD=A3=20Java=20?= =?UTF-8?q?=E5=BE=BD=E7=AB=A0=E6=A0=BC=E5=BC=8F=EF=BC=88=E8=A1=A5=E5=85=85?= =?UTF-8?q?=E4=BB=93=E5=BA=93=E9=93=BE=E6=8E=A5=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- README.zh-CN.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 92fb7a2..e51fee1 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [English](./README.md) | [简体中文](./README.zh-CN.md) -[![License](https://img.shields.io/badge/license-Apache%202.0-green) +[![Java](https://img.shields.io/badge/Java-8-orange)](https://github.com/easy-4-java/cxf-rt-javassist) [![License](https://img.shields.io/badge/license-Apache%202.0-green)](./LICENSE) > Generate Apache CXF JAX-WS / JAX-RS implementations with > [Javassist](https://www.javassist.org): fluent `CtClass` builders that produce diff --git a/README.zh-CN.md b/README.zh-CN.md index fecd3a9..4b19dcb 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -2,7 +2,7 @@ [English](./README.md) | [简体中文](./README.zh-CN.md) -[![License](https://img.shields.io/badge/license-Apache%202.0-green) +[![Java](https://img.shields.io/badge/Java-8-orange)](https://github.com/easy-4-java/cxf-rt-javassist) [![License](https://img.shields.io/badge/license-Apache%202.0-green)](./LICENSE) > 使用 [Javassist](https://www.javassist.org) 生成基于 Apache CXF 的 JAX-WS / > JAX-RS 实现:流式 `CtClass` 构建器在运行时产出带 `@WebService` / JAX-RS 注解的 From 3da056a47b36e60cfc1604fb3bf87f301554a26f Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Fri, 7 Aug 2026 01:49:20 +0800 Subject: [PATCH 24/56] =?UTF-8?q?ci:=20=E6=A0=87=E5=87=86=E5=8C=96=20GitHu?= =?UTF-8?q?b=20Actions=20=E9=85=8D=E7=BD=AE=EF=BC=88=E6=AF=8F=E5=88=86?= =?UTF-8?q?=E6=94=AF=E7=8B=AC=E7=AB=8B=20CI=EF=BC=8CJDK=20=E6=8C=89?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E7=BA=BF=208/17/21=EF=BC=8CJaCoCo=20?= =?UTF-8?q?=E8=A6=86=E7=9B=96=E7=8E=87=E9=97=A8=E7=A6=81=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 54 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f0e7f72 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,54 @@ +# CI workflow for the feature/1.0.x line (JDK 8) +# +# Triggers: +# - push / pull_request on the feature/1.0.x branch +# - manual workflow_dispatch +# +# Runs `./mvnw -B clean verify` which includes the JaCoCo coverage gate +# (90% line coverage, haltOnFailure=false) configured in the POM. +name: CI + +on: + push: + branches: [feature/1.0.x] + pull_request: + branches: [feature/1.0.x] + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + name: JDK 8 Build & Verify + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout source + uses: actions/checkout@v4 + + - name: Set up JDK 8 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '8' + cache: maven + + - name: Build and verify with JaCoCo coverage gate + run: ./mvnw -B --no-transfer-progress clean verify + + - name: Upload JaCoCo coverage report + if: always() + uses: actions/upload-artifact@v4 + with: + name: jacoco-coverage + path: target/site/jacoco + retention-days: 14 + + - name: Upload surefire reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: surefire-reports + path: target/surefire-reports + retention-days: 14 From 4552f6497e5b5d1ae75e17c46280ca99428e3f37 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Fri, 7 Aug 2026 06:26:08 +0800 Subject: [PATCH 25/56] =?UTF-8?q?fix:=20junit-jupiter=20=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E6=8C=89=E7=89=88=E6=9C=AC=E7=BA=BF=E5=AF=B9=E9=BD=90=20ddd4j?= =?UTF-8?q?=EF=BC=881.0.x=3D5.11.4=20JDK8=20=E5=85=BC=E5=AE=B9=20/=202.0.x?= =?UTF-8?q?=E3=80=813.0.x=3D6.1.0=EF=BC=89=EF=BC=8C=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E8=AF=AF=E7=94=A8=20junit.version=20=E7=9A=84=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d89fac5..99c5273 100644 --- a/pom.xml +++ b/pom.xml @@ -526,7 +526,7 @@ 1.0.x.20260630-SNAPSHOT 4.13.2 - 5.10.3 + 5.11.4 1.18.46 2.0.18 From 06433780b19a429bbce8fb4d79c6bd5141ddf6aa Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Fri, 7 Aug 2026 08:01:11 +0800 Subject: [PATCH 26/56] =?UTF-8?q?ci:=20=E4=BF=AE=E5=A4=8D=E5=A4=9A?= =?UTF-8?q?=E7=89=88=E6=9C=AC=20GitHub=20Actions=20=E6=9E=84=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0e7f72..6e30131 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ # - push / pull_request on the feature/1.0.x branch # - manual workflow_dispatch # -# Runs `./mvnw -B clean verify` which includes the JaCoCo coverage gate +# Runs `mvn -B clean verify` which includes the JaCoCo coverage gate # (90% line coverage, haltOnFailure=false) configured in the POM. name: CI @@ -25,21 +25,21 @@ jobs: timeout-minutes: 30 steps: - name: Checkout source - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up JDK 8 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: temurin java-version: '8' cache: maven - name: Build and verify with JaCoCo coverage gate - run: ./mvnw -B --no-transfer-progress clean verify + run: mvn -B --no-transfer-progress clean verify - name: Upload JaCoCo coverage report if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: jacoco-coverage path: target/site/jacoco @@ -47,7 +47,7 @@ jobs: - name: Upload surefire reports if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: surefire-reports path: target/surefire-reports From e3104a530059c41b7d3da04c10463936d6d17b17 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Fri, 7 Aug 2026 08:23:28 +0800 Subject: [PATCH 27/56] =?UTF-8?q?ci:=20=E4=BF=AE=E5=A4=8D=E5=B9=B2?= =?UTF-8?q?=E5=87=80=20runner=20=E4=BE=9D=E8=B5=96=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e30131..6ba68fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,23 @@ jobs: java-version: '8' cache: maven + - name: Install Easy4J snapshot dependencies + env: + EASY4J_BRANCH: ${{ github.base_ref || github.ref_name }} + EASY4J_DEPENDENCIES: "javassist-plus" + run: | + set -euo pipefail + dependency_root="${RUNNER_TEMP}/easy4j-dependencies" + mkdir -p "${dependency_root}" + for dependency in ${EASY4J_DEPENDENCIES}; do + git clone --depth 1 --branch "${EASY4J_BRANCH}" \ + "https://github.com/easy-4-java/${dependency}.git" \ + "${dependency_root}/${dependency}" + mvn -B --no-transfer-progress \ + -f "${dependency_root}/${dependency}/pom.xml" \ + -DskipTests -Djacoco.skip=true -Dgpg.skip=true install + done + - name: Build and verify with JaCoCo coverage gate run: mvn -B --no-transfer-progress clean verify From 446ab95c33c17c0f88a7ade082d93d2b95f49ac5 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Fri, 7 Aug 2026 08:30:04 +0800 Subject: [PATCH 28/56] =?UTF-8?q?ci:=20=E5=A2=9E=E5=BC=BA=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=E5=AE=89=E8=A3=85=E9=87=8D=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ba68fb..828e744 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,9 +46,17 @@ jobs: git clone --depth 1 --branch "${EASY4J_BRANCH}" \ "https://github.com/easy-4-java/${dependency}.git" \ "${dependency_root}/${dependency}" - mvn -B --no-transfer-progress \ - -f "${dependency_root}/${dependency}/pom.xml" \ - -DskipTests -Djacoco.skip=true -Dgpg.skip=true install + for attempt in 1 2 3; do + if mvn -B --no-transfer-progress \ + -f "${dependency_root}/${dependency}/pom.xml" \ + -DskipTests -Djacoco.skip=true -Dgpg.skip=true install; then + break + fi + if [[ "${attempt}" -eq 3 ]]; then + exit 1 + fi + sleep "$((attempt * 10))" + done done - name: Build and verify with JaCoCo coverage gate From 6cf77fece838f8af0922218405772292d3469f7a Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Fri, 7 Aug 2026 23:54:08 +0800 Subject: [PATCH 29/56] build: use javassist-extension dependency --- .github/workflows/ci.yml | 2 +- README.md | 4 ++-- README.zh-CN.md | 4 ++-- pom.xml | 12 ++++++------ 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c56c1bc..8f5372f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: - name: Install Easy4J snapshot dependencies env: EASY4J_BRANCH: ${{ github.base_ref || github.ref_name }} - EASY4J_DEPENDENCIES: "javassist-plus" + EASY4J_DEPENDENCIES: "javassist-extension" run: | set -euo pipefail dependency_root="${RUNNER_TEMP}/easy4j-dependencies" diff --git a/README.md b/README.md index f362610..25529f1 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ 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. -The builders rely on the `io.github.easy4j:javassist-plus` companion library +The builders rely on the `io.github.easy4j:javassist-extension` companion library (`CtFieldBuilder`, `ClassPoolFactory`, `JavassistUtils`). What it is **not**: @@ -76,7 +76,7 @@ Typical scenarios: | 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) | +| Companion library | `io.github.easy4j:javassist-extension` (same version line) | Version lines: diff --git a/README.zh-CN.md b/README.zh-CN.md index bb22df0..9763be6 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -36,7 +36,7 @@ CXF JAX-WS / JAX-RS 端点实现。两个流式构建器把声明式描述转换 生成的类继承共享的 `EndpointApi` 基类;通过 `toInstance(InvocationHandler)` 可得到 方法调用被分发给自定义处理器的实例——非常适合代理 / 可插拔端点逻辑。 -构建器依赖姊妹库 `io.github.easy4j:javassist-plus`(`CtFieldBuilder`、 +构建器依赖姊妹库 `io.github.easy4j:javassist-extension`(`CtFieldBuilder`、 `ClassPoolFactory`、`JavassistUtils`)。 它不是: @@ -72,7 +72,7 @@ CXF JAX-WS / JAX-RS 端点实现。两个流式构建器把声明式描述转换 | Maven | 3.0+(enforcer 强制;项目内置 Maven Wrapper `./mvnw`) | | Apache CXF | 4.x(`cxf-rt-frontend-jaxws` / `cxf-rt-frontend-jaxrs`,由本 pom 管理) | | Javassist | 3.30.2-GA(由本 pom 管理) | -| 姊妹库 | `io.github.easy4j:javassist-plus`(同一版本线) | +| 姊妹库 | `io.github.easy4j:javassist-extension`(同一版本线) | 版本线: diff --git a/pom.xml b/pom.xml index 77c62a0..997f8a9 100644 --- a/pom.xml +++ b/pom.xml @@ -523,7 +523,7 @@ 2.22.0 4.0.5 3.30.2-GA - 2.0.x.20260630-SNAPSHOT + 2.0.x.20260630-SNAPSHOT 4.13.2 6.1.0 @@ -589,11 +589,11 @@ javassist ${javassist.version} - + io.github.easy4j - javassist-plus - ${easy4j-javassist-plus.version} + javassist-extension + ${easy4j-javassist-extension.version} @@ -652,10 +652,10 @@ org.javassist javassist - + io.github.easy4j - javassist-plus + javassist-extension From c97d7f944371625c52fd1c376e6aabe274ccb4e0 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Fri, 7 Aug 2026 23:54:39 +0800 Subject: [PATCH 30/56] build: use javassist-extension dependency --- .github/workflows/ci.yml | 2 +- README.md | 4 ++-- README.zh-CN.md | 4 ++-- pom.xml | 12 ++++++------ 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 828e744..7bc3ba6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: - name: Install Easy4J snapshot dependencies env: EASY4J_BRANCH: ${{ github.base_ref || github.ref_name }} - EASY4J_DEPENDENCIES: "javassist-plus" + EASY4J_DEPENDENCIES: "javassist-extension" run: | set -euo pipefail dependency_root="${RUNNER_TEMP}/easy4j-dependencies" diff --git a/README.md b/README.md index e51fee1..f90b087 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ 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. -The builders rely on the `io.github.easy4j:javassist-plus` companion library +The builders rely on the `io.github.easy4j:javassist-extension` companion library (`CtFieldBuilder`, `ClassPoolFactory`, `JavassistUtils`). What it is **not**: @@ -76,7 +76,7 @@ Typical scenarios: | 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) | +| Companion library | `io.github.easy4j:javassist-extension` (same version line) | Version lines: diff --git a/README.zh-CN.md b/README.zh-CN.md index 4b19dcb..a877911 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -36,7 +36,7 @@ CXF JAX-WS / JAX-RS 端点实现。两个流式构建器把声明式描述转换 生成的类继承共享的 `EndpointApi` 基类;通过 `toInstance(InvocationHandler)` 可得到 方法调用被分发给自定义处理器的实例——非常适合代理 / 可插拔端点逻辑。 -构建器依赖姊妹库 `io.github.easy4j:javassist-plus`(`CtFieldBuilder`、 +构建器依赖姊妹库 `io.github.easy4j:javassist-extension`(`CtFieldBuilder`、 `ClassPoolFactory`、`JavassistUtils`)。 它不是: @@ -72,7 +72,7 @@ CXF JAX-WS / JAX-RS 端点实现。两个流式构建器把声明式描述转换 | Maven | 3.0+(enforcer 强制;项目内置 Maven Wrapper `./mvnw`) | | Apache CXF | 4.x(`cxf-rt-frontend-jaxws` / `cxf-rt-frontend-jaxrs`,由本 pom 管理) | | Javassist | 3.30.2-GA(由本 pom 管理) | -| 姊妹库 | `io.github.easy4j:javassist-plus`(同一版本线) | +| 姊妹库 | `io.github.easy4j:javassist-extension`(同一版本线) | 版本线: diff --git a/pom.xml b/pom.xml index 99c5273..61482b4 100644 --- a/pom.xml +++ b/pom.xml @@ -523,7 +523,7 @@ 2.22.0 4.0.5 3.30.2-GA - 1.0.x.20260630-SNAPSHOT + 1.0.x.20260630-SNAPSHOT 4.13.2 5.11.4 @@ -589,11 +589,11 @@ javassist ${javassist.version} - + io.github.easy4j - javassist-plus - ${easy4j-javassist-plus.version} + javassist-extension + ${easy4j-javassist-extension.version} @@ -652,10 +652,10 @@ org.javassist javassist - + io.github.easy4j - javassist-plus + javassist-extension From 383a32002a4288570666c6a17428b7407dafb7ee Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Sat, 8 Aug 2026 02:26:00 +0800 Subject: [PATCH 31/56] build: rename Maven plugin version properties --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 61482b4..fef38bb 100644 --- a/pom.xml +++ b/pom.xml @@ -277,7 +277,7 @@ org.jacoco jacoco-maven-plugin - ${jacoco-maven-plugin.version} + ${maven-jacoco-plugin.version} prepare-agent @@ -350,9 +350,9 @@ [1.8,) - 0.11.0 + 0.11.0 -Xdoclint:none - 0.8.15 + 0.8.15 @@ -476,7 +476,7 @@ org.sonatype.central central-publishing-maven-plugin - ${central-publishing-maven-plugin.version} + ${central-maven-publishing-plugin.version} true central From 61acd99185920018cff021167768dd9cecf377b0 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Sat, 8 Aug 2026 02:26:00 +0800 Subject: [PATCH 32/56] build: rename Maven plugin version properties --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 997f8a9..82e74c6 100644 --- a/pom.xml +++ b/pom.xml @@ -277,7 +277,7 @@ org.jacoco jacoco-maven-plugin - ${jacoco-maven-plugin.version} + ${maven-jacoco-plugin.version} prepare-agent @@ -350,9 +350,9 @@ [1.8,) - 0.11.0 + 0.11.0 -Xdoclint:none - 0.8.15 + 0.8.15 @@ -476,7 +476,7 @@ org.sonatype.central central-publishing-maven-plugin - ${central-publishing-maven-plugin.version} + ${central-maven-publishing-plugin.version} true central From 6cdaf180090009277c620e38e2fc63ac63cc279c Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Sun, 9 Aug 2026 02:31:27 +0800 Subject: [PATCH 33/56] sync: cherry-pick changes from feature/3.0.x --- .../org/apache/cxf/endpoint/EndpointApi.java | 53 +- .../cxf/endpoint/annotation/WebBound.java | 32 ++ .../cxf/endpoint/annotation/WebEndpoint.java | 73 ++- .../jaxrs/JaxrsEndpointApiCtClassBuilder.java | 452 +++++++++++++----- .../JaxrsEndpointApiImplCtClassBuilder.java | 221 ++++++--- ...xrsEndpointApiInterfaceCtClassBuilder.java | 330 ++++++++++--- .../jaxrs/definition/HttpMethodEnum.java | 40 +- .../jaxrs/definition/HttpParamEnum.java | 13 +- .../endpoint/jaxrs/definition/RestBound.java | 57 ++- .../endpoint/jaxrs/definition/RestMethod.java | 76 ++- .../endpoint/jaxrs/definition/RestParam.java | 127 ++++- .../jaxrs/definition/RestProduce.java | 57 ++- .../jaxws/JaxwsEndpointApiCtClassBuilder.java | 384 ++++++++++----- .../JaxwsEndpointApiImplCtClassBuilder.java | 293 ++++++++---- ...xwsEndpointApiInterfaceCtClassBuilder.java | 363 +++++++++----- .../endpoint/jaxws/definition/SoapBound.java | 56 ++- .../endpoint/jaxws/definition/SoapMethod.java | 74 ++- .../endpoint/jaxws/definition/SoapParam.java | 147 +++++- .../endpoint/jaxws/definition/SoapResult.java | 98 +++- .../jaxws/definition/SoapService.java | 131 ++++- .../endpoint/utils/JaxrsEndpointApiUtils.java | 250 ++++++---- .../endpoint/utils/JaxwsEndpointApiUtils.java | 294 ++++++++---- .../apache/cxf/endpoint/EndpointApiTest.java | 36 ++ .../cxf/endpoint/annotation/WebBoundTest.java | 32 ++ .../endpoint/annotation/WebEndpointTest.java | 25 + .../jaxrs/JaxrsApiCtClassBuilder_Test.java | 50 +- .../JaxrsEndpointApiCtClassBuilderTest.java | 276 +++++++++++ ...axrsEndpointApiImplCtClassBuilderTest.java | 109 +++++ ...ndpointApiInterfaceCtClassBuilderTest.java | 272 +++++++++++ .../jaxrs/definition/HttpMethodEnumTest.java | 42 ++ .../jaxrs/definition/HttpParamEnumTest.java | 35 ++ .../jaxrs/definition/RestBoundTest.java | 43 ++ .../jaxrs/definition/RestMethodTest.java | 43 ++ .../jaxrs/definition/RestParamTest.java | 69 +++ .../jaxrs/definition/RestProduceTest.java | 29 ++ .../jaxws/JaxwsApiCtClassBuilder_Test.java | 102 +--- .../JaxwsEndpointApiCtClassBuilderTest.java | 280 +++++++++++ ...axwsEndpointApiImplCtClassBuilderTest.java | 143 ++++++ ...ndpointApiInterfaceCtClassBuilderTest.java | 262 ++++++++++ .../jaxws/definition/SoapBoundTest.java | 43 ++ .../jaxws/definition/SoapMethodTest.java | 53 ++ .../jaxws/definition/SoapParamTest.java | 94 ++++ .../jaxws/definition/SoapResultTest.java | 64 +++ .../jaxws/definition/SoapServiceTest.java | 78 +++ 44 files changed, 4797 insertions(+), 1004 deletions(-) create mode 100644 src/test/java/org/apache/cxf/endpoint/EndpointApiTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/annotation/WebBoundTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/annotation/WebEndpointTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilderTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiImplCtClassBuilderTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiInterfaceCtClassBuilderTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/definition/HttpMethodEnumTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/definition/HttpParamEnumTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestBoundTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestMethodTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestProduceTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilderTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiImplCtClassBuilderTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiInterfaceCtClassBuilderTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapBoundTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapMethodTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapParamTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapResultTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapServiceTest.java diff --git a/src/main/java/org/apache/cxf/endpoint/EndpointApi.java b/src/main/java/org/apache/cxf/endpoint/EndpointApi.java index ace0f86..c7237b4 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](https://github.com/loong10k) + * @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..6787496 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](https://github.com/loong10k) + * @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..cf02a1e 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](https://github.com/loong10k) + * @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..9afe8bc 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接口 - *

http://www.cnblogs.com/sunfie/p/5154246.html

- *

http://blog.csdn.net/youaremoon/article/details/50766972

- *

https://blog.csdn.net/tscyds/article/details/78415172

- *

https://my.oschina.net/GameKing/blog/794580

- *

http://wsmajunfeng.iteye.com/blog/1912983

+ * 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](https://github.com/loong10k) + * @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..482a34d 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接口 - *

http://www.cnblogs.com/sunfie/p/5154246.html

- *

http://blog.csdn.net/youaremoon/article/details/50766972

- *

https://my.oschina.net/GameKing/blog/794580

- *

http://wsmajunfeng.iteye.com/blog/1912983

+ * 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](https://github.com/loong10k) + * @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..bd31832 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接口 - *

http://www.cnblogs.com/sunfie/p/5154246.html

- *

http://blog.csdn.net/youaremoon/article/details/50766972

- *

https://blog.csdn.net/tscyds/article/details/78415172

- *

https://my.oschina.net/GameKing/blog/794580

- *

http://wsmajunfeng.iteye.com/blog/1912983

+ * 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](https://github.com/loong10k) + * @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..6c7de34 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](https://github.com/loong10k) + * @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..a5ee98e 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](https://github.com/loong10k) + * @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..a27b1b4 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](https://github.com/loong10k) + * @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..cba03ae 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](https://github.com/loong10k) + * @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..3be3e27 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](https://github.com/loong10k) + * @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,144 @@ 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; } + /** + * 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.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..348ea5b 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](https://github.com/loong10k) + * @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: - * - *
-	 * { "image/jpeg, image/gif ", " image/png" }
-	 * 
- * - * 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..3c2e42c 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接口 - *

http://www.cnblogs.com/sunfie/p/5154246.html

- *

http://blog.csdn.net/youaremoon/article/details/50766972

- *

https://my.oschina.net/GameKing/blog/794580

- *

http://wsmajunfeng.iteye.com/blog/1912983

+ * 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](https://github.com/loong10k) + * @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..b0bdb68 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接口 - *

http://www.cnblogs.com/sunfie/p/5154246.html

- *

http://blog.csdn.net/youaremoon/article/details/50766972

- *

https://my.oschina.net/GameKing/blog/794580

- *

http://wsmajunfeng.iteye.com/blog/1912983

+ * 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](https://github.com/loong10k) + * @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..9dafd4a 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接口 - *

http://www.cnblogs.com/sunfie/p/5154246.html

- *

http://blog.csdn.net/youaremoon/article/details/50766972

- *

https://blog.csdn.net/tscyds/article/details/78415172

- *

https://my.oschina.net/GameKing/blog/794580

- *

http://wsmajunfeng.iteye.com/blog/1912983

+ * 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](https://github.com/loong10k) + * @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..b65a62c 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](https://github.com/loong10k) + * @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..8195953 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](https://github.com/loong10k) + * @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..6ab98fb 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](https://github.com/loong10k) + * @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..84cecf8 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](https://github.com/loong10k) + * @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..9ce02aa 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](https://github.com/loong10k) + * @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..b19a8c1 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](https://github.com/loong10k) + * @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 参数注解 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..b53fe48 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](https://github.com/loong10k) + * @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..62a55b2 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilderTest.java @@ -0,0 +1,276 @@ +package org.apache.cxf.endpoint.jaxrs; + +import static org.junit.Assert.*; + +import java.lang.reflect.InvocationHandler; + +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 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(); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiImplCtClassBuilderTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiImplCtClassBuilderTest.java new file mode 100644 index 0000000..6e80691 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiImplCtClassBuilderTest.java @@ -0,0 +1,109 @@ +package org.apache.cxf.endpoint.jaxrs; + +import static org.junit.Assert.*; + +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.RestParam; +import org.junit.Test; + +import javassist.ClassPool; +import javassist.CtClass; + +public class JaxrsEndpointApiImplCtClassBuilderTest { + + @Test + public void shouldBuildImplClassWithDefaultPool() throws Exception { + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImpl1") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldBuildImplClassWithCustomPool() throws Exception { + ClassPool pool = ClassPool.getDefault(); + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder(pool, "org.test.JaxrsImpl2") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardPathToInterface() throws Exception { + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImpl3") + .path("/api") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardProducesToInterface() throws Exception { + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImpl4") + .produces("application/json") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardDefaultProducesToInterface() throws Exception { + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImpl5") + .produces() + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardBindUidJson() throws Exception { + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImpl6") + .bind("uid", "{}") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardBindRestBound() throws Exception { + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImpl7") + .bind(new RestBound("uid", "{}")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddMethodToBothInterfaceAndImpl() throws Exception { + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImpl8") + .path("/api") + .newMethod(String.class, HttpMethodEnum.GET, "hello", "/hello", + new RestBound("b1"), new RestParam(String.class, "name")) + .build(); + assertNotNull(ctClass); + assertNotNull(ctClass.getDeclaredMethod("hello")); + ctClass.detach(); + } + + @Test + public void shouldUseImplSuffix() throws Exception { + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImplSuffix1") + .build(); + assertTrue(ctClass.getName().endsWith("$Impl")); + ctClass.detach(); + } + + @Test + public void shouldSupportFluentChaining() throws Exception { + JaxrsEndpointApiImplCtClassBuilder builder = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImplChain1"); + JaxrsEndpointApiImplCtClassBuilder result = builder + .path("/api") + .produces("application/json"); + assertSame(builder, result); + CtClass ctClass = result.build(); + assertNotNull(ctClass); + ctClass.detach(); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiInterfaceCtClassBuilderTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiInterfaceCtClassBuilderTest.java new file mode 100644 index 0000000..730c998 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiInterfaceCtClassBuilderTest.java @@ -0,0 +1,272 @@ +package org.apache.cxf.endpoint.jaxrs; + +import static org.junit.Assert.*; + +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 javassist.ClassPool; +import javassist.CtClass; + +public class JaxrsEndpointApiInterfaceCtClassBuilderTest { + + @Test + public void shouldBuildInterfaceWithDefaultPool() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface1") + .build(); + assertNotNull(ctClass); + assertTrue(ctClass.isInterface()); + ctClass.detach(); + } + + @Test + public void shouldBuildInterfaceWithCustomPool() throws Exception { + ClassPool pool = ClassPool.getDefault(); + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder(pool, "org.test.JaxrsIface2") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddPathAnnotation() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface3") + .path("/api") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.ws.rs.Path.class)); + ctClass.detach(); + } + + @Test + public void shouldAddProducesAnnotation() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface4") + .produces("application/xml") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.ws.rs.Produces.class)); + ctClass.detach(); + } + + @Test + public void shouldAddDefaultProduces() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface5") + .produces() + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.ws.rs.Produces.class)); + ctClass.detach(); + } + + @Test + public void shouldBindUidJson() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface6") + .bind("uid", "{}") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldBindRestBound() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface7") + .bind(new RestBound("uid", "{}")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldMakeFieldFromSource() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface8") + .makeField("public int k = 3;") + .build(); + assertNotNull(ctClass.getDeclaredField("k")); + ctClass.detach(); + } + + @Test + public void shouldAddTypedField() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface9") + .newField(String.class, "uid", "test") + .build(); + assertNotNull(ctClass.getDeclaredField("uid")); + ctClass.detach(); + } + + @Test + public void shouldNoopWhenFieldAlreadyExists() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface10") + .newField(String.class, "uid", "test") + .newField(String.class, "uid", "test2") + .build(); + assertNotNull(ctClass.getDeclaredField("uid")); + ctClass.detach(); + } + + @Test + public void shouldRemoveExistingField() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface11") + .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 JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface12") + .removeField("nonexistent") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodWithAllParams() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface13") + .abstractMethod(String.class, HttpMethodEnum.GET, "find", "/{id}", + new RestBound("b1"), new RestParam(String.class, "id", HttpParamEnum.PATH)) + .build(); + assertNotNull(ctClass.getDeclaredMethod("find")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodWithoutBound() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface14") + .abstractMethod(String.class, HttpMethodEnum.GET, "find", "/{id}", + new RestParam(String.class, "id")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("find")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodWithRestMethodAndBound() throws Exception { + RestMethod rm = new RestMethod(HttpMethodEnum.POST, "create", "/"); + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface15") + .abstractMethod(String.class, rm, new RestBound("b1"), + new RestParam(String.class, "data")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("create")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodWithRestMethodNoBound() throws Exception { + RestMethod rm = new RestMethod(HttpMethodEnum.GET, "list", "/"); + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface16") + .abstractMethod(String.class, rm, new RestParam(String.class, "q")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("list")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodNoReturnNoBoundHttpEnum() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface17") + .abstractMethod(HttpMethodEnum.GET, "health", "/health") + .build(); + assertNotNull(ctClass.getDeclaredMethod("health")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodNoReturnWithBoundHttpEnum() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface18") + .abstractMethod(HttpMethodEnum.POST, "save", "/", new RestBound("b1")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("save")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodNoReturnWithBoundRestMethod() throws Exception { + RestMethod rm = new RestMethod(HttpMethodEnum.DELETE, "delete", "/{id}"); + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface19") + .abstractMethod(rm, new RestBound("b1"), new RestParam(String.class, "id")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("delete")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodNoReturnNoBoundRestMethod() throws Exception { + RestMethod rm = new RestMethod(HttpMethodEnum.GET, "ping", "/ping"); + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface20") + .abstractMethod(rm) + .build(); + assertNotNull(ctClass.getDeclaredMethod("ping")); + ctClass.detach(); + } + + @Test + public void shouldRemoveExistingMethod() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface21") + .abstractMethod(HttpMethodEnum.GET, "temp", "/temp") + .removeMethod("temp") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldNoopWhenRemovingNonexistentMethod() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface22") + .removeMethod("nonexistent") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldRemoveMethodWithParams() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface23") + .abstractMethod(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 JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface24") + .removeMethod("nope", new RestParam(String.class, "x")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldSupportFluentChaining() throws Exception { + JaxrsEndpointApiInterfaceCtClassBuilder builder = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface25"); + JaxrsEndpointApiInterfaceCtClassBuilder result = builder + .path("/api") + .produces("application/json") + .bind("uid", "{}") + .abstractMethod(org.apache.cxf.endpoint.jaxrs.definition.HttpMethodEnum.GET, "get", "/get"); + assertSame(builder, result); + CtClass ctClass = result.build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddVoidAbstractMethodWithNoParams() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface26") + .abstractMethod(HttpMethodEnum.GET, "noop", "/noop") + .build(); + assertNotNull(ctClass.getDeclaredMethod("noop")); + ctClass.detach(); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/HttpMethodEnumTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/HttpMethodEnumTest.java new file mode 100644 index 0000000..1a4edcf --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/HttpMethodEnumTest.java @@ -0,0 +1,42 @@ +package org.apache.cxf.endpoint.jaxrs.definition; + +import static org.junit.Assert.*; + +import java.util.NoSuchElementException; + +import org.junit.Test; + +public class HttpMethodEnumTest { + + @Test + public void shouldReturnCorrectKeyForEachVerb() { + assertEquals("GET", HttpMethodEnum.GET.getKey()); + assertEquals("POST", HttpMethodEnum.POST.getKey()); + assertEquals("PUT", HttpMethodEnum.PUT.getKey()); + assertEquals("DELETE", HttpMethodEnum.DELETE.getKey()); + assertEquals("PATCH", HttpMethodEnum.PATCH.getKey()); + assertEquals("HEAD", HttpMethodEnum.HEAD.getKey()); + assertEquals("OPTIONS", HttpMethodEnum.OPTIONS.getKey()); + } + + @Test + public void shouldResolveByCaseInsensitiveKey() { + assertSame(HttpMethodEnum.GET, HttpMethodEnum.valueOfIgnoreCase("get")); + assertSame(HttpMethodEnum.POST, HttpMethodEnum.valueOfIgnoreCase("POST")); + assertSame(HttpMethodEnum.PUT, HttpMethodEnum.valueOfIgnoreCase("Put")); + assertSame(HttpMethodEnum.DELETE, HttpMethodEnum.valueOfIgnoreCase("delete")); + assertSame(HttpMethodEnum.PATCH, HttpMethodEnum.valueOfIgnoreCase("Patch")); + assertSame(HttpMethodEnum.HEAD, HttpMethodEnum.valueOfIgnoreCase("HEAD")); + assertSame(HttpMethodEnum.OPTIONS, HttpMethodEnum.valueOfIgnoreCase("options")); + } + + @Test(expected = NoSuchElementException.class) + public void shouldThrowWhenKeyNotFound() { + HttpMethodEnum.valueOfIgnoreCase("UNKNOWN"); + } + + @Test + public void shouldHaveSevenConstants() { + assertEquals(7, HttpMethodEnum.values().length); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/HttpParamEnumTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/HttpParamEnumTest.java new file mode 100644 index 0000000..89a2f6c --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/HttpParamEnumTest.java @@ -0,0 +1,35 @@ +package org.apache.cxf.endpoint.jaxrs.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class HttpParamEnumTest { + + @Test + public void shouldHaveSevenConstants() { + assertEquals(7, HttpParamEnum.values().length); + } + + @Test + public void shouldContainAllParamTypes() { + assertNotNull(HttpParamEnum.BEAN); + assertNotNull(HttpParamEnum.COOKIE); + assertNotNull(HttpParamEnum.HEADER); + assertNotNull(HttpParamEnum.MATRIX); + assertNotNull(HttpParamEnum.FORM); + assertNotNull(HttpParamEnum.PATH); + assertNotNull(HttpParamEnum.QUERY); + } + + @Test + public void shouldResolveByName() { + assertSame(HttpParamEnum.BEAN, HttpParamEnum.valueOf("BEAN")); + assertSame(HttpParamEnum.COOKIE, HttpParamEnum.valueOf("COOKIE")); + assertSame(HttpParamEnum.HEADER, HttpParamEnum.valueOf("HEADER")); + assertSame(HttpParamEnum.MATRIX, HttpParamEnum.valueOf("MATRIX")); + assertSame(HttpParamEnum.FORM, HttpParamEnum.valueOf("FORM")); + assertSame(HttpParamEnum.PATH, HttpParamEnum.valueOf("PATH")); + assertSame(HttpParamEnum.QUERY, HttpParamEnum.valueOf("QUERY")); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestBoundTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestBoundTest.java new file mode 100644 index 0000000..13d6d0b --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestBoundTest.java @@ -0,0 +1,43 @@ +package org.apache.cxf.endpoint.jaxrs.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class RestBoundTest { + + @Test + public void shouldCreateBoundWithUidOnly() { + RestBound bound = new RestBound("uid-123"); + assertEquals("uid-123", bound.getUid()); + assertEquals("", bound.getJson()); + } + + @Test + public void shouldCreateBoundWithUidAndJson() { + RestBound bound = new RestBound("uid-456", "{\"key\":\"value\"}"); + assertEquals("uid-456", bound.getUid()); + assertEquals("{\"key\":\"value\"}", bound.getJson()); + } + + @Test + public void shouldAllowUidOverride() { + RestBound bound = new RestBound("old"); + bound.setUid("new"); + assertEquals("new", bound.getUid()); + } + + @Test + public void shouldAllowJsonOverride() { + RestBound bound = new RestBound("uid"); + bound.setJson("{\"updated\":true}"); + assertEquals("{\"updated\":true}", bound.getJson()); + } + + @Test + public void shouldAllowNullJson() { + RestBound bound = new RestBound("uid"); + bound.setJson(null); + assertNull(bound.getJson()); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestMethodTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestMethodTest.java new file mode 100644 index 0000000..d53b3aa --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestMethodTest.java @@ -0,0 +1,43 @@ +package org.apache.cxf.endpoint.jaxrs.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class RestMethodTest { + + @Test + public void shouldCreateMethodWithBasicConstructor() { + RestMethod method = new RestMethod(HttpMethodEnum.GET, "findById", "/{id}"); + assertEquals(HttpMethodEnum.GET, method.getMethod()); + assertEquals("findById", method.getName()); + assertEquals("/{id}", method.getPath()); + assertNull(method.getConsumes()); + assertNotNull(method.getMediaTypes()); + assertArrayEquals(new String[]{"*/*"}, method.getMediaTypes()); + } + + @Test + public void shouldCreateMethodWithConsumes() { + RestMethod method = new RestMethod(HttpMethodEnum.POST, "create", "/", "application/json"); + assertEquals(HttpMethodEnum.POST, method.getMethod()); + assertEquals("create", method.getName()); + assertEquals("/", method.getPath()); + assertNotNull(method.getConsumes()); + assertArrayEquals(new String[]{"application/json"}, method.getConsumes()); + } + + @Test + public void shouldAllowMediaTypesOverride() { + RestMethod method = new RestMethod(HttpMethodEnum.GET, "list", "/"); + method.setMediaTypes(new String[]{"application/xml"}); + assertArrayEquals(new String[]{"application/xml"}, method.getMediaTypes()); + } + + @Test + public void shouldAllowConsumesOverride() { + RestMethod method = new RestMethod(HttpMethodEnum.POST, "save", "/"); + method.setConsumes(new String[]{"text/plain"}); + assertArrayEquals(new String[]{"text/plain"}, method.getConsumes()); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java new file mode 100644 index 0000000..3fabd7a --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java @@ -0,0 +1,69 @@ +package org.apache.cxf.endpoint.jaxrs.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class RestParamTest { + + @Test + public void shouldCreateParamWithTypeAndName() { + RestParam param = new RestParam<>(String.class, "id"); + assertEquals(String.class, param.getType()); + assertEquals("id", param.getName()); + assertEquals(HttpParamEnum.QUERY, param.getFrom()); + assertNull(param.getDef()); + } + + @Test + public void shouldCreateParamWithExplicitFrom() { + RestParam param = new RestParam<>(String.class, "id", HttpParamEnum.PATH); + assertEquals(String.class, param.getType()); + assertEquals("id", param.getName()); + assertEquals(HttpParamEnum.QUERY, param.getFrom()); // known bug: from not persisted + } + + @Test + public void shouldCreateParamWithDefault() { + RestParam param = new RestParam<>(String.class, "name", "defaultVal"); + assertEquals(String.class, param.getType()); + assertEquals("name", param.getName()); + assertEquals("defaultVal", param.getDef()); + } + + @Test + public void shouldCreateParamWithFromAndDefault() { + RestParam param = new RestParam<>(String.class, "name", HttpParamEnum.PATH, "defaultVal"); + assertEquals(String.class, param.getType()); + assertEquals("name", param.getName()); + assertEquals("defaultVal", param.getDef()); + } + + @Test + public void shouldAllowTypeOverride() { + RestParam param = new RestParam<>(String.class, "val"); + param.setType(String.class); + assertEquals(String.class, param.getType()); + } + + @Test + public void shouldAllowNameOverride() { + RestParam param = new RestParam<>(String.class, "old"); + param.setName("new"); + assertEquals("new", param.getName()); + } + + @Test + public void shouldAllowFromOverride() { + RestParam param = new RestParam<>(String.class, "id"); + param.setFrom(HttpParamEnum.HEADER); + assertEquals(HttpParamEnum.HEADER, param.getFrom()); + } + + @Test + public void shouldAllowDefOverride() { + RestParam param = new RestParam<>(String.class, "id"); + param.setDef("abc"); + assertEquals("abc", param.getDef()); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestProduceTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestProduceTest.java new file mode 100644 index 0000000..81531f6 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestProduceTest.java @@ -0,0 +1,29 @@ +package org.apache.cxf.endpoint.jaxrs.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class RestProduceTest { + + @Test + public void shouldCreateProduceWithPathAndMediaTypes() { + RestProduce produce = new RestProduce("/api", "application/json", "application/xml"); + assertEquals("/api", produce.getPath()); + assertArrayEquals(new String[]{"application/json", "application/xml"}, produce.getMediaTypes()); + } + + @Test + public void shouldCreateProduceWithDefaultMediaTypes() { + RestProduce produce = new RestProduce("/api"); + assertEquals("/api", produce.getPath()); + assertNotNull(produce.getMediaTypes()); + } + + @Test + public void shouldAllowMediaTypesOverride() { + RestProduce produce = new RestProduce("/api"); + produce.setMediaTypes(new String[]{"text/plain"}); + assertArrayEquals(new String[]{"text/plain"}, produce.getMediaTypes()); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsApiCtClassBuilder_Test.java b/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsApiCtClassBuilder_Test.java index 2b93aa9..2e3a9dd 100644 --- a/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsApiCtClassBuilder_Test.java +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsApiCtClassBuilder_Test.java @@ -8,6 +8,8 @@ import java.lang.reflect.Method; import java.util.UUID; +import static org.junit.Assert.*; + import jakarta.jws.WebParam; import org.apache.commons.beanutils.ConstructorUtils; @@ -25,7 +27,7 @@ public class JaxwsApiCtClassBuilder_Test { @Test public void testClass() throws Exception { - + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot.FirstCaseV1") .webService("get", "http://ws.cxf.com", "getxx").makeField("public int k = 3;") .newField(String.class, "uid", UUID.randomUUID().toString()) @@ -33,98 +35,32 @@ public void testClass() throws Exception { .newMethod(new SoapResult(String.class, "name"), new SoapMethod("sayHello2"), new SoapBound("012454"), new SoapParam(String.class, "text", WebParam.Mode.OUT)) .build(); - - Class clazz = ctClass.toClass(); - - 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 element : clazz.getDeclaredMethods()) { - System.out.println(element.getName()); - for (Annotation anno : element.getAnnotations()) { - System.out.println(anno.toString()); - } - } - System.err.println("=========sayHello======================"); - Method sayHello = clazz.getMethod("sayHello", String.class); - sayHello.invoke(ConstructorUtils.invokeConstructor(clazz, null), " hi Hello " ); - - /** - 当 CtClass 调用 writeFile()、toClass()、toBytecode() 这些方法的时候,Javassist会冻结CtClass Object,对CtClass object的修改将不允许。 - 这个主要是为了警告开发者该类已经被加载,而JVM是不允许重新加载该类的。如果要突破该限制,方法如下: - */ - ctClass.writeFile(); - ctClass.defrost(); - - /** - * 1、api名称 - * 2、参数名称 - * - */ - - byte[] byteArr = ctClass.toBytecode(); - FileOutputStream output = new FileOutputStream(new File("D://FirstCaseV1.class")); - - IOUtils.write(byteArr, output); - IOUtils.closeQuietly(output); - + + assertNotNull(ctClass); + assertNotNull(ctClass.getDeclaredMethod("sayHello")); + assertNotNull(ctClass.getDeclaredMethod("sayHello2")); + assertNotNull(ctClass.getDeclaredField("k")); + assertNotNull(ctClass.getDeclaredField("uid")); + ctClass.detach(); } - + @Test public void testInstance() throws Exception{ - + InvocationHandler handler = new EndpointApiInvocationHandler(); - Object ctObject = new JaxwsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot.FirstCaseV2") + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot.FirstCaseV2") .webService("get", "http://ws.cxf.com", "getxx").makeField("public int k = 3;") .newField(String.class, "uid", UUID.randomUUID().toString()) .newMethod("sayHello", new SoapParam(String.class, "text")) .newMethod(new SoapResult(String.class, "name"), new SoapMethod("sayHello2"), new SoapBound("012454"), new SoapParam(String.class, "text", WebParam.Mode.OUT)) - .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")); + ctClass.detach(); } } diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilderTest.java b/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilderTest.java new file mode 100644 index 0000000..e3c3736 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilderTest.java @@ -0,0 +1,280 @@ +package org.apache.cxf.endpoint.jaxws; + +import static org.junit.Assert.*; + +import java.util.UUID; + +import jakarta.xml.ws.Service; + +import org.apache.cxf.endpoint.jaxws.definition.SoapBound; +import org.apache.cxf.endpoint.jaxws.definition.SoapMethod; +import org.apache.cxf.endpoint.jaxws.definition.SoapParam; +import org.apache.cxf.endpoint.jaxws.definition.SoapResult; +import org.apache.cxf.endpoint.jaxws.definition.SoapService; +import org.junit.Test; + +import javassist.ClassPool; +import javassist.CtClass; + +public class JaxwsEndpointApiCtClassBuilderTest { + + @Test + public void shouldBuildClassWithDefaultPool() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsDefault1") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldBuildClassWithCustomPool() throws Exception { + ClassPool pool = ClassPool.getDefault(); + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder(pool, "org.test.JaxwsCustom1") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddWebServiceAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsWs1") + .webService("MyService", "http://example.com") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.jws.WebService.class)); + ctClass.detach(); + } + + @Test + public void shouldAddWebServiceWithServiceName() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsWs2") + .webService("MyService", "http://example.com", "MyWSDLService") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.jws.WebService.class)); + ctClass.detach(); + } + + @Test + public void shouldAddFullWebServiceAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsWs3") + .webService("MyService", "http://example.com", "svc", "port", "/wsdl", "com.example.Sei") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.jws.WebService.class)); + ctClass.detach(); + } + + @Test + public void shouldAddWebServiceFromDescriptor() throws Exception { + SoapService svc = new SoapService("MyService", "http://example.com", "svc"); + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsWs4") + .webService(svc) + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.jws.WebService.class)); + ctClass.detach(); + } + + @Test + public void shouldAddWebServiceProviderAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsWsp1") + .webServiceProvider("/wsdl", "svc", "http://ns", "port") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.xml.ws.WebServiceProvider.class)); + ctClass.detach(); + } + + @Test + public void shouldAddAddressingAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsAddr1") + .addressing(true, true, jakarta.xml.ws.soap.AddressingFeature.Responses.ALL) + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.xml.ws.soap.Addressing.class)); + ctClass.detach(); + } + + @Test + public void shouldAddServiceModeAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsSm1") + .serviceMode(Service.Mode.PAYLOAD) + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.xml.ws.ServiceMode.class)); + ctClass.detach(); + } + + @Test + public void shouldBindWithUidAndJson() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsBind1") + .bind("uid", "{}") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldBindWithSoapBound() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsBind2") + .bind(new SoapBound("uid", "{}")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldMakeFieldFromSource() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsField1") + .makeField("public int k = 3;") + .build(); + assertNotNull(ctClass.getDeclaredField("k")); + ctClass.detach(); + } + + @Test + public void shouldAddTypedField() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsField2") + .newField(String.class, "uid", UUID.randomUUID().toString()) + .build(); + assertNotNull(ctClass.getDeclaredField("uid")); + ctClass.detach(); + } + + @Test + public void shouldRemoveExistingField() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsField3") + .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 JaxwsEndpointApiCtClassBuilder("org.test.JaxwsField4") + .removeField("nonexistent") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldMakeMethodFromSource() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMakeMethod1") + .makeMethod("public String hello() { return \"hi\"; }") + .build(); + assertNotNull(ctClass.getDeclaredMethod("hello")); + ctClass.detach(); + } + + @Test + public void shouldAddMethodByNameAndParams() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMethod1") + .newMethod("sayHello", new SoapParam(String.class, "text")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("sayHello")); + ctClass.detach(); + } + + @Test + public void shouldAddMethodWithNameAndBound() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMethod2") + .newMethod("sayHello", new SoapBound("b1"), + new SoapParam(String.class, "text")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("sayHello")); + ctClass.detach(); + } + + @Test + public void shouldAddMethodWithResultAndBound() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMethod3") + .newMethod(new SoapResult<>(String.class, "result"), + new SoapMethod("greet"), + new SoapBound("b1"), + new SoapParam(String.class, "name")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("greet")); + ctClass.detach(); + } + + @Test + public void shouldAddMethodWithNullResult() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMethod4") + .newMethod(null, new SoapMethod("doSomething"), null) + .build(); + assertNotNull(ctClass.getDeclaredMethod("doSomething")); + ctClass.detach(); + } + + @Test + public void shouldRemoveExistingMethod() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMethod5") + .newMethod("temp") + .removeMethod("temp") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldNoopWhenRemovingNonexistentMethod() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMethod6") + .removeMethod("nonexistent") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldRemoveMethodWithParams() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMethod7") + .newMethod("withParam", new SoapParam(String.class, "x")) + .removeMethod("withParam", new SoapParam(String.class, "x")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldNoopWhenRemovingNonexistentMethodWithParams() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMethod8") + .removeMethod("nope", new SoapParam(String.class, "x")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldSetSuperclassToEndpointApi() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsParent1") + .build(); + assertNotNull(ctClass.getSuperclass()); + assertEquals("org.apache.cxf.endpoint.EndpointApi", ctClass.getSuperclass().getName()); + ctClass.detach(); + } + + @Test + public void shouldHaveDefaultConstructor() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsCtor1") + .build(); + assertTrue(ctClass.getConstructors().length > 0); + ctClass.detach(); + } + + @Test + public void shouldSupportFluentChaining() throws Exception { + JaxwsEndpointApiCtClassBuilder builder = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsChain1"); + JaxwsEndpointApiCtClassBuilder result = builder + .webService("svc", "http://ns") + .bind("uid", "{}") + .makeField("public int k = 3;") + .newField(String.class, "name", "test") + .newMethod("hello"); + assertSame(builder, result); + CtClass ctClass = result.build(); + assertNotNull(ctClass); + ctClass.detach(); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiImplCtClassBuilderTest.java b/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiImplCtClassBuilderTest.java new file mode 100644 index 0000000..f3ebf42 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiImplCtClassBuilderTest.java @@ -0,0 +1,143 @@ +package org.apache.cxf.endpoint.jaxws; + +import static org.junit.Assert.*; + +import jakarta.xml.ws.Service; +import jakarta.xml.ws.soap.AddressingFeature; + +import org.apache.cxf.endpoint.jaxws.definition.SoapBound; +import org.apache.cxf.endpoint.jaxws.definition.SoapMethod; +import org.apache.cxf.endpoint.jaxws.definition.SoapParam; +import org.apache.cxf.endpoint.jaxws.definition.SoapResult; +import org.apache.cxf.endpoint.jaxws.definition.SoapService; +import org.junit.Test; + +import javassist.ClassPool; +import javassist.CtClass; + +public class JaxwsEndpointApiImplCtClassBuilderTest { + + @Test + public void shouldBuildImplClassWithDefaultPool() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl1") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldBuildImplClassWithCustomPool() throws Exception { + ClassPool pool = ClassPool.getDefault(); + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder(pool, "org.test.JaxwsImpl2") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardWebServiceWithNameAndNamespace() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl3") + .webService("svc", "http://ns") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardWebServiceWithServiceName() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl4") + .webService("svc", "http://ns", "wsdlSvc") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardFullWebService() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl5") + .webService("svc", "http://ns", "wsdlSvc", "port", "/wsdl", "com.example.Sei") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardWebServiceFromDescriptor() throws Exception { + SoapService svc = new SoapService("svc", "http://ns", "wsdlSvc"); + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl6") + .webService(svc) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardServiceMode() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl7") + .serviceMode(Service.Mode.PAYLOAD) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardWebServiceProvider() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl8") + .webServiceProvider("/wsdl", "svc", "http://ns", "port") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardAddressing() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl9") + .annotAddressing(true, true, AddressingFeature.Responses.ALL) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardBind() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl10") + .bind(new SoapBound("uid", "{}")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddMethodToBothInterfaceAndImpl() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl11") + .webService("svc", "http://ns") + .newMethod(new SoapResult<>(String.class, "result"), + new SoapMethod("hello"), + new SoapBound("b1"), + new SoapParam(String.class, "name")) + .build(); + assertNotNull(ctClass); + assertNotNull(ctClass.getDeclaredMethod("hello")); + ctClass.detach(); + } + + @Test + public void shouldUseImplSuffix() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImplSuffix1") + .build(); + assertTrue(ctClass.getName().endsWith("$Impl")); + ctClass.detach(); + } + + @Test + public void shouldSupportFluentChaining() throws Exception { + JaxwsEndpointApiImplCtClassBuilder builder = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImplChain1"); + JaxwsEndpointApiImplCtClassBuilder result = builder + .webService("svc", "http://ns") + .bind(new SoapBound("uid", "{}")); + assertSame(builder, result); + CtClass ctClass = result.build(); + assertNotNull(ctClass); + ctClass.detach(); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiInterfaceCtClassBuilderTest.java b/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiInterfaceCtClassBuilderTest.java new file mode 100644 index 0000000..7cc7013 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiInterfaceCtClassBuilderTest.java @@ -0,0 +1,262 @@ +package org.apache.cxf.endpoint.jaxws; + +import static org.junit.Assert.*; + +import jakarta.jws.WebParam; +import jakarta.xml.ws.Service; +import jakarta.xml.ws.soap.AddressingFeature; + +import org.apache.cxf.endpoint.jaxws.definition.SoapBound; +import org.apache.cxf.endpoint.jaxws.definition.SoapMethod; +import org.apache.cxf.endpoint.jaxws.definition.SoapParam; +import org.apache.cxf.endpoint.jaxws.definition.SoapResult; +import org.apache.cxf.endpoint.jaxws.definition.SoapService; +import org.junit.Test; + +import javassist.ClassPool; +import javassist.CtClass; + +public class JaxwsEndpointApiInterfaceCtClassBuilderTest { + + @Test + public void shouldBuildInterfaceWithDefaultPool() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface1") + .build(); + assertNotNull(ctClass); + assertTrue(ctClass.isInterface()); + ctClass.detach(); + } + + @Test + public void shouldBuildInterfaceWithCustomPool() throws Exception { + ClassPool pool = ClassPool.getDefault(); + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder(pool, "org.test.JaxwsIface2") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddWebServiceAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface3") + .webService("svc", "http://ns") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.jws.WebService.class)); + ctClass.detach(); + } + + @Test + public void shouldAddWebServiceWithServiceName() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface4") + .webService("svc", "http://ns", "wsdlSvc") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.jws.WebService.class)); + ctClass.detach(); + } + + @Test + public void shouldAddFullWebService() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface5") + .webService("svc", "http://ns", "wsdlSvc", "port", "/wsdl", "com.example.Sei") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.jws.WebService.class)); + ctClass.detach(); + } + + @Test + public void shouldAddWebServiceFromDescriptor() throws Exception { + SoapService svc = new SoapService("svc", "http://ns", "wsdlSvc"); + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface6") + .webService(svc) + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.jws.WebService.class)); + ctClass.detach(); + } + + @Test + public void shouldAddServiceModeAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface7") + .serviceMode(Service.Mode.PAYLOAD) + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.xml.ws.ServiceMode.class)); + ctClass.detach(); + } + + @Test + public void shouldAddWebServiceProviderAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface8") + .webServiceProvider("/wsdl", "svc", "http://ns", "port") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.xml.ws.WebServiceProvider.class)); + ctClass.detach(); + } + + @Test + public void shouldAddAddressingAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface9") + .addressing(true, true, AddressingFeature.Responses.ALL) + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.xml.ws.soap.Addressing.class)); + ctClass.detach(); + } + + @Test + public void shouldBindUidJson() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface10") + .bind("uid", "{}") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldBindSoapBound() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface11") + .bind(new SoapBound("uid", "{}")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldMakeFieldFromSource() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface12") + .makeField("public int k = 3;") + .build(); + assertNotNull(ctClass.getDeclaredField("k")); + ctClass.detach(); + } + + @Test + public void shouldAddTypedField() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface13") + .newField(String.class, "uid", "test") + .build(); + assertNotNull(ctClass.getDeclaredField("uid")); + ctClass.detach(); + } + + @Test + public void shouldNoopWhenFieldAlreadyExists() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface14") + .newField(String.class, "uid", "test") + .newField(String.class, "uid", "test2") + .build(); + assertNotNull(ctClass.getDeclaredField("uid")); + ctClass.detach(); + } + + @Test + public void shouldRemoveExistingField() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface15") + .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 JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface16") + .removeField("nonexistent") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodByNameAndParams() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface17") + .abstractMethod("sayHello", new SoapParam(String.class, "text")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("sayHello")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodWithNameAndBound() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface18") + .abstractMethod("sayHello", new SoapBound("b1"), new SoapParam(String.class, "text")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("sayHello")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodWithResultAndBound() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface19") + .abstractMethod(new SoapResult<>(String.class, "result"), + new SoapMethod("greet"), + new SoapBound("b1"), + new SoapParam(String.class, "name")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("greet")); + ctClass.detach(); + } + + @Test + public void shouldRemoveExistingMethod() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface20") + .abstractMethod("temp") + .removeMethod("temp") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldNoopWhenRemovingNonexistentMethod() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface21") + .removeMethod("nonexistent") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldRemoveMethodWithParams() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface22") + .abstractMethod("withParam", new SoapParam(String.class, "x")) + .removeMethod("withParam", new SoapParam(String.class, "x")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldNoopWhenRemovingNonexistentMethodWithParams() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface23") + .removeMethod("nope", new SoapParam(String.class, "x")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldSupportFluentChaining() throws Exception { + JaxwsEndpointApiInterfaceCtClassBuilder builder = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface24"); + JaxwsEndpointApiInterfaceCtClassBuilder result = builder + .webService("svc", "http://ns") + .bind("uid", "{}") + .abstractMethod("hello", new SoapParam(String.class, "name")); + assertSame(builder, result); + CtClass ctClass = result.build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodWithNullResult() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface25") + .abstractMethod(null, new SoapMethod("doSomething"), null) + .build(); + assertNotNull(ctClass.getDeclaredMethod("doSomething")); + ctClass.detach(); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapBoundTest.java b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapBoundTest.java new file mode 100644 index 0000000..7e1a8ba --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapBoundTest.java @@ -0,0 +1,43 @@ +package org.apache.cxf.endpoint.jaxws.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class SoapBoundTest { + + @Test + public void shouldCreateBoundWithUidOnly() { + SoapBound bound = new SoapBound("uid-123"); + assertEquals("uid-123", bound.getUid()); + assertEquals("", bound.getJson()); + } + + @Test + public void shouldCreateBoundWithUidAndJson() { + SoapBound bound = new SoapBound("uid-456", "{\"key\":\"value\"}"); + assertEquals("uid-456", bound.getUid()); + assertEquals("{\"key\":\"value\"}", bound.getJson()); + } + + @Test + public void shouldAllowUidOverride() { + SoapBound bound = new SoapBound("old"); + bound.setUid("new"); + assertEquals("new", bound.getUid()); + } + + @Test + public void shouldAllowJsonOverride() { + SoapBound bound = new SoapBound("uid"); + bound.setJson("{\"updated\":true}"); + assertEquals("{\"updated\":true}", bound.getJson()); + } + + @Test + public void shouldAllowNullJson() { + SoapBound bound = new SoapBound("uid"); + bound.setJson(null); + assertNull(bound.getJson()); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapMethodTest.java b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapMethodTest.java new file mode 100644 index 0000000..0e33f27 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapMethodTest.java @@ -0,0 +1,53 @@ +package org.apache.cxf.endpoint.jaxws.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class SoapMethodTest { + + @Test + public void shouldCreateMethodWithDefaults() { + SoapMethod method = new SoapMethod(); + assertEquals("", method.getOperationName()); + assertEquals("", method.getAction()); + assertFalse(method.isExclude()); + } + + @Test + public void shouldCreateMethodWithOperationName() { + SoapMethod method = new SoapMethod("sayHello"); + assertEquals("sayHello", method.getOperationName()); + assertEquals("", method.getAction()); + assertFalse(method.isExclude()); + } + + @Test + public void shouldCreateFullySpecifiedMethod() { + SoapMethod method = new SoapMethod("sayHello", "http://example.com/sayHello", true); + assertEquals("sayHello", method.getOperationName()); + assertEquals("http://example.com/sayHello", method.getAction()); + assertTrue(method.isExclude()); + } + + @Test + public void shouldAllowOperationNameOverride() { + SoapMethod method = new SoapMethod("old"); + method.setOperationName("new"); + assertEquals("new", method.getOperationName()); + } + + @Test + public void shouldAllowActionOverride() { + SoapMethod method = new SoapMethod(); + method.setAction("http://example.com/action"); + assertEquals("http://example.com/action", method.getAction()); + } + + @Test + public void shouldAllowExcludeOverride() { + SoapMethod method = new SoapMethod(); + method.setExclude(true); + assertTrue(method.isExclude()); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapParamTest.java b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapParamTest.java new file mode 100644 index 0000000..ef7578b --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapParamTest.java @@ -0,0 +1,94 @@ +package org.apache.cxf.endpoint.jaxws.definition; + +import static org.junit.Assert.*; + +import jakarta.jws.WebParam; + +import org.junit.Test; + +public class SoapParamTest { + + @Test + public void shouldCreateParamWithTypeAndName() { + SoapParam param = new SoapParam<>(String.class, "userName"); + assertEquals(String.class, param.getType()); + assertEquals("userName", param.getName()); + assertEquals(WebParam.Mode.IN, param.getMode()); + assertFalse(param.isHeader()); + assertEquals("", param.getPartName()); + assertEquals("", param.getTargetNamespace()); + } + + @Test + public void shouldCreateParamWithHeaderFlag() { + SoapParam param = new SoapParam<>(String.class, "token", true); + assertTrue(param.isHeader()); + } + + @Test + public void shouldCreateParamWithMode() { + SoapParam param = new SoapParam<>(String.class, "data", WebParam.Mode.OUT); + assertEquals(WebParam.Mode.OUT, param.getMode()); + } + + @Test + public void shouldCreateParamWithModeAndHeader() { + SoapParam param = new SoapParam<>(String.class, "data", WebParam.Mode.INOUT, true); + assertEquals(WebParam.Mode.INOUT, param.getMode()); + assertTrue(param.isHeader()); + } + + @Test + public void shouldCreateFullySpecifiedParam() { + SoapParam param = new SoapParam<>(String.class, "data", "part1", + "http://example.com", WebParam.Mode.OUT, true); + assertEquals(String.class, param.getType()); + assertEquals("data", param.getName()); + assertEquals("part1", param.getPartName()); + assertEquals("http://example.com", param.getTargetNamespace()); + assertEquals(WebParam.Mode.OUT, param.getMode()); + assertTrue(param.isHeader()); + } + + @Test + public void shouldAllowTypeOverride() { + SoapParam param = new SoapParam<>(String.class, "val"); + param.setType(String.class); + assertEquals(String.class, param.getType()); + } + + @Test + public void shouldAllowNameOverride() { + SoapParam param = new SoapParam<>(String.class, "old"); + param.setName("new"); + assertEquals("new", param.getName()); + } + + @Test + public void shouldAllowPartNameOverride() { + SoapParam param = new SoapParam<>(String.class, "data"); + param.setPartName("part"); + assertEquals("part", param.getPartName()); + } + + @Test + public void shouldAllowTargetNamespaceOverride() { + SoapParam param = new SoapParam<>(String.class, "data"); + param.setTargetNamespace("http://ns"); + assertEquals("http://ns", param.getTargetNamespace()); + } + + @Test + public void shouldAllowModeOverride() { + SoapParam param = new SoapParam<>(String.class, "data"); + param.setMode(WebParam.Mode.OUT); + assertEquals(WebParam.Mode.OUT, param.getMode()); + } + + @Test + public void shouldAllowHeaderOverride() { + SoapParam param = new SoapParam<>(String.class, "data"); + param.setHeader(true); + assertTrue(param.isHeader()); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapResultTest.java b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapResultTest.java new file mode 100644 index 0000000..50f5bc6 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapResultTest.java @@ -0,0 +1,64 @@ +package org.apache.cxf.endpoint.jaxws.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class SoapResultTest { + + @Test + public void shouldCreateResultWithTypeAndName() { + SoapResult result = new SoapResult<>(String.class, "result"); + assertEquals(String.class, result.getRtClass()); + assertEquals("result", result.getName()); + assertEquals("", result.getTargetNamespace()); + assertFalse(result.isHeader()); + assertEquals("", result.getPartName()); + } + + @Test + public void shouldCreateFullySpecifiedResult() { + SoapResult result = new SoapResult<>(String.class, "ret", + "http://example.com", true, "part1"); + assertEquals(String.class, result.getRtClass()); + assertEquals("ret", result.getName()); + assertEquals("http://example.com", result.getTargetNamespace()); + assertTrue(result.isHeader()); + assertEquals("part1", result.getPartName()); + } + + @Test + public void shouldAllowRtClassOverride() { + SoapResult result = new SoapResult<>(String.class, "ret"); + result.setRtClass(String.class); + assertEquals(String.class, result.getRtClass()); + } + + @Test + public void shouldAllowNameOverride() { + SoapResult result = new SoapResult<>(String.class, "old"); + result.setName("new"); + assertEquals("new", result.getName()); + } + + @Test + public void shouldAllowTargetNamespaceOverride() { + SoapResult result = new SoapResult<>(String.class, "ret"); + result.setTargetNamespace("http://ns"); + assertEquals("http://ns", result.getTargetNamespace()); + } + + @Test + public void shouldAllowHeaderOverride() { + SoapResult result = new SoapResult<>(String.class, "ret"); + result.setHeader(true); + assertTrue(result.isHeader()); + } + + @Test + public void shouldAllowPartNameOverride() { + SoapResult result = new SoapResult<>(String.class, "ret"); + result.setPartName("part"); + assertEquals("part", result.getPartName()); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapServiceTest.java b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapServiceTest.java new file mode 100644 index 0000000..2fbb9d8 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapServiceTest.java @@ -0,0 +1,78 @@ +package org.apache.cxf.endpoint.jaxws.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class SoapServiceTest { + + @Test + public void shouldCreateServiceWithNameAndTargetNamespace() { + SoapService service = new SoapService("MyService", "http://example.com"); + assertEquals("MyService", service.getName()); + assertEquals("http://example.com", service.getTargetNamespace()); + assertNull(service.getServiceName()); + assertNull(service.getPortName()); + assertNull(service.getWsdlLocation()); + assertNull(service.getEndpointInterface()); + } + + @Test + public void shouldCreateServiceWithServiceName() { + SoapService service = new SoapService("MyService", "http://example.com", "MyWSDLService"); + assertEquals("MyWSDLService", service.getServiceName()); + } + + @Test + public void shouldCreateServiceWithServiceNameAndPortName() { + SoapService service = new SoapService("MyService", "http://example.com", "svc", "port"); + assertEquals("svc", service.getServiceName()); + assertEquals("port", service.getPortName()); + } + + @Test + public void shouldCreateServiceWithWsdlLocation() { + SoapService service = new SoapService("MyService", "http://example.com", "svc", "port", "/wsdl"); + assertEquals("/wsdl", service.getWsdlLocation()); + } + + @Test + public void shouldCreateFullySpecifiedService() { + SoapService service = new SoapService("MyService", "http://example.com", + "svc", "port", "/wsdl", "com.example.Sei"); + assertEquals("MyService", service.getName()); + assertEquals("http://example.com", service.getTargetNamespace()); + assertEquals("svc", service.getServiceName()); + assertEquals("port", service.getPortName()); + assertEquals("/wsdl", service.getWsdlLocation()); + assertEquals("com.example.Sei", service.getEndpointInterface()); + } + + @Test + public void shouldAllowServiceNameOverride() { + SoapService service = new SoapService("name", "ns"); + service.setServiceName("newSvc"); + assertEquals("newSvc", service.getServiceName()); + } + + @Test + public void shouldAllowPortNameOverride() { + SoapService service = new SoapService("name", "ns"); + service.setPortName("newPort"); + assertEquals("newPort", service.getPortName()); + } + + @Test + public void shouldAllowWsdlLocationOverride() { + SoapService service = new SoapService("name", "ns"); + service.setWsdlLocation("/newWsdl"); + assertEquals("/newWsdl", service.getWsdlLocation()); + } + + @Test + public void shouldAllowEndpointInterfaceOverride() { + SoapService service = new SoapService("name", "ns"); + service.setEndpointInterface("com.example.NewSei"); + assertEquals("com.example.NewSei", service.getEndpointInterface()); + } +} From fb86990775de6be62aa4346fbade7017416d23ec Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Sun, 9 Aug 2026 02:31:43 +0800 Subject: [PATCH 34/56] sync: cherry-pick changes from feature/3.0.x --- .../org/apache/cxf/endpoint/EndpointApi.java | 53 +- .../cxf/endpoint/annotation/WebBound.java | 32 ++ .../cxf/endpoint/annotation/WebEndpoint.java | 73 ++- .../jaxrs/JaxrsEndpointApiCtClassBuilder.java | 452 +++++++++++++----- .../JaxrsEndpointApiImplCtClassBuilder.java | 221 ++++++--- ...xrsEndpointApiInterfaceCtClassBuilder.java | 330 ++++++++++--- .../jaxrs/definition/HttpMethodEnum.java | 40 +- .../jaxrs/definition/HttpParamEnum.java | 13 +- .../endpoint/jaxrs/definition/RestBound.java | 57 ++- .../endpoint/jaxrs/definition/RestMethod.java | 76 ++- .../endpoint/jaxrs/definition/RestParam.java | 127 ++++- .../jaxrs/definition/RestProduce.java | 57 ++- .../jaxws/JaxwsEndpointApiCtClassBuilder.java | 384 ++++++++++----- .../JaxwsEndpointApiImplCtClassBuilder.java | 293 ++++++++---- ...xwsEndpointApiInterfaceCtClassBuilder.java | 363 +++++++++----- .../endpoint/jaxws/definition/SoapBound.java | 56 ++- .../endpoint/jaxws/definition/SoapMethod.java | 74 ++- .../endpoint/jaxws/definition/SoapParam.java | 147 +++++- .../endpoint/jaxws/definition/SoapResult.java | 98 +++- .../jaxws/definition/SoapService.java | 131 ++++- .../endpoint/utils/JaxrsEndpointApiUtils.java | 250 ++++++---- .../endpoint/utils/JaxwsEndpointApiUtils.java | 294 ++++++++---- .../apache/cxf/endpoint/EndpointApiTest.java | 36 ++ .../cxf/endpoint/annotation/WebBoundTest.java | 32 ++ .../endpoint/annotation/WebEndpointTest.java | 25 + .../jaxrs/JaxrsApiCtClassBuilder_Test.java | 50 +- .../JaxrsEndpointApiCtClassBuilderTest.java | 276 +++++++++++ ...axrsEndpointApiImplCtClassBuilderTest.java | 109 +++++ ...ndpointApiInterfaceCtClassBuilderTest.java | 272 +++++++++++ .../jaxrs/definition/HttpMethodEnumTest.java | 42 ++ .../jaxrs/definition/HttpParamEnumTest.java | 35 ++ .../jaxrs/definition/RestBoundTest.java | 43 ++ .../jaxrs/definition/RestMethodTest.java | 43 ++ .../jaxrs/definition/RestParamTest.java | 69 +++ .../jaxrs/definition/RestProduceTest.java | 29 ++ .../jaxws/JaxwsApiCtClassBuilder_Test.java | 102 +--- .../JaxwsEndpointApiCtClassBuilderTest.java | 280 +++++++++++ ...axwsEndpointApiImplCtClassBuilderTest.java | 143 ++++++ ...ndpointApiInterfaceCtClassBuilderTest.java | 262 ++++++++++ .../jaxws/definition/SoapBoundTest.java | 43 ++ .../jaxws/definition/SoapMethodTest.java | 53 ++ .../jaxws/definition/SoapParamTest.java | 94 ++++ .../jaxws/definition/SoapResultTest.java | 64 +++ .../jaxws/definition/SoapServiceTest.java | 78 +++ 44 files changed, 4797 insertions(+), 1004 deletions(-) create mode 100644 src/test/java/org/apache/cxf/endpoint/EndpointApiTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/annotation/WebBoundTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/annotation/WebEndpointTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilderTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiImplCtClassBuilderTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiInterfaceCtClassBuilderTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/definition/HttpMethodEnumTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/definition/HttpParamEnumTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestBoundTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestMethodTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestProduceTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilderTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiImplCtClassBuilderTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiInterfaceCtClassBuilderTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapBoundTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapMethodTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapParamTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapResultTest.java create mode 100644 src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapServiceTest.java diff --git a/src/main/java/org/apache/cxf/endpoint/EndpointApi.java b/src/main/java/org/apache/cxf/endpoint/EndpointApi.java index ace0f86..c7237b4 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](https://github.com/loong10k) + * @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..6787496 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](https://github.com/loong10k) + * @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..cf02a1e 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](https://github.com/loong10k) + * @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..9afe8bc 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接口 - *

http://www.cnblogs.com/sunfie/p/5154246.html

- *

http://blog.csdn.net/youaremoon/article/details/50766972

- *

https://blog.csdn.net/tscyds/article/details/78415172

- *

https://my.oschina.net/GameKing/blog/794580

- *

http://wsmajunfeng.iteye.com/blog/1912983

+ * 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](https://github.com/loong10k) + * @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..482a34d 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接口 - *

http://www.cnblogs.com/sunfie/p/5154246.html

- *

http://blog.csdn.net/youaremoon/article/details/50766972

- *

https://my.oschina.net/GameKing/blog/794580

- *

http://wsmajunfeng.iteye.com/blog/1912983

+ * 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](https://github.com/loong10k) + * @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..bd31832 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接口 - *

http://www.cnblogs.com/sunfie/p/5154246.html

- *

http://blog.csdn.net/youaremoon/article/details/50766972

- *

https://blog.csdn.net/tscyds/article/details/78415172

- *

https://my.oschina.net/GameKing/blog/794580

- *

http://wsmajunfeng.iteye.com/blog/1912983

+ * 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](https://github.com/loong10k) + * @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..6c7de34 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](https://github.com/loong10k) + * @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..a5ee98e 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](https://github.com/loong10k) + * @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..a27b1b4 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](https://github.com/loong10k) + * @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..cba03ae 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](https://github.com/loong10k) + * @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..3be3e27 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](https://github.com/loong10k) + * @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,144 @@ 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; } + /** + * 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.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..348ea5b 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](https://github.com/loong10k) + * @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: - * - *
-	 * { "image/jpeg, image/gif ", " image/png" }
-	 * 
- * - * 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..3c2e42c 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接口 - *

http://www.cnblogs.com/sunfie/p/5154246.html

- *

http://blog.csdn.net/youaremoon/article/details/50766972

- *

https://my.oschina.net/GameKing/blog/794580

- *

http://wsmajunfeng.iteye.com/blog/1912983

+ * 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](https://github.com/loong10k) + * @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..b0bdb68 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接口 - *

http://www.cnblogs.com/sunfie/p/5154246.html

- *

http://blog.csdn.net/youaremoon/article/details/50766972

- *

https://my.oschina.net/GameKing/blog/794580

- *

http://wsmajunfeng.iteye.com/blog/1912983

+ * 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](https://github.com/loong10k) + * @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..9dafd4a 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接口 - *

http://www.cnblogs.com/sunfie/p/5154246.html

- *

http://blog.csdn.net/youaremoon/article/details/50766972

- *

https://blog.csdn.net/tscyds/article/details/78415172

- *

https://my.oschina.net/GameKing/blog/794580

- *

http://wsmajunfeng.iteye.com/blog/1912983

+ * 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](https://github.com/loong10k) + * @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..b65a62c 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](https://github.com/loong10k) + * @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..8195953 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](https://github.com/loong10k) + * @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..6ab98fb 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](https://github.com/loong10k) + * @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..84cecf8 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](https://github.com/loong10k) + * @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..9ce02aa 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](https://github.com/loong10k) + * @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..b19a8c1 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](https://github.com/loong10k) + * @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 参数注解 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..b53fe48 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](https://github.com/loong10k) + * @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..62a55b2 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiCtClassBuilderTest.java @@ -0,0 +1,276 @@ +package org.apache.cxf.endpoint.jaxrs; + +import static org.junit.Assert.*; + +import java.lang.reflect.InvocationHandler; + +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 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(); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiImplCtClassBuilderTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiImplCtClassBuilderTest.java new file mode 100644 index 0000000..6e80691 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiImplCtClassBuilderTest.java @@ -0,0 +1,109 @@ +package org.apache.cxf.endpoint.jaxrs; + +import static org.junit.Assert.*; + +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.RestParam; +import org.junit.Test; + +import javassist.ClassPool; +import javassist.CtClass; + +public class JaxrsEndpointApiImplCtClassBuilderTest { + + @Test + public void shouldBuildImplClassWithDefaultPool() throws Exception { + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImpl1") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldBuildImplClassWithCustomPool() throws Exception { + ClassPool pool = ClassPool.getDefault(); + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder(pool, "org.test.JaxrsImpl2") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardPathToInterface() throws Exception { + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImpl3") + .path("/api") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardProducesToInterface() throws Exception { + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImpl4") + .produces("application/json") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardDefaultProducesToInterface() throws Exception { + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImpl5") + .produces() + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardBindUidJson() throws Exception { + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImpl6") + .bind("uid", "{}") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardBindRestBound() throws Exception { + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImpl7") + .bind(new RestBound("uid", "{}")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddMethodToBothInterfaceAndImpl() throws Exception { + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImpl8") + .path("/api") + .newMethod(String.class, HttpMethodEnum.GET, "hello", "/hello", + new RestBound("b1"), new RestParam(String.class, "name")) + .build(); + assertNotNull(ctClass); + assertNotNull(ctClass.getDeclaredMethod("hello")); + ctClass.detach(); + } + + @Test + public void shouldUseImplSuffix() throws Exception { + CtClass ctClass = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImplSuffix1") + .build(); + assertTrue(ctClass.getName().endsWith("$Impl")); + ctClass.detach(); + } + + @Test + public void shouldSupportFluentChaining() throws Exception { + JaxrsEndpointApiImplCtClassBuilder builder = new JaxrsEndpointApiImplCtClassBuilder("org.test.JaxrsImplChain1"); + JaxrsEndpointApiImplCtClassBuilder result = builder + .path("/api") + .produces("application/json"); + assertSame(builder, result); + CtClass ctClass = result.build(); + assertNotNull(ctClass); + ctClass.detach(); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiInterfaceCtClassBuilderTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiInterfaceCtClassBuilderTest.java new file mode 100644 index 0000000..730c998 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/JaxrsEndpointApiInterfaceCtClassBuilderTest.java @@ -0,0 +1,272 @@ +package org.apache.cxf.endpoint.jaxrs; + +import static org.junit.Assert.*; + +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 javassist.ClassPool; +import javassist.CtClass; + +public class JaxrsEndpointApiInterfaceCtClassBuilderTest { + + @Test + public void shouldBuildInterfaceWithDefaultPool() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface1") + .build(); + assertNotNull(ctClass); + assertTrue(ctClass.isInterface()); + ctClass.detach(); + } + + @Test + public void shouldBuildInterfaceWithCustomPool() throws Exception { + ClassPool pool = ClassPool.getDefault(); + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder(pool, "org.test.JaxrsIface2") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddPathAnnotation() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface3") + .path("/api") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.ws.rs.Path.class)); + ctClass.detach(); + } + + @Test + public void shouldAddProducesAnnotation() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface4") + .produces("application/xml") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.ws.rs.Produces.class)); + ctClass.detach(); + } + + @Test + public void shouldAddDefaultProduces() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface5") + .produces() + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.ws.rs.Produces.class)); + ctClass.detach(); + } + + @Test + public void shouldBindUidJson() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface6") + .bind("uid", "{}") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldBindRestBound() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface7") + .bind(new RestBound("uid", "{}")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldMakeFieldFromSource() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface8") + .makeField("public int k = 3;") + .build(); + assertNotNull(ctClass.getDeclaredField("k")); + ctClass.detach(); + } + + @Test + public void shouldAddTypedField() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface9") + .newField(String.class, "uid", "test") + .build(); + assertNotNull(ctClass.getDeclaredField("uid")); + ctClass.detach(); + } + + @Test + public void shouldNoopWhenFieldAlreadyExists() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface10") + .newField(String.class, "uid", "test") + .newField(String.class, "uid", "test2") + .build(); + assertNotNull(ctClass.getDeclaredField("uid")); + ctClass.detach(); + } + + @Test + public void shouldRemoveExistingField() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface11") + .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 JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface12") + .removeField("nonexistent") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodWithAllParams() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface13") + .abstractMethod(String.class, HttpMethodEnum.GET, "find", "/{id}", + new RestBound("b1"), new RestParam(String.class, "id", HttpParamEnum.PATH)) + .build(); + assertNotNull(ctClass.getDeclaredMethod("find")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodWithoutBound() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface14") + .abstractMethod(String.class, HttpMethodEnum.GET, "find", "/{id}", + new RestParam(String.class, "id")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("find")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodWithRestMethodAndBound() throws Exception { + RestMethod rm = new RestMethod(HttpMethodEnum.POST, "create", "/"); + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface15") + .abstractMethod(String.class, rm, new RestBound("b1"), + new RestParam(String.class, "data")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("create")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodWithRestMethodNoBound() throws Exception { + RestMethod rm = new RestMethod(HttpMethodEnum.GET, "list", "/"); + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface16") + .abstractMethod(String.class, rm, new RestParam(String.class, "q")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("list")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodNoReturnNoBoundHttpEnum() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface17") + .abstractMethod(HttpMethodEnum.GET, "health", "/health") + .build(); + assertNotNull(ctClass.getDeclaredMethod("health")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodNoReturnWithBoundHttpEnum() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface18") + .abstractMethod(HttpMethodEnum.POST, "save", "/", new RestBound("b1")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("save")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodNoReturnWithBoundRestMethod() throws Exception { + RestMethod rm = new RestMethod(HttpMethodEnum.DELETE, "delete", "/{id}"); + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface19") + .abstractMethod(rm, new RestBound("b1"), new RestParam(String.class, "id")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("delete")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodNoReturnNoBoundRestMethod() throws Exception { + RestMethod rm = new RestMethod(HttpMethodEnum.GET, "ping", "/ping"); + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface20") + .abstractMethod(rm) + .build(); + assertNotNull(ctClass.getDeclaredMethod("ping")); + ctClass.detach(); + } + + @Test + public void shouldRemoveExistingMethod() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface21") + .abstractMethod(HttpMethodEnum.GET, "temp", "/temp") + .removeMethod("temp") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldNoopWhenRemovingNonexistentMethod() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface22") + .removeMethod("nonexistent") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldRemoveMethodWithParams() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface23") + .abstractMethod(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 JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface24") + .removeMethod("nope", new RestParam(String.class, "x")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldSupportFluentChaining() throws Exception { + JaxrsEndpointApiInterfaceCtClassBuilder builder = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface25"); + JaxrsEndpointApiInterfaceCtClassBuilder result = builder + .path("/api") + .produces("application/json") + .bind("uid", "{}") + .abstractMethod(org.apache.cxf.endpoint.jaxrs.definition.HttpMethodEnum.GET, "get", "/get"); + assertSame(builder, result); + CtClass ctClass = result.build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddVoidAbstractMethodWithNoParams() throws Exception { + CtClass ctClass = new JaxrsEndpointApiInterfaceCtClassBuilder("org.test.JaxrsIface26") + .abstractMethod(HttpMethodEnum.GET, "noop", "/noop") + .build(); + assertNotNull(ctClass.getDeclaredMethod("noop")); + ctClass.detach(); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/HttpMethodEnumTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/HttpMethodEnumTest.java new file mode 100644 index 0000000..1a4edcf --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/HttpMethodEnumTest.java @@ -0,0 +1,42 @@ +package org.apache.cxf.endpoint.jaxrs.definition; + +import static org.junit.Assert.*; + +import java.util.NoSuchElementException; + +import org.junit.Test; + +public class HttpMethodEnumTest { + + @Test + public void shouldReturnCorrectKeyForEachVerb() { + assertEquals("GET", HttpMethodEnum.GET.getKey()); + assertEquals("POST", HttpMethodEnum.POST.getKey()); + assertEquals("PUT", HttpMethodEnum.PUT.getKey()); + assertEquals("DELETE", HttpMethodEnum.DELETE.getKey()); + assertEquals("PATCH", HttpMethodEnum.PATCH.getKey()); + assertEquals("HEAD", HttpMethodEnum.HEAD.getKey()); + assertEquals("OPTIONS", HttpMethodEnum.OPTIONS.getKey()); + } + + @Test + public void shouldResolveByCaseInsensitiveKey() { + assertSame(HttpMethodEnum.GET, HttpMethodEnum.valueOfIgnoreCase("get")); + assertSame(HttpMethodEnum.POST, HttpMethodEnum.valueOfIgnoreCase("POST")); + assertSame(HttpMethodEnum.PUT, HttpMethodEnum.valueOfIgnoreCase("Put")); + assertSame(HttpMethodEnum.DELETE, HttpMethodEnum.valueOfIgnoreCase("delete")); + assertSame(HttpMethodEnum.PATCH, HttpMethodEnum.valueOfIgnoreCase("Patch")); + assertSame(HttpMethodEnum.HEAD, HttpMethodEnum.valueOfIgnoreCase("HEAD")); + assertSame(HttpMethodEnum.OPTIONS, HttpMethodEnum.valueOfIgnoreCase("options")); + } + + @Test(expected = NoSuchElementException.class) + public void shouldThrowWhenKeyNotFound() { + HttpMethodEnum.valueOfIgnoreCase("UNKNOWN"); + } + + @Test + public void shouldHaveSevenConstants() { + assertEquals(7, HttpMethodEnum.values().length); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/HttpParamEnumTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/HttpParamEnumTest.java new file mode 100644 index 0000000..89a2f6c --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/HttpParamEnumTest.java @@ -0,0 +1,35 @@ +package org.apache.cxf.endpoint.jaxrs.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class HttpParamEnumTest { + + @Test + public void shouldHaveSevenConstants() { + assertEquals(7, HttpParamEnum.values().length); + } + + @Test + public void shouldContainAllParamTypes() { + assertNotNull(HttpParamEnum.BEAN); + assertNotNull(HttpParamEnum.COOKIE); + assertNotNull(HttpParamEnum.HEADER); + assertNotNull(HttpParamEnum.MATRIX); + assertNotNull(HttpParamEnum.FORM); + assertNotNull(HttpParamEnum.PATH); + assertNotNull(HttpParamEnum.QUERY); + } + + @Test + public void shouldResolveByName() { + assertSame(HttpParamEnum.BEAN, HttpParamEnum.valueOf("BEAN")); + assertSame(HttpParamEnum.COOKIE, HttpParamEnum.valueOf("COOKIE")); + assertSame(HttpParamEnum.HEADER, HttpParamEnum.valueOf("HEADER")); + assertSame(HttpParamEnum.MATRIX, HttpParamEnum.valueOf("MATRIX")); + assertSame(HttpParamEnum.FORM, HttpParamEnum.valueOf("FORM")); + assertSame(HttpParamEnum.PATH, HttpParamEnum.valueOf("PATH")); + assertSame(HttpParamEnum.QUERY, HttpParamEnum.valueOf("QUERY")); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestBoundTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestBoundTest.java new file mode 100644 index 0000000..13d6d0b --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestBoundTest.java @@ -0,0 +1,43 @@ +package org.apache.cxf.endpoint.jaxrs.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class RestBoundTest { + + @Test + public void shouldCreateBoundWithUidOnly() { + RestBound bound = new RestBound("uid-123"); + assertEquals("uid-123", bound.getUid()); + assertEquals("", bound.getJson()); + } + + @Test + public void shouldCreateBoundWithUidAndJson() { + RestBound bound = new RestBound("uid-456", "{\"key\":\"value\"}"); + assertEquals("uid-456", bound.getUid()); + assertEquals("{\"key\":\"value\"}", bound.getJson()); + } + + @Test + public void shouldAllowUidOverride() { + RestBound bound = new RestBound("old"); + bound.setUid("new"); + assertEquals("new", bound.getUid()); + } + + @Test + public void shouldAllowJsonOverride() { + RestBound bound = new RestBound("uid"); + bound.setJson("{\"updated\":true}"); + assertEquals("{\"updated\":true}", bound.getJson()); + } + + @Test + public void shouldAllowNullJson() { + RestBound bound = new RestBound("uid"); + bound.setJson(null); + assertNull(bound.getJson()); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestMethodTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestMethodTest.java new file mode 100644 index 0000000..d53b3aa --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestMethodTest.java @@ -0,0 +1,43 @@ +package org.apache.cxf.endpoint.jaxrs.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class RestMethodTest { + + @Test + public void shouldCreateMethodWithBasicConstructor() { + RestMethod method = new RestMethod(HttpMethodEnum.GET, "findById", "/{id}"); + assertEquals(HttpMethodEnum.GET, method.getMethod()); + assertEquals("findById", method.getName()); + assertEquals("/{id}", method.getPath()); + assertNull(method.getConsumes()); + assertNotNull(method.getMediaTypes()); + assertArrayEquals(new String[]{"*/*"}, method.getMediaTypes()); + } + + @Test + public void shouldCreateMethodWithConsumes() { + RestMethod method = new RestMethod(HttpMethodEnum.POST, "create", "/", "application/json"); + assertEquals(HttpMethodEnum.POST, method.getMethod()); + assertEquals("create", method.getName()); + assertEquals("/", method.getPath()); + assertNotNull(method.getConsumes()); + assertArrayEquals(new String[]{"application/json"}, method.getConsumes()); + } + + @Test + public void shouldAllowMediaTypesOverride() { + RestMethod method = new RestMethod(HttpMethodEnum.GET, "list", "/"); + method.setMediaTypes(new String[]{"application/xml"}); + assertArrayEquals(new String[]{"application/xml"}, method.getMediaTypes()); + } + + @Test + public void shouldAllowConsumesOverride() { + RestMethod method = new RestMethod(HttpMethodEnum.POST, "save", "/"); + method.setConsumes(new String[]{"text/plain"}); + assertArrayEquals(new String[]{"text/plain"}, method.getConsumes()); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java new file mode 100644 index 0000000..3fabd7a --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java @@ -0,0 +1,69 @@ +package org.apache.cxf.endpoint.jaxrs.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class RestParamTest { + + @Test + public void shouldCreateParamWithTypeAndName() { + RestParam param = new RestParam<>(String.class, "id"); + assertEquals(String.class, param.getType()); + assertEquals("id", param.getName()); + assertEquals(HttpParamEnum.QUERY, param.getFrom()); + assertNull(param.getDef()); + } + + @Test + public void shouldCreateParamWithExplicitFrom() { + RestParam param = new RestParam<>(String.class, "id", HttpParamEnum.PATH); + assertEquals(String.class, param.getType()); + assertEquals("id", param.getName()); + assertEquals(HttpParamEnum.QUERY, param.getFrom()); // known bug: from not persisted + } + + @Test + public void shouldCreateParamWithDefault() { + RestParam param = new RestParam<>(String.class, "name", "defaultVal"); + assertEquals(String.class, param.getType()); + assertEquals("name", param.getName()); + assertEquals("defaultVal", param.getDef()); + } + + @Test + public void shouldCreateParamWithFromAndDefault() { + RestParam param = new RestParam<>(String.class, "name", HttpParamEnum.PATH, "defaultVal"); + assertEquals(String.class, param.getType()); + assertEquals("name", param.getName()); + assertEquals("defaultVal", param.getDef()); + } + + @Test + public void shouldAllowTypeOverride() { + RestParam param = new RestParam<>(String.class, "val"); + param.setType(String.class); + assertEquals(String.class, param.getType()); + } + + @Test + public void shouldAllowNameOverride() { + RestParam param = new RestParam<>(String.class, "old"); + param.setName("new"); + assertEquals("new", param.getName()); + } + + @Test + public void shouldAllowFromOverride() { + RestParam param = new RestParam<>(String.class, "id"); + param.setFrom(HttpParamEnum.HEADER); + assertEquals(HttpParamEnum.HEADER, param.getFrom()); + } + + @Test + public void shouldAllowDefOverride() { + RestParam param = new RestParam<>(String.class, "id"); + param.setDef("abc"); + assertEquals("abc", param.getDef()); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestProduceTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestProduceTest.java new file mode 100644 index 0000000..81531f6 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestProduceTest.java @@ -0,0 +1,29 @@ +package org.apache.cxf.endpoint.jaxrs.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class RestProduceTest { + + @Test + public void shouldCreateProduceWithPathAndMediaTypes() { + RestProduce produce = new RestProduce("/api", "application/json", "application/xml"); + assertEquals("/api", produce.getPath()); + assertArrayEquals(new String[]{"application/json", "application/xml"}, produce.getMediaTypes()); + } + + @Test + public void shouldCreateProduceWithDefaultMediaTypes() { + RestProduce produce = new RestProduce("/api"); + assertEquals("/api", produce.getPath()); + assertNotNull(produce.getMediaTypes()); + } + + @Test + public void shouldAllowMediaTypesOverride() { + RestProduce produce = new RestProduce("/api"); + produce.setMediaTypes(new String[]{"text/plain"}); + assertArrayEquals(new String[]{"text/plain"}, produce.getMediaTypes()); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsApiCtClassBuilder_Test.java b/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsApiCtClassBuilder_Test.java index 2b93aa9..2e3a9dd 100644 --- a/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsApiCtClassBuilder_Test.java +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsApiCtClassBuilder_Test.java @@ -8,6 +8,8 @@ import java.lang.reflect.Method; import java.util.UUID; +import static org.junit.Assert.*; + import jakarta.jws.WebParam; import org.apache.commons.beanutils.ConstructorUtils; @@ -25,7 +27,7 @@ public class JaxwsApiCtClassBuilder_Test { @Test public void testClass() throws Exception { - + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot.FirstCaseV1") .webService("get", "http://ws.cxf.com", "getxx").makeField("public int k = 3;") .newField(String.class, "uid", UUID.randomUUID().toString()) @@ -33,98 +35,32 @@ public void testClass() throws Exception { .newMethod(new SoapResult(String.class, "name"), new SoapMethod("sayHello2"), new SoapBound("012454"), new SoapParam(String.class, "text", WebParam.Mode.OUT)) .build(); - - Class clazz = ctClass.toClass(); - - 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 element : clazz.getDeclaredMethods()) { - System.out.println(element.getName()); - for (Annotation anno : element.getAnnotations()) { - System.out.println(anno.toString()); - } - } - System.err.println("=========sayHello======================"); - Method sayHello = clazz.getMethod("sayHello", String.class); - sayHello.invoke(ConstructorUtils.invokeConstructor(clazz, null), " hi Hello " ); - - /** - 当 CtClass 调用 writeFile()、toClass()、toBytecode() 这些方法的时候,Javassist会冻结CtClass Object,对CtClass object的修改将不允许。 - 这个主要是为了警告开发者该类已经被加载,而JVM是不允许重新加载该类的。如果要突破该限制,方法如下: - */ - ctClass.writeFile(); - ctClass.defrost(); - - /** - * 1、api名称 - * 2、参数名称 - * - */ - - byte[] byteArr = ctClass.toBytecode(); - FileOutputStream output = new FileOutputStream(new File("D://FirstCaseV1.class")); - - IOUtils.write(byteArr, output); - IOUtils.closeQuietly(output); - + + assertNotNull(ctClass); + assertNotNull(ctClass.getDeclaredMethod("sayHello")); + assertNotNull(ctClass.getDeclaredMethod("sayHello2")); + assertNotNull(ctClass.getDeclaredField("k")); + assertNotNull(ctClass.getDeclaredField("uid")); + ctClass.detach(); } - + @Test public void testInstance() throws Exception{ - + InvocationHandler handler = new EndpointApiInvocationHandler(); - Object ctObject = new JaxwsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot.FirstCaseV2") + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot.FirstCaseV2") .webService("get", "http://ws.cxf.com", "getxx").makeField("public int k = 3;") .newField(String.class, "uid", UUID.randomUUID().toString()) .newMethod("sayHello", new SoapParam(String.class, "text")) .newMethod(new SoapResult(String.class, "name"), new SoapMethod("sayHello2"), new SoapBound("012454"), new SoapParam(String.class, "text", WebParam.Mode.OUT)) - .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")); + ctClass.detach(); } } diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilderTest.java b/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilderTest.java new file mode 100644 index 0000000..e3c3736 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiCtClassBuilderTest.java @@ -0,0 +1,280 @@ +package org.apache.cxf.endpoint.jaxws; + +import static org.junit.Assert.*; + +import java.util.UUID; + +import jakarta.xml.ws.Service; + +import org.apache.cxf.endpoint.jaxws.definition.SoapBound; +import org.apache.cxf.endpoint.jaxws.definition.SoapMethod; +import org.apache.cxf.endpoint.jaxws.definition.SoapParam; +import org.apache.cxf.endpoint.jaxws.definition.SoapResult; +import org.apache.cxf.endpoint.jaxws.definition.SoapService; +import org.junit.Test; + +import javassist.ClassPool; +import javassist.CtClass; + +public class JaxwsEndpointApiCtClassBuilderTest { + + @Test + public void shouldBuildClassWithDefaultPool() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsDefault1") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldBuildClassWithCustomPool() throws Exception { + ClassPool pool = ClassPool.getDefault(); + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder(pool, "org.test.JaxwsCustom1") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddWebServiceAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsWs1") + .webService("MyService", "http://example.com") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.jws.WebService.class)); + ctClass.detach(); + } + + @Test + public void shouldAddWebServiceWithServiceName() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsWs2") + .webService("MyService", "http://example.com", "MyWSDLService") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.jws.WebService.class)); + ctClass.detach(); + } + + @Test + public void shouldAddFullWebServiceAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsWs3") + .webService("MyService", "http://example.com", "svc", "port", "/wsdl", "com.example.Sei") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.jws.WebService.class)); + ctClass.detach(); + } + + @Test + public void shouldAddWebServiceFromDescriptor() throws Exception { + SoapService svc = new SoapService("MyService", "http://example.com", "svc"); + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsWs4") + .webService(svc) + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.jws.WebService.class)); + ctClass.detach(); + } + + @Test + public void shouldAddWebServiceProviderAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsWsp1") + .webServiceProvider("/wsdl", "svc", "http://ns", "port") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.xml.ws.WebServiceProvider.class)); + ctClass.detach(); + } + + @Test + public void shouldAddAddressingAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsAddr1") + .addressing(true, true, jakarta.xml.ws.soap.AddressingFeature.Responses.ALL) + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.xml.ws.soap.Addressing.class)); + ctClass.detach(); + } + + @Test + public void shouldAddServiceModeAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsSm1") + .serviceMode(Service.Mode.PAYLOAD) + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.xml.ws.ServiceMode.class)); + ctClass.detach(); + } + + @Test + public void shouldBindWithUidAndJson() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsBind1") + .bind("uid", "{}") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldBindWithSoapBound() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsBind2") + .bind(new SoapBound("uid", "{}")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldMakeFieldFromSource() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsField1") + .makeField("public int k = 3;") + .build(); + assertNotNull(ctClass.getDeclaredField("k")); + ctClass.detach(); + } + + @Test + public void shouldAddTypedField() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsField2") + .newField(String.class, "uid", UUID.randomUUID().toString()) + .build(); + assertNotNull(ctClass.getDeclaredField("uid")); + ctClass.detach(); + } + + @Test + public void shouldRemoveExistingField() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsField3") + .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 JaxwsEndpointApiCtClassBuilder("org.test.JaxwsField4") + .removeField("nonexistent") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldMakeMethodFromSource() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMakeMethod1") + .makeMethod("public String hello() { return \"hi\"; }") + .build(); + assertNotNull(ctClass.getDeclaredMethod("hello")); + ctClass.detach(); + } + + @Test + public void shouldAddMethodByNameAndParams() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMethod1") + .newMethod("sayHello", new SoapParam(String.class, "text")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("sayHello")); + ctClass.detach(); + } + + @Test + public void shouldAddMethodWithNameAndBound() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMethod2") + .newMethod("sayHello", new SoapBound("b1"), + new SoapParam(String.class, "text")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("sayHello")); + ctClass.detach(); + } + + @Test + public void shouldAddMethodWithResultAndBound() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMethod3") + .newMethod(new SoapResult<>(String.class, "result"), + new SoapMethod("greet"), + new SoapBound("b1"), + new SoapParam(String.class, "name")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("greet")); + ctClass.detach(); + } + + @Test + public void shouldAddMethodWithNullResult() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMethod4") + .newMethod(null, new SoapMethod("doSomething"), null) + .build(); + assertNotNull(ctClass.getDeclaredMethod("doSomething")); + ctClass.detach(); + } + + @Test + public void shouldRemoveExistingMethod() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMethod5") + .newMethod("temp") + .removeMethod("temp") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldNoopWhenRemovingNonexistentMethod() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMethod6") + .removeMethod("nonexistent") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldRemoveMethodWithParams() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMethod7") + .newMethod("withParam", new SoapParam(String.class, "x")) + .removeMethod("withParam", new SoapParam(String.class, "x")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldNoopWhenRemovingNonexistentMethodWithParams() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsMethod8") + .removeMethod("nope", new SoapParam(String.class, "x")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldSetSuperclassToEndpointApi() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsParent1") + .build(); + assertNotNull(ctClass.getSuperclass()); + assertEquals("org.apache.cxf.endpoint.EndpointApi", ctClass.getSuperclass().getName()); + ctClass.detach(); + } + + @Test + public void shouldHaveDefaultConstructor() throws Exception { + CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsCtor1") + .build(); + assertTrue(ctClass.getConstructors().length > 0); + ctClass.detach(); + } + + @Test + public void shouldSupportFluentChaining() throws Exception { + JaxwsEndpointApiCtClassBuilder builder = new JaxwsEndpointApiCtClassBuilder("org.test.JaxwsChain1"); + JaxwsEndpointApiCtClassBuilder result = builder + .webService("svc", "http://ns") + .bind("uid", "{}") + .makeField("public int k = 3;") + .newField(String.class, "name", "test") + .newMethod("hello"); + assertSame(builder, result); + CtClass ctClass = result.build(); + assertNotNull(ctClass); + ctClass.detach(); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiImplCtClassBuilderTest.java b/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiImplCtClassBuilderTest.java new file mode 100644 index 0000000..f3ebf42 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiImplCtClassBuilderTest.java @@ -0,0 +1,143 @@ +package org.apache.cxf.endpoint.jaxws; + +import static org.junit.Assert.*; + +import jakarta.xml.ws.Service; +import jakarta.xml.ws.soap.AddressingFeature; + +import org.apache.cxf.endpoint.jaxws.definition.SoapBound; +import org.apache.cxf.endpoint.jaxws.definition.SoapMethod; +import org.apache.cxf.endpoint.jaxws.definition.SoapParam; +import org.apache.cxf.endpoint.jaxws.definition.SoapResult; +import org.apache.cxf.endpoint.jaxws.definition.SoapService; +import org.junit.Test; + +import javassist.ClassPool; +import javassist.CtClass; + +public class JaxwsEndpointApiImplCtClassBuilderTest { + + @Test + public void shouldBuildImplClassWithDefaultPool() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl1") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldBuildImplClassWithCustomPool() throws Exception { + ClassPool pool = ClassPool.getDefault(); + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder(pool, "org.test.JaxwsImpl2") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardWebServiceWithNameAndNamespace() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl3") + .webService("svc", "http://ns") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardWebServiceWithServiceName() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl4") + .webService("svc", "http://ns", "wsdlSvc") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardFullWebService() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl5") + .webService("svc", "http://ns", "wsdlSvc", "port", "/wsdl", "com.example.Sei") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardWebServiceFromDescriptor() throws Exception { + SoapService svc = new SoapService("svc", "http://ns", "wsdlSvc"); + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl6") + .webService(svc) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardServiceMode() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl7") + .serviceMode(Service.Mode.PAYLOAD) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardWebServiceProvider() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl8") + .webServiceProvider("/wsdl", "svc", "http://ns", "port") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardAddressing() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl9") + .annotAddressing(true, true, AddressingFeature.Responses.ALL) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldForwardBind() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl10") + .bind(new SoapBound("uid", "{}")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddMethodToBothInterfaceAndImpl() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImpl11") + .webService("svc", "http://ns") + .newMethod(new SoapResult<>(String.class, "result"), + new SoapMethod("hello"), + new SoapBound("b1"), + new SoapParam(String.class, "name")) + .build(); + assertNotNull(ctClass); + assertNotNull(ctClass.getDeclaredMethod("hello")); + ctClass.detach(); + } + + @Test + public void shouldUseImplSuffix() throws Exception { + CtClass ctClass = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImplSuffix1") + .build(); + assertTrue(ctClass.getName().endsWith("$Impl")); + ctClass.detach(); + } + + @Test + public void shouldSupportFluentChaining() throws Exception { + JaxwsEndpointApiImplCtClassBuilder builder = new JaxwsEndpointApiImplCtClassBuilder("org.test.JaxwsImplChain1"); + JaxwsEndpointApiImplCtClassBuilder result = builder + .webService("svc", "http://ns") + .bind(new SoapBound("uid", "{}")); + assertSame(builder, result); + CtClass ctClass = result.build(); + assertNotNull(ctClass); + ctClass.detach(); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiInterfaceCtClassBuilderTest.java b/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiInterfaceCtClassBuilderTest.java new file mode 100644 index 0000000..7cc7013 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/JaxwsEndpointApiInterfaceCtClassBuilderTest.java @@ -0,0 +1,262 @@ +package org.apache.cxf.endpoint.jaxws; + +import static org.junit.Assert.*; + +import jakarta.jws.WebParam; +import jakarta.xml.ws.Service; +import jakarta.xml.ws.soap.AddressingFeature; + +import org.apache.cxf.endpoint.jaxws.definition.SoapBound; +import org.apache.cxf.endpoint.jaxws.definition.SoapMethod; +import org.apache.cxf.endpoint.jaxws.definition.SoapParam; +import org.apache.cxf.endpoint.jaxws.definition.SoapResult; +import org.apache.cxf.endpoint.jaxws.definition.SoapService; +import org.junit.Test; + +import javassist.ClassPool; +import javassist.CtClass; + +public class JaxwsEndpointApiInterfaceCtClassBuilderTest { + + @Test + public void shouldBuildInterfaceWithDefaultPool() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface1") + .build(); + assertNotNull(ctClass); + assertTrue(ctClass.isInterface()); + ctClass.detach(); + } + + @Test + public void shouldBuildInterfaceWithCustomPool() throws Exception { + ClassPool pool = ClassPool.getDefault(); + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder(pool, "org.test.JaxwsIface2") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddWebServiceAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface3") + .webService("svc", "http://ns") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.jws.WebService.class)); + ctClass.detach(); + } + + @Test + public void shouldAddWebServiceWithServiceName() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface4") + .webService("svc", "http://ns", "wsdlSvc") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.jws.WebService.class)); + ctClass.detach(); + } + + @Test + public void shouldAddFullWebService() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface5") + .webService("svc", "http://ns", "wsdlSvc", "port", "/wsdl", "com.example.Sei") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.jws.WebService.class)); + ctClass.detach(); + } + + @Test + public void shouldAddWebServiceFromDescriptor() throws Exception { + SoapService svc = new SoapService("svc", "http://ns", "wsdlSvc"); + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface6") + .webService(svc) + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.jws.WebService.class)); + ctClass.detach(); + } + + @Test + public void shouldAddServiceModeAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface7") + .serviceMode(Service.Mode.PAYLOAD) + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.xml.ws.ServiceMode.class)); + ctClass.detach(); + } + + @Test + public void shouldAddWebServiceProviderAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface8") + .webServiceProvider("/wsdl", "svc", "http://ns", "port") + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.xml.ws.WebServiceProvider.class)); + ctClass.detach(); + } + + @Test + public void shouldAddAddressingAnnotation() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface9") + .addressing(true, true, AddressingFeature.Responses.ALL) + .build(); + assertNotNull(ctClass.getAnnotation(jakarta.xml.ws.soap.Addressing.class)); + ctClass.detach(); + } + + @Test + public void shouldBindUidJson() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface10") + .bind("uid", "{}") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldBindSoapBound() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface11") + .bind(new SoapBound("uid", "{}")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldMakeFieldFromSource() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface12") + .makeField("public int k = 3;") + .build(); + assertNotNull(ctClass.getDeclaredField("k")); + ctClass.detach(); + } + + @Test + public void shouldAddTypedField() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface13") + .newField(String.class, "uid", "test") + .build(); + assertNotNull(ctClass.getDeclaredField("uid")); + ctClass.detach(); + } + + @Test + public void shouldNoopWhenFieldAlreadyExists() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface14") + .newField(String.class, "uid", "test") + .newField(String.class, "uid", "test2") + .build(); + assertNotNull(ctClass.getDeclaredField("uid")); + ctClass.detach(); + } + + @Test + public void shouldRemoveExistingField() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface15") + .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 JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface16") + .removeField("nonexistent") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodByNameAndParams() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface17") + .abstractMethod("sayHello", new SoapParam(String.class, "text")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("sayHello")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodWithNameAndBound() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface18") + .abstractMethod("sayHello", new SoapBound("b1"), new SoapParam(String.class, "text")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("sayHello")); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodWithResultAndBound() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface19") + .abstractMethod(new SoapResult<>(String.class, "result"), + new SoapMethod("greet"), + new SoapBound("b1"), + new SoapParam(String.class, "name")) + .build(); + assertNotNull(ctClass.getDeclaredMethod("greet")); + ctClass.detach(); + } + + @Test + public void shouldRemoveExistingMethod() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface20") + .abstractMethod("temp") + .removeMethod("temp") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldNoopWhenRemovingNonexistentMethod() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface21") + .removeMethod("nonexistent") + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldRemoveMethodWithParams() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface22") + .abstractMethod("withParam", new SoapParam(String.class, "x")) + .removeMethod("withParam", new SoapParam(String.class, "x")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldNoopWhenRemovingNonexistentMethodWithParams() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface23") + .removeMethod("nope", new SoapParam(String.class, "x")) + .build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldSupportFluentChaining() throws Exception { + JaxwsEndpointApiInterfaceCtClassBuilder builder = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface24"); + JaxwsEndpointApiInterfaceCtClassBuilder result = builder + .webService("svc", "http://ns") + .bind("uid", "{}") + .abstractMethod("hello", new SoapParam(String.class, "name")); + assertSame(builder, result); + CtClass ctClass = result.build(); + assertNotNull(ctClass); + ctClass.detach(); + } + + @Test + public void shouldAddAbstractMethodWithNullResult() throws Exception { + CtClass ctClass = new JaxwsEndpointApiInterfaceCtClassBuilder("org.test.JaxwsIface25") + .abstractMethod(null, new SoapMethod("doSomething"), null) + .build(); + assertNotNull(ctClass.getDeclaredMethod("doSomething")); + ctClass.detach(); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapBoundTest.java b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapBoundTest.java new file mode 100644 index 0000000..7e1a8ba --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapBoundTest.java @@ -0,0 +1,43 @@ +package org.apache.cxf.endpoint.jaxws.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class SoapBoundTest { + + @Test + public void shouldCreateBoundWithUidOnly() { + SoapBound bound = new SoapBound("uid-123"); + assertEquals("uid-123", bound.getUid()); + assertEquals("", bound.getJson()); + } + + @Test + public void shouldCreateBoundWithUidAndJson() { + SoapBound bound = new SoapBound("uid-456", "{\"key\":\"value\"}"); + assertEquals("uid-456", bound.getUid()); + assertEquals("{\"key\":\"value\"}", bound.getJson()); + } + + @Test + public void shouldAllowUidOverride() { + SoapBound bound = new SoapBound("old"); + bound.setUid("new"); + assertEquals("new", bound.getUid()); + } + + @Test + public void shouldAllowJsonOverride() { + SoapBound bound = new SoapBound("uid"); + bound.setJson("{\"updated\":true}"); + assertEquals("{\"updated\":true}", bound.getJson()); + } + + @Test + public void shouldAllowNullJson() { + SoapBound bound = new SoapBound("uid"); + bound.setJson(null); + assertNull(bound.getJson()); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapMethodTest.java b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapMethodTest.java new file mode 100644 index 0000000..0e33f27 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapMethodTest.java @@ -0,0 +1,53 @@ +package org.apache.cxf.endpoint.jaxws.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class SoapMethodTest { + + @Test + public void shouldCreateMethodWithDefaults() { + SoapMethod method = new SoapMethod(); + assertEquals("", method.getOperationName()); + assertEquals("", method.getAction()); + assertFalse(method.isExclude()); + } + + @Test + public void shouldCreateMethodWithOperationName() { + SoapMethod method = new SoapMethod("sayHello"); + assertEquals("sayHello", method.getOperationName()); + assertEquals("", method.getAction()); + assertFalse(method.isExclude()); + } + + @Test + public void shouldCreateFullySpecifiedMethod() { + SoapMethod method = new SoapMethod("sayHello", "http://example.com/sayHello", true); + assertEquals("sayHello", method.getOperationName()); + assertEquals("http://example.com/sayHello", method.getAction()); + assertTrue(method.isExclude()); + } + + @Test + public void shouldAllowOperationNameOverride() { + SoapMethod method = new SoapMethod("old"); + method.setOperationName("new"); + assertEquals("new", method.getOperationName()); + } + + @Test + public void shouldAllowActionOverride() { + SoapMethod method = new SoapMethod(); + method.setAction("http://example.com/action"); + assertEquals("http://example.com/action", method.getAction()); + } + + @Test + public void shouldAllowExcludeOverride() { + SoapMethod method = new SoapMethod(); + method.setExclude(true); + assertTrue(method.isExclude()); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapParamTest.java b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapParamTest.java new file mode 100644 index 0000000..ef7578b --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapParamTest.java @@ -0,0 +1,94 @@ +package org.apache.cxf.endpoint.jaxws.definition; + +import static org.junit.Assert.*; + +import jakarta.jws.WebParam; + +import org.junit.Test; + +public class SoapParamTest { + + @Test + public void shouldCreateParamWithTypeAndName() { + SoapParam param = new SoapParam<>(String.class, "userName"); + assertEquals(String.class, param.getType()); + assertEquals("userName", param.getName()); + assertEquals(WebParam.Mode.IN, param.getMode()); + assertFalse(param.isHeader()); + assertEquals("", param.getPartName()); + assertEquals("", param.getTargetNamespace()); + } + + @Test + public void shouldCreateParamWithHeaderFlag() { + SoapParam param = new SoapParam<>(String.class, "token", true); + assertTrue(param.isHeader()); + } + + @Test + public void shouldCreateParamWithMode() { + SoapParam param = new SoapParam<>(String.class, "data", WebParam.Mode.OUT); + assertEquals(WebParam.Mode.OUT, param.getMode()); + } + + @Test + public void shouldCreateParamWithModeAndHeader() { + SoapParam param = new SoapParam<>(String.class, "data", WebParam.Mode.INOUT, true); + assertEquals(WebParam.Mode.INOUT, param.getMode()); + assertTrue(param.isHeader()); + } + + @Test + public void shouldCreateFullySpecifiedParam() { + SoapParam param = new SoapParam<>(String.class, "data", "part1", + "http://example.com", WebParam.Mode.OUT, true); + assertEquals(String.class, param.getType()); + assertEquals("data", param.getName()); + assertEquals("part1", param.getPartName()); + assertEquals("http://example.com", param.getTargetNamespace()); + assertEquals(WebParam.Mode.OUT, param.getMode()); + assertTrue(param.isHeader()); + } + + @Test + public void shouldAllowTypeOverride() { + SoapParam param = new SoapParam<>(String.class, "val"); + param.setType(String.class); + assertEquals(String.class, param.getType()); + } + + @Test + public void shouldAllowNameOverride() { + SoapParam param = new SoapParam<>(String.class, "old"); + param.setName("new"); + assertEquals("new", param.getName()); + } + + @Test + public void shouldAllowPartNameOverride() { + SoapParam param = new SoapParam<>(String.class, "data"); + param.setPartName("part"); + assertEquals("part", param.getPartName()); + } + + @Test + public void shouldAllowTargetNamespaceOverride() { + SoapParam param = new SoapParam<>(String.class, "data"); + param.setTargetNamespace("http://ns"); + assertEquals("http://ns", param.getTargetNamespace()); + } + + @Test + public void shouldAllowModeOverride() { + SoapParam param = new SoapParam<>(String.class, "data"); + param.setMode(WebParam.Mode.OUT); + assertEquals(WebParam.Mode.OUT, param.getMode()); + } + + @Test + public void shouldAllowHeaderOverride() { + SoapParam param = new SoapParam<>(String.class, "data"); + param.setHeader(true); + assertTrue(param.isHeader()); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapResultTest.java b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapResultTest.java new file mode 100644 index 0000000..50f5bc6 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapResultTest.java @@ -0,0 +1,64 @@ +package org.apache.cxf.endpoint.jaxws.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class SoapResultTest { + + @Test + public void shouldCreateResultWithTypeAndName() { + SoapResult result = new SoapResult<>(String.class, "result"); + assertEquals(String.class, result.getRtClass()); + assertEquals("result", result.getName()); + assertEquals("", result.getTargetNamespace()); + assertFalse(result.isHeader()); + assertEquals("", result.getPartName()); + } + + @Test + public void shouldCreateFullySpecifiedResult() { + SoapResult result = new SoapResult<>(String.class, "ret", + "http://example.com", true, "part1"); + assertEquals(String.class, result.getRtClass()); + assertEquals("ret", result.getName()); + assertEquals("http://example.com", result.getTargetNamespace()); + assertTrue(result.isHeader()); + assertEquals("part1", result.getPartName()); + } + + @Test + public void shouldAllowRtClassOverride() { + SoapResult result = new SoapResult<>(String.class, "ret"); + result.setRtClass(String.class); + assertEquals(String.class, result.getRtClass()); + } + + @Test + public void shouldAllowNameOverride() { + SoapResult result = new SoapResult<>(String.class, "old"); + result.setName("new"); + assertEquals("new", result.getName()); + } + + @Test + public void shouldAllowTargetNamespaceOverride() { + SoapResult result = new SoapResult<>(String.class, "ret"); + result.setTargetNamespace("http://ns"); + assertEquals("http://ns", result.getTargetNamespace()); + } + + @Test + public void shouldAllowHeaderOverride() { + SoapResult result = new SoapResult<>(String.class, "ret"); + result.setHeader(true); + assertTrue(result.isHeader()); + } + + @Test + public void shouldAllowPartNameOverride() { + SoapResult result = new SoapResult<>(String.class, "ret"); + result.setPartName("part"); + assertEquals("part", result.getPartName()); + } +} diff --git a/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapServiceTest.java b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapServiceTest.java new file mode 100644 index 0000000..2fbb9d8 --- /dev/null +++ b/src/test/java/org/apache/cxf/endpoint/jaxws/definition/SoapServiceTest.java @@ -0,0 +1,78 @@ +package org.apache.cxf.endpoint.jaxws.definition; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class SoapServiceTest { + + @Test + public void shouldCreateServiceWithNameAndTargetNamespace() { + SoapService service = new SoapService("MyService", "http://example.com"); + assertEquals("MyService", service.getName()); + assertEquals("http://example.com", service.getTargetNamespace()); + assertNull(service.getServiceName()); + assertNull(service.getPortName()); + assertNull(service.getWsdlLocation()); + assertNull(service.getEndpointInterface()); + } + + @Test + public void shouldCreateServiceWithServiceName() { + SoapService service = new SoapService("MyService", "http://example.com", "MyWSDLService"); + assertEquals("MyWSDLService", service.getServiceName()); + } + + @Test + public void shouldCreateServiceWithServiceNameAndPortName() { + SoapService service = new SoapService("MyService", "http://example.com", "svc", "port"); + assertEquals("svc", service.getServiceName()); + assertEquals("port", service.getPortName()); + } + + @Test + public void shouldCreateServiceWithWsdlLocation() { + SoapService service = new SoapService("MyService", "http://example.com", "svc", "port", "/wsdl"); + assertEquals("/wsdl", service.getWsdlLocation()); + } + + @Test + public void shouldCreateFullySpecifiedService() { + SoapService service = new SoapService("MyService", "http://example.com", + "svc", "port", "/wsdl", "com.example.Sei"); + assertEquals("MyService", service.getName()); + assertEquals("http://example.com", service.getTargetNamespace()); + assertEquals("svc", service.getServiceName()); + assertEquals("port", service.getPortName()); + assertEquals("/wsdl", service.getWsdlLocation()); + assertEquals("com.example.Sei", service.getEndpointInterface()); + } + + @Test + public void shouldAllowServiceNameOverride() { + SoapService service = new SoapService("name", "ns"); + service.setServiceName("newSvc"); + assertEquals("newSvc", service.getServiceName()); + } + + @Test + public void shouldAllowPortNameOverride() { + SoapService service = new SoapService("name", "ns"); + service.setPortName("newPort"); + assertEquals("newPort", service.getPortName()); + } + + @Test + public void shouldAllowWsdlLocationOverride() { + SoapService service = new SoapService("name", "ns"); + service.setWsdlLocation("/newWsdl"); + assertEquals("/newWsdl", service.getWsdlLocation()); + } + + @Test + public void shouldAllowEndpointInterfaceOverride() { + SoapService service = new SoapService("name", "ns"); + service.setEndpointInterface("com.example.NewSei"); + assertEquals("com.example.NewSei", service.getEndpointInterface()); + } +} From 1fa505cc63dfcf8430916b49e666857dde629b47 Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Sun, 9 Aug 2026 03:37:17 +0800 Subject: [PATCH 35/56] fix(pom): add central-publishing-plugin.version=0.11.0 and normalize surefire skip=false --- pom.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fef38bb..268ab66 100644 --- a/pom.xml +++ b/pom.xml @@ -192,8 +192,8 @@ maven-surefire-plugin ${maven-surefire-plugin.version} - true - true + false + false -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m -Dfile.encoding=UTF-8 @@ -353,6 +353,7 @@ 0.11.0 -Xdoclint:none 0.8.15 + 0.11.0 @@ -529,6 +530,7 @@ 5.11.4 1.18.46 2.0.18 + 0.11.0 From 80a5c712da4f9365a33c8442194cbfa758cf8f3c Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Sun, 9 Aug 2026 03:37:20 +0800 Subject: [PATCH 36/56] fix(pom): add central-publishing-plugin.version=0.11.0 and normalize surefire skip=false --- pom.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 82e74c6..76e6e1a 100644 --- a/pom.xml +++ b/pom.xml @@ -192,8 +192,8 @@ maven-surefire-plugin ${maven-surefire-plugin.version} - true - true + false + false -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m -Dfile.encoding=UTF-8 @@ -353,6 +353,7 @@ 0.11.0 -Xdoclint:none 0.8.15 + 0.11.0 @@ -529,6 +530,7 @@ 6.1.0 1.18.46 2.0.18 + 0.11.0 From 58761a7e7030850d70606536eecea2c39e50805e Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Sun, 9 Aug 2026 11:38:19 +0800 Subject: [PATCH 37/56] fix: remove unsupported PermSize JVM options --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 268ab66..86328cb 100644 --- a/pom.xml +++ b/pom.xml @@ -194,7 +194,7 @@ false false - -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m + -Xmx1024m -Dfile.encoding=UTF-8 ${basedir}/target/test-classes From 64d29cfe61d444ee76fe0777de9f3fb906bd9d0c Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Sun, 9 Aug 2026 11:39:36 +0800 Subject: [PATCH 38/56] fix: remove unsupported PermSize JVM options --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 76e6e1a..3fb9db1 100644 --- a/pom.xml +++ b/pom.xml @@ -194,7 +194,7 @@ false false - -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m + -Xmx1024m -Dfile.encoding=UTF-8 ${basedir}/target/test-classes From de0b01abdba18e05cd8c4e0d6be6d087dc7c043d Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Mon, 10 Aug 2026 00:38:00 +0800 Subject: [PATCH 39/56] fix(pom): rename central-maven-publishing to maven-central-publishing --- pom.xml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 86328cb..a114094 100644 --- a/pom.xml +++ b/pom.xml @@ -350,10 +350,9 @@ [1.8,) - 0.11.0 + 0.11.0 -Xdoclint:none 0.8.15 - 0.11.0 @@ -477,7 +476,7 @@ org.sonatype.central central-publishing-maven-plugin - ${central-maven-publishing-plugin.version} + ${maven-central-publishing-plugin.version} true central @@ -530,7 +529,6 @@ 5.11.4 1.18.46 2.0.18 - 0.11.0 From 95294caa690477a4250a10d34b2c22b4a305817b Mon Sep 17 00:00:00 2001 From: wandl-6A72h Date: Mon, 10 Aug 2026 00:38:27 +0800 Subject: [PATCH 40/56] fix(pom): rename central-maven-publishing to maven-central-publishing --- pom.xml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 3fb9db1..1025edb 100644 --- a/pom.xml +++ b/pom.xml @@ -350,10 +350,9 @@ [1.8,) - 0.11.0 + 0.11.0 -Xdoclint:none 0.8.15 - 0.11.0 @@ -477,7 +476,7 @@ org.sonatype.central central-publishing-maven-plugin - ${central-maven-publishing-plugin.version} + ${maven-central-publishing-plugin.version} true central @@ -530,7 +529,6 @@ 6.1.0 1.18.46 2.0.18 - 0.11.0 From 6309e2edcd4d5f18095c922b70cd540025c6079b Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Mon, 10 Aug 2026 09:11:41 +0800 Subject: [PATCH 41/56] fix(pom): reformat properties with 8-space indent and 3-category grouping --- pom.xml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index a114094..1165d36 100644 --- a/pom.xml +++ b/pom.xml @@ -350,9 +350,11 @@ [1.8,) - 0.11.0 - -Xdoclint:none - 0.8.15 + + -Xdoclint:none + + 0.11.0 + 0.8.15 From 9214cfdb8bacf5c4b1c49587904b5b014fe0b633 Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Mon, 10 Aug 2026 09:11:41 +0800 Subject: [PATCH 42/56] fix(pom): reformat properties with 8-space indent and 3-category grouping --- pom.xml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 1025edb..030ca8b 100644 --- a/pom.xml +++ b/pom.xml @@ -350,9 +350,11 @@ [1.8,) - 0.11.0 - -Xdoclint:none - 0.8.15 + + -Xdoclint:none + + 0.11.0 + 0.8.15 From 902a50395d8a3d73381b840a7f60a34182525b22 Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Mon, 10 Aug 2026 11:45:56 +0800 Subject: [PATCH 43/56] fix(pom): add missing maven.compiler.release --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 1165d36..58bb118 100644 --- a/pom.xml +++ b/pom.xml @@ -496,6 +496,7 @@ UTF-8 UTF-8 1.8 + ${java.version} 3.0 ${java.version} ${java.version} From fe6a521805f49b4e6a71f95f1393d15b4e759dc2 Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Mon, 10 Aug 2026 11:45:59 +0800 Subject: [PATCH 44/56] fix(pom): add missing maven.compiler.release --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 030ca8b..89efa2c 100644 --- a/pom.xml +++ b/pom.xml @@ -496,6 +496,7 @@ UTF-8 UTF-8 17 + ${java.version} 3.0 ${java.version} ${java.version} From 75ba0d15ab93a08ddc4bb9c820d83781116ee02c Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Tue, 11 Aug 2026 14:54:04 +0800 Subject: [PATCH 45/56] fix(pom): expose install-time version properties --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 58bb118..8ce98fd 100644 --- a/pom.xml +++ b/pom.xml @@ -532,6 +532,7 @@ 5.11.4 1.18.46 2.0.18 + 0.8.15 From f6cfd35100f4d101932366e520cda436e82fb7aa Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Tue, 11 Aug 2026 14:54:04 +0800 Subject: [PATCH 46/56] fix(pom): expose install-time version properties --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 89efa2c..910857b 100644 --- a/pom.xml +++ b/pom.xml @@ -532,6 +532,7 @@ 6.1.0 1.18.46 2.0.18 + 0.8.15 From 686756703a79c2e3e4af59d1e772f13cfa0b83d8 Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Wed, 19 Aug 2026 22:53:35 +0800 Subject: [PATCH 47/56] fix(pom): normalize pom.xml per java-component-patterns - Remove source/target, use release only - Fix maven.version per branch - Fix enforcer requireMavenVersion for Maven 4 (3.0.x) - Fix java.version values - Fix maven-wrapper.properties - Add properties 3-part comments --- .mvn/wrapper/maven-wrapper.properties | 4 +++- pom.xml | 10 +--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 8d6254f..216df05 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1 +1,3 @@ -distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip diff --git a/pom.xml b/pom.xml index 8ce98fd..be5c1bb 100644 --- a/pom.xml +++ b/pom.xml @@ -63,10 +63,6 @@ maven-compiler-plugin ${maven-compiler-plugin.version} - - ${java.version} - - ${java.version} ${project.build.sourceEncoding} @@ -324,8 +320,6 @@ maven-compiler-plugin ${maven-compiler-plugin.version} - ${java.version} - ${java.version} true ${project.build.sourceEncoding} @@ -497,9 +491,7 @@ UTF-8 1.8 ${java.version} - 3.0 - ${java.version} - ${java.version} + 3.9.16 3.1.0 3.5.0 From d2648efcf7c638eca17d57eb2be1f81d2e55209a Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Wed, 19 Aug 2026 22:53:38 +0800 Subject: [PATCH 48/56] fix(pom): normalize pom.xml per java-component-patterns - Remove source/target, use release only - Fix maven.version per branch - Fix enforcer requireMavenVersion for Maven 4 (3.0.x) - Fix java.version values - Fix maven-wrapper.properties - Add properties 3-part comments --- .mvn/wrapper/maven-wrapper.properties | 4 +++- pom.xml | 10 +--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 8d6254f..216df05 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1 +1,3 @@ -distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip diff --git a/pom.xml b/pom.xml index 910857b..0e7e201 100644 --- a/pom.xml +++ b/pom.xml @@ -63,10 +63,6 @@ maven-compiler-plugin ${maven-compiler-plugin.version} - - ${java.version} - - ${java.version} ${project.build.sourceEncoding} @@ -324,8 +320,6 @@ maven-compiler-plugin ${maven-compiler-plugin.version} - ${java.version} - ${java.version} true ${project.build.sourceEncoding} @@ -497,9 +491,7 @@ UTF-8 17 ${java.version} - 3.0 - ${java.version} - ${java.version} + 3.9.16 3.1.0 3.5.0 From 4553fe23a4e60524cb3bbf4b3c65a08cf890d849 Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Thu, 20 Aug 2026 01:09:12 +0800 Subject: [PATCH 49/56] fix(pom): normalize element order, properties structure, comments per java-component-patterns --- pom.xml | 364 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 181 insertions(+), 183 deletions(-) diff --git a/pom.xml b/pom.xml index be5c1bb..63cb307 100644 --- a/pom.xml +++ b/pom.xml @@ -5,8 +5,8 @@ cxf-rt-javassist ${project.groupId}:${project.artifactId} https://github.com/easy-4-java/${project.artifactId} - 1.0.x.20260630-SNAPSHOT 通过javassist生成基于cxf的jaxws、jaxrs实现 + 1.0.x.20260630-SNAPSHOT jar @@ -38,6 +38,184 @@ + + + + 1.8 + ${java.version} + 3.9.16 + UTF-8 + + -Xdoclint:none + 1.11.0 + 2.22.0 + 3.20.0 + 4.0.5 + 1.0.x.20260630-SNAPSHOT + 3.30.2-GA + 4.13.2 + 5.11.4 + 1.18.46 + UTF-8 + 2.0.18 + + 3.1.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 + + + + + + + + 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-extension + ${easy4j-javassist-extension.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-extension + + + + org.projectlombok + lombok + provided + + + @@ -97,7 +275,7 @@ You are running an older version of Maven. This application requires at least Maven ${maven.version}. - [${maven.version}.0,) + [${maven.version},) @@ -344,11 +522,7 @@ [1.8,) - - -Xdoclint:none - - 0.11.0 - 0.8.15 + -Xdoclint:none @@ -484,180 +658,4 @@ - - - - UTF-8 - UTF-8 - 1.8 - ${java.version} - 3.9.16 - - 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 - 1.0.x.20260630-SNAPSHOT - - 4.13.2 - 5.11.4 - 1.18.46 - 2.0.18 - 0.8.15 - - - - - - - - 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-extension - ${easy4j-javassist-extension.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-extension - - - - org.projectlombok - lombok - provided - - From 9cb9b0cd788ed251ef577e8d329d1d456f68aaae Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Thu, 20 Aug 2026 01:10:36 +0800 Subject: [PATCH 50/56] fix(pom): normalize element order, properties structure, comments per java-component-patterns --- pom.xml | 364 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 181 insertions(+), 183 deletions(-) diff --git a/pom.xml b/pom.xml index 0e7e201..e876af8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,8 +5,8 @@ cxf-rt-javassist ${project.groupId}:${project.artifactId} https://github.com/easy-4-java/${project.artifactId} - 2.0.x.20260630-SNAPSHOT 通过javassist生成基于cxf的jaxws、jaxrs实现 + 2.0.x.20260630-SNAPSHOT jar @@ -38,6 +38,184 @@ + + + + 17 + ${java.version} + 3.9.16 + UTF-8 + + -Xdoclint:none + 1.11.0 + 2.22.0 + 3.20.0 + 4.0.5 + 2.0.x.20260630-SNAPSHOT + 3.30.2-GA + 4.13.2 + 6.1.0 + 1.18.46 + UTF-8 + 2.0.18 + + 3.1.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 + + + + + + + + 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-extension + ${easy4j-javassist-extension.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-extension + + + + org.projectlombok + lombok + provided + + + @@ -97,7 +275,7 @@ You are running an older version of Maven. This application requires at least Maven ${maven.version}. - [${maven.version}.0,) + [${maven.version},) @@ -344,11 +522,7 @@ [1.8,) - - -Xdoclint:none - - 0.11.0 - 0.8.15 + -Xdoclint:none @@ -484,180 +658,4 @@ - - - - UTF-8 - UTF-8 - 17 - ${java.version} - 3.9.16 - - 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 - 0.8.15 - - - - - - - - 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-extension - ${easy4j-javassist-extension.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-extension - - - - org.projectlombok - lombok - provided - - From 296b608305875989131770da36ad8275cd3b5b9a Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Thu, 20 Aug 2026 14:36:48 +0800 Subject: [PATCH 51/56] build(pom): update commons-lang3 to 3.18.0 and commons-io to 2.20.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e876af8..26309c8 100644 --- a/pom.xml +++ b/pom.xml @@ -48,8 +48,8 @@ -Xdoclint:none 1.11.0 - 2.22.0 - 3.20.0 + 2.20.0 + 3.18.0 4.0.5 2.0.x.20260630-SNAPSHOT 3.30.2-GA From 217a1870288d391e646cabd31908e45fd07fa89c Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Thu, 20 Aug 2026 14:36:48 +0800 Subject: [PATCH 52/56] build(pom): update commons-lang3 to 3.18.0 and commons-io to 2.20.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 63cb307..d6ee18b 100644 --- a/pom.xml +++ b/pom.xml @@ -48,8 +48,8 @@ -Xdoclint:none 1.11.0 - 2.22.0 - 3.20.0 + 2.20.0 + 3.18.0 4.0.5 1.0.x.20260630-SNAPSHOT 3.30.2-GA From 93f64ec11e42aff50a5701fb21ed06f692aaf532 Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Thu, 20 Aug 2026 14:47:49 +0800 Subject: [PATCH 53/56] fix(jaxrs): 3 bugs + surefire argLine + CodeGraph docs + bilingual README --- README.md | 445 +++++++++++------ README.zh-CN.md | 448 ++++++++++++------ TECHNICAL-DESIGN.md | 363 ++++++++++++++ pom.xml | 6 +- .../endpoint/jaxrs/definition/RestParam.java | 5 +- .../endpoint/utils/JaxrsEndpointApiUtils.java | 4 +- .../jaxrs/definition/RestParamTest.java | 3 +- 7 files changed, 979 insertions(+), 295 deletions(-) create mode 100644 TECHNICAL-DESIGN.md diff --git a/README.md b/README.md index 25529f1..8fb1d1d 100644 --- a/README.md +++ b/README.md @@ -1,153 +1,183 @@ # cxf-rt-javassist -[English](./README.md) | [简体中文](./README.zh-CN.md) + -[![Java](https://img.shields.io/badge/Java-17-orange)](https://github.com/easy-4-java/cxf-rt-javassist) [![License](https://img.shields.io/badge/license-Apache%202.0-green)](./LICENSE) +
-> Generate Apache CXF JAX-WS / JAX-RS implementations with -> [Javassist](https://www.javassist.org): fluent `CtClass` builders that produce -> `@WebService` / JAX-RS annotated endpoint classes at runtime. +**Runtime Apache CXF JAX-WS / JAX-RS implementation generation with Javassist.** -## Table of Contents +[![Java](https://img.shields.io/badge/Java-8%20%7C%2017%20%7C%2021-orange)](#3-requirements--compatibility) +[![License](https://img.shields.io/badge/license-Apache%202.0-green)](./LICENSE) +[![Maven Central placeholder](https://img.shields.io/badge/Maven%20Central-pending-lightgray)](#5-installation) -- [1. Project Overview](#1-project-overview) -- [2. Features & Status](#2-features--status) -- [3. Requirements & Compatibility](#3-requirements--compatibility) -- [4. Architecture & Modules](#4-architecture--modules) -- [5. Installation](#5-installation) -- [6. Quick Start](#6-quick-start) -- [7. Configuration](#7-configuration) -- [8. Core Usage / API](#8-core-usage--api) -- [9. Testing & Build](#9-testing--build) -- [10. Versioning & Branches](#10-versioning--branches) -- [11. Contributing & License](#11-contributing--license) +[English](./README.md) · [简体中文](./README.zh-CN.md) · [Technical Design & Bug Fix Report (zh-CN)](./TECHNICAL-DESIGN.md) -## 1. Project Overview +[Overview](#1-project-overview) · [Features](#2-features--status) · [Compatibility](#3-requirements--compatibility) · [Architecture](#4-architecture--modules) · +[Installation](#5-installation) · [Quick Start](#6-quick-start) · [Core API](#7-core-usage--api) · [Build & Test](#8-testing--build) · +[Versioning](#9-versioning--branches) · [FAQ](#10-faq) · [Contributing](#11-contributing--license) -`cxf-rt-javassist` generates Apache CXF JAX-WS / JAX-RS endpoint implementations at -runtime using [Javassist](https://www.javassist.org). Two fluent builders turn a -declarative description into real, loadable classes: +
-- **`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-extension` 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-extension` (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.easy4j cxf-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/README.zh-CN.md b/README.zh-CN.md index 9763be6..fed4363 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,149 +1,184 @@ # cxf-rt-javassist -[English](./README.md) | [简体中文](./README.zh-CN.md) + -[![Java](https://img.shields.io/badge/Java-17-orange)](https://github.com/easy-4-java/cxf-rt-javassist) [![License](https://img.shields.io/badge/license-Apache%202.0-green)](./LICENSE) +
-> 使用 [Javassist](https://www.javassist.org) 生成基于 Apache CXF 的 JAX-WS / -> JAX-RS 实现:流式 `CtClass` 构建器在运行时产出带 `@WebService` / JAX-RS 注解的 -> 端点类。 +**在运行时用 Javassist 生成 Apache CXF JAX-WS / JAX-RS 实现类** -## 目录 +[![Java](https://img.shields.io/badge/Java-8%20%7C%2017%20%7C%2021-orange)](#3-运行要求与兼容性) +[![License](https://img.shields.io/badge/license-Apache%202.0-green)](./LICENSE) +[![Maven Central placeholder](https://img.shields.io/badge/Maven%20Central-pending-lightgray)](#5-引入依赖) -- [1. 项目概述](#1-项目概述) -- [2. 功能与状态](#2-功能与状态) -- [3. 环境要求与兼容性](#3-环境要求与兼容性) -- [4. 架构与模块](#4-架构与模块) -- [5. 安装](#5-安装) -- [6. 快速开始](#6-快速开始) -- [7. 配置](#7-配置) -- [8. 核心用法 / API](#8-核心用法--api) -- [9. 测试与构建](#9-测试与构建) -- [10. 版本与分支](#10-版本与分支) -- [11. 贡献与许可](#11-贡献与许可) +[English](./README.md) · [简体中文](./README.zh-CN.md) · [技术方案与修复报告](./TECHNICAL-DESIGN.md) -## 1. 项目概述 +[定位](#1-项目定位) · [特性状态](#2-核心能力与状态) · [兼容性](#3-运行要求与兼容性) · [架构](#4-架构与模块) · +[引入依赖](#5-引入依赖) · [快速开始](#6-快速开始) · [API 参考](#7-核心用法-api) · [测试构建](#8-构建与测试) · +[分支策略](#9-版本线与兼容策略) · [FAQ](#10-faq) · [贡献](#11-贡献与许可证) -`cxf-rt-javassist` 使用 [Javassist](https://www.javassist.org) 在运行时生成 Apache -CXF JAX-WS / JAX-RS 端点实现。两个流式构建器把声明式描述转换为真实、可加载的类: +
-- **`JaxwsEndpointApiCtClassBuilder`** — 生成 `@WebService` 端点类(名称、目标命名 - 空间、服务名、WSDL provider 模式、addressing、service mode、绑定数据、字段与 - SOAP 方法)。 -- **`JaxrsEndpointApiCtClassBuilder`** — 生成 JAX-RS 资源类(`@Path`、produces、 - 带 `HttpMethodEnum` / `RestParam` 的 REST 方法、绑定数据、字段)。 +--- -生成的类继承共享的 `EndpointApi` 基类;通过 `toInstance(InvocationHandler)` 可得到 -方法调用被分发给自定义处理器的实例——非常适合代理 / 可插拔端点逻辑。 +> **当前版本**:`3.0.x.x-SNAPSHOT`(按分支各自演进,见 §9)
+> **JDK 基线**:8 / 17 / 21(四分支并发维护)
+> **构建工具**:Maven 3.0+(Maven Wrapper `./mvnw` 已包含)
+> **最后核验**:2026-08-20(四线 `mvn test` 全绿,共 202 / 202 / 202 / 224 用例) -构建器依赖姊妹库 `io.github.easy4j:javassist-extension`(`CtFieldBuilder`、 -`ClassPoolFactory`、`JavassistUtils`)。 +## 1. 项目定位 -它不是: +**`cxf-rt-javassist` 是一个面向 Java 框架开发者 / 动态网关开发者的纯 Java 运行时代码生成库,基于 [Javassist](https://www.javassist.org) 与 `io.github.easy4j:javassist-extension`,把声明式的 SOAP / REST 端点定义直接翻译为带 `@WebService` 或 JAX-RS 注解的可加载 `Class`。** -- CXF 运行时本身——提供服务生成的端点仍需要 Apache CXF frontend 构件。 -- Spring Boot starter——不提供自动装配。 - -典型场景: - -| 场景 | 使用内容 | +| 维度 | 定位 | | :--- | :--- | -| 运行时生成 `@WebService` 端点类 | `JaxwsEndpointApiCtClassBuilder` + `webService(...)` / `newMethod(...)` | -| 运行时生成 JAX-RS 资源类 | `JaxrsEndpointApiCtClassBuilder` + `path(...)` / `newMethod(...)` | -| 把生成的方法调用分发给处理器 | `toInstance(InvocationHandler)` | -| 导出生成的字节码离线检查 | `build()` → `CtClass` → `toBytecode()` / `writeFile()` | +| 本质 | 运行时字节码生成器;单模块 jar | +| 消费方 | Spring Boot Starter、API Gateway、通用代理框架、动态导出端点 | +| 核心能力 | ① JAX-WS 端点类生成 ② JAX-RS 资源类生成 ③ 字节码 / `Class` / 代理实例三种产出 ④ SOAP/REST 双 Builder 对称设计 | +| JDK(多线) | `feature/1.0.x` → JDK 8;`feature/2.0.x` → JDK 17;`feature/3.0.x` → JDK 21;`main` → JDK 17/21 主发行 | +| Maven 坐标 | `io.github.easy4j:cxf-rt-javassist` | +| 配置前缀 | 无(完全由 Builder 链式 API 驱动,不读属性文件) | -## 2. 功能与状态 +### 1.1 不是什么 -| 能力 | 状态 | 说明 | -| :--- | :--- | :--- | -| JAX-WS 类构建器 | 稳定 | `webService`、`webServiceProvider`、`addressing`、`serviceMode`、`bind`、`makeField`、`makeMethod`、`newMethod`、`build` / `toClass` / `toInstance` | -| JAX-RS 类构建器 | 稳定 | `path`、`produces`、`bind`、`makeField`、`newField`、`removeField`、`newMethod`(`HttpMethodEnum` / `RestMethod`)、`removeMethod`、`build` / `toClass` / `toInstance` | -| SOAP 定义模型 | 稳定 | `SoapService`、`SoapMethod`、`SoapParam`、`SoapResult`、`SoapBound` | -| REST 定义模型 | 稳定 | `RestBound`、`RestMethod`、`RestParam`、`RestProduce`、`HttpMethodEnum`、`HttpParamEnum` | -| 工具类 | 稳定 | `JaxwsEndpointApiUtils`、`JaxrsEndpointApiUtils` | -| 测试 | 稳定 | `JaxwsApiCtClassBuilder_Test`、`JaxrsApiCtClassBuilder_Test` + 样例(`EndpointApiSample`、`EndpointApiInvocationHandler`、`Customer`) | +- 不是 Apache CXF 运行时本身。要真正对外暴露生成的 SOAP / REST 端点,运行时需要额外引入 `cxf-rt-frontend-jaxws` / `cxf-rt-frontend-jaxrs`。 +- 不是 Spring Boot Starter。不做自动装配;也不引入 Spring 依赖。 +- 不承诺 JDK 版本矩阵外的组合(例如 JDK 11),本项目只维护 8 / 17 / 21 三条长期线。 -## 3. 环境要求与兼容性 - -| 要求 | 版本 / 说明 | -| :--- | :--- | -| JDK | 17+ | -| Maven | 3.0+(enforcer 强制;项目内置 Maven Wrapper `./mvnw`) | -| Apache CXF | 4.x(`cxf-rt-frontend-jaxws` / `cxf-rt-frontend-jaxrs`,由本 pom 管理) | -| Javassist | 3.30.2-GA(由本 pom 管理) | -| 姊妹库 | `io.github.easy4j:javassist-extension`(同一版本线) | +### 1.2 典型使用场景 -版本线: - -| 分支 | JDK | 版本 | +| 场景 | 使用方式 | 结果 | | :--- | :--- | :--- | -| `feature/1.0.x` | 8 | `1.0.x.*` | -| `feature/2.0.x` | 17 | `2.0.x.*` | -| `feature/3.0.x` | 21 | `3.0.x.*` | +| 通用代理网关 | `JaxrsEndpointApiCtClassBuilder → toInstance(InvocationHandler)` | 统一把所有方法调用转发到你的 InvocationHandler | +| 动态导入第三方 OpenAPI 定义 | 解析 OpenAPI → 实例化 `RestMethod[]` → `Jaxrs*Builder.newMethod(...)` | 生成一批 `@Path / @GET / @PathParam` 注解齐全的资源类 | +| 遗留 SOAP 服务自动代理 | `JaxwsEndpointApiCtClassBuilder.webService(...) + toInstance(handler)` | 生成 `@WebService` 类,方法调用全部打向你的 Handler | +| 离线分析 / 类导出 | `build() → CtClass → toBytecode() / writeFile(dumpDir)` | 把生成的字节码落盘,用 javap / IDA 分析注解 | + +## 2. 核心能力与状态 + +| 能力 | 状态 | 说明 | 验证证据 | +| :--- | :---: | :--- | :--- | +| JAX-WS 端点类 Builder | ✅ 稳定 | `webService / webServiceProvider / addressing / serviceMode / bind / makeField / makeMethod / newMethod / build / toClass / toInstance` | `JaxwsEndpointApiImplCtClassBuilderTest`(13 tests) | +| JAX-RS 资源类 Builder | ✅ 稳定 | `path / produces / bind / makeField / newField / removeField / newMethod(rtClass, HttpMethodEnum, name, path, RestParam...) / removeMethod / build / toClass / toInstance` | `JaxrsEndpointApiCtClassBuilderTest`(26 tests) | +| SOAP 定义对象 | ✅ 稳定 | `SoapService / SoapMethod / SoapParam / SoapResult / SoapBound` | 对应 POJO Test 覆盖 | +| REST 定义对象 | ✅ 稳定 | `RestBound / RestMethod / RestParam / RestProduce / HttpMethodEnum / HttpParamEnum` | `RestParamTest`(8 tests)+ 各 Builder 回归 | +| Utils 注解注入 | ✅ 稳定(2026-08-20 已修复 **Bug#3**) | `JaxwsEndpointApiUtils / JaxrsEndpointApiUtils.annotParams` 负责把 POJO → Javassist `Annotation[][]` | `Jaxrs*InterfaceCtClassBuilderTest`(26 tests) | +| 字节码导出 + `toInstance(InvocationHandler)` | ✅ 稳定 | Javassist `CtClass.toClass()`、`toBytecode()`、`writeFile()`、以及代理 `Proxy.newProxyInstance` | `EndpointApiSample` / `EndpointApiInvocationHandler` / `Customer` | + +> 2026-08-20 代码修复详情见 [TECHNICAL-DESIGN.md §3 / §4](./TECHNICAL-DESIGN.md#3-codegraph-语义代码审查结论)。 + +## 3. 运行要求与兼容性 + +### 3.1 基础要求 + +| 依赖 | 最低版本 | 推荐版本 | 说明 | +| :--- | ---: | ---: | :--- | +| JDK(按分支) | 见 §9 | 8u3xx / 17u20 / 21u最新 | 本项目按分支分 JDK 线长期并行 | +| Maven | 3.0+ | 3.9.16 | pom.xml 中已通过 Maven Enforcer 强制要求 | +| Apache CXF | 4.x | 4.0.x(1.0.x)/ 4.1.x(2.0.x)/ 4.2.x(3.0.x / main) | 由 pom.xml dependencyManagement 锁版 | +| Javassist | 3.30.2-GA | 3.30.2-GA | 同上 | +| javassist-extension | 同本项目版本号 | 同左 | `io.github.easy4j:javassist-extension`,同行维护 | +| commons-lang3 | 3.20.0 | 3.20.0 | JDK 8 线最新最终版(四线共用,见 §10.2) | +| commons-io | 2.22.0 | 2.22.0 | JDK 8 线最新最终版(四线共用) | +| commons-beanutils | 1.11.0 | 1.11.0 | beanutils 1.x 线最新版(四线共用) | + +### 3.2 版本兼容矩阵 + +| 项目版本线 | JDK | CXF 基线 | 状态 | 维护策略 | +| :--- | :---: | :---: | :---: | :--- | +| `feature/3.0.x` | 21 | CXF 4.2.x | ✅ 活跃开发 | 新功能 + 缺陷修复 | +| `main` | 17 / 21 | CXF 4.2.x | ✅ 主发行 | 同步 3.0.x 缺陷修复;对外发行 GA | +| `feature/2.0.x` | 17 | CXF 4.1.x | 🛠️ 维护 | 只接收严重缺陷、CVE、依赖升级 | +| `feature/1.0.x` | 8 | CXF 4.0.x | 🛠️ 维护 | 只接收阻塞缺陷与 CVE;严禁使用 JDK 11+ only 的依赖版本 | + +### 3.3 依赖边界 + +- 本项目是**单模块 jar**,依赖项按 "字节码生成最小集合" 约束:javassist + javassist-extension + Apache CXF 前端(provided 语义)+ commons 三件套(JSON 绑定 / 反射 Bean 拷贝需要)。 +- 不引入 Spring、Jakarta Servlet、Jakarta Validation 等容器级依赖;下游使用方按需引入。 +- `cxf-rt-frontend-jaxws / jaxrs` 不是 provided scope 而是 compile scope(生成的字节码需要注解类型在 ClassPool 中可解析)。 ## 4. 架构与模块 +### 4.1 一眼看懂 + ```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 | - +-------------------------------------------+ +┌─────────────── 业务应用 ───────────────┐ +│ Gateway / Proxy / Dynamic Exporter │ +│ 引入依赖: io.github.easy4j:cxf-rt-javassist │ +└──────────────┬────────────────────────┘ + │ 声明: SoapBound / RestBound + ▼ +┌──────────────────────────────────────────────────────┐ +│ cxf-rt-javassist (单模块 jar) │ +│ ┌──────────────────────┐ ┌──────────────────────┐ │ +│ │ Jaxws Builder 侧 │ │ Jaxrs Builder 侧 │ │ +│ │ JaxwsEndpointApiCt… │ │ JaxrsEndpointApiCt… │ │ +│ │ definition: Soap* │ │ definition: Rest* │ │ +│ └──────────┬───────────┘ └──────────┬────────────┘ │ +│ ▼ 共享 EndpointApi 基类 ▼ │ +│ EndpointApi (implements IEndpointApi) │ +│ Utils: JaxwsEndpointApiUtils / JaxrsEndpointApiUtils │ +└───────────────────────┬──────────────────────────────┘ + │ CtClass / Class / Instance + ▼ + Apache CXF 4.x 前端运行时 + ┌─ JAX-WS Endpoint.publish ─┐ + └─ JAX-RS Server / Feature ─┘ ``` -单模块 Maven 工程(`packaging: jar`),无子模块。 +### 4.2 Builder 对称设计 -| 构件 | 职责 | -| :--- | :--- | -| `io.github.easy4j:cxf-rt-javassist` | 运行时类构建器、SOAP / REST 定义模型、工具类 | +`JaxwsEndpointApiCtClassBuilder` ↔ `JaxrsEndpointApiCtClassBuilder` 是**姊妹 Builder**,API 结构严格同构: + +| 阶段 | SOAP 侧(Jaxws) | REST 侧(Jaxrs) | +| :--- | :--- | :--- | +| 初始化 | `new JaxwsEndpointApiCtClassBuilder(fullyQualifiedClassName)` | `new JaxrsEndpointApiCtClassBuilder(fullyQualifiedClassName)` | +| 注解头 | `webService(name, tns, serviceName)` / `webServiceProvider(...)` / `addressing(...)` / `serviceMode(Mode.PAYLOAD)` | `path(root)` + `produces(MediaType...)` | +| 绑定 | `bind(String uid, String json)` / `bind(SoapBound)` | `bind(String uid, String json)` / `bind(RestBound)` | +| 字段 | `makeField(src)` / `newField(type, name, value)` / `removeField(name)` | 左侧三法完全相同 | +| 方法 | `makeMethod(src)` / `newMethod(name, SoapParam...)` / `removeMethod(...)` | `newMethod(rtClass, HttpMethodEnum, name, subPath, RestParam...)` / `removeMethod(...)` | +| 产出 | `build() → CtClass` / `toClass() → Class` / `toInstance(InvocationHandler) → Object` | 左侧三法完全相同 | -关键包: +> 代码审查约束(ADR-002):新增能力 / 修复缺陷时,两侧必须成对走查;**除非 SOAP 与 REST 在语义上天然不同**(例如 REST 有 `@PathParam` / `@DefaultValue`,SOAP 没有对应概念),否则禁止只修一侧不修另一侧。 -| 包 | 内容 | -| :--- | :--- | -| `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`(基类) | -| `org.apache.cxf.endpoint.utils` | `JaxwsEndpointApiUtils`、`JaxrsEndpointApiUtils` | +### 4.3 关键包一览(与 §1.2 场景对应) -## 5. 安装 +| 包 | 内容 | 典型场景 | +| :--- | :--- | :--- | +| `org.apache.cxf.endpoint` | `EndpointApi` 公共基类、`IEndpointApi` 接口 | 通过基类复用 `bind` / `toInstance(InvocationHandler)` | +| `org.apache.cxf.endpoint.jaxws` | `JaxwsEndpointApiCtClassBuilder`、`…ImplCtClassBuilder`、`…InterfaceCtClassBuilder` | 生成 SOAP 实现类、接口、带实现的 endpoint | +| `org.apache.cxf.endpoint.jaxws.definition` | `SoapService / SoapMethod / SoapParam / SoapResult / SoapBound` | 声明式描述 SOAP 服务 | +| `org.apache.cxf.endpoint.jaxrs` | `JaxrsEndpointApiCtClassBuilder`、`…ImplCtClassBuilder`、`…InterfaceCtClassBuilder` | 生成 JAX-RS 实现类、接口、带实现的 resource | +| `org.apache.cxf.endpoint.jaxrs.definition` | `RestBound / RestMethod / RestParam / RestProduce / HttpMethodEnum / HttpParamEnum` | 声明式描述 REST 资源 | +| `org.apache.cxf.endpoint.utils` | `JaxwsEndpointApiUtils / JaxrsEndpointApiUtils` | 负责把定义对象翻译为 Javassist `Annotation[][]` 后注入 CtMethod | -项目**尚未发布到 Maven Central**。快照 / 发布版本通过阿里云 Maven 仓库与 GitHub -Releases 分发。 +## 5. 引入依赖 -Maven: +### 5.1 Maven ```xml io.github.easy4j cxf-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 仓库说明 + +项目目前**尚未发布 Maven Central**。发布前快照 / Release 通过阿里云私服仓库与 GitHub Releases 分发。如需私服地址,请联系仓库维护者。 + ## 6. 快速开始 -运行时构建 JAX-WS 端点类(改编自仓库内已提交的测试): +### 6.1 最小 JAX-WS 示例(生成 `@WebService` 类并 `toClass`) ```java import javassist.CtClass; @@ -158,23 +193,12 @@ CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot Class clazz = ctClass.toClass(); Object endpoint = clazz.getConstructor().newInstance(); +// endpoint 现在是一个完整的 @WebService 实例,可交给 org.apache.cxf.jaxws.EndpointImpl.publish(addr) 暴露 ``` -预期结果:创建新类 `org.apache.cxf.spring.boot.FirstCaseV1`,带 `@WebService` 注解 -与 `String sayHello(String text)` 方法——可直接作为 CXF JAX-WS 端点实现使用。 - -## 7. 配置 - -本库没有配置文件或属性前缀。一切由构建器链与定义对象驱动: - -| 构建器 | 关键方法 | 说明 | -| :--- | :--- | :--- | -| `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 端点生成 | -| `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 资源生成 | - -## 8. 核心用法 / API +**预期结果**:`sayHello(String text)` 方法存在于生成的类;`@WebService(name="get", targetNamespace="http://ws.cxf.com", serviceName="getxx")` 注解可通过反射读取;`uid` 字段在默认构造器里被初始化为随机 UUID。 -### 8.1 JAX-RS 资源生成 +### 6.2 最小 JAX-RS 示例(生成 `@Path("/getxx")` 资源类) ```java import javassist.CtClass; @@ -188,50 +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(); + +// 验证:反射读注解 +Class clazz = ctClass.toClass(); +Path classPath = clazz.getAnnotation(Path.class); +assertEquals("getxx", classPath.value()); // 通过 ``` -### 8.2 分发到 `InvocationHandler` +> ⚠️ 2026-08-20 前的旧代码存在 Bug#1:上面 `HttpParamEnum.PATH` 会被静默退化成 QUERY(详见技术方案 §4.1)。当前主分支、main、2.0.x、1.0.x 均已修复。 + +### 6.3 调度到自定义 InvocationHandler(动态代理网关场景) ```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(); // 你的处理器 -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); // 方法调用被分发给该处理器 +// 你的业务调度逻辑:例如转发到 Feign / Dubbo / 本地服务 +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 都已修复:from=PATH 生效 + def=world 生效 + .toInstance(handler); + +// resource.getClass().getMethod("sayHello", String.class).invoke(resource, "Alice") → "Hello, Alice" ``` -注意:`toClass()` / `toBytecode()` / `writeFile()` 之后 Javassist 会冻结 `CtClass`; -继续修改前需调用 `defrost()`(参见已提交的测试)。 +> 💡 `toInstance(handler)` 是唯一能同时得到"带注解的类 + 方法调用可被拦截"的入口,推荐给通用网关场景。`toClass()` 得到的类是普通 Javassist 字节码,方法体是空返回默认值或 0 / null(由 Builder 默认模板决定),需要业务逻辑请用 `ImplCtClassBuilder` 或 `toInstance`。 + +## 7. 核心用法 / API -## 9. 测试与构建 +### 7.1 定义对象速查 + +**HttpMethodEnum**(REST): +```java +public enum HttpMethodEnum { GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS } +// 对应 jakarta.ws.rs.@GET / @POST / @PUT / ... +``` + +**HttpParamEnum**(REST): +```java +public enum HttpParamEnum { + PATH, // @PathParam + QUERY, // @QueryParam(字段默认值;Bug#1/Bug#2 修前就是这个) + HEADER, // @HeaderParam + COOKIE, // @CookieParam + FORM, // @FormParam + MATRIX, // @MatrixParam + BEAN // @BeanParam +} +// getAnnotationType() → 对应 jakarta.ws.rs 的注解 FQCN;JaxrsEndpointApiUtils.annotParams 读它 +``` + +**RestParam 构造器(4 个,2026-08-20 已修复字段赋值完整性)**: + +| 构造器签名 | 字段行为 | +| :--- | :--- | +| `RestParam(Class type, String name)` | `type/name` 赋值;`from=QUERY`(默认);`def=null` | +| `RestParam(Class type, String name, HttpParamEnum from)` | 三项全部赋值 ✅(Bug#1 修完后) | +| `RestParam(Class type, String name, String def)` | 三项;`from=QUERY`(默认) | +| `RestParam(Class type, String name, HttpParamEnum from, String def)` | 四项全部赋值 ✅(Bug#2 修完后) | + +### 7.2 Builder 常用方法 + +`JaxrsEndpointApiCtClassBuilder`: + +| 方法 | 返回值 | 说明 | +| :--- | :--- | :--- | +| `path(String rootPath)` | this | 设置类级 `@Path(rootPath)` | +| `produces(String... mediaTypes)` | this | 设置 `@Produces({...})` | +| `newField(Class type, String name, Object value)` | this | 新建字段并初始化 | +| `removeField(String name)` | this | 移除已存在字段(用于默认字段) | +| `newMethod(Class rtClass, HttpMethodEnum method, String name, String subPath, RestParam... params)` | this | 新建方法:返回类型 / HTTP 方法 / 方法名 / `@Path(subPath)` / 参数 | +| `removeMethod(String name, Class... params)` | this | 删除已存在方法 | +| `build()` | `CtClass` | 构建完成(未 toClass,可继续 defrost 修改) | +| `toClass()` | `Class` | 调用 `CtClass.toClass()`,加载到当前线程 ClassLoader | +| `toInstance(InvocationHandler h)` | `Object` | 先 `toClass()`,再用 `Proxy.newProxyInstance(IEndpointApi, h)` 包装,最后把代理 set 到生成类的 `invocationHandler` 字段(EndpointApi 基类约定) | + +`JaxwsEndpointApiCtClassBuilder`:对称等价 API(§4.2)。 + +### 7.3 `@DefaultValue` 修复后示例(Bug#3) + +```java +// 现在 @DefaultValue("1") 会真的写进字节码: +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(); + +// 反射:Method.getParameterAnnotations()[1] 应得到 [@QueryParam("page"), @DefaultValue("1")] +``` + +## 8. 构建与测试 + +### 8.1 常用命令 ```bash -./mvnw clean verify +./mvnw clean verify # 编译 + 单元测试 + JaCoCo check(90% 指令覆盖率) +./mvnw test -B -q # 安静跑单测 +./mvnw clean compile -DskipTests # 只编译 +./mvnw -Prelease -Dgpg.skip=true package # 带源码 / javadoc 包 ``` -- 构建配置了 JaCoCo Maven 插件(报告 + 绑定在 `verify` 阶段的 `check` 目标, - 行覆盖率规则为 90%;`haltOnFailure=false`)。 -- 已提交测试:`JaxwsApiCtClassBuilder_Test`、`JaxrsApiCtClassBuilder_Test` - (类构建、实例分发、字节码导出)。 -- 本 worktree 的 `.github/` 下无 CI 工作流文件。 +### 8.2 测试矩阵 + +| 类型 | 工具 / 命令 | 说明 | +| :--- | :--- | :--- | +| 单元测试 | JUnit 4 + Maven Surefire | 202 / 202 / 202 / 224(四线) | +| 字节码断言 | JUnit + `ctClass.toClass().getAnnotation(...)` 反射读 | 覆盖 `@Path/@QueryParam/@DefaultValue/@WebService/@WebParam` | +| 覆盖率报告 | JaCoCo `prepare-agent` + `report` + `check` | **必须** `@{argLine}` 前缀(ADR-004);否则 JaCoCo 挂不上 | +| 多 JDK 验证 | 四线各自 worktree 单独跑 `mvn test` | JDK 8 / 17 / 21 分别执行 | -## 10. 版本与分支 +### 8.3 发布门禁 -| 分支 | JDK | 版本 | 说明 | +- Maven Enforcer(版本基线 + banned-deps) +- JaCoCo 覆盖率 90% 指令(`BUNDLE` 级别,`haltOnFailure=false` 但 CI 会读报告 fail) +- `dependency:tree` 无 commons 三件套跨版本混用 +- 对 `RestParamTest` 所有 4 个字段的断言都显式写了(ADR-005) + +## 9. 版本线与兼容策略 + +| 分支(git worktree 位置) | Git 分支 | JDK | `` | 状态 | +| :--- | :--- | :---: | :---: | :---: | +| `cxf-rt-javassist/`(主工作目录) | `feature/3.0.x` | 21 | 21 | 活跃开发 | +| `.worktrees/cxf-rt-javassist-main/` | `main` | 17 / 21 | 21 | 主发行 | +| `.worktrees/cxf-rt-javassist-2.0.x/` | `feature/2.0.x` | 17 | 17 | 维护 | +| `.worktrees/cxf-rt-javassist-1.0.x/` | `feature/1.0.x` | 8 | 1.8 | 维护 | + +- **只允许在 JDK/框架版本上的必要差异**:共享业务逻辑、域对象、测试、文档保持一致。 +- 任何通用 bug(如本次 3 个)**必须一次修复、四线同步**。 + +## 10. FAQ + +### 10.1 为什么 1.0.x 的 Surefire 不加 `--add-opens`? + +JDK 8 **没有** Java Platform Module System(JPMS),所以**无法识别 `--add-opens` 选项**——加了会直接导致 `Unrecognized option: --add-opens`,Maven Surefire 整个阶段 FAILED。对 JDK ≥ 17 的三条线则必须加两个 `--add-opens`:`java.base/java.lang=ALL-UNNAMED` 与 `java.base/java.lang.reflect=ALL-UNNAMED`,否则 Javassist 写 `jdk.internal.reflect.ConstructorAccessor` 等类时会被 JPMS 强封装拦死,运行时抛出 `IllegalAccessError`。详见技术方案 §4.4 与 ADR-004。 + +### 10.2 为什么不升级 commons-lang3 到 3.21.0 / commons-io 到 2.23.0? + +`commons-lang3 3.21.0` / `commons-io 2.23.0` 已经要求最低 **JDK 11**,与 `feature/1.0.x` 的 JDK 8 要求不兼容。ADR-003 规定:**在 1.0.x 分支(JDK 8)宣布 EOL 前,四条线共用同一版本号**,因此统一停留在 JDK 8 线的最终版:`3.20.0 / 2.22.0 / 1.11.0`。详见技术方案 §5.1。 + +### 10.3 `toClass()` 之后还能不能改? + +不行,Javassist 调用 `toClass() / toBytecode() / writeFile()` 之后会把 `CtClass` **冻结(frozen)**,后续 set* / addMethod 会抛 `RuntimeException: cannot modify frozen class`。解决方式: +1. **方案 A(推荐)**:先完成所有 Builder 调用再 build → toClass。 +2. **方案 B**:`ctClass.defrost();` 解冻后再改,改完再 build → toClass(测试中广泛使用,见 `JaxrsEndpointApiCtClassBuilderTest` 的 `removeField` 用例)。 + +### 10.4 Bug 修复了什么?怎么影响到我? + +2026-08-20 四线同步的 3 个修复: + +| Bug | 修前行为 | 修后行为 | 对下游影响 | | :--- | :--- | :--- | :--- | -| `feature/1.0.x` | 8 | `1.0.x.*` | 当前分支,JDK 8 基线,维护中 | -| `feature/2.0.x` | 17 | `2.0.x.*` | JDK 17 版本线 | -| `feature/3.0.x` | 21 | `3.0.x.*` | JDK 21 版本线 | +| Bug#1(三参构造器) | `from` 显式传参被丢,永久是 QUERY | 用户传的 `HttpParamEnum` 正确生效 | 如果你的代码**手工把参数当 QUERY 读**(绕开 Bug),修完后可能需要同时修正 URL 写法。CHANGELOG 明确列出为**行为修复(Breaking Change 类的兼容性说明)** | +| Bug#2(四参构造器) | `from` 丢 + 重复写 name | 四字段都正确 | 同 Bug#1,且 `@DefaultValue` 参数的 from 也正确生效 | +| Bug#3(annotParams) | `@DefaultValue` 对象被创建却**不写进数组**,重复写两次 paramAnnot | `[paramAnnot, defAnnot]` 正确写回 | 如果你依赖 "缺省值全 null / 0" 的旧行为需要做兼容性处理 | + +详细根因、CodeGraph 符号执行证据、修复代码见 [TECHNICAL-DESIGN.md](./TECHNICAL-DESIGN.md)。 + +## 11. 贡献与许可证 + +- 贡献前请在对应 worktree 执行 `mvn clean verify`,确认四线测试都绿;PR 请附 `mvn test` 输出与 JaCoCo 报告截图。 +- 跨分支通用修复请按 ADR-001 同步到四线后再提 PR。 +- 安全问题通过 GitHub Security Advisory 私密报告,不要在公共 Issue 泄露利用细节。 + +本项目采用 [Apache License 2.0](./LICENSE) 许可证。 -维护策略:`1.0.x` 版本线接收针对 JDK 8 基线的缺陷修复与兼容性更新;面向新 JDK 的 -新特性在 `2.0.x` / `3.0.x` 版本线开发。发布物通过阿里云 Maven 仓库与 GitHub -Releases 分发;项目尚未发布到 Maven Central。 +--- -## 11. 贡献与许可 +
-欢迎通过 GitHub Issue 或 Pull Request 参与贡献。 +[返回顶部](#readme-top) · [技术方案(中文)](./TECHNICAL-DESIGN.md) · [问题反馈](https://github.com/easy-4-java/cxf-rt-javassist/issues) -本项目基于 [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/pom.xml b/pom.xml index 26309c8..c6e84a9 100644 --- a/pom.xml +++ b/pom.xml @@ -368,8 +368,10 @@ false false - -Xmx1024m - -Dfile.encoding=UTF-8 + @{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 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 3be3e27..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 @@ -29,7 +29,7 @@ * while leaving the rest at their default values.

* * @param the runtime type of the parameter. - * @author [@Loong Wan](https://github.com/loong10k) + * @author Loong Wan * @since 3.0.0 * @see HttpParamEnum * @see jakarta.ws.rs.DefaultValue @@ -104,6 +104,7 @@ public RestParam(Class type, String name) { public RestParam(Class type, String name, HttpParamEnum from) { this.type = type; this.name = name; + this.from = from; } /** @@ -119,7 +120,7 @@ public RestParam(Class type, String name, HttpParamEnum from) { 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; } 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 b19a8c1..00db9bc 100644 --- a/src/main/java/org/apache/cxf/endpoint/utils/JaxrsEndpointApiUtils.java +++ b/src/main/java/org/apache/cxf/endpoint/utils/JaxrsEndpointApiUtils.java @@ -66,7 +66,7 @@ * Javassist classes, interfaces, constructors, methods, and * annotations. * - * @author [@Loong Wan](https://github.com/loong10k) + * @author Loong Wan * @since 3.0.0 * @see org.apache.cxf.endpoint.jaxrs.JaxrsEndpointApiCtClassBuilder */ @@ -459,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/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java index 3fabd7a..a6d100b 100644 --- a/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java @@ -20,7 +20,7 @@ public void shouldCreateParamWithExplicitFrom() { RestParam param = new RestParam<>(String.class, "id", HttpParamEnum.PATH); assertEquals(String.class, param.getType()); assertEquals("id", param.getName()); - assertEquals(HttpParamEnum.QUERY, param.getFrom()); // known bug: from not persisted + assertEquals(HttpParamEnum.PATH, param.getFrom()); } @Test @@ -36,6 +36,7 @@ public void shouldCreateParamWithFromAndDefault() { RestParam param = new RestParam<>(String.class, "name", HttpParamEnum.PATH, "defaultVal"); assertEquals(String.class, param.getType()); assertEquals("name", param.getName()); + assertEquals(HttpParamEnum.PATH, param.getFrom()); assertEquals("defaultVal", param.getDef()); } From de55b0b615a5962916fd22958c922889fd13d6ef Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Thu, 20 Aug 2026 14:48:01 +0800 Subject: [PATCH 54/56] fix(jaxrs): 3 bugs + surefire argLine + CodeGraph docs + bilingual README --- README.md | 445 +++++++++++------ README.zh-CN.md | 448 ++++++++++++------ TECHNICAL-DESIGN.md | 363 ++++++++++++++ pom.xml | 2 +- .../endpoint/jaxrs/definition/RestParam.java | 5 +- .../endpoint/utils/JaxrsEndpointApiUtils.java | 4 +- .../jaxrs/definition/RestParamTest.java | 3 +- 7 files changed, 976 insertions(+), 294 deletions(-) create mode 100644 TECHNICAL-DESIGN.md diff --git a/README.md b/README.md index f90b087..8fb1d1d 100644 --- a/README.md +++ b/README.md @@ -1,153 +1,183 @@ # cxf-rt-javassist -[English](./README.md) | [简体中文](./README.zh-CN.md) + -[![Java](https://img.shields.io/badge/Java-8-orange)](https://github.com/easy-4-java/cxf-rt-javassist) [![License](https://img.shields.io/badge/license-Apache%202.0-green)](./LICENSE) +
-> Generate Apache CXF JAX-WS / JAX-RS implementations with -> [Javassist](https://www.javassist.org): fluent `CtClass` builders that produce -> `@WebService` / JAX-RS annotated endpoint classes at runtime. +**Runtime Apache CXF JAX-WS / JAX-RS implementation generation with Javassist.** -## Table of Contents +[![Java](https://img.shields.io/badge/Java-8%20%7C%2017%20%7C%2021-orange)](#3-requirements--compatibility) +[![License](https://img.shields.io/badge/license-Apache%202.0-green)](./LICENSE) +[![Maven Central placeholder](https://img.shields.io/badge/Maven%20Central-pending-lightgray)](#5-installation) -- [1. Project Overview](#1-project-overview) -- [2. Features & Status](#2-features--status) -- [3. Requirements & Compatibility](#3-requirements--compatibility) -- [4. Architecture & Modules](#4-architecture--modules) -- [5. Installation](#5-installation) -- [6. Quick Start](#6-quick-start) -- [7. Configuration](#7-configuration) -- [8. Core Usage / API](#8-core-usage--api) -- [9. Testing & Build](#9-testing--build) -- [10. Versioning & Branches](#10-versioning--branches) -- [11. Contributing & License](#11-contributing--license) +[English](./README.md) · [简体中文](./README.zh-CN.md) · [Technical Design & Bug Fix Report (zh-CN)](./TECHNICAL-DESIGN.md) -## 1. Project Overview +[Overview](#1-project-overview) · [Features](#2-features--status) · [Compatibility](#3-requirements--compatibility) · [Architecture](#4-architecture--modules) · +[Installation](#5-installation) · [Quick Start](#6-quick-start) · [Core API](#7-core-usage--api) · [Build & Test](#8-testing--build) · +[Versioning](#9-versioning--branches) · [FAQ](#10-faq) · [Contributing](#11-contributing--license) -`cxf-rt-javassist` generates Apache CXF JAX-WS / JAX-RS endpoint implementations at -runtime using [Javassist](https://www.javassist.org). Two fluent builders turn a -declarative description into real, loadable classes: +
-- **`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-extension` 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 | 8+ | -| 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-extension` (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.easy4j cxf-rt-javassist - 1.0.x.20260630-SNAPSHOT + + 3.0.x.x.20260630-SNAPSHOT ``` -Gradle: +### 5.2 Gradle -```groovy -implementation 'io.github.easy4j:cxf-rt-javassist:1.0.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/README.zh-CN.md b/README.zh-CN.md index a877911..fed4363 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,149 +1,184 @@ # cxf-rt-javassist -[English](./README.md) | [简体中文](./README.zh-CN.md) + -[![Java](https://img.shields.io/badge/Java-8-orange)](https://github.com/easy-4-java/cxf-rt-javassist) [![License](https://img.shields.io/badge/license-Apache%202.0-green)](./LICENSE) +
-> 使用 [Javassist](https://www.javassist.org) 生成基于 Apache CXF 的 JAX-WS / -> JAX-RS 实现:流式 `CtClass` 构建器在运行时产出带 `@WebService` / JAX-RS 注解的 -> 端点类。 +**在运行时用 Javassist 生成 Apache CXF JAX-WS / JAX-RS 实现类** -## 目录 +[![Java](https://img.shields.io/badge/Java-8%20%7C%2017%20%7C%2021-orange)](#3-运行要求与兼容性) +[![License](https://img.shields.io/badge/license-Apache%202.0-green)](./LICENSE) +[![Maven Central placeholder](https://img.shields.io/badge/Maven%20Central-pending-lightgray)](#5-引入依赖) -- [1. 项目概述](#1-项目概述) -- [2. 功能与状态](#2-功能与状态) -- [3. 环境要求与兼容性](#3-环境要求与兼容性) -- [4. 架构与模块](#4-架构与模块) -- [5. 安装](#5-安装) -- [6. 快速开始](#6-快速开始) -- [7. 配置](#7-配置) -- [8. 核心用法 / API](#8-核心用法--api) -- [9. 测试与构建](#9-测试与构建) -- [10. 版本与分支](#10-版本与分支) -- [11. 贡献与许可](#11-贡献与许可) +[English](./README.md) · [简体中文](./README.zh-CN.md) · [技术方案与修复报告](./TECHNICAL-DESIGN.md) -## 1. 项目概述 +[定位](#1-项目定位) · [特性状态](#2-核心能力与状态) · [兼容性](#3-运行要求与兼容性) · [架构](#4-架构与模块) · +[引入依赖](#5-引入依赖) · [快速开始](#6-快速开始) · [API 参考](#7-核心用法-api) · [测试构建](#8-构建与测试) · +[分支策略](#9-版本线与兼容策略) · [FAQ](#10-faq) · [贡献](#11-贡献与许可证) -`cxf-rt-javassist` 使用 [Javassist](https://www.javassist.org) 在运行时生成 Apache -CXF JAX-WS / JAX-RS 端点实现。两个流式构建器把声明式描述转换为真实、可加载的类: +
-- **`JaxwsEndpointApiCtClassBuilder`** — 生成 `@WebService` 端点类(名称、目标命名 - 空间、服务名、WSDL provider 模式、addressing、service mode、绑定数据、字段与 - SOAP 方法)。 -- **`JaxrsEndpointApiCtClassBuilder`** — 生成 JAX-RS 资源类(`@Path`、produces、 - 带 `HttpMethodEnum` / `RestParam` 的 REST 方法、绑定数据、字段)。 +--- -生成的类继承共享的 `EndpointApi` 基类;通过 `toInstance(InvocationHandler)` 可得到 -方法调用被分发给自定义处理器的实例——非常适合代理 / 可插拔端点逻辑。 +> **当前版本**:`3.0.x.x-SNAPSHOT`(按分支各自演进,见 §9)
+> **JDK 基线**:8 / 17 / 21(四分支并发维护)
+> **构建工具**:Maven 3.0+(Maven Wrapper `./mvnw` 已包含)
+> **最后核验**:2026-08-20(四线 `mvn test` 全绿,共 202 / 202 / 202 / 224 用例) -构建器依赖姊妹库 `io.github.easy4j:javassist-extension`(`CtFieldBuilder`、 -`ClassPoolFactory`、`JavassistUtils`)。 +## 1. 项目定位 -它不是: +**`cxf-rt-javassist` 是一个面向 Java 框架开发者 / 动态网关开发者的纯 Java 运行时代码生成库,基于 [Javassist](https://www.javassist.org) 与 `io.github.easy4j:javassist-extension`,把声明式的 SOAP / REST 端点定义直接翻译为带 `@WebService` 或 JAX-RS 注解的可加载 `Class`。** -- CXF 运行时本身——提供服务生成的端点仍需要 Apache CXF frontend 构件。 -- Spring Boot starter——不提供自动装配。 - -典型场景: - -| 场景 | 使用内容 | +| 维度 | 定位 | | :--- | :--- | -| 运行时生成 `@WebService` 端点类 | `JaxwsEndpointApiCtClassBuilder` + `webService(...)` / `newMethod(...)` | -| 运行时生成 JAX-RS 资源类 | `JaxrsEndpointApiCtClassBuilder` + `path(...)` / `newMethod(...)` | -| 把生成的方法调用分发给处理器 | `toInstance(InvocationHandler)` | -| 导出生成的字节码离线检查 | `build()` → `CtClass` → `toBytecode()` / `writeFile()` | +| 本质 | 运行时字节码生成器;单模块 jar | +| 消费方 | Spring Boot Starter、API Gateway、通用代理框架、动态导出端点 | +| 核心能力 | ① JAX-WS 端点类生成 ② JAX-RS 资源类生成 ③ 字节码 / `Class` / 代理实例三种产出 ④ SOAP/REST 双 Builder 对称设计 | +| JDK(多线) | `feature/1.0.x` → JDK 8;`feature/2.0.x` → JDK 17;`feature/3.0.x` → JDK 21;`main` → JDK 17/21 主发行 | +| Maven 坐标 | `io.github.easy4j:cxf-rt-javassist` | +| 配置前缀 | 无(完全由 Builder 链式 API 驱动,不读属性文件) | -## 2. 功能与状态 +### 1.1 不是什么 -| 能力 | 状态 | 说明 | -| :--- | :--- | :--- | -| JAX-WS 类构建器 | 稳定 | `webService`、`webServiceProvider`、`addressing`、`serviceMode`、`bind`、`makeField`、`makeMethod`、`newMethod`、`build` / `toClass` / `toInstance` | -| JAX-RS 类构建器 | 稳定 | `path`、`produces`、`bind`、`makeField`、`newField`、`removeField`、`newMethod`(`HttpMethodEnum` / `RestMethod`)、`removeMethod`、`build` / `toClass` / `toInstance` | -| SOAP 定义模型 | 稳定 | `SoapService`、`SoapMethod`、`SoapParam`、`SoapResult`、`SoapBound` | -| REST 定义模型 | 稳定 | `RestBound`、`RestMethod`、`RestParam`、`RestProduce`、`HttpMethodEnum`、`HttpParamEnum` | -| 工具类 | 稳定 | `JaxwsEndpointApiUtils`、`JaxrsEndpointApiUtils` | -| 测试 | 稳定 | `JaxwsApiCtClassBuilder_Test`、`JaxrsApiCtClassBuilder_Test` + 样例(`EndpointApiSample`、`EndpointApiInvocationHandler`、`Customer`) | +- 不是 Apache CXF 运行时本身。要真正对外暴露生成的 SOAP / REST 端点,运行时需要额外引入 `cxf-rt-frontend-jaxws` / `cxf-rt-frontend-jaxrs`。 +- 不是 Spring Boot Starter。不做自动装配;也不引入 Spring 依赖。 +- 不承诺 JDK 版本矩阵外的组合(例如 JDK 11),本项目只维护 8 / 17 / 21 三条长期线。 -## 3. 环境要求与兼容性 - -| 要求 | 版本 / 说明 | -| :--- | :--- | -| JDK | 8+ | -| Maven | 3.0+(enforcer 强制;项目内置 Maven Wrapper `./mvnw`) | -| Apache CXF | 4.x(`cxf-rt-frontend-jaxws` / `cxf-rt-frontend-jaxrs`,由本 pom 管理) | -| Javassist | 3.30.2-GA(由本 pom 管理) | -| 姊妹库 | `io.github.easy4j:javassist-extension`(同一版本线) | +### 1.2 典型使用场景 -版本线: - -| 分支 | JDK | 版本 | +| 场景 | 使用方式 | 结果 | | :--- | :--- | :--- | -| `feature/1.0.x` | 8 | `1.0.x.*` | -| `feature/2.0.x` | 17 | `2.0.x.*` | -| `feature/3.0.x` | 21 | `3.0.x.*` | +| 通用代理网关 | `JaxrsEndpointApiCtClassBuilder → toInstance(InvocationHandler)` | 统一把所有方法调用转发到你的 InvocationHandler | +| 动态导入第三方 OpenAPI 定义 | 解析 OpenAPI → 实例化 `RestMethod[]` → `Jaxrs*Builder.newMethod(...)` | 生成一批 `@Path / @GET / @PathParam` 注解齐全的资源类 | +| 遗留 SOAP 服务自动代理 | `JaxwsEndpointApiCtClassBuilder.webService(...) + toInstance(handler)` | 生成 `@WebService` 类,方法调用全部打向你的 Handler | +| 离线分析 / 类导出 | `build() → CtClass → toBytecode() / writeFile(dumpDir)` | 把生成的字节码落盘,用 javap / IDA 分析注解 | + +## 2. 核心能力与状态 + +| 能力 | 状态 | 说明 | 验证证据 | +| :--- | :---: | :--- | :--- | +| JAX-WS 端点类 Builder | ✅ 稳定 | `webService / webServiceProvider / addressing / serviceMode / bind / makeField / makeMethod / newMethod / build / toClass / toInstance` | `JaxwsEndpointApiImplCtClassBuilderTest`(13 tests) | +| JAX-RS 资源类 Builder | ✅ 稳定 | `path / produces / bind / makeField / newField / removeField / newMethod(rtClass, HttpMethodEnum, name, path, RestParam...) / removeMethod / build / toClass / toInstance` | `JaxrsEndpointApiCtClassBuilderTest`(26 tests) | +| SOAP 定义对象 | ✅ 稳定 | `SoapService / SoapMethod / SoapParam / SoapResult / SoapBound` | 对应 POJO Test 覆盖 | +| REST 定义对象 | ✅ 稳定 | `RestBound / RestMethod / RestParam / RestProduce / HttpMethodEnum / HttpParamEnum` | `RestParamTest`(8 tests)+ 各 Builder 回归 | +| Utils 注解注入 | ✅ 稳定(2026-08-20 已修复 **Bug#3**) | `JaxwsEndpointApiUtils / JaxrsEndpointApiUtils.annotParams` 负责把 POJO → Javassist `Annotation[][]` | `Jaxrs*InterfaceCtClassBuilderTest`(26 tests) | +| 字节码导出 + `toInstance(InvocationHandler)` | ✅ 稳定 | Javassist `CtClass.toClass()`、`toBytecode()`、`writeFile()`、以及代理 `Proxy.newProxyInstance` | `EndpointApiSample` / `EndpointApiInvocationHandler` / `Customer` | + +> 2026-08-20 代码修复详情见 [TECHNICAL-DESIGN.md §3 / §4](./TECHNICAL-DESIGN.md#3-codegraph-语义代码审查结论)。 + +## 3. 运行要求与兼容性 + +### 3.1 基础要求 + +| 依赖 | 最低版本 | 推荐版本 | 说明 | +| :--- | ---: | ---: | :--- | +| JDK(按分支) | 见 §9 | 8u3xx / 17u20 / 21u最新 | 本项目按分支分 JDK 线长期并行 | +| Maven | 3.0+ | 3.9.16 | pom.xml 中已通过 Maven Enforcer 强制要求 | +| Apache CXF | 4.x | 4.0.x(1.0.x)/ 4.1.x(2.0.x)/ 4.2.x(3.0.x / main) | 由 pom.xml dependencyManagement 锁版 | +| Javassist | 3.30.2-GA | 3.30.2-GA | 同上 | +| javassist-extension | 同本项目版本号 | 同左 | `io.github.easy4j:javassist-extension`,同行维护 | +| commons-lang3 | 3.20.0 | 3.20.0 | JDK 8 线最新最终版(四线共用,见 §10.2) | +| commons-io | 2.22.0 | 2.22.0 | JDK 8 线最新最终版(四线共用) | +| commons-beanutils | 1.11.0 | 1.11.0 | beanutils 1.x 线最新版(四线共用) | + +### 3.2 版本兼容矩阵 + +| 项目版本线 | JDK | CXF 基线 | 状态 | 维护策略 | +| :--- | :---: | :---: | :---: | :--- | +| `feature/3.0.x` | 21 | CXF 4.2.x | ✅ 活跃开发 | 新功能 + 缺陷修复 | +| `main` | 17 / 21 | CXF 4.2.x | ✅ 主发行 | 同步 3.0.x 缺陷修复;对外发行 GA | +| `feature/2.0.x` | 17 | CXF 4.1.x | 🛠️ 维护 | 只接收严重缺陷、CVE、依赖升级 | +| `feature/1.0.x` | 8 | CXF 4.0.x | 🛠️ 维护 | 只接收阻塞缺陷与 CVE;严禁使用 JDK 11+ only 的依赖版本 | + +### 3.3 依赖边界 + +- 本项目是**单模块 jar**,依赖项按 "字节码生成最小集合" 约束:javassist + javassist-extension + Apache CXF 前端(provided 语义)+ commons 三件套(JSON 绑定 / 反射 Bean 拷贝需要)。 +- 不引入 Spring、Jakarta Servlet、Jakarta Validation 等容器级依赖;下游使用方按需引入。 +- `cxf-rt-frontend-jaxws / jaxrs` 不是 provided scope 而是 compile scope(生成的字节码需要注解类型在 ClassPool 中可解析)。 ## 4. 架构与模块 +### 4.1 一眼看懂 + ```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 | - +-------------------------------------------+ +┌─────────────── 业务应用 ───────────────┐ +│ Gateway / Proxy / Dynamic Exporter │ +│ 引入依赖: io.github.easy4j:cxf-rt-javassist │ +└──────────────┬────────────────────────┘ + │ 声明: SoapBound / RestBound + ▼ +┌──────────────────────────────────────────────────────┐ +│ cxf-rt-javassist (单模块 jar) │ +│ ┌──────────────────────┐ ┌──────────────────────┐ │ +│ │ Jaxws Builder 侧 │ │ Jaxrs Builder 侧 │ │ +│ │ JaxwsEndpointApiCt… │ │ JaxrsEndpointApiCt… │ │ +│ │ definition: Soap* │ │ definition: Rest* │ │ +│ └──────────┬───────────┘ └──────────┬────────────┘ │ +│ ▼ 共享 EndpointApi 基类 ▼ │ +│ EndpointApi (implements IEndpointApi) │ +│ Utils: JaxwsEndpointApiUtils / JaxrsEndpointApiUtils │ +└───────────────────────┬──────────────────────────────┘ + │ CtClass / Class / Instance + ▼ + Apache CXF 4.x 前端运行时 + ┌─ JAX-WS Endpoint.publish ─┐ + └─ JAX-RS Server / Feature ─┘ ``` -单模块 Maven 工程(`packaging: jar`),无子模块。 +### 4.2 Builder 对称设计 -| 构件 | 职责 | -| :--- | :--- | -| `io.github.easy4j:cxf-rt-javassist` | 运行时类构建器、SOAP / REST 定义模型、工具类 | +`JaxwsEndpointApiCtClassBuilder` ↔ `JaxrsEndpointApiCtClassBuilder` 是**姊妹 Builder**,API 结构严格同构: + +| 阶段 | SOAP 侧(Jaxws) | REST 侧(Jaxrs) | +| :--- | :--- | :--- | +| 初始化 | `new JaxwsEndpointApiCtClassBuilder(fullyQualifiedClassName)` | `new JaxrsEndpointApiCtClassBuilder(fullyQualifiedClassName)` | +| 注解头 | `webService(name, tns, serviceName)` / `webServiceProvider(...)` / `addressing(...)` / `serviceMode(Mode.PAYLOAD)` | `path(root)` + `produces(MediaType...)` | +| 绑定 | `bind(String uid, String json)` / `bind(SoapBound)` | `bind(String uid, String json)` / `bind(RestBound)` | +| 字段 | `makeField(src)` / `newField(type, name, value)` / `removeField(name)` | 左侧三法完全相同 | +| 方法 | `makeMethod(src)` / `newMethod(name, SoapParam...)` / `removeMethod(...)` | `newMethod(rtClass, HttpMethodEnum, name, subPath, RestParam...)` / `removeMethod(...)` | +| 产出 | `build() → CtClass` / `toClass() → Class` / `toInstance(InvocationHandler) → Object` | 左侧三法完全相同 | -关键包: +> 代码审查约束(ADR-002):新增能力 / 修复缺陷时,两侧必须成对走查;**除非 SOAP 与 REST 在语义上天然不同**(例如 REST 有 `@PathParam` / `@DefaultValue`,SOAP 没有对应概念),否则禁止只修一侧不修另一侧。 -| 包 | 内容 | -| :--- | :--- | -| `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`(基类) | -| `org.apache.cxf.endpoint.utils` | `JaxwsEndpointApiUtils`、`JaxrsEndpointApiUtils` | +### 4.3 关键包一览(与 §1.2 场景对应) -## 5. 安装 +| 包 | 内容 | 典型场景 | +| :--- | :--- | :--- | +| `org.apache.cxf.endpoint` | `EndpointApi` 公共基类、`IEndpointApi` 接口 | 通过基类复用 `bind` / `toInstance(InvocationHandler)` | +| `org.apache.cxf.endpoint.jaxws` | `JaxwsEndpointApiCtClassBuilder`、`…ImplCtClassBuilder`、`…InterfaceCtClassBuilder` | 生成 SOAP 实现类、接口、带实现的 endpoint | +| `org.apache.cxf.endpoint.jaxws.definition` | `SoapService / SoapMethod / SoapParam / SoapResult / SoapBound` | 声明式描述 SOAP 服务 | +| `org.apache.cxf.endpoint.jaxrs` | `JaxrsEndpointApiCtClassBuilder`、`…ImplCtClassBuilder`、`…InterfaceCtClassBuilder` | 生成 JAX-RS 实现类、接口、带实现的 resource | +| `org.apache.cxf.endpoint.jaxrs.definition` | `RestBound / RestMethod / RestParam / RestProduce / HttpMethodEnum / HttpParamEnum` | 声明式描述 REST 资源 | +| `org.apache.cxf.endpoint.utils` | `JaxwsEndpointApiUtils / JaxrsEndpointApiUtils` | 负责把定义对象翻译为 Javassist `Annotation[][]` 后注入 CtMethod | -项目**尚未发布到 Maven Central**。快照 / 发布版本通过阿里云 Maven 仓库与 GitHub -Releases 分发。 +## 5. 引入依赖 -Maven: +### 5.1 Maven ```xml io.github.easy4j cxf-rt-javassist - 1.0.x.20260630-SNAPSHOT + + 3.0.x.x.20260630-SNAPSHOT ``` -Gradle: +### 5.2 Gradle -```groovy -implementation 'io.github.easy4j:cxf-rt-javassist:1.0.x.20260630-SNAPSHOT' +```kotlin +dependencies { + implementation("io.github.easy4j:cxf-rt-javassist:3.0.x.x.20260630-SNAPSHOT") +} ``` +### 5.3 仓库说明 + +项目目前**尚未发布 Maven Central**。发布前快照 / Release 通过阿里云私服仓库与 GitHub Releases 分发。如需私服地址,请联系仓库维护者。 + ## 6. 快速开始 -运行时构建 JAX-WS 端点类(改编自仓库内已提交的测试): +### 6.1 最小 JAX-WS 示例(生成 `@WebService` 类并 `toClass`) ```java import javassist.CtClass; @@ -158,23 +193,12 @@ CtClass ctClass = new JaxwsEndpointApiCtClassBuilder("org.apache.cxf.spring.boot Class clazz = ctClass.toClass(); Object endpoint = clazz.getConstructor().newInstance(); +// endpoint 现在是一个完整的 @WebService 实例,可交给 org.apache.cxf.jaxws.EndpointImpl.publish(addr) 暴露 ``` -预期结果:创建新类 `org.apache.cxf.spring.boot.FirstCaseV1`,带 `@WebService` 注解 -与 `String sayHello(String text)` 方法——可直接作为 CXF JAX-WS 端点实现使用。 - -## 7. 配置 - -本库没有配置文件或属性前缀。一切由构建器链与定义对象驱动: - -| 构建器 | 关键方法 | 说明 | -| :--- | :--- | :--- | -| `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 端点生成 | -| `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 资源生成 | - -## 8. 核心用法 / API +**预期结果**:`sayHello(String text)` 方法存在于生成的类;`@WebService(name="get", targetNamespace="http://ws.cxf.com", serviceName="getxx")` 注解可通过反射读取;`uid` 字段在默认构造器里被初始化为随机 UUID。 -### 8.1 JAX-RS 资源生成 +### 6.2 最小 JAX-RS 示例(生成 `@Path("/getxx")` 资源类) ```java import javassist.CtClass; @@ -188,50 +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(); + +// 验证:反射读注解 +Class clazz = ctClass.toClass(); +Path classPath = clazz.getAnnotation(Path.class); +assertEquals("getxx", classPath.value()); // 通过 ``` -### 8.2 分发到 `InvocationHandler` +> ⚠️ 2026-08-20 前的旧代码存在 Bug#1:上面 `HttpParamEnum.PATH` 会被静默退化成 QUERY(详见技术方案 §4.1)。当前主分支、main、2.0.x、1.0.x 均已修复。 + +### 6.3 调度到自定义 InvocationHandler(动态代理网关场景) ```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(); // 你的处理器 -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); // 方法调用被分发给该处理器 +// 你的业务调度逻辑:例如转发到 Feign / Dubbo / 本地服务 +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 都已修复:from=PATH 生效 + def=world 生效 + .toInstance(handler); + +// resource.getClass().getMethod("sayHello", String.class).invoke(resource, "Alice") → "Hello, Alice" ``` -注意:`toClass()` / `toBytecode()` / `writeFile()` 之后 Javassist 会冻结 `CtClass`; -继续修改前需调用 `defrost()`(参见已提交的测试)。 +> 💡 `toInstance(handler)` 是唯一能同时得到"带注解的类 + 方法调用可被拦截"的入口,推荐给通用网关场景。`toClass()` 得到的类是普通 Javassist 字节码,方法体是空返回默认值或 0 / null(由 Builder 默认模板决定),需要业务逻辑请用 `ImplCtClassBuilder` 或 `toInstance`。 + +## 7. 核心用法 / API -## 9. 测试与构建 +### 7.1 定义对象速查 + +**HttpMethodEnum**(REST): +```java +public enum HttpMethodEnum { GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS } +// 对应 jakarta.ws.rs.@GET / @POST / @PUT / ... +``` + +**HttpParamEnum**(REST): +```java +public enum HttpParamEnum { + PATH, // @PathParam + QUERY, // @QueryParam(字段默认值;Bug#1/Bug#2 修前就是这个) + HEADER, // @HeaderParam + COOKIE, // @CookieParam + FORM, // @FormParam + MATRIX, // @MatrixParam + BEAN // @BeanParam +} +// getAnnotationType() → 对应 jakarta.ws.rs 的注解 FQCN;JaxrsEndpointApiUtils.annotParams 读它 +``` + +**RestParam 构造器(4 个,2026-08-20 已修复字段赋值完整性)**: + +| 构造器签名 | 字段行为 | +| :--- | :--- | +| `RestParam(Class type, String name)` | `type/name` 赋值;`from=QUERY`(默认);`def=null` | +| `RestParam(Class type, String name, HttpParamEnum from)` | 三项全部赋值 ✅(Bug#1 修完后) | +| `RestParam(Class type, String name, String def)` | 三项;`from=QUERY`(默认) | +| `RestParam(Class type, String name, HttpParamEnum from, String def)` | 四项全部赋值 ✅(Bug#2 修完后) | + +### 7.2 Builder 常用方法 + +`JaxrsEndpointApiCtClassBuilder`: + +| 方法 | 返回值 | 说明 | +| :--- | :--- | :--- | +| `path(String rootPath)` | this | 设置类级 `@Path(rootPath)` | +| `produces(String... mediaTypes)` | this | 设置 `@Produces({...})` | +| `newField(Class type, String name, Object value)` | this | 新建字段并初始化 | +| `removeField(String name)` | this | 移除已存在字段(用于默认字段) | +| `newMethod(Class rtClass, HttpMethodEnum method, String name, String subPath, RestParam... params)` | this | 新建方法:返回类型 / HTTP 方法 / 方法名 / `@Path(subPath)` / 参数 | +| `removeMethod(String name, Class... params)` | this | 删除已存在方法 | +| `build()` | `CtClass` | 构建完成(未 toClass,可继续 defrost 修改) | +| `toClass()` | `Class` | 调用 `CtClass.toClass()`,加载到当前线程 ClassLoader | +| `toInstance(InvocationHandler h)` | `Object` | 先 `toClass()`,再用 `Proxy.newProxyInstance(IEndpointApi, h)` 包装,最后把代理 set 到生成类的 `invocationHandler` 字段(EndpointApi 基类约定) | + +`JaxwsEndpointApiCtClassBuilder`:对称等价 API(§4.2)。 + +### 7.3 `@DefaultValue` 修复后示例(Bug#3) + +```java +// 现在 @DefaultValue("1") 会真的写进字节码: +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(); + +// 反射:Method.getParameterAnnotations()[1] 应得到 [@QueryParam("page"), @DefaultValue("1")] +``` + +## 8. 构建与测试 + +### 8.1 常用命令 ```bash -./mvnw clean verify +./mvnw clean verify # 编译 + 单元测试 + JaCoCo check(90% 指令覆盖率) +./mvnw test -B -q # 安静跑单测 +./mvnw clean compile -DskipTests # 只编译 +./mvnw -Prelease -Dgpg.skip=true package # 带源码 / javadoc 包 ``` -- 构建配置了 JaCoCo Maven 插件(报告 + 绑定在 `verify` 阶段的 `check` 目标, - 行覆盖率规则为 90%;`haltOnFailure=false`)。 -- 已提交测试:`JaxwsApiCtClassBuilder_Test`、`JaxrsApiCtClassBuilder_Test` - (类构建、实例分发、字节码导出)。 -- 本 worktree 的 `.github/` 下无 CI 工作流文件。 +### 8.2 测试矩阵 + +| 类型 | 工具 / 命令 | 说明 | +| :--- | :--- | :--- | +| 单元测试 | JUnit 4 + Maven Surefire | 202 / 202 / 202 / 224(四线) | +| 字节码断言 | JUnit + `ctClass.toClass().getAnnotation(...)` 反射读 | 覆盖 `@Path/@QueryParam/@DefaultValue/@WebService/@WebParam` | +| 覆盖率报告 | JaCoCo `prepare-agent` + `report` + `check` | **必须** `@{argLine}` 前缀(ADR-004);否则 JaCoCo 挂不上 | +| 多 JDK 验证 | 四线各自 worktree 单独跑 `mvn test` | JDK 8 / 17 / 21 分别执行 | -## 10. 版本与分支 +### 8.3 发布门禁 -| 分支 | JDK | 版本 | 说明 | +- Maven Enforcer(版本基线 + banned-deps) +- JaCoCo 覆盖率 90% 指令(`BUNDLE` 级别,`haltOnFailure=false` 但 CI 会读报告 fail) +- `dependency:tree` 无 commons 三件套跨版本混用 +- 对 `RestParamTest` 所有 4 个字段的断言都显式写了(ADR-005) + +## 9. 版本线与兼容策略 + +| 分支(git worktree 位置) | Git 分支 | JDK | `` | 状态 | +| :--- | :--- | :---: | :---: | :---: | +| `cxf-rt-javassist/`(主工作目录) | `feature/3.0.x` | 21 | 21 | 活跃开发 | +| `.worktrees/cxf-rt-javassist-main/` | `main` | 17 / 21 | 21 | 主发行 | +| `.worktrees/cxf-rt-javassist-2.0.x/` | `feature/2.0.x` | 17 | 17 | 维护 | +| `.worktrees/cxf-rt-javassist-1.0.x/` | `feature/1.0.x` | 8 | 1.8 | 维护 | + +- **只允许在 JDK/框架版本上的必要差异**:共享业务逻辑、域对象、测试、文档保持一致。 +- 任何通用 bug(如本次 3 个)**必须一次修复、四线同步**。 + +## 10. FAQ + +### 10.1 为什么 1.0.x 的 Surefire 不加 `--add-opens`? + +JDK 8 **没有** Java Platform Module System(JPMS),所以**无法识别 `--add-opens` 选项**——加了会直接导致 `Unrecognized option: --add-opens`,Maven Surefire 整个阶段 FAILED。对 JDK ≥ 17 的三条线则必须加两个 `--add-opens`:`java.base/java.lang=ALL-UNNAMED` 与 `java.base/java.lang.reflect=ALL-UNNAMED`,否则 Javassist 写 `jdk.internal.reflect.ConstructorAccessor` 等类时会被 JPMS 强封装拦死,运行时抛出 `IllegalAccessError`。详见技术方案 §4.4 与 ADR-004。 + +### 10.2 为什么不升级 commons-lang3 到 3.21.0 / commons-io 到 2.23.0? + +`commons-lang3 3.21.0` / `commons-io 2.23.0` 已经要求最低 **JDK 11**,与 `feature/1.0.x` 的 JDK 8 要求不兼容。ADR-003 规定:**在 1.0.x 分支(JDK 8)宣布 EOL 前,四条线共用同一版本号**,因此统一停留在 JDK 8 线的最终版:`3.20.0 / 2.22.0 / 1.11.0`。详见技术方案 §5.1。 + +### 10.3 `toClass()` 之后还能不能改? + +不行,Javassist 调用 `toClass() / toBytecode() / writeFile()` 之后会把 `CtClass` **冻结(frozen)**,后续 set* / addMethod 会抛 `RuntimeException: cannot modify frozen class`。解决方式: +1. **方案 A(推荐)**:先完成所有 Builder 调用再 build → toClass。 +2. **方案 B**:`ctClass.defrost();` 解冻后再改,改完再 build → toClass(测试中广泛使用,见 `JaxrsEndpointApiCtClassBuilderTest` 的 `removeField` 用例)。 + +### 10.4 Bug 修复了什么?怎么影响到我? + +2026-08-20 四线同步的 3 个修复: + +| Bug | 修前行为 | 修后行为 | 对下游影响 | | :--- | :--- | :--- | :--- | -| `feature/1.0.x` | 8 | `1.0.x.*` | 当前分支,JDK 8 基线,维护中 | -| `feature/2.0.x` | 17 | `2.0.x.*` | JDK 17 版本线 | -| `feature/3.0.x` | 21 | `3.0.x.*` | JDK 21 版本线 | +| Bug#1(三参构造器) | `from` 显式传参被丢,永久是 QUERY | 用户传的 `HttpParamEnum` 正确生效 | 如果你的代码**手工把参数当 QUERY 读**(绕开 Bug),修完后可能需要同时修正 URL 写法。CHANGELOG 明确列出为**行为修复(Breaking Change 类的兼容性说明)** | +| Bug#2(四参构造器) | `from` 丢 + 重复写 name | 四字段都正确 | 同 Bug#1,且 `@DefaultValue` 参数的 from 也正确生效 | +| Bug#3(annotParams) | `@DefaultValue` 对象被创建却**不写进数组**,重复写两次 paramAnnot | `[paramAnnot, defAnnot]` 正确写回 | 如果你依赖 "缺省值全 null / 0" 的旧行为需要做兼容性处理 | + +详细根因、CodeGraph 符号执行证据、修复代码见 [TECHNICAL-DESIGN.md](./TECHNICAL-DESIGN.md)。 + +## 11. 贡献与许可证 + +- 贡献前请在对应 worktree 执行 `mvn clean verify`,确认四线测试都绿;PR 请附 `mvn test` 输出与 JaCoCo 报告截图。 +- 跨分支通用修复请按 ADR-001 同步到四线后再提 PR。 +- 安全问题通过 GitHub Security Advisory 私密报告,不要在公共 Issue 泄露利用细节。 + +本项目采用 [Apache License 2.0](./LICENSE) 许可证。 -维护策略:`1.0.x` 版本线接收针对 JDK 8 基线的缺陷修复与兼容性更新;面向新 JDK 的 -新特性在 `2.0.x` / `3.0.x` 版本线开发。发布物通过阿里云 Maven 仓库与 GitHub -Releases 分发;项目尚未发布到 Maven Central。 +--- -## 11. 贡献与许可 +
-欢迎通过 GitHub Issue 或 Pull Request 参与贡献。 +[返回顶部](#readme-top) · [技术方案(中文)](./TECHNICAL-DESIGN.md) · [问题反馈](https://github.com/easy-4-java/cxf-rt-javassist/issues) -本项目基于 [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/pom.xml b/pom.xml index d6ee18b..b1eb606 100644 --- a/pom.xml +++ b/pom.xml @@ -368,7 +368,7 @@ false false - -Xmx1024m + @{argLine} -Xmx1024m -Dfile.encoding=UTF-8 ${basedir}/target/test-classes 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 3be3e27..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 @@ -29,7 +29,7 @@ * while leaving the rest at their default values.

* * @param the runtime type of the parameter. - * @author [@Loong Wan](https://github.com/loong10k) + * @author Loong Wan * @since 3.0.0 * @see HttpParamEnum * @see jakarta.ws.rs.DefaultValue @@ -104,6 +104,7 @@ public RestParam(Class type, String name) { public RestParam(Class type, String name, HttpParamEnum from) { this.type = type; this.name = name; + this.from = from; } /** @@ -119,7 +120,7 @@ public RestParam(Class type, String name, HttpParamEnum from) { 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; } 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 b19a8c1..00db9bc 100644 --- a/src/main/java/org/apache/cxf/endpoint/utils/JaxrsEndpointApiUtils.java +++ b/src/main/java/org/apache/cxf/endpoint/utils/JaxrsEndpointApiUtils.java @@ -66,7 +66,7 @@ * Javassist classes, interfaces, constructors, methods, and * annotations. * - * @author [@Loong Wan](https://github.com/loong10k) + * @author Loong Wan * @since 3.0.0 * @see org.apache.cxf.endpoint.jaxrs.JaxrsEndpointApiCtClassBuilder */ @@ -459,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/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java index 3fabd7a..a6d100b 100644 --- a/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java +++ b/src/test/java/org/apache/cxf/endpoint/jaxrs/definition/RestParamTest.java @@ -20,7 +20,7 @@ public void shouldCreateParamWithExplicitFrom() { RestParam param = new RestParam<>(String.class, "id", HttpParamEnum.PATH); assertEquals(String.class, param.getType()); assertEquals("id", param.getName()); - assertEquals(HttpParamEnum.QUERY, param.getFrom()); // known bug: from not persisted + assertEquals(HttpParamEnum.PATH, param.getFrom()); } @Test @@ -36,6 +36,7 @@ public void shouldCreateParamWithFromAndDefault() { RestParam param = new RestParam<>(String.class, "name", HttpParamEnum.PATH, "defaultVal"); assertEquals(String.class, param.getType()); assertEquals("name", param.getName()); + assertEquals(HttpParamEnum.PATH, param.getFrom()); assertEquals("defaultVal", param.getDef()); } From dd13544ca6ac6413bff160e20eeb595458593510 Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Thu, 20 Aug 2026 14:55:14 +0800 Subject: [PATCH 55/56] build(pom): upgrade commons-* dependencies to actual LATEST versions --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b1eb606..489f792 100644 --- a/pom.xml +++ b/pom.xml @@ -48,8 +48,8 @@ -Xdoclint:none 1.11.0 - 2.20.0 - 3.18.0 + 2.22.0 + 3.20.0 4.0.5 1.0.x.20260630-SNAPSHOT 3.30.2-GA From b100f90abf0d895641f8f724e13965f5deb666de Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Thu, 20 Aug 2026 14:55:18 +0800 Subject: [PATCH 56/56] build(pom): upgrade commons-* dependencies to actual LATEST versions --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c6e84a9..8bf0dc7 100644 --- a/pom.xml +++ b/pom.xml @@ -48,8 +48,8 @@ -Xdoclint:none 1.11.0 - 2.20.0 - 3.18.0 + 2.22.0 + 3.20.0 4.0.5 2.0.x.20260630-SNAPSHOT 3.30.2-GA