diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fb3dd3d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ +# Normalize line endings to LF. +* text eol=lf + +# Ensure that line endings for DOS batch files are not modified. +*.bat -text + +# Ensure the following are treated as binary. +*.gif binary +*.jar binary +*.jpeg binary +*.jpg binary +*.png binary +*.vsd binary +*.class binary +*.clazz binary diff --git a/.github/actions/await-http-resource/action.yml b/.github/actions/await-http-resource/action.yml new file mode 100644 index 0000000..ba177fb --- /dev/null +++ b/.github/actions/await-http-resource/action.yml @@ -0,0 +1,20 @@ +name: Await HTTP Resource +description: 'Waits for an HTTP resource to be available (a HEAD request succeeds)' +inputs: + url: + description: 'URL of the resource to await' + required: true +runs: + using: composite + steps: + - name: Await HTTP resource + shell: bash + run: | + url=${{ inputs.url }} + echo "Waiting for $url" + until curl --fail --head --silent ${{ inputs.url }} > /dev/null + do + echo "." + sleep 60 + done + echo "$url is available" diff --git a/.github/actions/building/action.yml b/.github/actions/building/action.yml new file mode 100644 index 0000000..d9550c5 --- /dev/null +++ b/.github/actions/building/action.yml @@ -0,0 +1,101 @@ +name: 'Build' +description: 'Builds the project, optionally publishing it to a local deployment repository' +inputs: + java-distribution: + description: 'Java distribution to use' + required: false + default: 'liberica' + java-early-access: + description: 'Whether the Java version is in early access' + required: false + default: 'false' + java-toolchain: + description: 'Whether a Java toolchain should be used' + required: false + default: 'false' + java-version: + description: 'Java version to compile and test with' + required: false + default: '24' + publish: + description: 'Whether to publish artifacts ready for deployment to Artifactory' + required: false + default: 'false' + token-password: + description: 'Password for authentication with central.sonatype.com' + required: false + token-username: + description: 'Username for authentication with central.sonatype.com' + required: false + signing-keyId: + description: 'signing keyId' + required: false + signing-password: + description: 'signing password' + required: false + gpg-passphrase: + description: 'Environment variable name for the GPG private key passphrase. Default is $GPG_PASSPHRASE.' + required: false + gpg-private-key: + description: 'GPG private key to import. Default is empty string.' + required: false + server-id: + description: 'ID of the distributionManagement repository in the pom.xml file. Default is `github`' + required: false + default: 'github' + server-username: + description: 'Environment variable name for the username for authentication to the Apache Maven repository. Default is $GITHUB_ACTOR' + required: false + default: 'GITHUB_ACTOR' + server-password: + description: 'Environment variable name for password or token for authentication to the Apache Maven repository. Default is $GITHUB_TOKEN' + required: false + default: 'GITHUB_TOKEN' +outputs: + build-scan-url: + description: 'URL, if any, of the build scan produced by the build' + value: ${{ (inputs.publish == 'true' && steps.publish.outputs.build-scan-url) || steps.build.outputs.build-scan-url }} + version: + description: 'Version that was built' + value: ${{ steps.read-version.outputs.version }} +runs: + using: composite + steps: + - name: Set Up Java + uses: actions/setup-java@v4 + with: + distribution: ${{ inputs.java-early-access == 'true' && 'temurin' || (inputs.java-distribution || 'liberica') }} + java-version: | + ${{ inputs.java-early-access == 'true' && format('{0}-ea', inputs.java-version) || inputs.java-version }} + ${{ inputs.java-toolchain == 'true' && '17' || '' }} + gpg-passphrase: ${{ inputs.gpg-passphrase }} + gpg-private-key: ${{ inputs.gpg-private-key }} + server-id: ${{ inputs.server-id }} + server-username: ${{ inputs.server-username }} + server-password: ${{ inputs.server-password }} + + - name: Print JDK Version + shell: bash + run: java -version + + - name: Set Up Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Build + id: build + if: ${{ inputs.publish == 'false' }} + shell: bash + run: ./gradlew -DCI=true --no-daemon --no-parallel check --scan + + - name: Publish + id: publish + if: ${{ inputs.publish == 'true' }} + shell: bash + run: ./gradlew --no-daemon --no-parallel publishMavenPublicationToSonatypeRepository closeAndReleaseStagingRepositories + -PrepoUsername=${{ inputs.token-username }} -PrepoPassword=${{ inputs.token-password }} + -Psigning.keyId=${{ inputs.signing-password }} -Psigning.password=${{ inputs.signing-password }} + + - name: Read Version From gradle.properties + id: read-version + uses: ./.github/actions/read-version + diff --git a/.github/actions/read-version/action.yml b/.github/actions/read-version/action.yml new file mode 100644 index 0000000..b5ae248 --- /dev/null +++ b/.github/actions/read-version/action.yml @@ -0,0 +1,24 @@ +name: read-version +author: today +description: Read project version + +outputs: + version: + description: 'Version of this project' + value: ${{ steps.read-version.outputs.version }} + snapshot: + description: 'Version is snapshot' + value: ${{ steps.read-version.outputs.snapshot }} +runs: + using: composite + steps: + - name: Read Version From gradle.properties + id: read-version + shell: bash + run: | + version=$(sed -n 's/version=\(.*\)/\1/p' gradle.properties) + snapshot=$(echo $version | grep -q 'SNAPSHOT' && echo 'true' || echo 'false') + echo "Version is $version" + echo "snapshot = $snapshot" + echo "version=$version" >> $GITHUB_OUTPUT + echo "snapshot=$snapshot" >> $GITHUB_OUTPUT \ No newline at end of file diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..56c0b06 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,48 @@ +name: CI + +on: + push: + branches: + - master + + pull_request: + branches: + - master + - 'dev/**' + - 'fix/**' + - 'feat/**' + - 'feature/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + check: + if: github.repository_owner == 'today-tech' + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + strategy: + matrix: + os: [ ubuntu-latest, macOS-latest ] #, windows-latest + java: + - version: 21 + toolchain: true + - version: 24 + toolchain: true + fail-fast: false + max-parallel: 4 + name: JDK ${{ matrix.java.version }} on ${{ matrix.os }} + + steps: + - name: Checkout project sources + uses: actions/checkout@v4.1.2 + + - name: Build + id: build + uses: ./.github/actions/building + with: + java-early-access: ${{ matrix.java.early-access || 'false' }} + java-distribution: ${{ matrix.java.distribution }} + java-toolchain: ${{ matrix.java.toolchain }} + java-version: ${{ matrix.java.version }} diff --git a/.run/UserServiceApplication.run.xml b/.run/UserServiceApplication.run.xml deleted file mode 100644 index 9fafb9b..0000000 --- a/.run/UserServiceApplication.run.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - \ No newline at end of file diff --git a/.run/publishToMavenLocal.run.xml b/.run/publishToMavenLocal.run.xml new file mode 100644 index 0000000..248f0ff --- /dev/null +++ b/.run/publishToMavenLocal.run.xml @@ -0,0 +1,27 @@ + + + + + + + true + true + false + false + false + false + false + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE index c59803d..261eeb9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,675 +1,201 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright © TODAY & 2017 - 2019 All Rights Reserved. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright © TODAY & 2021 All Rights Reserved. - - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/README.md b/README.md index f1dfefd..b8932ed 100644 --- a/README.md +++ b/README.md @@ -3,5 +3,5 @@ Study to create a microservices framework like Spring Cloud ![Java17](https://img.shields.io/badge/JDK-17+-success.svg) -[![GPLv3](https://img.shields.io/badge/License-GPLv3-blue.svg)](./LICENSE) +[![Apache License 2.0](https://img.shields.io/github/license/today-tech/today-cloud?color=blue)](./LICENSE) [![Author](https://img.shields.io/badge/Author-TODAY-blue.svg)](https://github.com/TAKETODAY) diff --git a/build.gradle b/build.gradle index 208e741..9e48866 100644 --- a/build.gradle +++ b/build.gradle @@ -1,39 +1,38 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - plugins { id 'de.undercouch.download' version '5.4.0' + id "io.github.gradle-nexus.publish-plugin" version "2.0.0" } ext { + isSnapshot = version.endsWith('SNAPSHOT') + isReleaseVersion = !isSnapshot + configProjects = [project(":today-cloud-bom"), project(":today-cloud-dependencies")] javaProjects = subprojects - configProjects samplesProjects = subprojects.findAll { it.path.startsWith(":today-cloud-samples") } - moduleProjects = javaProjects - samplesProjects + moduleProjects = javaProjects - samplesProjects - [ + project(":today-cloud-build"), + ] } configure(allprojects) { - group = "cn.taketoday.cloud" + group = "cn.taketoday" + + if (it.hasProperty("skipDocs")) { + it.afterEvaluate { + it.getTasks().matching(task -> { + return JavaBasePlugin.DOCUMENTATION_GROUP == task.getGroup() + || "distribution" == task.getGroup() + }).forEach(task -> task.setEnabled(false)) + } + } repositories { mavenLocal() mavenCentral() - maven { url "https://repo.spring.io/milestone" } - if (version.endsWith('-SNAPSHOT')) { + if (isSnapshot) { + maven { url = "https://central.sonatype.com/repository/maven-snapshots/" } + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } } @@ -64,6 +63,8 @@ configure(javaProjects) { testImplementation("org.mockito:mockito-core") testImplementation("org.mockito:mockito-junit-jupiter") testImplementation("org.assertj:assertj-core") + testImplementation("cn.taketoday:infra-test") + testImplementation("cn.taketoday:infra-test-support") testImplementation 'org.projectlombok:lombok' testAnnotationProcessor("org.projectlombok:lombok") @@ -71,6 +72,16 @@ configure(javaProjects) { testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") testRuntimeOnly("org.junit.platform:junit-platform-launcher") testRuntimeOnly("org.junit.platform:junit-platform-suite-engine") + + // 去除 测试警告 + testRuntimeOnly("com.google.code.findbugs:jsr305") + testRuntimeOnly("com.google.code.findbugs:findbugs") + testRuntimeOnly('org.jboss.logging:jboss-logging:3.5.3.Final') + + // JSR-305 only used for non-required meta-annotations + compileOnly("com.google.code.findbugs:jsr305") + compileOnly("com.google.code.findbugs:findbugs") + compileOnly('org.jboss.logging:jboss-logging:3.5.3.Final') } } @@ -82,3 +93,16 @@ configure(rootProject) { description = "TODAY Cloud" } +nexusPublishing { + String repoUsername = project.properties["repoUsername"] ?: System.getenv("SNAPSHOTS_USERNAME") + String repoPassword = project.properties["repoPassword"] ?: System.getenv("SNAPSHOTS_TOKEN") + + repositories { + sonatype { + username.set(repoUsername) + password.set(repoPassword) + nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/")) + snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/")) + } + } +} diff --git a/buildSrc/README.md b/buildSrc/README.md deleted file mode 100644 index 48a0f75..0000000 --- a/buildSrc/README.md +++ /dev/null @@ -1,58 +0,0 @@ -# Infra Cloud Build - -This folder contains the custom plugins and conventions for the Infra build. -They are declared in the `build.gradle` file in this folder. - -## Build Conventions - -The `infra.cloud.conventions` plugin applies all conventions to the Framework build: - -* Configuring the Java compiler, see `JavaConventions` -* Configuring testing in the build with `TestConventions` - - -## Build Plugins - -### Optional dependencies - -The `infra.cloud.optional-dependencies` plugin creates a new `optional` -Gradle configuration - it adds the dependencies to the project's compile and runtime classpath -but doesn't affect the classpath of dependent projects. -This plugin does not provide a `provided` configuration, as the native `compileOnly` and `testCompileOnly` -configurations are preferred. - -### RuntimeHints Java Agent - -The `today-core-test` project module contributes the `RuntimeHintsAgent` Java agent. - -The `RuntimeHintsAgentPlugin` Gradle plugin creates a dedicated `"runtimeHintsTest"` test task for each project. -This task will detect and execute [tests tagged](https://junit.org/junit5/docs/current/user-guide/#running-tests-build-gradle) -with the `"RuntimeHintsTests"` [JUnit tag](https://junit.org/junit5/docs/current/user-guide/#running-tests-tags). -In the Infra test suite, those are usually annotated with the `@EnabledIfRuntimeHintsAgent` annotation. - -By default, the agent will instrument all classes located in the `"cn.taketoday"` package, as they are loaded. -The `RuntimeHintsAgentExtension` allows to customize this using a DSL: - -```groovy -// this applies the `RuntimeHintsAgentPlugin` to the project -plugins { - id 'infra.cloud.runtimehints-agent' -} - -// You can configure the agent to include and exclude packages from the instrumentation process. -runtimeHintsAgent { - includedPackages = ["cn.taketoday", "io.spring"] - excludedPackages = ["org.example"] -} - -dependencies { - // to use the test infrastructure, the project should also depend on the "today-core-test" module - testImplementation(project(":today-core-test")) -} -``` - -With this configuration, `./gradlew runtimeHintsTest` will run all tests instrumented by this java agent. -The global `./gradlew check` task depends on `runtimeHintsTest`. - -NOTE: the "today-core-test" module doesn't shade "today-core" by design, so the agent should never instrument -code that doesn't have "today-core" on its classpath. \ No newline at end of file diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index ddb17f0..7980a3c 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ plugins { @@ -29,10 +28,9 @@ repositories { dependencies { implementation(platform("cn.taketoday:infra-bom:$infraVersion")) - implementation "cn.taketoday:today-core" + implementation "cn.taketoday:infra-core" implementation "cn.taketoday:infra-gradle-plugin" implementation "org.gradle:test-retry-gradle-plugin:1.4.1" - implementation "org.apache.maven:maven-artifact:3.6.3" } gradlePlugin { diff --git a/buildSrc/gradle.properties b/buildSrc/gradle.properties index a49e315..c36331c 100644 --- a/buildSrc/gradle.properties +++ b/buildSrc/gradle.properties @@ -1,3 +1,3 @@ org.gradle.caching=true -infraVersion=5.0-Draft.3-SNAPSHOT +infraVersion=5.0-Draft.6-SNAPSHOT diff --git a/buildSrc/src/main/java/infra/cloud/ConventionsPlugin.java b/buildSrc/src/main/java/infra/cloud/ConventionsPlugin.java index 3610d1e..74e1952 100644 --- a/buildSrc/src/main/java/infra/cloud/ConventionsPlugin.java +++ b/buildSrc/src/main/java/infra/cloud/ConventionsPlugin.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.cloud; diff --git a/buildSrc/src/main/java/infra/cloud/JavaConventions.java b/buildSrc/src/main/java/infra/cloud/JavaConventions.java index 65de80f..ce55a00 100644 --- a/buildSrc/src/main/java/infra/cloud/JavaConventions.java +++ b/buildSrc/src/main/java/infra/cloud/JavaConventions.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.cloud; @@ -50,6 +49,17 @@ public class JavaConventions { private static final List TEST_COMPILER_ARGS; + /** + * The Java version we should use as the JVM baseline for building the project + */ + private static final JavaLanguageVersion DEFAULT_LANGUAGE_VERSION = JavaLanguageVersion.of(25); + + /** + * The Java version we should use as the baseline for the compiled bytecode + * (the "-release" compiler argument). + */ + private static final JavaLanguageVersion DEFAULT_RELEASE_VERSION = JavaLanguageVersion.of(17); + static { List commonCompilerArgs = Arrays.asList( /*"-Xlint:serial",*/ "-Xlint:cast", "-Xlint:classfile",/* "-Xlint:dep-ann",*/ @@ -71,33 +81,65 @@ public class JavaConventions { public void apply(Project project) { project.getPlugins().withType(JavaBasePlugin.class, javaPlugin -> { + applyToolchainConventions(project); applyJavaCompileConventions(project); configureDependencyManagement(project); }); } /** - * Applies the common Java compiler options for main sources, test fixture sources, and + * Configure the Toolchain support for the project. + * + * @param project the current project + */ + private static void applyToolchainConventions(Project project) { + project.getExtensions().getByType(JavaPluginExtension.class).toolchain(toolchain -> { + toolchain.getLanguageVersion().set(DEFAULT_LANGUAGE_VERSION); + }); + } + + /** + * Apply the common Java compiler options for main sources, test fixture sources, and * test sources. * * @param project the current project */ private void applyJavaCompileConventions(Project project) { - project.getExtensions().getByType(JavaPluginExtension.class) - .getToolchain().getLanguageVersion().set(JavaLanguageVersion.of(17)); - project.getTasks().withType(JavaCompile.class) - .matching(compileTask -> compileTask.getName().equals(JavaPlugin.COMPILE_JAVA_TASK_NAME)) - .forEach(compileTask -> { - compileTask.getOptions().setCompilerArgs(COMPILER_ARGS); - compileTask.getOptions().setEncoding("UTF-8"); - }); - project.getTasks().withType(JavaCompile.class) - .matching(compileTask -> compileTask.getName().equals(JavaPlugin.COMPILE_TEST_JAVA_TASK_NAME) - || compileTask.getName().equals("compileTestFixturesJava")) - .forEach(compileTask -> { - compileTask.getOptions().setCompilerArgs(TEST_COMPILER_ARGS); - compileTask.getOptions().setEncoding("UTF-8"); - }); + project.afterEvaluate(p -> { + p.getTasks().withType(JavaCompile.class) + .matching(compileTask -> compileTask.getName().startsWith(JavaPlugin.COMPILE_JAVA_TASK_NAME)) + .forEach(compileTask -> { + compileTask.getOptions().setCompilerArgs(COMPILER_ARGS); + compileTask.getOptions().setEncoding("UTF-8"); + setJavaRelease(compileTask); + }); + p.getTasks().withType(JavaCompile.class) + .matching(compileTask -> compileTask.getName().startsWith(JavaPlugin.COMPILE_TEST_JAVA_TASK_NAME) + || compileTask.getName().equals("compileTestFixturesJava")) + .forEach(compileTask -> { + compileTask.getOptions().setCompilerArgs(TEST_COMPILER_ARGS); + compileTask.getOptions().setEncoding("UTF-8"); + setJavaRelease(compileTask); + }); + + }); + } + + /** + * We should pick the {@link #DEFAULT_RELEASE_VERSION} for all compiled classes, + * unless the current task is compiling multi-release JAR code with a higher version. + */ + private void setJavaRelease(JavaCompile task) { + int defaultVersion = DEFAULT_RELEASE_VERSION.asInt(); + int releaseVersion = defaultVersion; + int compilerVersion = task.getJavaCompiler().get().getMetadata().getLanguageVersion().asInt(); + for (int version = defaultVersion; version <= compilerVersion; version++) { + if (task.getName().contains("Java" + version)) { + releaseVersion = version; + break; + } + } + task.getOptions().getRelease().set(releaseVersion); } private void configureDependencyManagement(Project project) { diff --git a/buildSrc/src/main/java/infra/cloud/TestConventions.java b/buildSrc/src/main/java/infra/cloud/TestConventions.java index 117b6e9..2fdac73 100644 --- a/buildSrc/src/main/java/infra/cloud/TestConventions.java +++ b/buildSrc/src/main/java/infra/cloud/TestConventions.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.cloud; @@ -57,10 +56,7 @@ private void configureTests(Project project, Test test) { test.include("**/*Tests.class", "**/*Test.class"); test.setSystemProperties(Map.of( "java.awt.headless", "true", - "io.netty.leakDetection.level", "paranoid", - "io.netty5.leakDetectionLevel", "paranoid", - "io.netty5.leakDetection.targetRecords", "32", - "io.netty5.buffer.lifecycleTracingEnabled", "true" + "io.netty.leakDetection.level", "paranoid" )); if (project.hasProperty("testGroups")) { test.systemProperty("testGroups", project.getProperties().get("testGroups")); diff --git a/buildSrc/src/main/java/infra/cloud/optional/OptionalDependenciesPlugin.java b/buildSrc/src/main/java/infra/cloud/optional/OptionalDependenciesPlugin.java index 6574724..3d96014 100644 --- a/buildSrc/src/main/java/infra/cloud/optional/OptionalDependenciesPlugin.java +++ b/buildSrc/src/main/java/infra/cloud/optional/OptionalDependenciesPlugin.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.cloud.optional; diff --git a/gradle.properties b/gradle.properties index 3521bff..4d42727 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,19 +1,3 @@ -# -# Copyright 2021 - 2024 the original author or authors. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see [http://www.gnu.org/licenses/] -# version=1.0.0-Draft.1-SNAPSHOT diff --git a/gradle/cloud-module.gradle b/gradle/cloud-module.gradle index 1e35e96..2f5bfed 100644 --- a/gradle/cloud-module.gradle +++ b/gradle/cloud-module.gradle @@ -1,22 +1,5 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - //file:noinspection ConfigurationAvoidance -import java.time.LocalDateTime +import java.time.OffsetDateTime apply plugin: 'java-library' apply plugin: 'infra.cloud.conventions' @@ -30,12 +13,19 @@ String[] javadocLinks = [ jar { manifest.attributes["Implementation-Title"] = project.name manifest.attributes["Implementation-Version"] = project.version - manifest.attributes["Implementation-Build"] = LocalDateTime.now() + manifest.attributes["Implementation-Build"] = OffsetDateTime.now() manifest.attributes["Implementation-Vendor"] = 'TODAY Tech' manifest.attributes["Implementation-Vendor-Id"] = 'cn.taketoday' manifest.attributes["Automatic-Module-Name"] = project.name.replace('-', '.') // for Jigsaw manifest.attributes["Created-By"] = "${System.getProperty("java.version")} (${System.getProperty("java.specification.vendor")})" + + from("${rootDir}") { + include "LICENSE" + include "NOTICE" + into "META-INF" + expand(copyright: new Date().format("yyyy"), version: project.version) + } } normalization { @@ -87,6 +77,8 @@ publishing { } } +apply from: "$rootDir/gradle/signing.gradle" + // Disable publication of test fixture artifacts. if (configurations.findByName("testFixturesApiElements") != null) { components.java.withVariantsFromConfiguration(configurations.testFixturesApiElements) { skip() } diff --git a/gradle/publications.gradle b/gradle/publications.gradle index b3c2b78..b7f8cb1 100644 --- a/gradle/publications.gradle +++ b/gradle/publications.gradle @@ -1,67 +1,47 @@ apply plugin: "signing" apply plugin: "maven-publish" -String repoUsername = System.getProperty("repoUsername") -String repoPassword = System.getProperty("repoPassword") - -publishing { - publications { - mavenJava(MavenPublication) { - pom { - afterEvaluate { - name = project.name - description = project.description - } - url = "https://github.com/today-tech/today-cloud" - organization { - name = "TODAY Tech" - url = "https://taketoday.cn" - } - licenses { - license { - name = "GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007" - url = "https://www.gnu.org/licenses/gpl.txt" - distribution = "repo" - } - } - scm { - url = "https://github.com/today-tech/today-cloud.git" - connection = "scm:git:git://github.com/today-tech/today-cloud" - developerConnection = "scm:git:git://github.com/today-tech/today-cloud" - } - developers { - developer { - id = "taketoday" - name = "Harry Yang" - email = "taketoday@foxmail.com" - } - } - issueManagement { - system = "GitHub" - url = "https://github.com/today-tech/today-cloud/issues" - } +publishing.publications.withType(MavenPublication.class) { + pom { + afterEvaluate { + name = project.name + description = project.description + } + url = "https://github.com/today-tech/today-cloud" + organization { + name = "TODAY Tech" + url = "https://taketoday.cn" + } + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'https://www.apache.org/licenses/LICENSE-2.0.txt' + distribution = "repo" } - versionMapping { - usage('java-api') { - fromResolutionResult() - } - usage('java-runtime') { - fromResolutionResult() - } + } + scm { + url = "https://github.com/today-tech/today-cloud.git" + connection = "scm:git:git://github.com/today-tech/today-cloud" + developerConnection = "scm:git:git://github.com/today-tech/today-cloud" + } + developers { + developer { + id = "taketoday" + name = "Harry Yang" + email = "taketoday@foxmail.com" } } + issueManagement { + system = "GitHub" + url = "https://github.com/today-tech/today-cloud/issues" + } } - - repositories { - maven { - url = version.endsWith('SNAPSHOT') - ? 'https://oss.sonatype.org/content/repositories/snapshots/' - : 'https://oss.sonatype.org/service/local/staging/deploy/maven2/' - - credentials { - username = repoUsername - password = repoPassword - } + versionMapping { + usage('java-api') { + fromResolutionResult() + } + usage('java-runtime') { + fromResolutionResult() } } -} +} \ No newline at end of file diff --git a/gradle/signing.gradle b/gradle/signing.gradle new file mode 100644 index 0000000..ce4517e --- /dev/null +++ b/gradle/signing.gradle @@ -0,0 +1,13 @@ + +if (isReleaseVersion) { + apply plugin: "signing" + + signing { + def signingKey = findProperty("signing.key") + def signingKeyId = findProperty("signing.keyId") + def signingPassword = findProperty("signing.password") + useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + + sign(publishing.publications) + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55..d997cfc 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d4081da..dbc3ce4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a9..0262dcb 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -57,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" diff --git a/gradlew.bat b/gradlew.bat index db3a6ac..e509b2d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,94 +1,93 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH= - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle index f1bbd1c..fef3d7d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,6 +6,20 @@ pluginManagement { } } +plugins { + id("com.gradle.enterprise") version("3.14.1") +} + +gradleEnterprise { + if (System.getenv("CI") != null || Boolean.getBoolean("CI")) { + buildScan { + publishAlways() + termsOfServiceUrl = "https://gradle.com/terms-of-service" + termsOfServiceAgree = "yes" + } + } +} + // A Java library for studying to create a microservices framework like Spring Cloud. rootProject.name = 'today-cloud' @@ -16,22 +30,18 @@ include 'today-cloud-dependencies' include 'today-cloud-core' include 'today-cloud-gateway' include 'today-cloud-config-server' -include 'today-cloud-config-server-etcd' +include 'today-service-api' include 'today-service-client' include 'today-service-provider' -include 'today-service-registry' +include 'today-service-discovery' +include 'today-service-registration' +include 'today-service-serialization' include 'today-remoting' +include 'today-remoting-micrometer' include 'today-remoting-transport-tcp' include 'today-remoting-transport-local' +include 'today-remoting-transport-websocket' -// Samples - -include 'today-cloud-samples:demo-tests:demo-registry' -include 'today-cloud-samples:demo-tests:demo-user-api' -include 'today-cloud-samples:demo-tests:demo-user-rest' -include 'today-cloud-samples:demo-tests:demo-user-service' -include 'today-cloud-samples:today-petclinic' -include 'today-service-registry-etcd' - +include 'today-cloud-build:today-cloud-gradle-plugin' diff --git a/today-cloud-bom/build.gradle b/today-cloud-bom/build.gradle index dc95c93..f23064e 100644 --- a/today-cloud-bom/build.gradle +++ b/today-cloud-bom/build.gradle @@ -1,6 +1,7 @@ description = "TODAY Cloud (Bill of Materials)" apply plugin: 'java-platform' + apply from: "$rootDir/gradle/publications.gradle" dependencies { @@ -18,4 +19,6 @@ publishing { from components.javaPlatform } } -} \ No newline at end of file +} + +apply from: "$rootDir/gradle/signing.gradle" diff --git a/today-cloud-build/today-cloud-gradle-plugin/build.gradle b/today-cloud-build/today-cloud-gradle-plugin/build.gradle new file mode 100644 index 0000000..f1190ca --- /dev/null +++ b/today-cloud-build/today-cloud-gradle-plugin/build.gradle @@ -0,0 +1,31 @@ +plugins { + id "java-gradle-plugin" +// id "com.gradle.plugin-publish" version "1.3.1" +} + +description = "TODAY Cloud Applications Gradle Plugins" + +dependencies { + api 'cn.taketoday:infra-core' + api "cn.taketoday:infra-gradle-plugin" + + optional "org.graalvm.buildtools:native-gradle-plugin" + implementation 'org.apache.commons:commons-compress' + implementation 'io.spring.gradle:dependency-management-plugin' +} + +gradlePlugin { + automatedPublishing = true + website = "https://github.com/today-tech/today-cloud" + vcsUrl = "https://github.com/today-tech/today-cloud" + plugins { + todayCloudPlugin { + id = "today.cloud-application" + tags.set(['cloud', 'application']) + displayName = "TODAY Cloud Applications Gradle Plugin" + description = "TODAY Cloud Applications Gradle Plugin" + implementationClass = "infra.cloud.plugin.InfraCloudPlugin" + } + } +} + diff --git a/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/CloudApplicationExtension.java b/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/CloudApplicationExtension.java new file mode 100644 index 0000000..410d3c7 --- /dev/null +++ b/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/CloudApplicationExtension.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.plugin; + +import org.gradle.api.Project; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; + +/** + * @author 海子 Yang + * @since 1.0 2026/3/10 22:50 + */ +public abstract class CloudApplicationExtension { + + public CloudApplicationExtension(Project project) { + } + + @Input + public abstract Property getServiceModuleType(); + +} diff --git a/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/GenerateServiceMetadata.java b/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/GenerateServiceMetadata.java new file mode 100644 index 0000000..dc4b8d9 --- /dev/null +++ b/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/GenerateServiceMetadata.java @@ -0,0 +1,115 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.plugin; + +import org.gradle.api.DefaultTask; +import org.gradle.api.Project; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.TaskAction; +import org.gradle.work.DisableCachingByDefault; +import org.jspecify.annotations.Nullable; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import infra.util.CollectionUtils; +import infra.util.StringUtils; + +/** + * A Gradle task that generates service metadata properties file. + *

+ * This task reads configuration from {@link ServiceMetadataExtension} and writes + * a {@code service-metadata.properties} file to the build resources directory. + * The generated file contains service identification, versioning, grouping, + * description, and any additional custom properties. + *

+ * + * @author 海子 Yang + * @since 1.0 2026/3/10 21:59 + */ +@DisableCachingByDefault(because = "Not worth caching") +public abstract class GenerateServiceMetadata extends DefaultTask { + + @TaskAction + public void generate() throws IOException { + Project project = getProject(); + List interfaces = ServiceInterfacesFinder.findInterfaces(project.getProjectDir()); + if (interfaces.isEmpty()) { + return; + } + + ServiceMetadataExtension serviceMetadata = project.getExtensions().getByType(ServiceMetadataExtension.class); + + var outputDir = project.getLayout().getBuildDirectory().dir("resources/main/META-INF").get().getAsFile(); + File propertiesFile = new File(outputDir, "service-metadata.properties"); + createFileIfNecessary(propertiesFile); + + Properties properties = CollectionUtils.createSortedProperties(false); + + properties.setProperty("service.id", serviceMetadata.getServiceId().get()); + properties.setProperty("service.version", serviceMetadata.getServiceVersion().get()); + properties.setProperty("service.group", serviceMetadata.getServiceGroup().get()); + properties.setProperty("service.description", serviceMetadata.getServiceDescription().get()); + properties.setProperty("service.interfaces", StringUtils.collectionToCommaDelimitedString(interfaces)); + + convertToStringValues(serviceMetadata.getAdditional().get()) + .forEach((name, value) -> { + if (value != null) { + if (!name.startsWith("service.")) { + name = "service." + name; + } + properties.put(name, value); + } + }); + + try (FileOutputStream outputStream = new FileOutputStream(propertiesFile)) { + properties.store(outputStream, "Generated service metadata - DO NOT EDIT"); + } + + } + + private void createFileIfNecessary(File file) throws IOException { + if (file.exists()) { + return; + } + File parent = file.getParentFile(); + if (!parent.isDirectory() && !parent.mkdirs()) { + throw new IllegalStateException( + "Cannot create parent directory for '" + file.getAbsolutePath() + "'"); + } + if (!file.createNewFile()) { + throw new IllegalStateException("Cannot create target file '" + file.getAbsolutePath() + "'"); + } + } + + private Map convertToStringValues(Map input) { + Map output = new LinkedHashMap<>(); + input.forEach((key, value) -> { + if (value instanceof Provider provider) { + value = provider.getOrNull(); + } + output.put(key, value != null ? value.toString() : null); + }); + return output; + } + +} diff --git a/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/InfraCloudPlugin.java b/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/InfraCloudPlugin.java new file mode 100644 index 0000000..3d26a93 --- /dev/null +++ b/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/InfraCloudPlugin.java @@ -0,0 +1,72 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.plugin; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.plugins.BasePlugin; +import org.gradle.api.plugins.JavaLibraryPlugin; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.tasks.TaskContainer; + +import infra.gradle.plugin.InfraApplicationPlugin; +import infra.lang.VersionExtractor; +import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension; + +/** + * @author 海子 Yang + * @since 1.0 2026/3/10 21:31 + */ +public class InfraCloudPlugin implements Plugin { + + private static final String INFRA_CLOUD_VERSION = VersionExtractor.forClass(InfraCloudPlugin.class); + + public static final String GENERATE_SERVICE_METADATA_TASK_NAME = "generateServiceMetadata"; + + /** + * The coordinates {@code (group:name:version)} of the + * {@code infra-cloud-dependencies} bom. + */ + public static final String BOM_COORDINATES = "cn.taketoday:today-cloud-dependencies:" + INFRA_CLOUD_VERSION; + + @Override + public void apply(Project project) { + project.getPlugins().apply(JavaPlugin.class); + project.getPlugins().apply(JavaLibraryPlugin.class); + project.getPlugins().apply(InfraApplicationPlugin.class); + + TaskContainer tasks = project.getTasks(); + createExtensions(project); + + tasks.register(GENERATE_SERVICE_METADATA_TASK_NAME, GenerateServiceMetadata.class, task -> { + task.setGroup(BasePlugin.BUILD_GROUP); + task.setDescription("Generates a service-metadata.properties file."); + }); + + tasks.getByName(JavaPlugin.PROCESS_RESOURCES_TASK_NAME) + .dependsOn(GENERATE_SERVICE_METADATA_TASK_NAME); + + project.getExtensions().getByType(DependencyManagementExtension.class) + .imports(imports -> imports.mavenBom(BOM_COORDINATES)); + } + + private void createExtensions(Project project) { + project.getExtensions().create("serviceMetadata", ServiceMetadataExtension.class, project); + project.getExtensions().create("cloudApplication", CloudApplicationExtension.class, project); + } + +} diff --git a/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/ModuleType.java b/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/ModuleType.java new file mode 100644 index 0000000..c9456a7 --- /dev/null +++ b/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/ModuleType.java @@ -0,0 +1,27 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.plugin; + +/** + * @author 海子 Yang + * @since 1.0 2026/3/10 22:51 + */ +public enum ModuleType { + SERVICE_API, + + SERVICE_IMPLEMENTATION, +} diff --git a/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/ServiceInterfacesFinder.java b/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/ServiceInterfacesFinder.java new file mode 100644 index 0000000..4ec61fc --- /dev/null +++ b/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/ServiceInterfacesFinder.java @@ -0,0 +1,322 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.plugin; + +import org.jspecify.annotations.Nullable; + +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Modifier; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Deque; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import infra.bytecode.AnnotationVisitor; +import infra.bytecode.ClassReader; +import infra.bytecode.ClassVisitor; +import infra.bytecode.Type; +import infra.lang.Constant; + +/** + * Utility class for finding service interfaces within a given directory structure by scanning + * compiled {@code .class} files. This class traverses the directory tree, reads class bytecode + * to identify public interfaces, and optionally filters them based on specific annotations. + *

+ * The traversal order is deterministic: directories and files are processed in alphabetical order + * to ensure consistent results across runs. + * + * @author Harry Yang + * @since 4.0 + */ +public abstract class ServiceInterfacesFinder { + + private static final String DOT_CLASS = ".class"; + + private static final String SERVICE = "infra.stereotype.Service"; + + /** + * File filter that accepts only files ending with {@code .class}. + */ + private static final FileFilter CLASS_FILE_FILTER = ServiceInterfacesFinder::isClassFile; + + /** + * File filter that accepts only directories that do not start with a dot (hidden directories). + */ + private static final FileFilter PACKAGE_DIRECTORY_FILTER = ServiceInterfacesFinder::isPackageDirectory; + + /** + * Checks if the given file is a valid Java class file. + * + * @param file the file to check + * @return {@code true} if the file is a regular file ending with {@code .class}, {@code false} otherwise + */ + private static boolean isClassFile(File file) { + return file.isFile() && file.getName().endsWith(DOT_CLASS); + } + + /** + * Checks if the given file is a valid package directory. + *

+ * A valid package directory is a directory that does not start with a dot (i.e., not hidden). + * + * @param file the file to check + * @return {@code true} if the file is a directory and not hidden, {@code false} otherwise + */ + private static boolean isPackageDirectory(File file) { + return file.isDirectory() && !file.getName().startsWith("."); + } + + /** + * Finds all public interface names within the specified root directory that are annotated + * with the given annotation name. + * + * @param rootDirectory the root directory to start scanning from + * @param annotationName the fully qualified name of the annotation to filter by, or {@code null} to ignore annotation filtering + * @return a list of fully qualified interface names matching the criteria + * @throws IOException if an I/O error occurs while reading class files + */ + public static List findInterfaces(File rootDirectory, @Nullable String annotationName) throws IOException { + InterfacesCallback callback = new InterfacesCallback(annotationName); + ServiceInterfacesFinder.doWithInterface(rootDirectory, callback); + return callback.getInterfaces(); + } + + /** + * Finds all public interface names within the specified root directory that are annotated + * with the given annotation name. + * + * @param rootDirectory the root directory to start scanning from + * @throws IOException if an I/O error occurs while reading class files + */ + public static List findInterfaces(File rootDirectory) throws IOException { + return findInterfaces(rootDirectory, SERVICE); + } + + /** + * Traverses the directory structure starting from {@code rootDirectory}, reads each {@code .class} file, + * and invokes the provided {@code callback} for every valid public interface found. + *

+ * If the callback returns a non-null result, the traversal stops immediately and the result is returned. + * + * @param rootDirectory the root directory to start scanning from + * @param callback the callback to invoke for each discovered interface + * @param the return type of the callback + * @return the first non-null result returned by the callback, or {@code null} if no such result is produced + * @throws IOException if an I/O error occurs or if {@code rootDirectory} is not a valid directory + */ + static @Nullable T doWithInterface(File rootDirectory, InterfaceCallback callback) throws IOException { + if (!rootDirectory.exists()) { + return null; // nothing to do + } + if (!rootDirectory.isDirectory()) { + throw new IllegalArgumentException("Invalid root directory '" + rootDirectory + "'"); + } + Deque stack = new ArrayDeque<>(); + stack.push(rootDirectory); + while (!stack.isEmpty()) { + File file = stack.pop(); + if (file.isFile()) { + try (InputStream inputStream = new FileInputStream(file)) { + ClassDescriptor classDescriptor = createClassDescriptor(inputStream); + if (classDescriptor != null && classDescriptor.name != null) { + T result = callback.doWith(new InterfaceClass(classDescriptor.name, classDescriptor.annotationNames)); + if (result != null) { + return result; + } + } + } + } + if (file.isDirectory()) { + pushAllSorted(stack, file.listFiles(PACKAGE_DIRECTORY_FILTER)); + pushAllSorted(stack, file.listFiles(CLASS_FILE_FILTER)); + } + } + return null; + } + + /** + * Pushes all files from the given array onto the stack in sorted order (by name). + *

+ * Sorting ensures a deterministic traversal order. + * + * @param stack the stack to push files onto + * @param files the array of files to sort and push; may be {@code null} + */ + private static void pushAllSorted(Deque stack, File @Nullable [] files) { + if (files != null) { + Arrays.sort(files, Comparator.comparing(File::getName)); + for (File file : files) { + stack.push(file); + } + } + } + + /** + * Reads the input stream containing bytecode and extracts class metadata including the class name + * and associated annotation names. + * + * @param inputStream the input stream containing the class file data + * @return a {@link ClassDescriptor} containing the extracted metadata, or {@code null} if parsing fails + */ + private static @Nullable ClassDescriptor createClassDescriptor(InputStream inputStream) { + try { + ClassReader classReader = new ClassReader(inputStream); + ClassDescriptor classDescriptor = new ClassDescriptor(); + classReader.accept(classDescriptor, ClassReader.SKIP_CODE); + return classDescriptor; + } + catch (IOException ex) { + return null; + } + } + + /** + * A {@link ClassVisitor} implementation that captures the name of a public interface + * and collects the names of all annotations present on the class. + */ + private static final class ClassDescriptor extends ClassVisitor { + + /** + * The set of fully qualified annotation names found on the class. + */ + public final Set annotationNames = new LinkedHashSet<>(); + + /** + * The internal name of the class if it is a public interface; otherwise {@code null}. + */ + public @Nullable String name; + + @Override + public @Nullable AnnotationVisitor visitAnnotation(String desc, boolean visible) { + this.annotationNames.add(Type.forDescriptor(desc).getClassName()); + return null; + } + + @Override + public void visit(int version, int access, String name, @Nullable String signature, @Nullable String superName, String @Nullable [] interfaces) { + if (Modifier.isPublic(access) && Modifier.isInterface(access)) { + this.name = name.replace(Constant.PATH_SEPARATOR, Constant.PACKAGE_SEPARATOR); + } + } + + } + + /** + * A callback interface used during the traversal of class files. + * + * @param the return type of the {@link #doWith(InterfaceClass)} method + */ + interface InterfaceCallback { + + /** + * Invoked for each discovered public interface. + * + * @param interfaceClass the descriptor of the discovered interface + * @return a result value, or {@code null} to continue traversal + */ + @Nullable + T doWith(InterfaceClass interfaceClass); + + } + + /** + * Represents a discovered public interface along with its associated annotations. + */ + static final class InterfaceClass { + + private final String name; + + private final Set annotationNames; + + InterfaceClass(String name, Set annotationNames) { + this.name = name; + this.annotationNames = Set.copyOf(annotationNames); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + InterfaceClass other = (InterfaceClass) obj; + return this.name.equals(other.name); + } + + @Override + public int hashCode() { + return this.name.hashCode(); + } + + @Override + public String toString() { + return this.name; + } + + } + + /** + * A callback implementation that collects interface names matching a specific annotation. + */ + private static final class InterfacesCallback implements InterfaceCallback { + + private final Set interfaces = new LinkedHashSet<>(); + + private final @Nullable String annotationName; + + /** + * Constructs a new callback with the optional annotation filter. + * + * @param annotationName the annotation name to filter by, or {@code null} to accept all interfaces + */ + private InterfacesCallback(@Nullable String annotationName) { + this.annotationName = annotationName; + } + + @Override + public @Nullable Object doWith(InterfaceClass interfaceClass) { + if (interfaceClass.annotationNames.contains(annotationName)) { + interfaces.add(interfaceClass.name); + } + return null; + } + + /** + * Returns the collected list of interface names. + * + * @return a list of interface names that matched the annotation filter + */ + public List getInterfaces() { + return new ArrayList<>(interfaces); + } + + } + +} diff --git a/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/ServiceMetadataExtension.java b/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/ServiceMetadataExtension.java new file mode 100644 index 0000000..d160999 --- /dev/null +++ b/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/ServiceMetadataExtension.java @@ -0,0 +1,93 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.plugin; + +import org.gradle.api.Project; +import org.gradle.api.provider.MapProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Optional; + +import java.io.Serializable; + +/** + * Extension for configuring service metadata in a Gradle project. + *

+ * This class holds metadata such as the service ID, version, and additional custom properties. + * It is designed to be used within a Gradle plugin to manage service-related configuration. + * + * @author 海子 Yang + * @since 1.0 2026/3/10 21:41 + */ +public abstract class ServiceMetadataExtension implements Serializable { + + /** + * Creates a new {@code ServiceMetadataExtension} associated with the given {@code project}. + *

+ * The service ID is defaulted to the project name, and the service version is defaulted + * to the project's version string. + * + * @param project the Gradle project to associate with this extension + */ + public ServiceMetadataExtension(Project project) { + getServiceId().convention(project.provider(project::getName)); + getServiceDescription().convention(project.provider(project::getDescription)); + getServiceVersion().convention(project.provider(() -> project.getVersion().toString())); + getServiceGroup().convention("default"); + } + + /** + * Gets the property representing the unique identifier of the service. + *

+ * This property is marked as an input for Gradle's up-to-date checks. + * + * @return the service ID property + */ + @Input + @Optional + public abstract Property getServiceId(); + + /** + * Gets the property representing the version of the service. + *

+ * This property is optional and marked as an input for Gradle's up-to-date checks. + * + * @return the service version property, or null if not set + */ + @Input + @Optional + public abstract Property getServiceVersion(); + + @Input + @Optional + public abstract Property getServiceDescription(); + + @Input + @Optional + public abstract Property getServiceGroup(); + + /** + * Gets the map property for storing additional custom metadata. + *

+ * This property is internal and not considered for Gradle's up-to-date checks. + * + * @return the map of additional metadata + */ + @Optional + public abstract MapProperty getAdditional(); + +} diff --git a/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/package-info.java b/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/package-info.java new file mode 100644 index 0000000..cda2a6a --- /dev/null +++ b/today-cloud-build/today-cloud-gradle-plugin/src/main/java/infra/cloud/plugin/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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. + */ + +/** + * Central classes for the Infra Cloud Gradle plugin. + */ +@NullMarked +package infra.cloud.plugin; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-cloud-config-server-etcd/build.gradle b/today-cloud-config-server-etcd/build.gradle deleted file mode 100644 index 2857ca4..0000000 --- a/today-cloud-config-server-etcd/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -description = "TODAY Cloud Etcd Config Server" - -dependencies { - - implementation project(":today-cloud-config-server") - implementation 'cn.taketoday:today-framework' - implementation "io.etcd:jetcd-core:0.6.1" - - annotationProcessor 'cn.taketoday:infra-configuration-processor' - - testImplementation "cn.taketoday:today-test" - -} diff --git a/today-cloud-config-server-etcd/src/main/java/infra/config/etcd/ByteSequenceUtils.java b/today-cloud-config-server-etcd/src/main/java/infra/config/etcd/ByteSequenceUtils.java deleted file mode 100644 index 452e834..0000000 --- a/today-cloud-config-server-etcd/src/main/java/infra/config/etcd/ByteSequenceUtils.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.config.etcd; - -import java.nio.charset.StandardCharsets; - -import io.etcd.jetcd.ByteSequence; - -/** - * @author Harry Yang - * @since 1.0 2023/11/7 22:13 - */ -public class ByteSequenceUtils { - - public static ByteSequence forString(String source) { - return ByteSequence.from(source, StandardCharsets.UTF_8); - } - -} diff --git a/today-cloud-config-server-etcd/src/main/java/infra/config/etcd/ConfigApplicationStartupListener.java b/today-cloud-config-server-etcd/src/main/java/infra/config/etcd/ConfigApplicationStartupListener.java deleted file mode 100644 index 592f863..0000000 --- a/today-cloud-config-server-etcd/src/main/java/infra/config/etcd/ConfigApplicationStartupListener.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.config.etcd; - -import infra.app.Application; -import infra.app.env.EnvironmentPostProcessor; -import infra.context.properties.bind.BindResult; -import infra.context.properties.bind.Binder; -import infra.core.Ordered; -import infra.core.env.ConfigurableEnvironment; -import io.etcd.jetcd.Client; -import io.etcd.jetcd.KV; - -/** - * @author Harry Yang - * @since 1.0 2023/11/7 21:34 - */ -public class ConfigApplicationStartupListener implements EnvironmentPostProcessor, Ordered { - - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, Application application) { - BindResult result = Binder.get(environment).bind("config.server.etcd", EtcdProperties.class); - if (result.isBound()) { - EtcdProperties properties = result.get(); - Client client = Client.builder() - .endpoints(properties.getEndpoints()) - .user(ByteSequenceUtils.forString(properties.getUsername())) - .password(ByteSequenceUtils.forString(properties.getPassword())) - .build(); - - KV kvClient = client.getKVClient(); - environment.getPropertySources() - .addLast(new EtcdPropertySource(kvClient, properties.getNamespace())); - } - } - - @Override - public int getOrder() { - return Ordered.LOWEST_PRECEDENCE; - } - -} diff --git a/today-cloud-config-server-etcd/src/main/java/infra/config/etcd/EtcdProperties.java b/today-cloud-config-server-etcd/src/main/java/infra/config/etcd/EtcdProperties.java deleted file mode 100644 index e22cfe3..0000000 --- a/today-cloud-config-server-etcd/src/main/java/infra/config/etcd/EtcdProperties.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.config.etcd; - -import infra.beans.factory.annotation.Value; -import infra.context.properties.ConfigurationProperties; -import infra.lang.Nullable; - -/** - * @author Harry Yang - * @since 1.0 2023/11/7 21:51 - */ -@ConfigurationProperties("config.server.etcd") -public class EtcdProperties { - - /** - * Etcd server endpoints. - */ - private String[] endpoints; - - /** - * Etcd server key namespace. - */ - @Nullable - @Value("${app.name:application}") - private String namespace; - - /** - * Etcd server user name. - */ - private String username; - - /** - * Etcd server user password. - */ - private String password; - - public void setNamespace(@Nullable String namespace) { - this.namespace = namespace; - } - - public void setPassword(String password) { - this.password = password; - } - - public void setUsername(String username) { - this.username = username; - } - - public void setEndpoints(String[] endpoints) { - this.endpoints = endpoints; - } - - public String[] getEndpoints() { - return endpoints; - } - - @Nullable - public String getNamespace() { - return namespace; - } - - public String getPassword() { - return password; - } - - public String getUsername() { - return username; - } -} diff --git a/today-cloud-config-server-etcd/src/main/java/infra/config/etcd/EtcdPropertySource.java b/today-cloud-config-server-etcd/src/main/java/infra/config/etcd/EtcdPropertySource.java deleted file mode 100644 index fcdc5f4..0000000 --- a/today-cloud-config-server-etcd/src/main/java/infra/config/etcd/EtcdPropertySource.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.config.etcd; - -import java.nio.charset.StandardCharsets; -import java.util.List; - -import infra.core.env.PropertySource; -import infra.lang.Nullable; -import infra.logging.Logger; -import infra.logging.LoggerFactory; -import infra.util.CollectionUtils; -import infra.util.ExceptionUtils; -import io.etcd.jetcd.ByteSequence; -import io.etcd.jetcd.KV; -import io.etcd.jetcd.KeyValue; - -/** - * @author Harry Yang - * @since 1.0 2023/10/6 21:41 - */ -public class EtcdPropertySource extends PropertySource { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - @Nullable - private final String namespace; - - public EtcdPropertySource(KV kvClient) { - this(kvClient, null); - } - - public EtcdPropertySource(KV kvClient, @Nullable String namespace) { - super("etcd", kvClient); - this.namespace = namespace; - } - - @Nullable - @Override - public Object getProperty(String name) { - ByteSequence key = getKey(name); - try { - List kvs = source.get(key).get().getKvs(); - KeyValue keyValue = CollectionUtils.firstElement(kvs); - if (keyValue != null) { - logger.info("found property: {}: key: {} value: {}", name, key, keyValue.getValue()); - return keyValue.getValue().toString(); - } - return null; - } - catch (Exception e) { - throw ExceptionUtils.sneakyThrow(e); - } - } - - private ByteSequence getKey(String name) { - if (namespace != null) { - return ByteSequence.from(namespace + name, StandardCharsets.UTF_8); - } - return ByteSequence.from(name, StandardCharsets.UTF_8); - } - -} diff --git a/today-cloud-config-server-etcd/src/main/resources/META-INF/additional-infra-configuration-metadata.json b/today-cloud-config-server-etcd/src/main/resources/META-INF/additional-infra-configuration-metadata.json deleted file mode 100644 index 879bec0..0000000 --- a/today-cloud-config-server-etcd/src/main/resources/META-INF/additional-infra-configuration-metadata.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "groups": [ - { - "name": "logging", - "type": "infra.app.context.logging.LoggingApplicationListener" - } - ], - "properties": [ - { - "name": "config.server.etcd.endpoints", - "type": "java.lang.String[]", - "description": "Etcd server endpoints.", - "sourceType": "infra.config.etcd.EtcdProperties", - "defaultValue": [] - }, - { - "name": "config.server.etcd.username", - "type": "java.lang.String", - "description": "Etcd server user name.", - "sourceType": "infra.config.etcd.EtcdProperties", - "defaultValue": "root" - }, - { - "name": "config.server.etcd.password", - "type": "java.lang.String", - "description": "Etcd server user password.", - "sourceType": "infra.config.etcd.EtcdProperties" - }, - { - "name": "config.server.etcd.namespace", - "type": "java.lang.String", - "description": "Etcd server key namespace.", - "sourceType": "infra.config.etcd.EtcdProperties" - } - ], - "hints": [ - { - "name": "app.config.import", - "values": [ - { - "value": "file:" - }, - { - "value": "classpath:" - }, - { - "value": "configtree:" - }, - { - "value": "config-server:" - } - ], - "providers": [ - { - "name": "any" - } - ] - } - ] -} diff --git a/today-cloud-config-server-etcd/src/main/resources/META-INF/today.strategies b/today-cloud-config-server-etcd/src/main/resources/META-INF/today.strategies deleted file mode 100644 index f009692..0000000 --- a/today-cloud-config-server-etcd/src/main/resources/META-INF/today.strategies +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright 2021 - 2024 the original author or authors. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see [http://www.gnu.org/licenses/] -# -infra.app.env.EnvironmentPostProcessor=\ -infra.config.etcd.ConfigApplicationStartupListener diff --git a/today-cloud-config-server-etcd/src/test/java/infra/config/etcd/EtcdPropertySourceTests.java b/today-cloud-config-server-etcd/src/test/java/infra/config/etcd/EtcdPropertySourceTests.java deleted file mode 100644 index 31237f1..0000000 --- a/today-cloud-config-server-etcd/src/test/java/infra/config/etcd/EtcdPropertySourceTests.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.config.etcd; - -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; - -import io.etcd.jetcd.ByteSequence; -import io.etcd.jetcd.Client; -import io.etcd.jetcd.KV; -import io.etcd.jetcd.Watch; -import io.etcd.jetcd.options.WatchOption; -import io.etcd.jetcd.watch.WatchResponse; - -/** - * @author Harry Yang - * @since 1.0 2023/10/6 22:20 - */ -class EtcdPropertySourceTests { - - @Test - void test() throws InterruptedException { - Client client = Client.builder() - .endpoints("http://localhost:2379") - .user(ByteSequence.from("root", StandardCharsets.UTF_8)) - .password(ByteSequence.from("88888888", StandardCharsets.UTF_8)) - .build(); - - KV kvClient = client.getKVClient(); - EtcdPropertySource source = new EtcdPropertySource(kvClient); - - Object property = source.getProperty("key"); - System.out.println(property); - - Watch watchClient = client.getWatchClient(); - watchClient.watch(ByteSequence.from("cn.taketoday.blog.BlogApplication", StandardCharsets.UTF_8), - WatchOption.newBuilder().isPrefix(true).build(), new Watch.Listener() { - - @Override - public void onNext(WatchResponse response) { - System.out.println(response); - } - - @Override - public void onError(Throwable throwable) { - throwable.printStackTrace(); - } - - @Override - public void onCompleted() { - System.out.println("onCompleted"); - } - }); - - Thread.currentThread().join(); - } - -} \ No newline at end of file diff --git a/today-cloud-config-server-etcd/src/test/java/infra/config/etcd/EtcdTests.java b/today-cloud-config-server-etcd/src/test/java/infra/config/etcd/EtcdTests.java deleted file mode 100644 index 0c3790d..0000000 --- a/today-cloud-config-server-etcd/src/test/java/infra/config/etcd/EtcdTests.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.config.etcd; - -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; - -import infra.util.CollectionUtils; -import io.etcd.jetcd.ByteSequence; -import io.etcd.jetcd.Client; -import io.etcd.jetcd.KV; -import io.etcd.jetcd.kv.GetResponse; - -/** - * @author Harry Yang - * @since 1.0 2023/10/6 20:40 - */ -class EtcdTests { - - @Test - void test() throws Exception { - Client client = Client.builder() - .endpoints("http://localhost:2379") - .user(ByteSequence.from("root", StandardCharsets.UTF_8)) - .password(ByteSequence.from("88888888", StandardCharsets.UTF_8)) - .build(); - - KV kvClient = client.getKVClient(); - - ByteSequence key = ByteSequence.from("key", StandardCharsets.UTF_8); - kvClient.put(key, ByteSequence.from("value", StandardCharsets.UTF_8)); - - GetResponse response = kvClient.get(key).get(); - - System.out.println(CollectionUtils.firstElement(response.getKvs()).getValue()); - - } - -} diff --git a/today-cloud-config-server/build.gradle b/today-cloud-config-server/build.gradle index 4ed91bb..79fd520 100644 --- a/today-cloud-config-server/build.gradle +++ b/today-cloud-config-server/build.gradle @@ -1,10 +1,10 @@ description = "TODAY Cloud Config Server" dependencies { - implementation "cn.taketoday:today-core" - implementation 'cn.taketoday:today-framework' + implementation "cn.taketoday:infra-core" + implementation 'cn.taketoday:infra-app' - testImplementation "cn.taketoday:today-test" + testImplementation "cn.taketoday:infra-test" annotationProcessor 'cn.taketoday:infra-configuration-processor' } \ No newline at end of file diff --git a/today-cloud-config-server/src/main/java/infra/config/DynamicProperty.java b/today-cloud-config-server/src/main/java/infra/config/DynamicProperty.java index 8b9efac..598f4ae 100644 --- a/today-cloud-config-server/src/main/java/infra/config/DynamicProperty.java +++ b/today-cloud-config-server/src/main/java/infra/config/DynamicProperty.java @@ -1,26 +1,26 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.config; +import org.jspecify.annotations.Nullable; + import java.util.function.Supplier; import infra.lang.Assert; -import infra.lang.Nullable; /** * @author Harry Yang diff --git a/today-cloud-config-server/src/main/java/infra/config/DynamicPropertyDependencyResolvingStrategy.java b/today-cloud-config-server/src/main/java/infra/config/DynamicPropertyDependencyResolvingStrategy.java index ed9521c..3f31492 100644 --- a/today-cloud-config-server/src/main/java/infra/config/DynamicPropertyDependencyResolvingStrategy.java +++ b/today-cloud-config-server/src/main/java/infra/config/DynamicPropertyDependencyResolvingStrategy.java @@ -1,26 +1,26 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.config; +import org.jspecify.annotations.Nullable; + import infra.beans.factory.config.DependencyDescriptor; import infra.beans.factory.support.DependencyResolvingStrategy; import infra.context.BootstrapContext; -import infra.lang.Nullable; /** * for DynamicProperty diff --git a/today-cloud-core/build.gradle b/today-cloud-core/build.gradle index d16de18..793a138 100644 --- a/today-cloud-core/build.gradle +++ b/today-cloud-core/build.gradle @@ -1,14 +1,11 @@ description = "TODAY Cloud Core Technologies" dependencies { - implementation 'cn.taketoday:today-core' - implementation 'cn.taketoday:today-starter-web' + api 'cn.taketoday:infra-core' - optional 'com.esotericsoftware:kryo:5.5.0' + optional 'cn.taketoday:infra-app' - optional 'io.netty:netty-codec-http' + implementation 'cn.taketoday:infra-context' -// implementation 'org.msgpack:msgpack-core:0.9.8' - implementation 'io.protostuff:protostuff-core:1.7.4' - implementation 'io.protostuff:protostuff-runtime:1.7.4' + annotationProcessor 'cn.taketoday:infra-configuration-processor' } \ No newline at end of file diff --git a/today-cloud-core/src/main/java/infra/cloud/DefaultServiceInstance.java b/today-cloud-core/src/main/java/infra/cloud/DefaultServiceInstance.java deleted file mode 100644 index 7174ecc..0000000 --- a/today-cloud-core/src/main/java/infra/cloud/DefaultServiceInstance.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud; - -import java.net.URI; -import java.util.Objects; - -import infra.core.AttributeAccessorSupport; -import infra.core.style.ToStringBuilder; - -/** - * Default implementation of {@link ServiceInstance}. - * - * @author Harry Yang - * @since 1.0 2023/11/19 20:52 - */ -public class DefaultServiceInstance extends AttributeAccessorSupport implements ServiceInstance { - - private String instanceId; - - private String serviceId; - - private String host; - - private int port; - - private URI uri; - - public DefaultServiceInstance() { - - } - - /** - * @param instanceId the id of the instance. - * @param serviceId the id of the service. - * @param host the host where the service instance can be found. - * @param port the port on which the service is running. - */ - public DefaultServiceInstance(String instanceId, String serviceId, String host, int port) { - this.instanceId = instanceId; - this.serviceId = serviceId; - this.host = host; - this.port = port; - } - - /** - * Creates a URI from the given ServiceInstance's host:port. - * - * @param instance the ServiceInstance. - * @return URI of the form "host:port". Scheme port default used - * if port not set. - */ - public static URI getUri(ServiceInstance instance) { - int port = instance.getPort(); - if (port <= 0) { - port = 80; - } - String uri = String.format("http://%s:%s", instance.getHost(), port); - return URI.create(uri); - } - - @Override - public URI getHttpURI() { - if (uri == null) { - uri = getUri(this); - } - return uri; - } - - @Override - public String getInstanceId() { - return instanceId; - } - - @Override - public String getServiceId() { - return serviceId; - } - - @Override - public String getHost() { - return host; - } - - @Override - public int getPort() { - return port; - } - - public void setInstanceId(String instanceId) { - this.instanceId = instanceId; - } - - public void setServiceId(String serviceId) { - this.serviceId = serviceId; - } - - public void setHost(String host) { - this.host = host; - } - - public void setPort(int port) { - this.port = port; - } - - public void setUri(URI uri) { - this.uri = uri; - this.host = this.uri.getHost(); - this.port = this.uri.getPort(); - } - - @Override - public String toString() { - return ToStringBuilder.forInstance(this) - .append("instanceId", instanceId) - .append("serviceId", serviceId) - .append("host", host) - .append("port", port) - .append("uri", uri) - .toString(); - } - - @Override - public boolean equals(Object object) { - if (this == object) - return true; - if (!(object instanceof DefaultServiceInstance that)) - return false; - if (!super.equals(object)) - return false; - return port == that.port - && Objects.equals(instanceId, that.instanceId) - && Objects.equals(serviceId, that.serviceId) - && Objects.equals(host, that.host) - && Objects.equals(uri, that.uri); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), instanceId, serviceId, host, port, uri); - } - -} diff --git a/today-cloud-core/src/main/java/infra/cloud/DiscoveryClient.java b/today-cloud-core/src/main/java/infra/cloud/DiscoveryClient.java deleted file mode 100644 index b38270b..0000000 --- a/today-cloud-core/src/main/java/infra/cloud/DiscoveryClient.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; - -import infra.core.annotation.AnnotationAwareOrderComparator; - -/** - * Represents read operations commonly available to discovery - * services such as Netflix Eureka or consul.io. - * - * @author Harry Yang - * @since 1.0 - */ -public interface DiscoveryClient { - - /** - * Gets all ServiceInstances associated with a particular serviceId. - * - * @param serviceId The serviceId to query. - * @return A List of ServiceInstance. - */ - List getInstances(String serviceId); - - /** - * @return All known service IDs. - */ - List getServices(); - - static Composite composite(List clients) { - return new Composite(clients); - } - - /** - * A {@link DiscoveryClient} that is composed of other discovery clients and delegates - * calls to each of them in order. - * - * @author Harry Yang - */ - class Composite implements DiscoveryClient { - - private final List clients; - - public Composite(List clients) { - AnnotationAwareOrderComparator.sort(clients); - this.clients = clients; - } - - @Override - public List getInstances(String serviceId) { - if (this.clients != null) { - for (DiscoveryClient discoveryClient : this.clients) { - List instances = discoveryClient.getInstances(serviceId); - if (instances != null && !instances.isEmpty()) { - return instances; - } - } - } - return Collections.emptyList(); - } - - @Override - public List getServices() { - LinkedHashSet services = new LinkedHashSet<>(); - if (this.clients != null) { - for (DiscoveryClient discoveryClient : this.clients) { - List serviceForClient = discoveryClient.getServices(); - if (serviceForClient != null) { - services.addAll(serviceForClient); - } - } - } - return new ArrayList<>(services); - } - - public List getDiscoveryClients() { - return this.clients; - } - - } - -} diff --git a/today-cloud-core/src/main/java/infra/cloud/Registration.java b/today-cloud-core/src/main/java/infra/cloud/Registration.java deleted file mode 100644 index d4f3ab4..0000000 --- a/today-cloud-core/src/main/java/infra/cloud/Registration.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud; - -/** - * @author Harry Yang - * @since 1.0 2023/11/20 21:50 - */ -public interface Registration { - -} diff --git a/today-cloud-core/src/main/java/infra/cloud/RemotingException.java b/today-cloud-core/src/main/java/infra/cloud/RemotingException.java index 1ef66c7..411c250 100644 --- a/today-cloud-core/src/main/java/infra/cloud/RemotingException.java +++ b/today-cloud-core/src/main/java/infra/cloud/RemotingException.java @@ -1,25 +1,24 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.cloud; -import java.io.Serial; +import org.jspecify.annotations.Nullable; -import infra.lang.Nullable; +import java.io.Serial; /** * Exception for default remoting problems diff --git a/today-cloud-core/src/main/java/infra/cloud/ServiceInstance.java b/today-cloud-core/src/main/java/infra/cloud/ServiceInstance.java deleted file mode 100644 index 8abefcf..0000000 --- a/today-cloud-core/src/main/java/infra/cloud/ServiceInstance.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud; - -import java.net.URI; - -import infra.core.AttributeAccessor; - -/** - * Represents an instance of a service in a discovery system. - * - * @author Harry Yang - * @since 1.0 2023/11/19 20:52 - */ -public interface ServiceInstance extends AttributeAccessor { - - /** - * @return The unique instance ID as registered. - */ - default String getInstanceId() { - return null; - } - - /** - * @return The service ID as registered. - */ - String getServiceId(); - - /** - * @return The hostname of the registered service instance. - */ - String getHost(); - - /** - * @return The port of the registered service instance. - */ - int getPort(); - - /** - * @return The service URI address. - */ - URI getHttpURI(); - -} diff --git a/today-cloud-core/src/main/java/infra/cloud/client/DefaultServiceInstance.java b/today-cloud-core/src/main/java/infra/cloud/client/DefaultServiceInstance.java new file mode 100644 index 0000000..2b36c56 --- /dev/null +++ b/today-cloud-core/src/main/java/infra/cloud/client/DefaultServiceInstance.java @@ -0,0 +1,170 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; + +import infra.core.style.ToStringBuilder; + +/** + * Default implementation of {@link ServiceInstance}. + *

+ * This class represents a service instance with properties such as instance ID, + * service ID, host, port, security status, and metadata. It provides constructors + * for creating instances with varying levels of detail and includes standard + * object methods like {@code toString()}, {@code equals()}, and {@code hashCode()}. + *

+ * + * @author Harry Yang + * @since 1.0 2023/11/19 20:52 + */ +public class DefaultServiceInstance implements ServiceInstance { + + private String instanceId; + + private String serviceId; + + private String host; + + private int port; + + private boolean secure; + + private Map metadata = new LinkedHashMap<>(); + + public DefaultServiceInstance() { + } + + /** + * @param instanceId the id of the instance. + * @param serviceId the id of the service. + * @param host the host where the service instance can be found. + * @param port the port on which the service is running. + * @param secure indicates whether the connection needs to be secure. + */ + public DefaultServiceInstance(String instanceId, String serviceId, String host, int port, boolean secure) { + this(instanceId, serviceId, host, port, secure, new LinkedHashMap<>()); + } + + /** + * @param instanceId the id of the instance. + * @param serviceId the id of the service. + * @param host the host where the service instance can be found. + * @param port the port on which the service is running. + * @param secure indicates whether the connection needs to be secure. + * @param metadata a map containing metadata. + */ + public DefaultServiceInstance(String instanceId, String serviceId, String host, + int port, boolean secure, Map metadata) { + this.instanceId = instanceId; + this.serviceId = serviceId; + this.host = host; + this.port = port; + this.secure = secure; + this.metadata = metadata; + } + + @Override + public Map getMetadata() { + return metadata; + } + + @Override + public String getInstanceId() { + return instanceId; + } + + @Override + public String getServiceId() { + return serviceId; + } + + @Override + public String getHost() { + return host; + } + + @Override + public int getPort() { + return port; + } + + @Override + public boolean isSecure() { + return secure; + } + + public void setDefaultInstanceId() { + setInstanceId(getHost() + ":" + getPort() + ":" + getServiceId()); + } + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + public void setHost(String host) { + this.host = host; + } + + public void setPort(int port) { + this.port = port; + } + + public void setSecure(boolean secure) { + this.secure = secure; + } + + @Override + public String toString() { + return ToStringBuilder.forInstance(this) + .append("instanceId", instanceId) + .append("serviceId", serviceId) + .append("host", host) + .append("port", port) + .append("secure", secure) + .append("metadata", metadata) + .toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DefaultServiceInstance that = (DefaultServiceInstance) o; + return port == that.port && secure == that.secure + && Objects.equals(instanceId, that.instanceId) + && Objects.equals(serviceId, that.serviceId) + && Objects.equals(host, that.host) + && Objects.equals(metadata, that.metadata); + } + + @Override + public int hashCode() { + return Objects.hash(instanceId, serviceId, host, port, secure, metadata); + } + +} diff --git a/today-cloud-core/src/main/java/infra/cloud/client/Registration.java b/today-cloud-core/src/main/java/infra/cloud/client/Registration.java new file mode 100644 index 0000000..8b2bab9 --- /dev/null +++ b/today-cloud-core/src/main/java/infra/cloud/client/Registration.java @@ -0,0 +1,29 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client; + +/** + * Represents a registration of a service instance in the cloud infrastructure. + * This interface extends {@link ServiceInstance} to provide additional capabilities + * related to service registration and management within a cloud environment. + * + * @author Harry Yang + * @since 1.0 2023/11/20 21:50 + */ +public interface Registration extends ServiceInstance { + +} diff --git a/today-cloud-core/src/main/java/infra/cloud/client/ServiceInstance.java b/today-cloud-core/src/main/java/infra/cloud/client/ServiceInstance.java new file mode 100644 index 0000000..fa1ed36 --- /dev/null +++ b/today-cloud-core/src/main/java/infra/cloud/client/ServiceInstance.java @@ -0,0 +1,63 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client; + +import java.util.Map; + +/** + * Represents an instance of a service in the cloud infrastructure. + * This interface defines the contract for accessing information about + * a specific service instance including its identity, location, and metadata. + * + * @author Harry Yang + * @since 1.0 2023/11/19 20:52 + */ +public interface ServiceInstance { + + /** + * @return The unique instance ID as registered. + */ + default String getInstanceId() { + return getHost() + ":" + getPort() + ":" + getServiceId(); + } + + /** + * @return The service ID as registered. + */ + String getServiceId(); + + /** + * @return The hostname of the registered service instance. + */ + String getHost(); + + /** + * @return The port of the registered service instance. + */ + int getPort(); + + /** + * @return Whether the port of the registered service instance uses HTTPS. + */ + boolean isSecure(); + + /** + * @return The key / value pair metadata associated with the service instance. + */ + Map getMetadata(); + +} diff --git a/today-cloud-core/src/main/java/infra/cloud/client/annotation/ConditionalOnDiscoveryEnabled.java b/today-cloud-core/src/main/java/infra/cloud/client/annotation/ConditionalOnDiscoveryEnabled.java new file mode 100644 index 0000000..960d0bc --- /dev/null +++ b/today-cloud-core/src/main/java/infra/cloud/client/annotation/ConditionalOnDiscoveryEnabled.java @@ -0,0 +1,41 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client.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; + +import infra.context.condition.ConditionalOnProperty; + +/** + * Provides a more succinct conditional infra.cloud.discovery.enabled. + * + * @author Olga Maciaszek-Sharma + * @author 海子 Yang + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@ConditionalOnProperty(value = "infra.cloud.discovery.enabled", matchIfMissing = true) +public @interface ConditionalOnDiscoveryEnabled { + +} diff --git a/today-cloud-core/src/main/java/infra/cloud/client/package-info.java b/today-cloud-core/src/main/java/infra/cloud/client/package-info.java new file mode 100644 index 0000000..45dfba1 --- /dev/null +++ b/today-cloud-core/src/main/java/infra/cloud/client/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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. + */ + +@NullMarked +package infra.cloud.client; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-cloud-core/src/main/java/infra/cloud/core/serialize/DeserializeFailedException.java b/today-cloud-core/src/main/java/infra/cloud/core/serialize/DeserializeFailedException.java deleted file mode 100644 index 14e647d..0000000 --- a/today-cloud-core/src/main/java/infra/cloud/core/serialize/DeserializeFailedException.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.core.serialize; - -import java.io.Serial; - -import infra.cloud.RemotingException; - -/** - * Deserialize Failed - * - * @author TODAY 2021/7/11 23:54 - */ -public class DeserializeFailedException extends RemotingException { - @Serial - private static final long serialVersionUID = 1L; - - public DeserializeFailedException() { - super(); - } - - public DeserializeFailedException(String message) { - super(message); - } - - public DeserializeFailedException(Throwable cause) { - super(cause); - } - - public DeserializeFailedException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/today-cloud-core/src/main/java/infra/cloud/core/serialize/Deserializer.java b/today-cloud-core/src/main/java/infra/cloud/core/serialize/Deserializer.java deleted file mode 100644 index 192c41f..0000000 --- a/today-cloud-core/src/main/java/infra/cloud/core/serialize/Deserializer.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.core.serialize; - -import java.io.IOException; -import java.io.InputStream; - -/** - * A strategy interface for converting from data in an InputStream to an Object. - * - * @author TODAY 2021/7/8 22:53 - * @author Gary Russell - * @author Mark Fisher - */ -@Deprecated -@FunctionalInterface -public interface Deserializer { - - /** - * Read (assemble) an object of type T from the given InputStream. - *

Note: Implementations should not close the given InputStream - * (or any decorators of that InputStream) but rather leave this up - * to the caller. - * - * @param inputStream the input stream - * @return the deserialized object - * @throws IOException in case of errors reading from the stream - */ - Object deserialize(InputStream inputStream) throws IOException, ClassNotFoundException; - -} diff --git a/today-cloud-core/src/main/java/infra/cloud/core/serialize/JdkSerialization.java b/today-cloud-core/src/main/java/infra/cloud/core/serialize/JdkSerialization.java deleted file mode 100644 index bffb97c..0000000 --- a/today-cloud-core/src/main/java/infra/cloud/core/serialize/JdkSerialization.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.core.serialize; - -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.OutputStream; - -/** - * Jdk Serialization - * - * @author TODAY 2021/7/9 21:25 - */ -public class JdkSerialization extends Serialization { - - @Override - public void serialize(final Object object, final OutputStream output) throws IOException { - try (ObjectOutputStream oos = new ObjectOutputStream(output)) { - oos.writeObject(object); - } - } - - @Override - protected final Object deserializeInternal(final InputStream inputStream) throws IOException, ClassNotFoundException { - try (final ObjectInputStream objectInput = new ObjectInputStream(inputStream)) { - return objectInput.readObject(); - } - } -} diff --git a/today-cloud-core/src/main/java/infra/cloud/core/serialize/KryoSerialization.java b/today-cloud-core/src/main/java/infra/cloud/core/serialize/KryoSerialization.java deleted file mode 100644 index b9a1c16..0000000 --- a/today-cloud-core/src/main/java/infra/cloud/core/serialize/KryoSerialization.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.core.serialize; - -import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryo.io.Input; -import com.esotericsoftware.kryo.io.Output; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import infra.lang.Assert; - -/** - * Kryo Serialization - * - * @author TODAY 2021/7/21 22:12 - */ -public class KryoSerialization extends Serialization { - private final Kryo kryo; - - public KryoSerialization() { - this(new Kryo()); - } - - public KryoSerialization(Kryo kryo) { - Assert.notNull(kryo, "Kryo is required"); - this.kryo = kryo; - } - - @Override - public void serialize(Object object, OutputStream output) throws IOException { - Output kryoOutput = new Output(output); - kryo.writeClassAndObject(kryoOutput, object); - } - - @Override - public Object deserializeInternal(InputStream inputStream) { - Input input = new Input(inputStream); - return kryo.readClassAndObject(input); - } - - public Kryo getKryo() { - return kryo; - } - -} diff --git a/today-cloud-core/src/main/java/infra/cloud/core/serialize/Serialization.java b/today-cloud-core/src/main/java/infra/cloud/core/serialize/Serialization.java deleted file mode 100644 index 942c7f6..0000000 --- a/today-cloud-core/src/main/java/infra/cloud/core/serialize/Serialization.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.core.serialize; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * For streaming an object to an OutputStream and - * for converting from data in an InputStream to an Object - * - * @author TODAY 2021/7/8 23:04 - */ -@Deprecated -public abstract class Serialization implements Serializer, Deserializer { - - /** - * Write an object of type T to the given OutputStream. - *

Note: Implementations should not close the given OutputStream - * (or any decorators of that OutputStream) but rather leave this up - * to the caller. - * - * @param object the object to serialize - * @param output the output stream - * @throws IOException in case of errors writing to the stream - */ - @Override - public abstract void serialize(Object object, OutputStream output) throws IOException; - - /** - * Read (assemble) an object of type T from the given InputStream. - *

Note: Implementations should not close the given InputStream - * (or any decorators of that InputStream) but rather leave this up - * to the caller. - * - * @param inputStream the input stream - * @return the deserialized object - * @throws IOException in case of errors reading from the stream - * @throws ClassNotFoundException if target type not in classpath - */ - @Override - @SuppressWarnings("unchecked") - public T deserialize(InputStream inputStream) throws IOException, ClassNotFoundException { - return (T) deserializeInternal(inputStream); - } - - protected Object deserializeInternal(InputStream inputStream) throws IOException, ClassNotFoundException { - throw new UnsupportedOperationException(); - } - -} diff --git a/today-cloud-core/src/main/java/infra/cloud/core/serialize/Serializer.java b/today-cloud-core/src/main/java/infra/cloud/core/serialize/Serializer.java deleted file mode 100644 index b4324be..0000000 --- a/today-cloud-core/src/main/java/infra/cloud/core/serialize/Serializer.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.core.serialize; - -import java.io.IOException; -import java.io.OutputStream; - -/** - * A strategy interface for streaming an object to an OutputStream. - * - * @author Gary Russell - * @author Mark Fisher - * @author TODAY 2021/7/8 22:52 - */ -@Deprecated -@FunctionalInterface -public interface Serializer { - - /** - * Write an object of type T to the given OutputStream. - *

Note: Implementations should not close the given OutputStream - * (or any decorators of that OutputStream) but rather leave this up - * to the caller. - * - * @param object the object to serialize - * @param output the output stream - * @throws IOException in case of errors writing to the stream - */ - void serialize(Object object, OutputStream output) throws IOException; - -} diff --git a/today-cloud-core/src/main/java/infra/cloud/net/HostInfo.java b/today-cloud-core/src/main/java/infra/cloud/net/HostInfo.java new file mode 100644 index 0000000..14b1919 --- /dev/null +++ b/today-cloud-core/src/main/java/infra/cloud/net/HostInfo.java @@ -0,0 +1,67 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.net; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; + +import infra.lang.Assert; + +/** + * Host information. + * + * @author 海子 Yang + * @since 1.0 2025/8/6 23:07 + */ +public class HostInfo { + + private final String hostname; + + private final String ipAddress; + + public HostInfo(String hostname, String ipAddress) { + Assert.notNull(hostname, "hostname is required"); + Assert.notNull(ipAddress, "ipAddress is required"); + this.hostname = hostname; + this.ipAddress = ipAddress; + } + + public int getIpAddressAsInt() { + InetAddress inetAddress; + String host = this.ipAddress; + if (host == null) { + host = this.hostname; + } + try { + inetAddress = InetAddress.getByName(host); + } + catch (final UnknownHostException e) { + throw new IllegalArgumentException(e); + } + return ByteBuffer.wrap(inetAddress.getAddress()).getInt(); + } + + public String getIpAddress() { + return this.ipAddress; + } + + public String getHostname() { + return this.hostname; + } + +} diff --git a/today-cloud-core/src/main/java/infra/cloud/net/HostInfoEnvironmentPostProcessor.java b/today-cloud-core/src/main/java/infra/cloud/net/HostInfoEnvironmentPostProcessor.java new file mode 100644 index 0000000..4536dec --- /dev/null +++ b/today-cloud-core/src/main/java/infra/cloud/net/HostInfoEnvironmentPostProcessor.java @@ -0,0 +1,60 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.net; + +import java.util.LinkedHashMap; + +import infra.app.Application; +import infra.app.context.config.ConfigDataEnvironmentPostProcessor; +import infra.app.env.EnvironmentPostProcessor; +import infra.context.properties.bind.Bindable; +import infra.context.properties.bind.Binder; +import infra.context.properties.source.ConfigurationPropertySources; +import infra.core.Ordered; +import infra.core.env.ConfigurableEnvironment; +import infra.core.env.MapPropertySource; + +/** + * @author 海子 Yang + */ +public class HostInfoEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { + + private static final int ORDER = Math.addExact(ConfigDataEnvironmentPostProcessor.ORDER, 1); + + @Override + public int getOrder() { + return ORDER; + } + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, Application application) { + HostInfo hostInfo = getFirstNonLoopbackHostInfo(environment); + LinkedHashMap map = new LinkedHashMap<>(); + map.put("infra.app.hostname", hostInfo.getHostname()); + map.put("infra.app.ip-address", hostInfo.getIpAddress()); + MapPropertySource propertySource = new MapPropertySource("infraAppHostInfo", map); + environment.getPropertySources().addLast(propertySource); + } + + private HostInfo getFirstNonLoopbackHostInfo(ConfigurableEnvironment environment) { + InetProperties target = new InetProperties(); + ConfigurationPropertySources.attach(environment); + Binder.get(environment).bind(InetProperties.PREFIX, Bindable.ofInstance(target)); + return new InetService(target).findFirstNonLoopbackHostInfo(); + } + +} diff --git a/today-cloud-core/src/main/java/infra/cloud/net/InetProperties.java b/today-cloud-core/src/main/java/infra/cloud/net/InetProperties.java new file mode 100644 index 0000000..c36b1ff --- /dev/null +++ b/today-cloud-core/src/main/java/infra/cloud/net/InetProperties.java @@ -0,0 +1,122 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.net; + +import java.net.InetAddress; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; + +import infra.context.properties.ConfigurationProperties; +import infra.format.annotation.DurationUnit; + +/** + * Properties for {@link InetService}. + * + * @author Spencer Gibb + * @author 海子 Yang + */ +@ConfigurationProperties(InetProperties.PREFIX) +public class InetProperties { + + /** + * Prefix for the Inet properties. + */ + public static final String PREFIX = "infra.cloud.inet"; + + /** + * The default hostname. Used in case of errors. + */ + private String defaultHostname = "localhost"; + + /** + * The default IP address. Used in case of errors. + */ + private String defaultIpAddress = "127.0.0.1"; + + /** + * Timeout for calculating hostname. + */ + @DurationUnit(ChronoUnit.SECONDS) + private Duration timeout = Duration.ofSeconds(4); + + /** + * List of Java regular expressions for network interfaces that will be ignored. + */ + private List ignoredInterfaces = new ArrayList<>(); + + /** + * Whether to use only interfaces with site local addresses. See + * {@link InetAddress#isSiteLocalAddress()} for more details. + */ + private boolean useOnlySiteLocalInterfaces = false; + + /** + * List of Java regular expressions for network addresses that will be preferred. + */ + private List preferredNetworks = new ArrayList<>(); + + public String getDefaultHostname() { + return this.defaultHostname; + } + + public void setDefaultHostname(String defaultHostname) { + this.defaultHostname = defaultHostname; + } + + public String getDefaultIpAddress() { + return this.defaultIpAddress; + } + + public void setDefaultIpAddress(String defaultIpAddress) { + this.defaultIpAddress = defaultIpAddress; + } + + public Duration getTimeout() { + return timeout; + } + + public void setTimeout(Duration timeout) { + this.timeout = timeout; + } + + public List getIgnoredInterfaces() { + return this.ignoredInterfaces; + } + + public void setIgnoredInterfaces(List ignoredInterfaces) { + this.ignoredInterfaces = ignoredInterfaces; + } + + public boolean isUseOnlySiteLocalInterfaces() { + return this.useOnlySiteLocalInterfaces; + } + + public void setUseOnlySiteLocalInterfaces(boolean useOnlySiteLocalInterfaces) { + this.useOnlySiteLocalInterfaces = useOnlySiteLocalInterfaces; + } + + public List getPreferredNetworks() { + return this.preferredNetworks; + } + + public void setPreferredNetworks(List preferredNetworks) { + this.preferredNetworks = preferredNetworks; + } + +} diff --git a/today-cloud-core/src/main/java/infra/cloud/net/InetService.java b/today-cloud-core/src/main/java/infra/cloud/net/InetService.java new file mode 100644 index 0000000..66a9675 --- /dev/null +++ b/today-cloud-core/src/main/java/infra/cloud/net/InetService.java @@ -0,0 +1,152 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.net; + +import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.UnknownHostException; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.util.concurrent.Future; + +import static infra.util.concurrent.Future.run; + +/** + * @author Spencer Gibb + * @author Sergey Tsypanov + * @author 海子 Yang + */ +public class InetService { + + private static final Logger log = LoggerFactory.getLogger(InetService.class); + + private final InetProperties properties; + + public InetService(InetProperties properties) { + this.properties = properties; + } + + public HostInfo findFirstNonLoopbackHostInfo() { + InetAddress address = findFirstNonLoopbackAddress(); + if (address != null) { + return convertAddress(address); + } + return new HostInfo(properties.getDefaultHostname(), properties.getDefaultIpAddress()); + } + + public InetAddress findFirstNonLoopbackAddress() { + InetAddress result = null; + try { + int lowest = Integer.MAX_VALUE; + var networkInterfaces = NetworkInterface.getNetworkInterfaces(); + while (networkInterfaces.hasMoreElements()) { + NetworkInterface ifc = networkInterfaces.nextElement(); + if (ifc.isUp()) { + log.trace("Testing interface: {}", ifc.getDisplayName()); + if (ifc.getIndex() < lowest || result == null) { + lowest = ifc.getIndex(); + } + else if (result != null) { + continue; + } + + if (!ignoreInterface(ifc.getDisplayName())) { + var addresses = ifc.getInetAddresses(); + while (addresses.hasMoreElements()) { + InetAddress address = addresses.nextElement(); + if (address instanceof Inet4Address && !address.isLoopbackAddress() && isPreferredAddress(address)) { + log.trace("Found non-loopback interface: {}", ifc.getDisplayName()); + result = address; + } + } + } + } + } + } + catch (IOException ex) { + log.error("Cannot get first non-loopback address", ex); + } + + if (result != null) { + return result; + } + + try { + return InetAddress.getLocalHost(); + } + catch (UnknownHostException e) { + log.warn("Unable to retrieve localhost"); + } + + return null; + } + + // For testing. + boolean isPreferredAddress(InetAddress address) { + if (properties.isUseOnlySiteLocalInterfaces()) { + final boolean siteLocalAddress = address.isSiteLocalAddress(); + if (!siteLocalAddress) { + log.trace("Ignoring address: {}", address.getHostAddress()); + } + return siteLocalAddress; + } + final List preferredNetworks = properties.getPreferredNetworks(); + if (preferredNetworks.isEmpty()) { + return true; + } + for (String regex : preferredNetworks) { + final String hostAddress = address.getHostAddress(); + if (hostAddress.matches(regex) || hostAddress.startsWith(regex)) { + return true; + } + } + log.trace("Ignoring address: {}", address.getHostAddress()); + return false; + } + + // For testing + boolean ignoreInterface(String interfaceName) { + for (String regex : properties.getIgnoredInterfaces()) { + if (interfaceName.matches(regex)) { + log.trace("Ignoring interface: {}", interfaceName); + return true; + } + } + return false; + } + + public HostInfo convertAddress(final InetAddress address) { + Future result = run(address::getHostName); + + String hostname; + try { + hostname = result.get(properties.getTimeout().toMillis(), TimeUnit.MILLISECONDS); + } + catch (Exception e) { + result.cancel(true); + log.info("Cannot determine local hostname"); + hostname = "localhost"; + } + return new HostInfo(hostname, address.getHostAddress()); + } + +} diff --git a/today-cloud-core/src/main/java/infra/cloud/package-info.java b/today-cloud-core/src/main/java/infra/cloud/package-info.java new file mode 100644 index 0000000..e911378 --- /dev/null +++ b/today-cloud-core/src/main/java/infra/cloud/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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. + */ + +@NullMarked +package infra.cloud; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-cloud-core/src/main/resources/META-INF/additional-infra-configuration-metadata.json b/today-cloud-core/src/main/resources/META-INF/additional-infra-configuration-metadata.json new file mode 100644 index 0000000..a64fcb4 --- /dev/null +++ b/today-cloud-core/src/main/resources/META-INF/additional-infra-configuration-metadata.json @@ -0,0 +1,10 @@ +{ + "properties": [ + { + "name": "infra.cloud.discovery.enabled", + "type": "java.lang.Boolean", + "description": "Service discovery enabled.", + "defaultValue": "true" + } + ] +} diff --git a/today-cloud-core/src/main/resources/META-INF/today.strategies b/today-cloud-core/src/main/resources/META-INF/today.strategies new file mode 100644 index 0000000..bb850ef --- /dev/null +++ b/today-cloud-core/src/main/resources/META-INF/today.strategies @@ -0,0 +1,4 @@ +infra.app.env.EnvironmentPostProcessor=infra.cloud.net.HostInfoEnvironmentPostProcessor + + + diff --git a/today-cloud-core/src/test/java/infra/cloud/net/InetServiceTests.java b/today-cloud-core/src/test/java/infra/cloud/net/InetServiceTests.java new file mode 100644 index 0000000..f87188e --- /dev/null +++ b/today-cloud-core/src/test/java/infra/cloud/net/InetServiceTests.java @@ -0,0 +1,120 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.net; + +import org.junit.jupiter.api.Test; + +import java.net.InetAddress; +import java.util.Arrays; +import java.util.Collections; + +import static org.assertj.core.api.BDDAssertions.then; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/6 22:16 + */ +class InetServiceTests { + + @Test + public void testGetFirstNonLoopbackHostInfo() { + InetService inetService = new InetService(new InetProperties()); + then(inetService.findFirstNonLoopbackHostInfo()).isNotNull(); + } + + @Test + public void testGetFirstNonLoopbackAddress() { + InetService inetService = new InetService(new InetProperties()); + then(inetService.findFirstNonLoopbackAddress()).isNotNull(); + } + + @Test + public void testConvert() throws Exception { + InetService inetService = new InetService(new InetProperties()); + then(inetService.convertAddress(InetAddress.getByName("localhost"))).isNotNull(); + } + + @Test + public void testHostInfo() { + InetService inetService = new InetService(new InetProperties()); + HostInfo info = inetService.findFirstNonLoopbackHostInfo(); + then(info.getIpAddressAsInt()).isNotNull(); + } + + @Test + public void testIgnoreInterface() { + InetProperties properties = new InetProperties(); + properties.setIgnoredInterfaces(Arrays.asList("docker0", "veth.*")); + InetService inetService = new InetService(properties); + + then(inetService.ignoreInterface("docker0")).isTrue().as("docker0 not ignored"); + then(inetService.ignoreInterface("vethAQI2QT")).as("vethAQI2QT0 not ignored").isTrue(); + then(inetService.ignoreInterface("docker1")).as("docker1 ignored").isFalse(); + } + + @Test + public void testDefaultIgnoreInterface() { + InetService inetService = new InetService(new InetProperties()); + then(inetService.ignoreInterface("docker0")).as("docker0 ignored").isFalse(); + } + + @Test + public void testSiteLocalAddresses() throws Exception { + InetProperties properties = new InetProperties(); + properties.setUseOnlySiteLocalInterfaces(true); + + InetService inetService = new InetService(properties); + then(inetService.isPreferredAddress(InetAddress.getByName("192.168.0.1"))).isTrue(); + then(inetService.isPreferredAddress(InetAddress.getByName("5.5.8.1"))).isFalse(); + } + + @Test + public void testPreferredNetworksRegex() throws Exception { + InetProperties properties = new InetProperties(); + properties.setPreferredNetworks(Arrays.asList("192.168.*", "10.0.*")); + + InetService inetService = new InetService(properties); + then(inetService.isPreferredAddress(InetAddress.getByName("192.168.0.1"))).isTrue(); + then(inetService.isPreferredAddress(InetAddress.getByName("5.5.8.1"))).isFalse(); + then(inetService.isPreferredAddress(InetAddress.getByName("10.0.10.1"))).isTrue(); + then(inetService.isPreferredAddress(InetAddress.getByName("10.255.10.1"))).isFalse(); + } + + @Test + public void testPreferredNetworksSimple() throws Exception { + InetProperties properties = new InetProperties(); + properties.setPreferredNetworks(Arrays.asList("192", "10.0")); + + InetService inetService = new InetService(properties); + then(inetService.isPreferredAddress(InetAddress.getByName("192.168.0.1"))).isTrue(); + then(inetService.isPreferredAddress(InetAddress.getByName("5.5.8.1"))).isFalse(); + then(inetService.isPreferredAddress(InetAddress.getByName("10.255.10.1"))).isFalse(); + then(inetService.isPreferredAddress(InetAddress.getByName("10.0.10.1"))).isTrue(); + } + + @Test + public void testPreferredNetworksListIsEmpty() throws Exception { + InetProperties properties = new InetProperties(); + properties.setPreferredNetworks(Collections.emptyList()); + InetService inetService = new InetService(properties); + then(inetService.isPreferredAddress(InetAddress.getByName("192.168.0.1"))).isTrue(); + then(inetService.isPreferredAddress(InetAddress.getByName("5.5.8.1"))).isTrue(); + then(inetService.isPreferredAddress(InetAddress.getByName("10.255.10.1"))).isTrue(); + then(inetService.isPreferredAddress(InetAddress.getByName("10.0.10.1"))).isTrue(); + } + +} \ No newline at end of file diff --git a/today-cloud-dependencies/build.gradle b/today-cloud-dependencies/build.gradle index 5b07dca..b3f5f70 100644 --- a/today-cloud-dependencies/build.gradle +++ b/today-cloud-dependencies/build.gradle @@ -7,7 +7,20 @@ javaPlatform { allowDependencies() } +def infraVersion() { + Properties properties = new Properties() + file("$rootDir/buildSrc/gradle.properties").withReader { + properties.load(it) + } + return properties['infraVersion'] +} + dependencies { + var version = infraVersion() + + api(platform("cn.taketoday:infra-bom:$version")) + api platform('io.micrometer:micrometer-tracing-bom:1.5.2') + api(platform(project(":today-cloud-bom"))) constraints { @@ -17,8 +30,11 @@ dependencies { publishing { publications { - mavenJava(MavenPublication) { + maven(MavenPublication) { from components.javaPlatform } } } + + +apply from: "$rootDir/gradle/signing.gradle" diff --git a/today-cloud-samples/.gitignore b/today-cloud-samples/.gitignore new file mode 100644 index 0000000..a783f21 --- /dev/null +++ b/today-cloud-samples/.gitignore @@ -0,0 +1,24 @@ +#*.class + +*.jar +*.war +*.ear +/target +/.settings +/.classpath +/.project +/.sts4-cache +.sts4-cache +/.idea/ +!/.idea/icon.svg +.DS_Store + +/.idea +*.iml +*.DS_Store +*.class +*/build/* + +.gradle +/build/ +/tmp/ diff --git a/.run/ClientApplication.run.xml b/today-cloud-samples/.run/ClientApplication.run.xml similarity index 82% rename from .run/ClientApplication.run.xml rename to today-cloud-samples/.run/ClientApplication.run.xml index 1e4c0aa..f075b08 100644 --- a/.run/ClientApplication.run.xml +++ b/today-cloud-samples/.run/ClientApplication.run.xml @@ -2,7 +2,7 @@

+ * This class extends {@link DefaultServiceInstance} and implements {@link Registration} + * to provide service registration capabilities over HTTP, including status management. + *

+ * + * @author Harry Yang + * @since 1.0 2023/11/20 21:59 + */ +public class HttpRegistration extends DefaultServiceInstance implements Registration { + + private Status status; + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } +} diff --git a/today-cloud-samples/today-service-registry-simple-api/src/main/java/infra/cloud/registry/simple/Status.java b/today-cloud-samples/today-service-registry-simple-api/src/main/java/infra/cloud/registry/simple/Status.java new file mode 100644 index 0000000..c325f07 --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-api/src/main/java/infra/cloud/registry/simple/Status.java @@ -0,0 +1,29 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.simple; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/24 16:48 + */ +public enum Status { + + UP, + + OUT_OF_SERVICE, + +} diff --git a/today-cloud-samples/today-service-registry-simple-api/src/main/java/infra/cloud/registry/simple/api/SimpleHttpServiceRegistryAPI.java b/today-cloud-samples/today-service-registry-simple-api/src/main/java/infra/cloud/registry/simple/api/SimpleHttpServiceRegistryAPI.java new file mode 100644 index 0000000..9880b11 --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-api/src/main/java/infra/cloud/registry/simple/api/SimpleHttpServiceRegistryAPI.java @@ -0,0 +1,57 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.simple.api; + +import java.util.List; + +import infra.cloud.registry.simple.HttpRegistration; +import infra.http.MediaType; +import infra.util.MultiValueMap; +import infra.web.annotation.DELETE; +import infra.web.annotation.GET; +import infra.web.annotation.POST; +import infra.web.annotation.PUT; +import infra.web.annotation.PathVariable; +import infra.web.annotation.RequestBody; +import infra.web.annotation.RequestMapping; + +/** + * HTTP API interface for simple service registry operations. + * Provides methods for registering, unregistering, updating and looking up services. + * + * @author 海子 Yang + * @since 1.0 2025/8/8 11:18 + */ +@RequestMapping("${registry.services.uri:/services}") +public interface SimpleHttpServiceRegistryAPI { + + @GET(produces = MediaType.APPLICATION_JSON_VALUE) + MultiValueMap services(); + + @GET("/{serviceId}") + List lookup(@PathVariable String serviceId); + + @POST + void register(@RequestBody HttpRegistration registration); + + @DELETE + void unregister(@RequestBody HttpRegistration registration); + + @PUT + void update(@RequestBody HttpRegistration registration); + +} diff --git a/today-cloud-samples/today-service-registry-simple-api/src/main/java/infra/cloud/registry/simple/config/SimpleHttpProperties.java b/today-cloud-samples/today-service-registry-simple-api/src/main/java/infra/cloud/registry/simple/config/SimpleHttpProperties.java new file mode 100644 index 0000000..7f8d72b --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-api/src/main/java/infra/cloud/registry/simple/config/SimpleHttpProperties.java @@ -0,0 +1,40 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.simple.config; + +import java.net.URI; + +import infra.context.properties.ConfigurationProperties; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/24 17:54 + */ +@ConfigurationProperties(prefix = "today.service-registry.simple") +public class SimpleHttpProperties { + + private URI uri; + + public void setUri(URI uri) { + this.uri = uri; + } + + public URI getUri() { + return uri; + } + +} diff --git a/today-cloud-samples/today-service-registry-simple-api/src/main/java/infra/cloud/registry/simple/config/SimpleHttpServiceRegistryAPIAutoConfiguration.java b/today-cloud-samples/today-service-registry-simple-api/src/main/java/infra/cloud/registry/simple/config/SimpleHttpServiceRegistryAPIAutoConfiguration.java new file mode 100644 index 0000000..afdd60b --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-api/src/main/java/infra/cloud/registry/simple/config/SimpleHttpServiceRegistryAPIAutoConfiguration.java @@ -0,0 +1,52 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.simple.config; + +import infra.beans.factory.config.ConfigurableBeanFactory; +import infra.beans.factory.config.EmbeddedValueResolver; +import infra.cloud.client.annotation.ConditionalOnDiscoveryEnabled; +import infra.cloud.registry.simple.api.SimpleHttpServiceRegistryAPI; +import infra.context.annotation.config.DisableDIAutoConfiguration; +import infra.context.condition.ConditionalOnMissingBean; +import infra.context.properties.EnableConfigurationProperties; +import infra.http.service.invoker.HttpServiceProxyFactory; +import infra.http.service.support.RestClientAdapter; +import infra.stereotype.Component; +import infra.web.client.RestClient; +import infra.web.client.config.RestClientAutoConfiguration; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/24 17:44 + */ +@ConditionalOnDiscoveryEnabled +@EnableConfigurationProperties(SimpleHttpProperties.class) +@DisableDIAutoConfiguration(after = RestClientAutoConfiguration.class) +public class SimpleHttpServiceRegistryAPIAutoConfiguration { + + @Component + @ConditionalOnMissingBean + public static SimpleHttpServiceRegistryAPI simpleHttpServiceRegistryAPI( + ConfigurableBeanFactory factory, SimpleHttpProperties properties, RestClient.Builder builder) { + RestClientAdapter adapter = RestClientAdapter.create(builder.baseURI(properties.getUri()).build()); + return HttpServiceProxyFactory.forAdapter(adapter) + .embeddedValueResolver(new EmbeddedValueResolver(factory)) + .build() + .createClient(SimpleHttpServiceRegistryAPI.class); + } + +} diff --git a/today-cloud-samples/today-service-registry-simple-api/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports b/today-cloud-samples/today-service-registry-simple-api/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports new file mode 100644 index 0000000..2d3a786 --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-api/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports @@ -0,0 +1 @@ +infra.cloud.registry.simple.config.SimpleHttpServiceRegistryAPIAutoConfiguration \ No newline at end of file diff --git a/today-cloud-samples/today-service-registry-simple-api/src/main/resources/META-INF/today.strategies b/today-cloud-samples/today-service-registry-simple-api/src/main/resources/META-INF/today.strategies new file mode 100644 index 0000000..e69de29 diff --git a/today-cloud-samples/today-service-registry-simple-discovery/build.gradle b/today-cloud-samples/today-service-registry-simple-discovery/build.gradle new file mode 100644 index 0000000..d1060b5 --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-discovery/build.gradle @@ -0,0 +1,11 @@ +description = "TODAY Service Simple Registry discovery" + + +dependencies { + api "cn.taketoday:today-service-discovery" + api project(":today-service-registry-simple-api") + + implementation 'cn.taketoday:infra-starter-webmvc' + + annotationProcessor 'cn.taketoday:infra-configuration-processor' +} \ No newline at end of file diff --git a/today-cloud-samples/today-service-registry-simple-discovery/src/main/java/infra/cloud/registry/simple/discovery/SimpleHttpDiscoveryClient.java b/today-cloud-samples/today-service-registry-simple-discovery/src/main/java/infra/cloud/registry/simple/discovery/SimpleHttpDiscoveryClient.java new file mode 100644 index 0000000..7028a42 --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-discovery/src/main/java/infra/cloud/registry/simple/discovery/SimpleHttpDiscoveryClient.java @@ -0,0 +1,62 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.simple.discovery; + +import java.util.ArrayList; +import java.util.List; + +import infra.cloud.client.DiscoveryClient; +import infra.cloud.client.ServiceInstance; +import infra.cloud.registry.simple.HttpRegistration; +import infra.cloud.registry.simple.Status; +import infra.cloud.registry.simple.api.SimpleHttpServiceRegistryAPI; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/24 16:42 + */ +public class SimpleHttpDiscoveryClient implements DiscoveryClient { + + private final SimpleHttpServiceRegistryAPI serviceRegistryAPI; + + public SimpleHttpDiscoveryClient(SimpleHttpServiceRegistryAPI serviceRegistryAPI) { + this.serviceRegistryAPI = serviceRegistryAPI; + } + + @Override + public String getDescription() { + return "Simple Http DiscoveryClient"; + } + + @Override + public List getInstances(String serviceId) { + List registrations = serviceRegistryAPI.lookup(serviceId); + List instances = new ArrayList<>(registrations.size()); + for (HttpRegistration registration : registrations) { + if (registration.getStatus() == Status.UP) { + instances.add(registration); + } + } + return instances; + } + + @Override + public List getServices() { + return new ArrayList<>(serviceRegistryAPI.services().keySet()); + } + +} diff --git a/today-cloud-samples/today-service-registry-simple-discovery/src/main/java/infra/cloud/registry/simple/discovery/SimpleHttpDiscoveryClientAutoConfiguration.java b/today-cloud-samples/today-service-registry-simple-discovery/src/main/java/infra/cloud/registry/simple/discovery/SimpleHttpDiscoveryClientAutoConfiguration.java new file mode 100644 index 0000000..d5e3b0c --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-discovery/src/main/java/infra/cloud/registry/simple/discovery/SimpleHttpDiscoveryClientAutoConfiguration.java @@ -0,0 +1,40 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.simple.discovery; + +import infra.cloud.client.annotation.ConditionalOnDiscoveryEnabled; +import infra.cloud.client.config.DiscoveryClientAutoConfiguration; +import infra.cloud.registry.simple.api.SimpleHttpServiceRegistryAPI; +import infra.cloud.registry.simple.config.SimpleHttpServiceRegistryAPIAutoConfiguration; +import infra.context.annotation.config.DisableDIAutoConfiguration; +import infra.stereotype.Component; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/24 17:40 + */ +@ConditionalOnDiscoveryEnabled +@DisableDIAutoConfiguration(before = DiscoveryClientAutoConfiguration.class, + after = SimpleHttpServiceRegistryAPIAutoConfiguration.class) +public class SimpleHttpDiscoveryClientAutoConfiguration { + + @Component + public static SimpleHttpDiscoveryClient simpleHttpDiscoveryClient(SimpleHttpServiceRegistryAPI simpleHttpServiceRegistryAPI) { + return new SimpleHttpDiscoveryClient(simpleHttpServiceRegistryAPI); + } + +} diff --git a/today-cloud-samples/today-service-registry-simple-discovery/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports b/today-cloud-samples/today-service-registry-simple-discovery/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports new file mode 100644 index 0000000..4681e25 --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-discovery/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports @@ -0,0 +1 @@ +infra.cloud.registry.simple.discovery.SimpleHttpDiscoveryClientAutoConfiguration \ No newline at end of file diff --git a/today-cloud-samples/today-service-registry-simple-registration/build.gradle b/today-cloud-samples/today-service-registry-simple-registration/build.gradle new file mode 100644 index 0000000..f1c4797 --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-registration/build.gradle @@ -0,0 +1,11 @@ +description = "TODAY Service Simple Registry" + + +dependencies { + api "cn.taketoday:today-service-registration" + api project(":today-service-registry-simple-api") + + implementation 'cn.taketoday:infra-starter-webmvc' + + annotationProcessor 'cn.taketoday:infra-configuration-processor' +} \ No newline at end of file diff --git a/today-cloud-samples/today-service-registry-simple-registration/src/main/java/infra/cloud/registry/simple/HttpRegistrationFactory.java b/today-cloud-samples/today-service-registry-simple-registration/src/main/java/infra/cloud/registry/simple/HttpRegistrationFactory.java new file mode 100644 index 0000000..511ffa3 --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-registration/src/main/java/infra/cloud/registry/simple/HttpRegistrationFactory.java @@ -0,0 +1,62 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.simple; + +import infra.cloud.provider.ServiceServerProperties; +import infra.cloud.registry.RegistrationFactory; +import infra.cloud.service.ServiceMetadata; +import infra.util.StringUtils; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/24 22:26 + */ +public class HttpRegistrationFactory implements RegistrationFactory { + + private final ServiceServerProperties serviceServerProperties; + + private final SimpleRegistryProperties registryProperties; + + public HttpRegistrationFactory(ServiceServerProperties serviceServerProperties, SimpleRegistryProperties registryProperties) { + this.serviceServerProperties = serviceServerProperties; + this.registryProperties = registryProperties; + } + + @Override + public HttpRegistration createRegistration(ServiceMetadata serviceMetadata) { + String host = registryProperties.getInstanceHost(); + if (StringUtils.isBlank(host)) { + throw new IllegalStateException("instanceHost must not be empty"); + } + + HttpRegistration registration = new HttpRegistration(); + registration.setStatus(Status.UP); + registration.setHost(host); + + registration.setServiceId(serviceMetadata.getId()); + registration.setPort(serviceServerProperties.getPort()); + registration.setInstanceId(registryProperties.getInstanceId()); + if (registration.getServiceId() == null) { + registration.setDefaultInstanceId(); + } + + registration.setDefaultInstanceId(); + registration.getMetadata().putAll(serviceMetadata.getProperties()); + return registration; + } + +} diff --git a/today-cloud-samples/today-service-registry-simple-registration/src/main/java/infra/cloud/registry/simple/SimpleAutoServiceRegistration.java b/today-cloud-samples/today-service-registry-simple-registration/src/main/java/infra/cloud/registry/simple/SimpleAutoServiceRegistration.java new file mode 100644 index 0000000..ca9a501 --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-registration/src/main/java/infra/cloud/registry/simple/SimpleAutoServiceRegistration.java @@ -0,0 +1,68 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.simple; + +import java.util.List; + +import infra.cloud.provider.ServicesProvider; +import infra.cloud.registry.AbstractAutoServiceRegistration; +import infra.cloud.registry.RegistrationFactory; +import infra.cloud.registry.RegistrationLifecycle; +import infra.cloud.registry.ServiceRegistry; + +/** + * Auto-service registration implementation for the simple registry. + *

+ * This class extends {@link AbstractAutoServiceRegistration} to provide + * automatic service registration capabilities using {@link HttpRegistration} + * and {@link Status}. It utilizes {@link HttpRegistrationFactory} for creating + * registration instances and {@link SimpleRegistryProperties} for configuration. + *

+ * + * @author 海子 Yang + * @since 1.0 2025/8/23 21:50 + */ +public class SimpleAutoServiceRegistration extends AbstractAutoServiceRegistration { + + private final HttpRegistrationFactory registrationFactory; + + private final SimpleRegistryProperties properties; + + public SimpleAutoServiceRegistration(ServiceRegistry serviceRegistry, + ServicesProvider servicesProvider, HttpRegistrationFactory registrationFactory, + SimpleRegistryProperties properties, List> registrationLifecycles) { + super(serviceRegistry, registrationLifecycles, servicesProvider); + this.registrationFactory = registrationFactory; + this.properties = properties; + } + + @Override + protected Object getConfiguration() { + return properties; + } + + @Override + protected RegistrationFactory getRegistrationFactory() { + return registrationFactory; + } + + @Override + protected boolean isEnabled() { + return properties.isEnabled(); + } + +} diff --git a/today-cloud-samples/today-service-registry-simple-registration/src/main/java/infra/cloud/registry/simple/SimpleHttpServiceRegistry.java b/today-cloud-samples/today-service-registry-simple-registration/src/main/java/infra/cloud/registry/simple/SimpleHttpServiceRegistry.java new file mode 100644 index 0000000..85da7b2 --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-registration/src/main/java/infra/cloud/registry/simple/SimpleHttpServiceRegistry.java @@ -0,0 +1,60 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.simple; + +import infra.cloud.registry.ServiceRegistry; +import infra.cloud.registry.simple.api.SimpleHttpServiceRegistryAPI; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/7 20:40 + */ +public class SimpleHttpServiceRegistry implements ServiceRegistry { + + private final SimpleHttpServiceRegistryAPI serviceRegistryAPI; + + public SimpleHttpServiceRegistry(SimpleHttpServiceRegistryAPI serviceRegistryAPI) { + this.serviceRegistryAPI = serviceRegistryAPI; + } + + @Override + public void register(HttpRegistration registration) { + serviceRegistryAPI.register(registration); + } + + @Override + public void unregister(HttpRegistration registration) { + serviceRegistryAPI.unregister(registration); + } + + @Override + public void close() { + + } + + @Override + public void setStatus(HttpRegistration registration, Status status) { + registration.setStatus(status); + serviceRegistryAPI.update(registration); + } + + @Override + public Status getStatus(HttpRegistration registration) { + return registration.getStatus(); + } + +} diff --git a/today-cloud-samples/today-service-registry-simple-registration/src/main/java/infra/cloud/registry/simple/SimpleRegistryProperties.java b/today-cloud-samples/today-service-registry-simple-registration/src/main/java/infra/cloud/registry/simple/SimpleRegistryProperties.java new file mode 100644 index 0000000..a571b51 --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-registration/src/main/java/infra/cloud/registry/simple/SimpleRegistryProperties.java @@ -0,0 +1,94 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.simple; + +import infra.cloud.net.InetService; +import infra.context.properties.ConfigurationProperties; +import infra.util.StringUtils; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/23 21:52 + */ +@ConfigurationProperties(prefix = "today.service-registry.simple") +public class SimpleRegistryProperties { + + private boolean enabled = true; + + /** ID used to register with. Defaults to a random UUID. */ + private String instanceId; + + /** + * Predefined host with which a service can register itself. + */ + private String instanceHost; + + /** + * IP address to use when accessing service (must also set preferIpAddress to use). + */ + private String instanceIpAddress; + + /** + * Use ip address rather than hostname during registration. + */ + private boolean preferIpAddress = false; + + public SimpleRegistryProperties(InetService inetService) { + var hostInfo = inetService.findFirstNonLoopbackHostInfo(); + this.instanceHost = hostInfo.getHostname(); + this.instanceIpAddress = hostInfo.getIpAddress(); + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getInstanceHost() { + if (this.preferIpAddress && StringUtils.hasText(this.instanceIpAddress)) { + return this.instanceIpAddress; + } + return this.instanceHost; + } + + public void setInstanceHost(String instanceHost) { + this.instanceHost = instanceHost; + } + + public String getInstanceId() { + return instanceId; + } + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + public void setInstanceIpAddress(String instanceIpAddress) { + this.instanceIpAddress = instanceIpAddress; + } + + public boolean isPreferIpAddress() { + return preferIpAddress; + } + + public void setPreferIpAddress(boolean preferIpAddress) { + this.preferIpAddress = preferIpAddress; + } +} diff --git a/today-cloud-samples/today-service-registry-simple-registration/src/main/java/infra/cloud/registry/simple/annotation/config/SimpleHttpServiceRegistryAutoConfiguration.java b/today-cloud-samples/today-service-registry-simple-registration/src/main/java/infra/cloud/registry/simple/annotation/config/SimpleHttpServiceRegistryAutoConfiguration.java new file mode 100644 index 0000000..6ea40c5 --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-registration/src/main/java/infra/cloud/registry/simple/annotation/config/SimpleHttpServiceRegistryAutoConfiguration.java @@ -0,0 +1,78 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.simple.annotation.config; + +import infra.beans.factory.ObjectProvider; +import infra.cloud.client.annotation.ConditionalOnDiscoveryEnabled; +import infra.cloud.net.InetProperties; +import infra.cloud.net.InetService; +import infra.cloud.provider.ServiceServerProperties; +import infra.cloud.provider.ServicesProvider; +import infra.cloud.registry.RegistrationLifecycle; +import infra.cloud.registry.simple.HttpRegistration; +import infra.cloud.registry.simple.HttpRegistrationFactory; +import infra.cloud.registry.simple.SimpleAutoServiceRegistration; +import infra.cloud.registry.simple.SimpleHttpServiceRegistry; +import infra.cloud.registry.simple.SimpleRegistryProperties; +import infra.cloud.registry.simple.api.SimpleHttpServiceRegistryAPI; +import infra.cloud.registry.simple.config.SimpleHttpServiceRegistryAPIAutoConfiguration; +import infra.context.annotation.config.DisableDIAutoConfiguration; +import infra.context.condition.ConditionalOnMissingBean; +import infra.context.properties.EnableConfigurationProperties; +import infra.stereotype.Component; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/23 21:39 + */ +@ConditionalOnDiscoveryEnabled +@EnableConfigurationProperties({ InetProperties.class }) +@DisableDIAutoConfiguration(after = SimpleHttpServiceRegistryAPIAutoConfiguration.class) +public class SimpleHttpServiceRegistryAutoConfiguration { + + @Component + public static SimpleRegistryProperties simpleRegistryProperties(InetService inetService) { + return new SimpleRegistryProperties(inetService); + } + + @Component + @ConditionalOnMissingBean + public static SimpleHttpServiceRegistry simpleHttpServiceRegistry(SimpleHttpServiceRegistryAPI serviceRegistryAPI) { + return new SimpleHttpServiceRegistry(serviceRegistryAPI); + } + + @Component + public static SimpleAutoServiceRegistration autoServiceRegistration( + HttpRegistrationFactory httpRegistrationFactory, ServicesProvider servicesProvider, + SimpleRegistryProperties properties, SimpleHttpServiceRegistry serviceRegistry, + ObjectProvider> registrationLifecycles) { + return new SimpleAutoServiceRegistration(serviceRegistry, servicesProvider, + httpRegistrationFactory, properties, registrationLifecycles.orderedList()); + } + + @Component + @ConditionalOnMissingBean + public static InetService inetService(InetProperties inetProperties) { + return new InetService(inetProperties); + } + + @Component + public static HttpRegistrationFactory httpRegistrationFactory(ServiceServerProperties serverProperties, SimpleRegistryProperties properties) { + return new HttpRegistrationFactory(serverProperties, properties); + } + +} diff --git a/today-cloud-samples/today-service-registry-simple-registration/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports b/today-cloud-samples/today-service-registry-simple-registration/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports new file mode 100644 index 0000000..2b72363 --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-registration/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports @@ -0,0 +1 @@ +infra.cloud.registry.simple.annotation.config.SimpleHttpServiceRegistryAutoConfiguration \ No newline at end of file diff --git a/today-cloud-samples/today-service-registry-simple-registration/src/main/resources/META-INF/today.strategies b/today-cloud-samples/today-service-registry-simple-registration/src/main/resources/META-INF/today.strategies new file mode 100644 index 0000000..e69de29 diff --git a/today-cloud-samples/today-service-registry-simple-server/build.gradle b/today-cloud-samples/today-service-registry-simple-server/build.gradle new file mode 100644 index 0000000..f113c6e --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-server/build.gradle @@ -0,0 +1,11 @@ +description = "TODAY Service Simple Registry Server" + + +dependencies { + api "cn.taketoday:today-cloud-core" + api project(":today-service-registry-simple-api") + + implementation 'cn.taketoday:infra-starter-webmvc' + + annotationProcessor 'cn.taketoday:infra-configuration-processor' +} \ No newline at end of file diff --git a/today-service-registry/src/main/java/infra/cloud/registry/ServiceNotFoundException.java b/today-cloud-samples/today-service-registry-simple-server/src/main/java/infra/cloud/registry/simple/server/ServiceNotFoundException.java similarity index 57% rename from today-service-registry/src/main/java/infra/cloud/registry/ServiceNotFoundException.java rename to today-cloud-samples/today-service-registry-simple-server/src/main/java/infra/cloud/registry/simple/server/ServiceNotFoundException.java index 1bede91..f741ef1 100644 --- a/today-service-registry/src/main/java/infra/cloud/registry/ServiceNotFoundException.java +++ b/today-cloud-samples/today-service-registry-simple-server/src/main/java/infra/cloud/registry/simple/server/ServiceNotFoundException.java @@ -1,26 +1,26 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.cloud.registry; +package infra.cloud.registry.simple.server; + +import org.jspecify.annotations.Nullable; import java.io.Serial; import infra.cloud.RemotingException; -import infra.lang.Nullable; /** * @author Harry Yang diff --git a/today-cloud-samples/today-service-registry-simple-server/src/main/java/infra/cloud/registry/simple/server/SimpleHttpServiceRegistryEndpoint.java b/today-cloud-samples/today-service-registry-simple-server/src/main/java/infra/cloud/registry/simple/server/SimpleHttpServiceRegistryEndpoint.java new file mode 100644 index 0000000..71f7948 --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-server/src/main/java/infra/cloud/registry/simple/server/SimpleHttpServiceRegistryEndpoint.java @@ -0,0 +1,108 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.simple.server; + +import java.util.List; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +import infra.cloud.registry.simple.HttpRegistration; +import infra.cloud.registry.simple.api.SimpleHttpServiceRegistryAPI; +import infra.http.HttpStatus; +import infra.http.MediaType; +import infra.lang.Assert; +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.util.CollectionUtils; +import infra.util.MultiValueMap; +import infra.web.annotation.DELETE; +import infra.web.annotation.ExceptionHandler; +import infra.web.annotation.GET; +import infra.web.annotation.POST; +import infra.web.annotation.PUT; +import infra.web.annotation.PathVariable; +import infra.web.annotation.RequestBody; +import infra.web.annotation.ResponseStatus; +import infra.web.annotation.RestController; + +/** + * @author TODAY 2021/7/9 23:08 + */ +@RestController +class SimpleHttpServiceRegistryEndpoint implements SimpleHttpServiceRegistryAPI { + + private static final Logger log = LoggerFactory.getLogger(SimpleHttpServiceRegistryEndpoint.class); + + private final MultiValueMap serviceMapping + = MultiValueMap.forAdaption(new ConcurrentHashMap<>()); + + @Override + @GET(produces = MediaType.APPLICATION_JSON_VALUE) + public MultiValueMap services() { + return serviceMapping; + } + + @Override + @GET("/{serviceId}") + public List lookup(@PathVariable String serviceId) { + List registrations = serviceMapping.get(serviceId); + if (CollectionUtils.isEmpty(registrations)) { + throw new ServiceNotFoundException(serviceId); + } + + return registrations; + } + + @POST + @Override + public void register(@RequestBody HttpRegistration registration) { + Assert.notNull(registration.getServiceId(), "Service ID is required"); + log.info("Registering service: [{}] ", registration); + serviceMapping.add(registration.getServiceId(), registration); + } + + @PUT + @Override + public void update(HttpRegistration registration) { + log.info("Updating service: [{}] ", registration); + List registrations = serviceMapping.get(registration.getServiceId()); + if (CollectionUtils.isEmpty(registrations)) { + throw new ServiceNotFoundException(registration.getServiceId()); + } + else { + registrations.removeIf(r -> r.getInstanceId().equals(registration.getInstanceId())); + registrations.add(registration); + } + } + + @DELETE + @Override + public void unregister(@RequestBody HttpRegistration registration) { + List serviceDefinitions = serviceMapping.get(registration.getServiceId()); + if (CollectionUtils.isNotEmpty(serviceDefinitions) + && serviceDefinitions.removeIf(def -> Objects.equals(def, registration))) { + log.info("un-register service: [{}] ", registration); + } + } + + @ResponseStatus(HttpStatus.NOT_FOUND) + @ExceptionHandler(ServiceNotFoundException.class) + void handleServiceNotFound(ServiceNotFoundException serviceNotFound) { + + } + +} diff --git a/today-cloud-samples/today-service-registry-simple-server/src/main/java/infra/cloud/registry/simple/server/SimpleHttpServiceRegistryServerAutoConfiguration.java b/today-cloud-samples/today-service-registry-simple-server/src/main/java/infra/cloud/registry/simple/server/SimpleHttpServiceRegistryServerAutoConfiguration.java new file mode 100644 index 0000000..85045c1 --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-server/src/main/java/infra/cloud/registry/simple/server/SimpleHttpServiceRegistryServerAutoConfiguration.java @@ -0,0 +1,34 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.simple.server; + +import infra.context.annotation.config.DisableDIAutoConfiguration; +import infra.stereotype.Component; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/24 18:23 + */ +@DisableDIAutoConfiguration +public class SimpleHttpServiceRegistryServerAutoConfiguration { + + @Component + static SimpleHttpServiceRegistryEndpoint simpleHttpServiceRegistryEndpoint() { + return new SimpleHttpServiceRegistryEndpoint(); + } + +} diff --git a/today-cloud-samples/today-service-registry-simple-server/src/main/resources/META-INF/additional-infra-configuration-metadata.json b/today-cloud-samples/today-service-registry-simple-server/src/main/resources/META-INF/additional-infra-configuration-metadata.json new file mode 100644 index 0000000..d4c884d --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-server/src/main/resources/META-INF/additional-infra-configuration-metadata.json @@ -0,0 +1,10 @@ +{ + "properties": [ + { + "name": "registry.services.uri", + "type": "java.lang.String", + "description": "Simple service registry URI.", + "defaultValue": "/services" + } + ] +} diff --git a/today-cloud-samples/today-service-registry-simple-server/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports b/today-cloud-samples/today-service-registry-simple-server/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports new file mode 100644 index 0000000..faabf92 --- /dev/null +++ b/today-cloud-samples/today-service-registry-simple-server/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports @@ -0,0 +1 @@ +infra.cloud.registry.simple.server.SimpleHttpServiceRegistryServerAutoConfiguration \ No newline at end of file diff --git a/today-cloud-samples/today-service-registry-simple-server/src/main/resources/META-INF/today.strategies b/today-cloud-samples/today-service-registry-simple-server/src/main/resources/META-INF/today.strategies new file mode 100644 index 0000000..e69de29 diff --git a/today-remoting-micrometer/build.gradle b/today-remoting-micrometer/build.gradle new file mode 100644 index 0000000..58b716e --- /dev/null +++ b/today-remoting-micrometer/build.gradle @@ -0,0 +1,31 @@ +plugins { + id 'java-library' + id 'maven-publish' + id 'signing' +} + +description = 'Transparent Metrics exposure to Micrometer' + +dependencies { + api project(':today-remoting') + api 'io.micrometer:micrometer-observation' + api 'io.micrometer:micrometer-core' + api 'io.micrometer:micrometer-tracing' + + testImplementation testFixtures(project(':today-remoting')) + testImplementation project(':today-remoting-transport-tcp') + testImplementation project(':today-remoting-transport-local') + testImplementation 'org.awaitility:awaitility' + testImplementation 'io.projectreactor:reactor-test' + testImplementation "io.micrometer:micrometer-test" + testImplementation "io.micrometer:micrometer-tracing-integration-test" + + testRuntimeOnly 'ch.qos.logback:logback-classic' +} + +jar { + manifest { + attributes("Automatic-Module-Name": "infra.remoting.micrometer") + } +} + diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/MicrometerChannel.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/MicrometerChannel.java new file mode 100644 index 0000000..5bd611b --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/MicrometerChannel.java @@ -0,0 +1,195 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer; + +import org.reactivestreams.Publisher; + +import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import infra.remoting.Channel; +import infra.remoting.DecoratingChannel; +import infra.remoting.Payload; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.Timer.Sample; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.SignalType; + +import static reactor.core.publisher.SignalType.CANCEL; +import static reactor.core.publisher.SignalType.ON_COMPLETE; +import static reactor.core.publisher.SignalType.ON_ERROR; + +/** + * An implementation of {@link Channel} that intercepts interactions and gathers Micrometer metrics + * about them. + * + *

The metrics are called {@code infra.remoting.[ metadata.push | request.channel | request.fnf | + * request.response | request.stream ]} and is tagged with {@code signal.type} ({@link SignalType}) + * and any additional configured tags. + * + * @see Micrometer + */ +final class MicrometerChannel extends DecoratingChannel { + + private final InteractionCounters metadataPush; + + private final InteractionCounters requestChannel; + + private final InteractionCounters requestFireAndForget; + + private final InteractionTimers requestResponse; + + private final InteractionCounters requestStream; + + /** + * Creates a new {@link Channel}. + * + * @param delegate the {@link Channel} to delegate to + * @param meterRegistry the {@link MeterRegistry} to use + * @param tags additional tags to attach to {@link Meter}s + * @throws NullPointerException if {@code delegate} or {@code meterRegistry} is {@code null} + */ + MicrometerChannel(Channel delegate, MeterRegistry meterRegistry, Tag... tags) { + super(delegate); + Objects.requireNonNull(meterRegistry, "meterRegistry is required"); + + this.metadataPush = new InteractionCounters(meterRegistry, "metadata.push", tags); + this.requestChannel = new InteractionCounters(meterRegistry, "request.channel", tags); + this.requestFireAndForget = new InteractionCounters(meterRegistry, "request.fnf", tags); + this.requestResponse = new InteractionTimers(meterRegistry, "request.response", tags); + this.requestStream = new InteractionCounters(meterRegistry, "request.stream", tags); + } + + @Override + public Mono fireAndForget(Payload payload) { + return delegate.fireAndForget(payload).doFinally(requestFireAndForget); + } + + @Override + public Mono metadataPush(Payload payload) { + return delegate.metadataPush(payload).doFinally(metadataPush); + } + + @Override + public Flux requestChannel(Publisher payloads) { + return delegate.requestChannel(payloads).doFinally(requestChannel); + } + + @Override + public Mono requestResponse(Payload payload) { + return Mono.defer(() -> { + Sample sample = requestResponse.start(); + return delegate + .requestResponse(payload) + .doFinally(signalType -> requestResponse.accept(sample, signalType)); + }); + } + + @Override + public Flux requestStream(Payload payload) { + return delegate.requestStream(payload).doFinally(requestStream); + } + + private static final class InteractionCounters implements Consumer { + + private final Counter cancel; + + private final Counter onComplete; + + private final Counter onError; + + private InteractionCounters(MeterRegistry meterRegistry, String interactionModel, Tag... tags) { + this.cancel = counter(meterRegistry, interactionModel, CANCEL, tags); + this.onComplete = counter(meterRegistry, interactionModel, ON_COMPLETE, tags); + this.onError = counter(meterRegistry, interactionModel, ON_ERROR, tags); + } + + @Override + public void accept(SignalType signalType) { + switch (signalType) { + case CANCEL: + cancel.increment(); + break; + case ON_COMPLETE: + onComplete.increment(); + break; + case ON_ERROR: + onError.increment(); + break; + } + } + + private static Counter counter( + MeterRegistry meterRegistry, String interactionModel, SignalType signalType, Tag... tags) { + + return meterRegistry.counter( + "infra.remoting." + interactionModel, Tags.of(tags).and("signal.type", signalType.name())); + } + } + + private static final class InteractionTimers implements BiConsumer { + + private final Timer cancel; + + private final MeterRegistry meterRegistry; + + private final Timer onComplete; + + private final Timer onError; + + private InteractionTimers(MeterRegistry meterRegistry, String interactionModel, Tag... tags) { + this.meterRegistry = meterRegistry; + + this.cancel = timer(meterRegistry, interactionModel, CANCEL, tags); + this.onComplete = timer(meterRegistry, interactionModel, ON_COMPLETE, tags); + this.onError = timer(meterRegistry, interactionModel, ON_ERROR, tags); + } + + @Override + public void accept(Sample sample, SignalType signalType) { + switch (signalType) { + case CANCEL: + sample.stop(cancel); + break; + case ON_COMPLETE: + sample.stop(onComplete); + break; + case ON_ERROR: + sample.stop(onError); + break; + } + } + + Sample start() { + return Timer.start(meterRegistry); + } + + private static Timer timer(MeterRegistry meterRegistry, + String interactionModel, SignalType signalType, Tag... tags) { + + return meterRegistry.timer( + "infra.remoting." + interactionModel, Tags.of(tags).and("signal.type", signalType.name())); + } + } +} diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/MicrometerChannelDecorator.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/MicrometerChannelDecorator.java new file mode 100644 index 0000000..477e104 --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/MicrometerChannelDecorator.java @@ -0,0 +1,61 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer; + +import java.util.Objects; + +import infra.remoting.Channel; +import infra.remoting.plugins.ChannelDecorator; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import reactor.core.publisher.SignalType; + +/** + * An implementation of {@link ChannelDecorator} that intercepts interactions and gathers + * Micrometer metrics about them. + * + *

The metrics are called {@code infra.remoting.[ metadata.push | request.channel | request.fnf | + * request.response | request.stream ]} and is tagged with {@code signal.type} ({@link SignalType}) + * and any additional configured tags. + * + * @see Micrometer + */ +public final class MicrometerChannelDecorator implements ChannelDecorator { + + private final MeterRegistry meterRegistry; + + private final Tag[] tags; + + /** + * Creates a new {@link ChannelDecorator}. + * + * @param meterRegistry the {@link MeterRegistry} to use to create {@link Meter}s. + * @param tags the additional tags to attach to each {@link Meter} + * @throws NullPointerException if {@code meterRegistry} is {@code null} + */ + public MicrometerChannelDecorator(MeterRegistry meterRegistry, Tag... tags) { + this.meterRegistry = Objects.requireNonNull(meterRegistry, "meterRegistry is required"); + this.tags = tags; + } + + @Override + public Channel decorate(Channel delegate) { + Objects.requireNonNull(delegate, "delegate is required"); + return new MicrometerChannel(delegate, meterRegistry, tags); + } +} diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/MicrometerConnection.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/MicrometerConnection.java new file mode 100644 index 0000000..75925cb --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/MicrometerConnection.java @@ -0,0 +1,266 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer; + +import java.util.Objects; +import java.util.function.Consumer; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.Connection; +import infra.remoting.DecoratingConnection; +import infra.remoting.frame.FrameHeaderCodec; +import infra.remoting.frame.FrameType; +import infra.remoting.plugins.ConnectionDecorator.Type; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; +import io.netty.buffer.ByteBuf; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import static infra.remoting.frame.FrameType.CANCEL; +import static infra.remoting.frame.FrameType.COMPLETE; +import static infra.remoting.frame.FrameType.ERROR; +import static infra.remoting.frame.FrameType.EXT; +import static infra.remoting.frame.FrameType.KEEPALIVE; +import static infra.remoting.frame.FrameType.LEASE; +import static infra.remoting.frame.FrameType.METADATA_PUSH; +import static infra.remoting.frame.FrameType.NEXT; +import static infra.remoting.frame.FrameType.NEXT_COMPLETE; +import static infra.remoting.frame.FrameType.PAYLOAD; +import static infra.remoting.frame.FrameType.REQUEST_CHANNEL; +import static infra.remoting.frame.FrameType.REQUEST_FNF; +import static infra.remoting.frame.FrameType.REQUEST_N; +import static infra.remoting.frame.FrameType.REQUEST_RESPONSE; +import static infra.remoting.frame.FrameType.REQUEST_STREAM; +import static infra.remoting.frame.FrameType.RESUME; +import static infra.remoting.frame.FrameType.RESUME_OK; +import static infra.remoting.frame.FrameType.SETUP; + +/** + * An implementation of {@link Connection} that intercepts frames and gathers Micrometer + * metrics about them. + * + *

The metric is called {@code infra.remoting.frame} and is tagged with {@code connection.type} ({@link + * Type}), {@code frame.type} ({@link FrameType}), and any additional configured tags. {@code + * infra.remoting.duplex.connection.close} and {@code infra.remoting.duplex.connection.dispose} metrics, tagged + * with {@code connection.type} ({@link Type}) and any additional configured tags are also + * collected. + * + * @see Micrometer + */ +final class MicrometerConnection extends DecoratingConnection { + + private final Counter close; + + private final Counter dispose; + + private final FrameCounters frameCounters; + + /** + * Creates a new {@link Connection}. + * + * @param connectionType the type of connection being monitored + * @param delegate the {@link Connection} to delegate to + * @param meterRegistry the {@link MeterRegistry} to use + * @param tags additional tags to attach to {@link Meter}s + * @throws NullPointerException if {@code connectionType}, {@code delegate}, or {@code + * meterRegistry} is {@code null} + */ + MicrometerConnection(Type connectionType, Connection delegate, MeterRegistry meterRegistry, Tag... tags) { + super(delegate); + Objects.requireNonNull(connectionType, "connectionType is required"); + Objects.requireNonNull(meterRegistry, "meterRegistry is required"); + + this.close = meterRegistry.counter("infra.remoting.duplex.connection.close", + Tags.of(tags).and("connection.type", connectionType.name())); + + this.dispose = meterRegistry.counter("infra.remoting.duplex.connection.dispose", + Tags.of(tags).and("connection.type", connectionType.name())); + + this.frameCounters = new FrameCounters(connectionType, meterRegistry, tags); + } + + @Override + public void dispose() { + delegate.dispose(); + dispose.increment(); + } + + @Override + public Mono onClose() { + return delegate.onClose().doAfterTerminate(close::increment); + } + + @Override + public Flux receive() { + return delegate.receive().doOnNext(frameCounters); + } + + @Override + public void sendFrame(int streamId, ByteBuf frame) { + frameCounters.accept(frame); + delegate.sendFrame(streamId, frame); + } + + private static final class FrameCounters implements Consumer { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final Counter cancel; + + private final Counter complete; + + private final Counter error; + + private final Counter extension; + + private final Counter keepalive; + + private final Counter lease; + + private final Counter metadataPush; + + private final Counter next; + + private final Counter nextComplete; + + private final Counter payload; + + private final Counter requestChannel; + + private final Counter requestFireAndForget; + + private final Counter requestN; + + private final Counter requestResponse; + + private final Counter requestStream; + + private final Counter resume; + + private final Counter resumeOk; + + private final Counter setup; + + private final Counter unknown; + + private FrameCounters(Type connectionType, MeterRegistry meterRegistry, Tag... tags) { + this.cancel = counter(connectionType, meterRegistry, CANCEL, tags); + this.complete = counter(connectionType, meterRegistry, COMPLETE, tags); + this.error = counter(connectionType, meterRegistry, ERROR, tags); + this.extension = counter(connectionType, meterRegistry, EXT, tags); + this.keepalive = counter(connectionType, meterRegistry, KEEPALIVE, tags); + this.lease = counter(connectionType, meterRegistry, LEASE, tags); + this.metadataPush = counter(connectionType, meterRegistry, METADATA_PUSH, tags); + this.next = counter(connectionType, meterRegistry, NEXT, tags); + this.nextComplete = counter(connectionType, meterRegistry, NEXT_COMPLETE, tags); + this.payload = counter(connectionType, meterRegistry, PAYLOAD, tags); + this.requestChannel = counter(connectionType, meterRegistry, REQUEST_CHANNEL, tags); + this.requestFireAndForget = counter(connectionType, meterRegistry, REQUEST_FNF, tags); + this.requestN = counter(connectionType, meterRegistry, REQUEST_N, tags); + this.requestResponse = counter(connectionType, meterRegistry, REQUEST_RESPONSE, tags); + this.requestStream = counter(connectionType, meterRegistry, REQUEST_STREAM, tags); + this.resume = counter(connectionType, meterRegistry, RESUME, tags); + this.resumeOk = counter(connectionType, meterRegistry, RESUME_OK, tags); + this.setup = counter(connectionType, meterRegistry, SETUP, tags); + this.unknown = counter(connectionType, meterRegistry, "UNKNOWN", tags); + } + + private static Counter counter( + Type connectionType, MeterRegistry meterRegistry, FrameType frameType, Tag... tags) { + + return counter(connectionType, meterRegistry, frameType.name(), tags); + } + + private static Counter counter( + Type connectionType, MeterRegistry meterRegistry, String frameType, Tag... tags) { + + return meterRegistry.counter( + "infra.remoting.frame", + Tags.of(tags).and("connection.type", connectionType.name()).and("frame.type", frameType)); + } + + @Override + public void accept(ByteBuf frame) { + FrameType frameType = FrameHeaderCodec.frameType(frame); + + switch (frameType) { + case SETUP: + this.setup.increment(); + break; + case LEASE: + this.lease.increment(); + break; + case KEEPALIVE: + this.keepalive.increment(); + break; + case REQUEST_RESPONSE: + this.requestResponse.increment(); + break; + case REQUEST_FNF: + this.requestFireAndForget.increment(); + break; + case REQUEST_STREAM: + this.requestStream.increment(); + break; + case REQUEST_CHANNEL: + this.requestChannel.increment(); + break; + case REQUEST_N: + this.requestN.increment(); + break; + case CANCEL: + this.cancel.increment(); + break; + case PAYLOAD: + this.payload.increment(); + break; + case ERROR: + this.error.increment(); + break; + case METADATA_PUSH: + this.metadataPush.increment(); + break; + case RESUME: + this.resume.increment(); + break; + case RESUME_OK: + this.resumeOk.increment(); + break; + case NEXT: + this.next.increment(); + break; + case COMPLETE: + this.complete.increment(); + break; + case NEXT_COMPLETE: + this.nextComplete.increment(); + break; + case EXT: + this.extension.increment(); + break; + default: + this.logger.debug("Skipping count of unknown frame type: {}", frameType); + this.unknown.increment(); + } + } + } +} diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/MicrometerConnectionDecorator.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/MicrometerConnectionDecorator.java new file mode 100644 index 0000000..d2a609a --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/MicrometerConnectionDecorator.java @@ -0,0 +1,65 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer; + +import java.util.Objects; + +import infra.remoting.Connection; +import infra.remoting.frame.FrameType; +import infra.remoting.plugins.ConnectionDecorator; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; + +/** + * An implementation of {@link ConnectionDecorator} that intercepts frames and gathers + * Micrometer metrics about them. + * + *

The metric is called {@code infra.remoting.frame} and is tagged with {@code connection.type} ({@link + * Type}), {@code frame.type} ({@link FrameType}), and any additional configured tags. {@code + * infra.remoting.duplex.connection.close} and {@code infra.remoting.duplex.connection.dispose} metrics, tagged + * with {@code connection.type} ({@link Type}) and any additional configured tags are also + * collected. + * + * @see Micrometer + */ +public final class MicrometerConnectionDecorator implements ConnectionDecorator { + + private final MeterRegistry meterRegistry; + + private final Tag[] tags; + + /** + * Creates a new {@link ConnectionDecorator}. + * + * @param meterRegistry the {@link MeterRegistry} to use to create {@link Meter}s. + * @param tags the additional tags to attach to each {@link Meter} + * @throws NullPointerException if {@code meterRegistry} is {@code null} + */ + public MicrometerConnectionDecorator(MeterRegistry meterRegistry, Tag... tags) { + this.meterRegistry = Objects.requireNonNull(meterRegistry, "meterRegistry is required"); + this.tags = tags; + } + + @Override + public Connection decorate(Type connectionType, Connection delegate) { + Objects.requireNonNull(delegate, "delegate is required"); + Objects.requireNonNull(connectionType, "connectionType is required"); + + return new MicrometerConnection(connectionType, delegate, meterRegistry, tags); + } +} diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ChannelContext.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ChannelContext.java new file mode 100644 index 0000000..3195843 --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ChannelContext.java @@ -0,0 +1,77 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.observation; + +import org.jspecify.annotations.Nullable; + +import infra.remoting.Payload; +import infra.remoting.frame.FrameType; +import io.micrometer.observation.Observation; +import io.netty.buffer.ByteBuf; + +public class ChannelContext extends Observation.Context { + + final Payload payload; + + final ByteBuf metadata; + + final FrameType frameType; + + final String route; + + final Side side; + + Payload modifiedPayload; + + ChannelContext(Payload payload, ByteBuf metadata, + FrameType frameType, @Nullable String route, Side side) { + this.payload = payload; + this.metadata = metadata; + this.frameType = frameType; + this.route = route; + this.side = side; + } + + public enum Side { + REQUESTER, + RESPONDER + } + + public Payload getPayload() { + return payload; + } + + public ByteBuf getMetadata() { + return metadata; + } + + public FrameType getFrameType() { + return frameType; + } + + public String getRoute() { + return route; + } + + public Side getSide() { + return side; + } + + public Payload getModifiedPayload() { + return modifiedPayload; + } +} diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ChannelRequesterObservationConvention.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ChannelRequesterObservationConvention.java new file mode 100644 index 0000000..16732ed --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ChannelRequesterObservationConvention.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.observation; + +import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationConvention; + +/** + * {@link ObservationConvention} for requester {@link ChannelContext}. + * + * @author Marcin Grzejszczak + * @author 海子 Yang + */ +public interface ChannelRequesterObservationConvention extends ObservationConvention { + + @Override + default boolean supportsContext(Observation.Context context) { + return context instanceof ChannelContext + && ((ChannelContext) context).side == ChannelContext.Side.REQUESTER; + } +} diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ChannelRequesterTracingObservationHandler.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ChannelRequesterTracingObservationHandler.java new file mode 100644 index 0000000..c006644 --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ChannelRequesterTracingObservationHandler.java @@ -0,0 +1,93 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.observation; + +import java.util.HashSet; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.Payload; +import io.micrometer.observation.Observation; +import io.micrometer.tracing.Span; +import io.micrometer.tracing.TraceContext; +import io.micrometer.tracing.Tracer; +import io.micrometer.tracing.handler.TracingObservationHandler; +import io.micrometer.tracing.propagation.Propagator; +import io.netty.buffer.ByteBuf; + +public class ChannelRequesterTracingObservationHandler implements TracingObservationHandler { + + private static final Logger log = LoggerFactory.getLogger(ChannelRequesterTracingObservationHandler.class); + + private final Propagator propagator; + + private final Propagator.Setter setter; + + private final Tracer tracer; + + public ChannelRequesterTracingObservationHandler(Tracer tracer, + Propagator propagator, Propagator.Setter setter) { + this.tracer = tracer; + this.propagator = propagator; + this.setter = setter; + } + + @Override + public boolean supportsContext(Observation.Context context) { + return context instanceof ChannelContext + && ((ChannelContext) context).side == ChannelContext.Side.REQUESTER; + } + + @Override + public Tracer getTracer() { + return this.tracer; + } + + @Override + public void onStart(ChannelContext context) { + Payload payload = context.payload; + Span.Builder spanBuilder = this.tracer.spanBuilder(); + Span parentSpan = getParentSpan(context); + if (parentSpan != null) { + spanBuilder.setParent(parentSpan.context()); + } + Span span = spanBuilder.kind(Span.Kind.PRODUCER).start(); + log.debug("Extracted result from context or thread local {}", span); + + final ByteBuf newMetadata = PayloadUtils.cleanTracingMetadata(payload, new HashSet<>(propagator.fields())); + TraceContext traceContext = span.context(); + this.propagator.inject(traceContext, newMetadata, this.setter); + context.modifiedPayload = PayloadUtils.payload(payload, newMetadata); + getTracingContext(context).setSpan(span); + } + + @Override + public void onError(ChannelContext context) { + Throwable error = context.getError(); + if (error != null) { + getRequiredSpan(context).error(error); + } + } + + @Override + public void onStop(ChannelContext context) { + Span span = getRequiredSpan(context); + tagSpan(context, span); + span.name(context.getContextualName()).end(); + } + +} diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ChannelResponderObservationConvention.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ChannelResponderObservationConvention.java new file mode 100644 index 0000000..541ab3b --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ChannelResponderObservationConvention.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.observation; + +import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationConvention; + +/** + * {@link ObservationConvention} for responder {@link ChannelContext}. + * + * @author Marcin Grzejszczak + * @author 海子 Yang + */ +public interface ChannelResponderObservationConvention extends ObservationConvention { + + @Override + default boolean supportsContext(Observation.Context context) { + return context instanceof ChannelContext + && ((ChannelContext) context).side == ChannelContext.Side.RESPONDER; + } +} diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ChannelResponderTracingObservationHandler.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ChannelResponderTracingObservationHandler.java new file mode 100644 index 0000000..fa6cb7d --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ChannelResponderTracingObservationHandler.java @@ -0,0 +1,92 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.observation; + +import java.util.HashSet; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.Payload; +import infra.remoting.frame.FrameType; +import io.micrometer.observation.Observation; +import io.micrometer.tracing.Span; +import io.micrometer.tracing.Tracer; +import io.micrometer.tracing.handler.TracingObservationHandler; +import io.micrometer.tracing.propagation.Propagator; +import io.netty.buffer.ByteBuf; + +public class ChannelResponderTracingObservationHandler implements TracingObservationHandler { + + private static final Logger log = LoggerFactory.getLogger(ChannelResponderTracingObservationHandler.class); + + private final Propagator propagator; + + private final Propagator.Getter getter; + + private final Tracer tracer; + + public ChannelResponderTracingObservationHandler(Tracer tracer, Propagator propagator, Propagator.Getter getter) { + this.tracer = tracer; + this.propagator = propagator; + this.getter = getter; + } + + @Override + public void onStart(ChannelContext context) { + Span handle = consumerSpanBuilder(context.payload, context.metadata, context.frameType); + ByteBuf bufs = PayloadUtils.cleanTracingMetadata(context.payload, new HashSet<>(propagator.fields())); + context.modifiedPayload = PayloadUtils.payload(context.payload, bufs); + getTracingContext(context).setSpan(handle); + } + + @Override + public void onError(ChannelContext context) { + Throwable error = context.getError(); + if (error != null) { + getRequiredSpan(context).error(error); + } + } + + @Override + public void onStop(ChannelContext context) { + Span span = getRequiredSpan(context); + tagSpan(context, span); + span.end(); + } + + @Override + public boolean supportsContext(Observation.Context context) { + return context instanceof ChannelContext + && ((ChannelContext) context).side == ChannelContext.Side.RESPONDER; + } + + @Override + public Tracer getTracer() { + return this.tracer; + } + + private Span consumerSpanBuilder(Payload payload, ByteBuf headers, FrameType requestType) { + Span.Builder consumerSpanBuilder = consumerSpanBuilder(payload, headers); + log.debug("Extracted result from headers {}", consumerSpanBuilder); + String name = "handle"; + return consumerSpanBuilder.kind(Span.Kind.CONSUMER).name(name).start(); + } + + private Span.Builder consumerSpanBuilder(Payload payload, ByteBuf headers) { + return this.propagator.extract(headers, this.getter); + } +} diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/DefaultChannelObservationConvention.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/DefaultChannelObservationConvention.java new file mode 100644 index 0000000..0c67c5e --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/DefaultChannelObservationConvention.java @@ -0,0 +1,50 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.observation; + +import infra.remoting.frame.FrameType; + +/** + * Default {@link ChannelRequesterObservationConvention} implementation. + * + * @author Marcin Grzejszczak + */ +class DefaultChannelObservationConvention { + + private final ChannelContext channelContext; + + public DefaultChannelObservationConvention(ChannelContext channelContext) { + this.channelContext = channelContext; + } + + String getName() { + if (this.channelContext.frameType == FrameType.REQUEST_FNF) { + return "infra.remoting.fnf"; + } + else if (this.channelContext.frameType == FrameType.REQUEST_STREAM) { + return "infra.remoting.stream"; + } + else if (this.channelContext.frameType == FrameType.REQUEST_CHANNEL) { + return "infra.remoting.channel"; + } + return "%s"; + } + + protected ChannelContext getChannelContext() { + return this.channelContext; + } +} diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/DefaultChannelRequesterObservationConvention.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/DefaultChannelRequesterObservationConvention.java new file mode 100644 index 0000000..3755076 --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/DefaultChannelRequesterObservationConvention.java @@ -0,0 +1,58 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.observation; + +import infra.remoting.frame.FrameType; +import io.micrometer.common.KeyValues; +import io.micrometer.common.util.StringUtils; +import io.micrometer.observation.Observation; + +/** + * Default {@link ChannelRequesterObservationConvention} implementation. + * + * @author Marcin Grzejszczak + * @author 海子 Yang + */ +public class DefaultChannelRequesterObservationConvention extends DefaultChannelObservationConvention implements ChannelRequesterObservationConvention { + + public DefaultChannelRequesterObservationConvention(ChannelContext channelContext) { + super(channelContext); + } + + @Override + public KeyValues getLowCardinalityKeyValues(ChannelContext context) { + KeyValues values = KeyValues.of( + RemotingObservationDocumentation.ResponderTags.REQUEST_TYPE.withValue(context.frameType.name())); + if (StringUtils.isNotBlank(context.route)) { + values = values.and(RemotingObservationDocumentation.ResponderTags.ROUTE.withValue(context.route)); + } + return values; + } + + @Override + public boolean supportsContext(Observation.Context context) { + return context instanceof ChannelContext; + } + + @Override + public String getName() { + if (getChannelContext().frameType == FrameType.REQUEST_RESPONSE) { + return "infra.remoting.request"; + } + return super.getName(); + } +} diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/DefaultChannelResponderObservationConvention.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/DefaultChannelResponderObservationConvention.java new file mode 100644 index 0000000..e07f09d --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/DefaultChannelResponderObservationConvention.java @@ -0,0 +1,59 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.observation; + +import infra.remoting.frame.FrameType; +import io.micrometer.common.KeyValues; +import io.micrometer.common.util.StringUtils; +import io.micrometer.observation.Observation; + +/** + * Default {@link ChannelRequesterObservationConvention} implementation. + * + * @author Marcin Grzejszczak + */ +public class DefaultChannelResponderObservationConvention + extends DefaultChannelObservationConvention implements ChannelResponderObservationConvention { + + public DefaultChannelResponderObservationConvention(ChannelContext channelContext) { + super(channelContext); + } + + @Override + public KeyValues getLowCardinalityKeyValues(ChannelContext context) { + KeyValues tags = KeyValues.of( + RemotingObservationDocumentation.ResponderTags.REQUEST_TYPE.withValue( + context.frameType.name())); + if (StringUtils.isNotBlank(context.route)) { + tags = tags.and(RemotingObservationDocumentation.ResponderTags.ROUTE.withValue(context.route)); + } + return tags; + } + + @Override + public boolean supportsContext(Observation.Context context) { + return context instanceof ChannelContext; + } + + @Override + public String getName() { + if (getChannelContext().frameType == FrameType.REQUEST_RESPONSE) { + return "infra.remoting.response"; + } + return super.getName(); + } +} diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ObservationRequesterChannel.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ObservationRequesterChannel.java new file mode 100644 index 0000000..d1346a1 --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ObservationRequesterChannel.java @@ -0,0 +1,184 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.observation; + +import org.jspecify.annotations.Nullable; +import org.reactivestreams.Publisher; + +import java.util.function.Function; + +import infra.remoting.Channel; +import infra.remoting.DecoratingChannel; +import infra.remoting.Payload; +import infra.remoting.frame.FrameType; +import io.micrometer.common.util.StringUtils; +import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationRegistry; +import io.micrometer.observation.docs.ObservationDocumentation; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.util.context.ContextView; + +/** + * Tracing representation of a {@link DecoratingChannel} for the requester. + * + * @author Marcin Grzejszczak + * @author Oleh Dokuka + * @author 海子 Yang + */ +public class ObservationRequesterChannel extends DecoratingChannel { + + /** + * Aligned with ObservationThreadLocalAccessor#KEY + */ + private static final String MICROMETER_OBSERVATION_KEY = "micrometer.observation"; + + private final ObservationRegistry observationRegistry; + + @Nullable + private final ChannelRequesterObservationConvention observationConvention; + + public ObservationRequesterChannel(Channel source, ObservationRegistry observationRegistry) { + this(source, observationRegistry, null); + } + + public ObservationRequesterChannel(Channel source, ObservationRegistry observationRegistry, + @Nullable ChannelRequesterObservationConvention observationConvention) { + super(source); + this.observationRegistry = observationRegistry; + this.observationConvention = observationConvention; + } + + @Override + public Mono fireAndForget(Payload payload) { + return setObservation( + super::fireAndForget, + payload, + FrameType.REQUEST_FNF, + RemotingObservationDocumentation.REQUESTER_FNF); + } + + @Override + public Mono requestResponse(Payload payload) { + return setObservation( + super::requestResponse, + payload, + FrameType.REQUEST_RESPONSE, + RemotingObservationDocumentation.REQUESTER_REQUEST_RESPONSE); + } + + Mono setObservation(Function> input, Payload payload, + FrameType frameType, ObservationDocumentation observation) { + return Mono.deferContextual( + contextView -> observe(input, payload, frameType, observation, contextView)); + } + + private String route(Payload payload) { + return null; + } + + private Mono observe( + Function> input, + Payload payload, + FrameType frameType, + ObservationDocumentation obs, + ContextView contextView) { + String route = route(payload); + ChannelContext channelContext = + new ChannelContext( + payload, payload.sliceMetadata(), frameType, route, ChannelContext.Side.REQUESTER); + Observation parentObservation = contextView.getOrDefault(MICROMETER_OBSERVATION_KEY, null); + Observation observation = obs.observation(this.observationConvention, + new DefaultChannelRequesterObservationConvention(channelContext), + () -> channelContext, observationRegistry) + .parentObservation(parentObservation); + setContextualName(frameType, route, observation); + observation.start(); + Payload newPayload = payload; + if (channelContext.modifiedPayload != null) { + newPayload = channelContext.modifiedPayload; + } + return input + .apply(newPayload) + .doOnError(observation::error) + .doFinally(signalType -> observation.stop()) + .contextWrite(context -> context.put(MICROMETER_OBSERVATION_KEY, observation)); + } + + @Override + public Flux requestStream(Payload payload) { + return observationFlux( + super::requestStream, + payload, + FrameType.REQUEST_STREAM, + RemotingObservationDocumentation.REQUESTER_REQUEST_STREAM); + } + + @Override + public Flux requestChannel(Publisher inbound) { + return Flux.from(inbound) + .switchOnFirst( + (firstSignal, flux) -> { + final Payload firstPayload = firstSignal.get(); + if (firstPayload != null) { + return observationFlux( + p -> super.requestChannel(flux.skip(1).startWith(p)), + firstPayload, + FrameType.REQUEST_CHANNEL, + RemotingObservationDocumentation.REQUESTER_REQUEST_CHANNEL); + } + return flux; + }); + } + + private Flux observationFlux(Function> input, + Payload payload, FrameType frameType, ObservationDocumentation obs) { + return Flux.deferContextual(contextView -> { + String route = route(payload); + ChannelContext channelContext = + new ChannelContext( + payload, + payload.sliceMetadata(), + frameType, + route, + ChannelContext.Side.REQUESTER); + Observation parentObservation = + contextView.getOrDefault(MICROMETER_OBSERVATION_KEY, null); + Observation newObservation = + obs.observation(this.observationConvention, + new DefaultChannelRequesterObservationConvention(channelContext), + () -> channelContext, this.observationRegistry) + .parentObservation(parentObservation); + setContextualName(frameType, route, newObservation); + newObservation.start(); + return input + .apply(channelContext.modifiedPayload) + .doOnError(newObservation::error) + .doFinally(signalType -> newObservation.stop()) + .contextWrite(context -> context.put(MICROMETER_OBSERVATION_KEY, newObservation)); + }); + } + + private void setContextualName(FrameType frameType, String route, Observation newObservation) { + if (StringUtils.isNotBlank(route)) { + newObservation.contextualName(frameType.name() + " " + route); + } + else { + newObservation.contextualName(frameType.name()); + } + } +} diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ObservationResponderChannel.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ObservationResponderChannel.java new file mode 100644 index 0000000..e4641f5 --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/ObservationResponderChannel.java @@ -0,0 +1,155 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.observation; + +import org.jspecify.annotations.Nullable; +import org.reactivestreams.Publisher; + +import infra.remoting.Channel; +import infra.remoting.DecoratingChannel; +import infra.remoting.Payload; +import infra.remoting.frame.FrameType; +import io.micrometer.common.util.StringUtils; +import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationRegistry; +import io.netty.buffer.ByteBuf; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * Tracing representation of a {@link DecoratingChannel} for the responder. + * + * @author Marcin Grzejszczak + * @author Oleh Dokuka + */ +public class ObservationResponderChannel extends DecoratingChannel { + /** Aligned with ObservationThreadLocalAccessor#KEY */ + private static final String MICROMETER_OBSERVATION_KEY = "micrometer.observation"; + + private final ObservationRegistry observationRegistry; + + @Nullable + private final ChannelResponderObservationConvention observationConvention; + + public ObservationResponderChannel(Channel source, ObservationRegistry observationRegistry) { + this(source, observationRegistry, null); + } + + public ObservationResponderChannel(Channel source, + ObservationRegistry observationRegistry, + @Nullable ChannelResponderObservationConvention observationConvention) { + super(source); + this.observationRegistry = observationRegistry; + this.observationConvention = observationConvention; + } + + @Override + public Mono fireAndForget(Payload payload) { + // called on Netty EventLoop + // there can't be observation in thread local here + ByteBuf sliceMetadata = payload.sliceMetadata(); + String route = route(payload, sliceMetadata); + ChannelContext channelContext = new ChannelContext(payload, + payload.sliceMetadata(), FrameType.REQUEST_FNF, route, + ChannelContext.Side.RESPONDER); + Observation newObservation = startObservation(RemotingObservationDocumentation.RESPONDER_FNF, channelContext); + return super.fireAndForget(channelContext.modifiedPayload) + .doOnError(newObservation::error) + .doFinally(signalType -> newObservation.stop()) + .contextWrite(context -> context.put(MICROMETER_OBSERVATION_KEY, newObservation)); + } + + private Observation startObservation( + RemotingObservationDocumentation observation, ChannelContext channelContext) { + return observation.start(this.observationConvention, + new DefaultChannelResponderObservationConvention(channelContext), + () -> channelContext, this.observationRegistry); + } + + @Override + public Mono requestResponse(Payload payload) { + ByteBuf sliceMetadata = payload.sliceMetadata(); + String route = route(payload, sliceMetadata); + ChannelContext channelContext = + new ChannelContext( + payload, + payload.sliceMetadata(), + FrameType.REQUEST_RESPONSE, + route, + ChannelContext.Side.RESPONDER); + Observation newObservation = + startObservation( + RemotingObservationDocumentation.RESPONDER_REQUEST_RESPONSE, channelContext); + return super.requestResponse(channelContext.modifiedPayload) + .doOnError(newObservation::error) + .doFinally(signalType -> newObservation.stop()) + .contextWrite(context -> context.put(MICROMETER_OBSERVATION_KEY, newObservation)); + } + + @Override + public Flux requestStream(Payload payload) { + ByteBuf sliceMetadata = payload.sliceMetadata(); + String route = route(payload, sliceMetadata); + ChannelContext channelContext = + new ChannelContext( + payload, sliceMetadata, FrameType.REQUEST_STREAM, route, ChannelContext.Side.RESPONDER); + Observation newObservation = + startObservation( + RemotingObservationDocumentation.RESPONDER_REQUEST_STREAM, channelContext); + return super.requestStream(channelContext.modifiedPayload) + .doOnError(newObservation::error) + .doFinally(signalType -> newObservation.stop()) + .contextWrite(context -> context.put(MICROMETER_OBSERVATION_KEY, newObservation)); + } + + @Override + public Flux requestChannel(Publisher payloads) { + return Flux.from(payloads) + .switchOnFirst( + (firstSignal, flux) -> { + final Payload firstPayload = firstSignal.get(); + if (firstPayload != null) { + ByteBuf sliceMetadata = firstPayload.sliceMetadata(); + String route = route(firstPayload, sliceMetadata); + ChannelContext channelContext = + new ChannelContext( + firstPayload, + firstPayload.sliceMetadata(), + FrameType.REQUEST_CHANNEL, + route, + ChannelContext.Side.RESPONDER); + Observation newObservation = + startObservation( + RemotingObservationDocumentation.RESPONDER_REQUEST_CHANNEL, + channelContext); + if (StringUtils.isNotBlank(route)) { + newObservation.contextualName(channelContext.frameType.name() + " " + route); + } + return super.requestChannel(flux.skip(1).startWith(channelContext.modifiedPayload)) + .doOnError(newObservation::error) + .doFinally(signalType -> newObservation.stop()) + .contextWrite( + context -> context.put(MICROMETER_OBSERVATION_KEY, newObservation)); + } + return flux; + }); + } + + private String route(Payload payload, ByteBuf headers) { + return null; + } +} diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/PayloadUtils.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/PayloadUtils.java new file mode 100644 index 0000000..71a6757 --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/PayloadUtils.java @@ -0,0 +1,52 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.observation; + +import java.util.Set; + +import infra.remoting.Payload; +import infra.remoting.util.ByteBufPayload; +import infra.remoting.util.DefaultPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; + +final class PayloadUtils { + + private PayloadUtils() { + throw new IllegalStateException("Can't instantiate a utility class"); + } + + static ByteBuf cleanTracingMetadata(Payload payload, Set fields) { + return Unpooled.EMPTY_BUFFER; + } + + static Payload payload(Payload payload, ByteBuf metadata) { + final Payload newPayload; + try { + if (payload instanceof ByteBufPayload) { + newPayload = ByteBufPayload.create(payload.data().retain(), metadata); + } + else { + newPayload = DefaultPayload.create(payload.data().retain(), metadata); + } + } + finally { + payload.release(); + } + return newPayload; + } +} diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/RemotingObservationDocumentation.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/RemotingObservationDocumentation.java new file mode 100644 index 0000000..cf467de --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/observation/RemotingObservationDocumentation.java @@ -0,0 +1,228 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.observation; + +import io.micrometer.common.docs.KeyName; +import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationConvention; +import io.micrometer.observation.docs.ObservationDocumentation; + +enum RemotingObservationDocumentation implements ObservationDocumentation { + + /** + * Observation created on the responder side. + */ + RESPONDER { + @Override + public Class> getDefaultConvention() { + return DefaultChannelResponderObservationConvention.class; + } + }, + + /** + * Observation created on the requester side for Fire and Forget frame type. + */ + REQUESTER_FNF { + @Override + public Class> getDefaultConvention() { + return DefaultChannelRequesterObservationConvention.class; + } + + @Override + public KeyName[] getLowCardinalityKeyNames() { + return RequesterTags.values(); + } + + @Override + public String getPrefix() { + return "infra.remoting."; + } + }, + + /** Observation created on the responder side for Fire and Forget frame type. */ + RESPONDER_FNF { + @Override + public Class> getDefaultConvention() { + return DefaultChannelResponderObservationConvention.class; + } + + @Override + public KeyName[] getLowCardinalityKeyNames() { + return ResponderTags.values(); + } + + @Override + public String getPrefix() { + return "infra.remoting."; + } + }, + + /** Observation created on the requester side for Request Response frame type. */ + REQUESTER_REQUEST_RESPONSE { + @Override + public Class> + getDefaultConvention() { + return DefaultChannelRequesterObservationConvention.class; + } + + @Override + public KeyName[] getLowCardinalityKeyNames() { + return RequesterTags.values(); + } + + @Override + public String getPrefix() { + return "infra.remoting."; + } + }, + + /** Observation created on the responder side for Request Response frame type. */ + RESPONDER_REQUEST_RESPONSE { + @Override + public Class> getDefaultConvention() { + return DefaultChannelResponderObservationConvention.class; + } + + @Override + public KeyName[] getLowCardinalityKeyNames() { + return ResponderTags.values(); + } + + @Override + public String getPrefix() { + return "infra.remoting."; + } + }, + + /** Observation created on the requester side for Request Stream frame type. */ + REQUESTER_REQUEST_STREAM { + @Override + public Class> getDefaultConvention() { + return DefaultChannelRequesterObservationConvention.class; + } + + @Override + public KeyName[] getLowCardinalityKeyNames() { + return RequesterTags.values(); + } + + @Override + public String getPrefix() { + return "infra.remoting."; + } + }, + + /** Observation created on the responder side for Request Stream frame type. */ + RESPONDER_REQUEST_STREAM { + @Override + public Class> getDefaultConvention() { + return DefaultChannelResponderObservationConvention.class; + } + + @Override + public KeyName[] getLowCardinalityKeyNames() { + return ResponderTags.values(); + } + + @Override + public String getPrefix() { + return "infra.remoting."; + } + }, + + /** Observation created on the requester side for Request Channel frame type. */ + REQUESTER_REQUEST_CHANNEL { + @Override + public Class> getDefaultConvention() { + return DefaultChannelRequesterObservationConvention.class; + } + + @Override + public KeyName[] getLowCardinalityKeyNames() { + return RequesterTags.values(); + } + + @Override + public String getPrefix() { + return "infra.remoting."; + } + }, + + /** Observation created on the responder side for Request Channel frame type. */ + RESPONDER_REQUEST_CHANNEL { + @Override + public Class> getDefaultConvention() { + return DefaultChannelResponderObservationConvention.class; + } + + @Override + public KeyName[] getLowCardinalityKeyNames() { + return ResponderTags.values(); + } + + @Override + public String getPrefix() { + return "infra.remoting."; + } + }; + + enum RequesterTags implements KeyName { + + /** Name of the route. */ + ROUTE { + @Override + public String asString() { + return "infra.remoting.route"; + } + }, + + /** Name of the request type. */ + REQUEST_TYPE { + @Override + public String asString() { + return "infra.remoting.request-type"; + } + }, + + /** Name of the content type. */ + CONTENT_TYPE { + @Override + public String asString() { + return "infra.remoting.content-type"; + } + } + } + + enum ResponderTags implements KeyName { + + /** Name of the route. */ + ROUTE { + @Override + public String asString() { + return "infra.remoting.route"; + } + }, + + /** Name of the request type. */ + REQUEST_TYPE { + @Override + public String asString() { + return "infra.remoting.request-type"; + } + } + } +} diff --git a/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/package-info.java b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/package-info.java new file mode 100644 index 0000000..ee55704 --- /dev/null +++ b/today-remoting-micrometer/src/main/java/infra/remoting/micrometer/package-info.java @@ -0,0 +1,25 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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. + */ + +/** + * Transparent metrics exposure for Micrometer. + * + * @see Micrometer + */ +@NullMarked +package infra.remoting.micrometer; + +import org.jspecify.annotations.NullMarked; diff --git a/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/MicrometerChannelDecoratorTests.java b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/MicrometerChannelDecoratorTests.java new file mode 100644 index 0000000..8d97b7d --- /dev/null +++ b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/MicrometerChannelDecoratorTests.java @@ -0,0 +1,58 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import infra.remoting.Channel; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; +import static org.mockito.Mockito.RETURNS_SMART_NULLS; +import static org.mockito.Mockito.mock; + +final class MicrometerChannelDecoratorTests { + + private final Channel delegate = mock(Channel.class, RETURNS_SMART_NULLS); + + private final SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry(); + + @DisplayName("creates Micrometer") + @Test + void apply() { + assertThat(new MicrometerChannelDecorator(meterRegistry).decorate(delegate)) + .isInstanceOf(MicrometerChannel.class); + } + + @DisplayName("apply throws NullPointerException with null delegate") + @Test + void applyNullDelegate() { + assertThatNullPointerException() + .isThrownBy(() -> new MicrometerChannelDecorator(meterRegistry).decorate(null)) + .withMessage("delegate is required"); + } + + @DisplayName("constructor throws NullPointerException with null meterRegistry") + @Test + void constructorNullMeterRegistry() { + assertThatNullPointerException() + .isThrownBy(() -> new MicrometerChannelDecorator(null)) + .withMessage("meterRegistry is required"); + } +} diff --git a/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/MicrometerChannelTests.java b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/MicrometerChannelTests.java new file mode 100644 index 0000000..1068a2e --- /dev/null +++ b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/MicrometerChannelTests.java @@ -0,0 +1,147 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import infra.remoting.Channel; +import infra.remoting.Payload; +import infra.remoting.util.DefaultPayload; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.SignalType; +import reactor.test.StepVerifier; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; +import static org.mockito.Mockito.RETURNS_SMART_NULLS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +final class MicrometerChannelTests { + + private final Channel delegate = mock(Channel.class, RETURNS_SMART_NULLS); + + private final SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry(); + + @DisplayName("constructor throws NullPointerException with null delegate") + @Test + void constructorNullDelegate() { + assertThatNullPointerException() + .isThrownBy(() -> new MicrometerChannel(null, meterRegistry)) + .withMessage("delegate is required"); + } + + @DisplayName("constructor throws NullPointerException with null meterRegistry") + @Test + void constructorNullMeterRegistry() { + assertThatNullPointerException() + .isThrownBy(() -> new MicrometerChannel(delegate, null)) + .withMessage("meterRegistry is required"); + } + + @DisplayName("fireAndForget gathers metrics") + @Test + void fireAndForget() { + Payload payload = DefaultPayload.create("test-metadata", "test-data"); + when(delegate.fireAndForget(payload)).thenReturn(Mono.empty()); + + new MicrometerChannel(delegate, meterRegistry, Tag.of("test-key", "test-value")) + .fireAndForget(payload) + .as(StepVerifier::create) + .verifyComplete(); + + assertThat(findCounter("request.fnf", SignalType.ON_COMPLETE).count()).isEqualTo(1); + } + + @DisplayName("metadataPush gathers metrics") + @Test + void metadataPush() { + Payload payload = DefaultPayload.create("test-metadata", "test-data"); + when(delegate.metadataPush(payload)).thenReturn(Mono.empty()); + + new MicrometerChannel(delegate, meterRegistry, Tag.of("test-key", "test-value")) + .metadataPush(payload) + .as(StepVerifier::create) + .verifyComplete(); + + assertThat(findCounter("metadata.push", SignalType.ON_COMPLETE).count()).isEqualTo(1); + } + + @DisplayName("requestChannel gathers metrics") + @Test + void requestChannel() { + Mono payload = Mono.just(DefaultPayload.create("test-metadata", "test-data")); + when(delegate.requestChannel(payload)).thenReturn(Flux.empty()); + + new MicrometerChannel(delegate, meterRegistry, Tag.of("test-key", "test-value")) + .requestChannel(payload) + .as(StepVerifier::create) + .verifyComplete(); + + assertThat(findCounter("request.channel", SignalType.ON_COMPLETE).count()).isEqualTo(1); + } + + @DisplayName("requestResponse gathers metrics") + @Test + void requestResponse() { + Payload payload = DefaultPayload.create("test-metadata", "test-data"); + when(delegate.requestResponse(payload)).thenReturn(Mono.empty()); + + new MicrometerChannel(delegate, meterRegistry, Tag.of("test-key", "test-value")) + .requestResponse(payload) + .as(StepVerifier::create) + .verifyComplete(); + + assertThat(findTimer("request.response", SignalType.ON_COMPLETE).count()).isEqualTo(1); + } + + @DisplayName("requestStream gathers metrics") + @Test + void requestStream() { + Payload payload = DefaultPayload.create("test-metadata", "test-data"); + when(delegate.requestStream(payload)).thenReturn(Flux.empty()); + + new MicrometerChannel(delegate, meterRegistry, Tag.of("test-key", "test-value")) + .requestStream(payload) + .as(StepVerifier::create) + .verifyComplete(); + + assertThat(findCounter("request.stream", SignalType.ON_COMPLETE).count()).isEqualTo(1); + } + + private Counter findCounter(String interactionModel, SignalType signalType) { + return meterRegistry + .get(String.format("infra.remoting.%s", interactionModel)) + .tag("signal.type", signalType.name()) + .tag("test-key", "test-value") + .counter(); + } + + private Timer findTimer(String interactionModel, SignalType signalType) { + return meterRegistry + .get(String.format("infra.remoting.%s", interactionModel)) + .tag("signal.type", signalType.name()) + .tag("test-key", "test-value") + .timer(); + } +} diff --git a/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/MicrometerConnectionDecoratorTests.java b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/MicrometerConnectionDecoratorTests.java new file mode 100644 index 0000000..73d3f9c --- /dev/null +++ b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/MicrometerConnectionDecoratorTests.java @@ -0,0 +1,69 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import infra.remoting.Connection; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; + +import static infra.remoting.plugins.ConnectionDecorator.Type.CLIENT; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; +import static org.mockito.Mockito.RETURNS_SMART_NULLS; +import static org.mockito.Mockito.mock; + +final class MicrometerConnectionDecoratorTests { + + private final Connection delegate = mock(Connection.class, RETURNS_SMART_NULLS); + + private final SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry(); + + @DisplayName("creates MicrometerConnection") + @Test + void apply() { + assertThat(new MicrometerConnectionDecorator(meterRegistry).decorate(CLIENT, delegate)) + .isInstanceOf(MicrometerConnection.class); + } + + @DisplayName("apply throws NullPointerException with null connectionType") + @Test + void applyNullConnectionType() { + assertThatNullPointerException() + .isThrownBy( + () -> new MicrometerConnectionDecorator(meterRegistry).decorate(null, delegate)) + .withMessage("connectionType is required"); + } + + @DisplayName("apply throws NullPointerException with null delegate") + @Test + void applyNullDelegate() { + assertThatNullPointerException() + .isThrownBy( + () -> new MicrometerConnectionDecorator(meterRegistry).decorate(CLIENT, null)) + .withMessage("delegate is required"); + } + + @DisplayName("constructor throws NullPointer exception with null meterRegistry") + @Test + void constructorNullMeterRegistry() { + assertThatNullPointerException() + .isThrownBy(() -> new MicrometerConnectionDecorator(null)) + .withMessage("meterRegistry is required"); + } +} diff --git a/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/MicrometerConnectionTests.java b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/MicrometerConnectionTests.java new file mode 100644 index 0000000..4dd6dad --- /dev/null +++ b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/MicrometerConnectionTests.java @@ -0,0 +1,227 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import infra.remoting.Connection; +import infra.remoting.frame.FrameType; +import infra.remoting.plugins.ConnectionDecorator.Type; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import io.netty.buffer.ByteBuf; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.Operators; +import reactor.test.StepVerifier; + +import static infra.remoting.frame.FrameType.CANCEL; +import static infra.remoting.frame.FrameType.COMPLETE; +import static infra.remoting.frame.FrameType.ERROR; +import static infra.remoting.frame.FrameType.KEEPALIVE; +import static infra.remoting.frame.FrameType.LEASE; +import static infra.remoting.frame.FrameType.METADATA_PUSH; +import static infra.remoting.frame.FrameType.REQUEST_CHANNEL; +import static infra.remoting.frame.FrameType.REQUEST_FNF; +import static infra.remoting.frame.FrameType.REQUEST_N; +import static infra.remoting.frame.FrameType.REQUEST_RESPONSE; +import static infra.remoting.frame.FrameType.REQUEST_STREAM; +import static infra.remoting.frame.FrameType.SETUP; +import static infra.remoting.plugins.ConnectionDecorator.Type.CLIENT; +import static infra.remoting.plugins.ConnectionDecorator.Type.SERVER; +import static infra.remoting.test.TestFrames.createTestCancelFrame; +import static infra.remoting.test.TestFrames.createTestErrorFrame; +import static infra.remoting.test.TestFrames.createTestKeepaliveFrame; +import static infra.remoting.test.TestFrames.createTestLeaseFrame; +import static infra.remoting.test.TestFrames.createTestMetadataPushFrame; +import static infra.remoting.test.TestFrames.createTestPayloadFrame; +import static infra.remoting.test.TestFrames.createTestRequestChannelFrame; +import static infra.remoting.test.TestFrames.createTestRequestFireAndForgetFrame; +import static infra.remoting.test.TestFrames.createTestRequestNFrame; +import static infra.remoting.test.TestFrames.createTestRequestResponseFrame; +import static infra.remoting.test.TestFrames.createTestRequestStreamFrame; +import static infra.remoting.test.TestFrames.createTestSetupFrame; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; +import static org.mockito.Mockito.RETURNS_SMART_NULLS; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +final class MicrometerConnectionTests { + + private final Connection delegate = mock(Connection.class, RETURNS_SMART_NULLS); + + private final SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry(); + + @DisplayName("constructor throws NullPointerException with null connectionType") + @Test + void constructorNullConnectionType() { + assertThatNullPointerException() + .isThrownBy(() -> new MicrometerConnection(null, delegate, meterRegistry)) + .withMessage("connectionType is required"); + } + + @DisplayName("constructor throws NullPointerException with null delegate") + @Test + void constructorNullDelegate() { + assertThatNullPointerException() + .isThrownBy(() -> new MicrometerConnection(CLIENT, null, meterRegistry)) + .withMessage("delegate is required"); + } + + @DisplayName("constructor throws NullPointerException with null meterRegistry") + @Test + void constructorNullMeterRegistry() { + + assertThatNullPointerException() + .isThrownBy(() -> new MicrometerConnection(CLIENT, delegate, null)) + .withMessage("meterRegistry is required"); + } + + @DisplayName("dispose gathers metrics") + @Test + void dispose() { + new MicrometerConnection( + CLIENT, delegate, meterRegistry, Tag.of("test-key", "test-value")) + .dispose(); + + assertThat( + meterRegistry + .get("infra.remoting.duplex.connection.dispose") + .tag("connection.type", CLIENT.name()) + .tag("test-key", "test-value") + .counter() + .count()) + .isEqualTo(1); + } + + @DisplayName("onClose gathers metrics") + @Test + void onClose() { + when(delegate.onClose()).thenReturn(Mono.empty()); + + new MicrometerConnection( + CLIENT, delegate, meterRegistry, Tag.of("test-key", "test-value")) + .onClose() + .subscribe(Operators.drainSubscriber()); + + assertThat( + meterRegistry + .get("infra.remoting.duplex.connection.close") + .tag("connection.type", CLIENT.name()) + .tag("test-key", "test-value") + .counter() + .count()) + .isEqualTo(1); + } + + @DisplayName("receive gathers metrics") + @Test + void receive() { + Flux frames = + Flux.just( + createTestCancelFrame(), + createTestErrorFrame(), + createTestKeepaliveFrame(), + createTestLeaseFrame(), + createTestMetadataPushFrame(), + createTestPayloadFrame(), + createTestRequestChannelFrame(), + createTestRequestFireAndForgetFrame(), + createTestRequestNFrame(), + createTestRequestResponseFrame(), + createTestRequestStreamFrame(), + createTestSetupFrame()); + + when(delegate.receive()).thenReturn(frames); + + new MicrometerConnection( + CLIENT, delegate, meterRegistry, Tag.of("test-key", "test-value")) + .receive() + .as(StepVerifier::create) + .expectNextCount(12) + .verifyComplete(); + + assertThat(findCounter(CLIENT, CANCEL).count()).isEqualTo(1); + assertThat(findCounter(CLIENT, COMPLETE).count()).isEqualTo(1); + assertThat(findCounter(CLIENT, ERROR).count()).isEqualTo(1); + assertThat(findCounter(CLIENT, KEEPALIVE).count()).isEqualTo(1); + assertThat(findCounter(CLIENT, LEASE).count()).isEqualTo(1); + assertThat(findCounter(CLIENT, METADATA_PUSH).count()).isEqualTo(1); + assertThat(findCounter(CLIENT, REQUEST_CHANNEL).count()).isEqualTo(1); + assertThat(findCounter(CLIENT, REQUEST_FNF).count()).isEqualTo(1); + assertThat(findCounter(CLIENT, REQUEST_N).count()).isEqualTo(1); + assertThat(findCounter(CLIENT, REQUEST_RESPONSE).count()).isEqualTo(1); + assertThat(findCounter(CLIENT, REQUEST_STREAM).count()).isEqualTo(1); + assertThat(findCounter(CLIENT, SETUP).count()).isEqualTo(1); + } + + @DisplayName("send gathers metrics") + @SuppressWarnings("unchecked") + @Test + void send() { + ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class); + doNothing().when(delegate).sendFrame(Mockito.anyInt(), captor.capture()); + + final MicrometerConnection micrometerConnection = + new MicrometerConnection( + SERVER, delegate, meterRegistry, Tag.of("test-key", "test-value")); + micrometerConnection.sendFrame(1, createTestCancelFrame()); + micrometerConnection.sendFrame(1, createTestErrorFrame()); + micrometerConnection.sendFrame(1, createTestKeepaliveFrame()); + micrometerConnection.sendFrame(1, createTestLeaseFrame()); + micrometerConnection.sendFrame(1, createTestMetadataPushFrame()); + micrometerConnection.sendFrame(1, createTestPayloadFrame()); + micrometerConnection.sendFrame(1, createTestRequestChannelFrame()); + micrometerConnection.sendFrame(1, createTestRequestFireAndForgetFrame()); + micrometerConnection.sendFrame(1, createTestRequestNFrame()); + micrometerConnection.sendFrame(1, createTestRequestResponseFrame()); + micrometerConnection.sendFrame(1, createTestRequestStreamFrame()); + micrometerConnection.sendFrame(1, createTestSetupFrame()); + + StepVerifier.create(Flux.fromIterable(captor.getAllValues())) + .expectNextCount(12) + .verifyComplete(); + + assertThat(findCounter(SERVER, CANCEL).count()).isEqualTo(1); + assertThat(findCounter(SERVER, COMPLETE).count()).isEqualTo(1); + assertThat(findCounter(SERVER, ERROR).count()).isEqualTo(1); + assertThat(findCounter(SERVER, KEEPALIVE).count()).isEqualTo(1); + assertThat(findCounter(SERVER, LEASE).count()).isEqualTo(1); + assertThat(findCounter(SERVER, METADATA_PUSH).count()).isEqualTo(1); + assertThat(findCounter(SERVER, REQUEST_CHANNEL).count()).isEqualTo(1); + assertThat(findCounter(SERVER, REQUEST_FNF).count()).isEqualTo(1); + assertThat(findCounter(SERVER, REQUEST_N).count()).isEqualTo(1); + assertThat(findCounter(SERVER, REQUEST_RESPONSE).count()).isEqualTo(1); + assertThat(findCounter(SERVER, REQUEST_STREAM).count()).isEqualTo(1); + assertThat(findCounter(SERVER, SETUP).count()).isEqualTo(1); + } + + private Counter findCounter(Type connectionType, FrameType frameType) { + return meterRegistry + .get("infra.remoting.frame") + .tag("connection.type", connectionType.name()) + .tag("frame.type", frameType.name()) + .tag("test-key", "test-value") + .counter(); + } +} diff --git a/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/IntegrationTests.java b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/IntegrationTests.java new file mode 100644 index 0000000..573bf58 --- /dev/null +++ b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/IntegrationTests.java @@ -0,0 +1,195 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.integration; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; + +import infra.remoting.Channel; +import infra.remoting.DecoratingChannel; +import infra.remoting.Payload; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.plugins.ChannelAcceptorDecorator; +import infra.remoting.plugins.ChannelDecorator; +import infra.remoting.plugins.ConnectionDecorator; +import infra.remoting.test.TestSubscriber; +import infra.remoting.transport.netty.client.TcpClientTransport; +import infra.remoting.transport.netty.server.CloseableChannel; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.remoting.util.DefaultPayload; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +public class IntegrationTests { + + private static final ChannelDecorator requesterInterceptor; + private static final ChannelDecorator responderInterceptor; + private static final ChannelAcceptorDecorator clientAcceptorInterceptor; + private static final ChannelAcceptorDecorator serverAcceptorInterceptor; + private static final ConnectionDecorator CONNECTION_DECORATOR; + + private static volatile boolean calledRequester = false; + private static volatile boolean calledResponder = false; + private static volatile boolean calledClientAcceptor = false; + private static volatile boolean calledServerAcceptor = false; + private static volatile boolean calledFrame = false; + + static { + requesterInterceptor = channel -> + new DecoratingChannel(channel) { + @Override + public Mono requestResponse(Payload payload) { + calledRequester = true; + return channel.requestResponse(payload); + } + }; + + responderInterceptor = channel -> + new DecoratingChannel(channel) { + @Override + public Mono requestResponse(Payload payload) { + calledResponder = true; + return channel.requestResponse(payload); + } + }; + + clientAcceptorInterceptor = acceptor -> + (setup, channel) -> { + calledClientAcceptor = true; + return acceptor.accept(setup, channel); + }; + + serverAcceptorInterceptor = acceptor -> (setup, channel) -> { + calledServerAcceptor = true; + return acceptor.accept(setup, channel); + }; + + CONNECTION_DECORATOR = (type, connection) -> { + calledFrame = true; + return connection; + }; + } + + private CloseableChannel server; + private Channel client; + private AtomicInteger requestCount; + private CountDownLatch disconnectionCounter; + private AtomicInteger errorCount; + + @BeforeEach + public void startup() { + errorCount = new AtomicInteger(); + requestCount = new AtomicInteger(); + disconnectionCounter = new CountDownLatch(1); + + server = RemotingServer.create((setup, channel) -> { + channel.onClose() + .doFinally(signalType -> disconnectionCounter.countDown()) + .subscribe(); + + return Mono.just(new Channel() { + @Override + public Mono requestResponse(Payload payload) { + return Mono.just(DefaultPayload.create("RESPONSE", "METADATA")) + .doOnSubscribe(s -> requestCount.incrementAndGet()); + } + + @Override + public Flux requestStream(Payload payload) { + return Flux.range(1, 10_000) + .map(i -> DefaultPayload.create("data -> " + i)); + } + + @Override + public Flux requestChannel(Publisher payloads) { + return Flux.from(payloads); + } + }); + }) + .interceptors(registry -> registry + .forResponder(responderInterceptor) + .forChannelAcceptor(serverAcceptorInterceptor) + .forConnection(CONNECTION_DECORATOR)) + .bind(TcpServerTransport.create("localhost", 0)) + .block(); + + client = ChannelConnector.create() + .interceptors(registry -> registry + .forRequester(requesterInterceptor) + .forChannelAcceptor(clientAcceptorInterceptor) + .forConnection(CONNECTION_DECORATOR)) + .connect(TcpClientTransport.create(server.address())) + .block(); + } + + @AfterEach + public void teardown() { + server.dispose(); + } + + @Test + @Timeout(5_000L) + public void testRequest() { + client.requestResponse(DefaultPayload.create("REQUEST", "META")).block(); + assertThat(requestCount).as("Server did not see the request.").hasValue(1); + + assertThat(calledRequester).isTrue(); + assertThat(calledResponder).isTrue(); + assertThat(calledClientAcceptor).isTrue(); + assertThat(calledServerAcceptor).isTrue(); + assertThat(calledFrame).isTrue(); + } + + @Test + @Timeout(5_000L) + public void testStream() { + Subscriber subscriber = TestSubscriber.createCancelling(); + client.requestStream(DefaultPayload.create("start")).subscribe(subscriber); + + verify(subscriber).onSubscribe(any()); + verifyNoMoreInteractions(subscriber); + } + + @Test + @Timeout(5_000L) + public void testClose() throws InterruptedException { + client.dispose(); + disconnectionCounter.await(); + } + + @Test // (timeout = 5_000L) + public void testCallRequestWithErrorAndThenRequest() { + assertThatThrownBy(client.requestChannel(Mono.error(new Throwable("test")))::blockLast) + .hasMessage("java.lang.Throwable: test"); + + testRequest(); + } +} diff --git a/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/InteractionsLoadTests.java b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/InteractionsLoadTests.java new file mode 100644 index 0000000..12a4bed --- /dev/null +++ b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/InteractionsLoadTests.java @@ -0,0 +1,109 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.integration; + +import org.junit.jupiter.api.Test; +import org.reactivestreams.Publisher; + +import java.time.Duration; +import java.util.function.Supplier; + +import infra.remoting.Channel; +import infra.remoting.ChannelAcceptor; +import infra.remoting.Payload; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.test.SlowTest; +import infra.remoting.transport.netty.client.TcpClientTransport; +import infra.remoting.transport.netty.server.CloseableChannel; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.remoting.util.DefaultPayload; +import reactor.core.publisher.Flux; + +public class InteractionsLoadTests { + + @Test + @SlowTest + public void channel() { + CloseableChannel server = RemotingServer.create(ChannelAcceptor.with(new EchoChannel())) + .bind(TcpServerTransport.create("localhost", 0)) + .block(Duration.ofSeconds(10)); + + Channel clientChannel = ChannelConnector.connectWith(TcpClientTransport.create(server.address())) + .block(Duration.ofSeconds(10)); + + int concurrency = 16; + Flux.range(1, concurrency) + .flatMap(v -> clientChannel + .requestChannel(input().onBackpressureDrop().map(iv -> DefaultPayload.create("foo"))) + .limitRate(10000), + concurrency) + .timeout(Duration.ofSeconds(5)) + .doOnNext(p -> { + String data = p.getDataUtf8(); + if (!data.equals("bar")) { + throw new IllegalStateException("Channel Client Bad message: " + data); + } + }) + .window(Duration.ofSeconds(1)) + .flatMap(Flux::count) + .doOnNext(d -> System.out.println("Got: " + d)) + .take(Duration.ofMinutes(1)) + .doOnTerminate(server::dispose) + .subscribe(); + + server.onClose().block(); + } + + private static Flux input() { + Flux interval = Flux.interval(Duration.ofMillis(1)).onBackpressureDrop(); + for (int i = 0; i < 10; i++) { + interval = interval.mergeWith(interval); + } + return interval; + } + + private static class EchoChannel implements Channel { + + @Override + public Flux requestChannel(Publisher payloads) { + return Flux.from(payloads) + .map(p -> { + String data = p.getDataUtf8(); + if (!data.equals("foo")) { + throw new IllegalStateException("Channel Server Bad message: " + data); + } + return DefaultPayload.create("bar"); + }); + } + + @Override + public Flux requestStream(Payload payload) { + return Flux.just(payload) + .map(Payload::getDataUtf8) + .doOnNext((data) -> { + if (!data.equals("foo")) { + throw new IllegalStateException("Stream Server Bad message: " + data); + } + }) + .flatMap(data -> { + Supplier p = () -> DefaultPayload.create("bar"); + return Flux.range(1, 100).map(v -> p.get()); + }); + } + } +} diff --git a/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/StreamingTests.java b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/StreamingTests.java new file mode 100644 index 0000000..870fa68 --- /dev/null +++ b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/StreamingTests.java @@ -0,0 +1,122 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.integration; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; + +import infra.remoting.ChannelAcceptor; +import infra.remoting.Closeable; +import infra.remoting.Payload; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.error.ApplicationErrorException; +import infra.remoting.transport.local.LocalClientTransport; +import infra.remoting.transport.local.LocalServerTransport; +import infra.remoting.util.DefaultPayload; +import reactor.core.publisher.Flux; + +class StreamingTests { + LocalServerTransport serverTransport = LocalServerTransport.create("test"); + + @Test + public void testRangeButThrowException() { + Closeable server = null; + try { + server = RemotingServer.create(ChannelAcceptor.forRequestStream(payload -> + Flux.range(1, 1000) + .doOnNext(i -> { + if (i > 3) { + throw new RuntimeException("BOOM!"); + } + }) + .map((Function) l -> DefaultPayload.create("l -> " + l)) + .cast(Payload.class))) + .bind(serverTransport) + .block(); + + Assertions.assertThatThrownBy( + Flux.range(1, 6).flatMap(i -> consumer("connection number -> " + i))::blockLast) + .isInstanceOf(ApplicationErrorException.class); + + } + finally { + server.dispose(); + } + } + + @Test + public void testRangeOfConsumers() { + Closeable server = null; + try { + server = + RemotingServer.create( + ChannelAcceptor.forRequestStream( + payload -> + Flux.range(1, 1000) + .map((Function) l -> DefaultPayload.create("l -> " + l)) + .cast(Payload.class))) + .bind(serverTransport) + .block(); + + Flux.range(1, 6).flatMap(i -> consumer("connection number -> " + i)).blockLast(); + } + finally { + server.dispose(); + } + } + + private Flux consumer(String s) { + return ChannelConnector.connectWith(LocalClientTransport.create("test")) + .flatMapMany(channel -> { + AtomicInteger count = new AtomicInteger(); + return Flux.range(1, 100) + .flatMap( + i -> channel.requestStream(DefaultPayload.create("i -> " + i)).take(100), 1); + }); + } + + @Test + public void testSingleConsumer() { + Closeable server = null; + try { + server = + RemotingServer.create(ChannelAcceptor.forRequestStream(payload -> Flux.range(1, 10_000) + .map((Function) l -> DefaultPayload.create("l -> " + l)) + .cast(Payload.class))) + .bind(serverTransport) + .block(); + + consumer("1").blockLast(); + + } + finally { + server.dispose(); + } + } + + @Test + public void testFluxOnly() { + Flux longFlux = Flux.interval(Duration.ofMillis(1)).onBackpressureDrop(); + + Flux.range(1, 60).flatMap(i -> longFlux.take(1000)).blockLast(); + } +} diff --git a/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/TcpIntegrationTests.java b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/TcpIntegrationTests.java new file mode 100644 index 0000000..fc60072 --- /dev/null +++ b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/TcpIntegrationTests.java @@ -0,0 +1,196 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.integration; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; + +import infra.remoting.Channel; +import infra.remoting.DecoratingChannel; +import infra.remoting.Payload; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.transport.netty.client.TcpClientTransport; +import infra.remoting.transport.netty.server.CloseableChannel; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.remoting.util.DefaultPayload; +import infra.remoting.util.EmptyPayload; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.Sinks; +import reactor.core.scheduler.Schedulers; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TcpIntegrationTests { + private Channel handler; + + private CloseableChannel server; + + @BeforeEach + public void startup() { + server = RemotingServer.create((setup, channel) -> Mono.just(new DecoratingChannel(handler))) + .bind(TcpServerTransport.create("localhost", 0)) + .block(); + } + + private Channel buildClient() { + return ChannelConnector.connectWith(TcpClientTransport.create(server.address())).block(); + } + + @AfterEach + public void cleanup() { + server.dispose(); + } + + @Test + @Timeout(15_000L) + public void testCompleteWithoutNext() { + handler = + new Channel() { + @Override + public Flux requestStream(Payload payload) { + return Flux.empty(); + } + }; + Channel client = buildClient(); + Boolean hasElements = + client.requestStream(DefaultPayload.create("REQUEST", "META")).log().hasElements().block(); + + assertThat(hasElements).isFalse(); + } + + @Test + @Timeout(15_000L) + public void testSingleStream() { + handler = + new Channel() { + @Override + public Flux requestStream(Payload payload) { + return Flux.just(DefaultPayload.create("RESPONSE", "METADATA")); + } + }; + + Channel client = buildClient(); + + Payload result = client.requestStream(DefaultPayload.create("REQUEST", "META")).blockLast(); + + assertThat(result.getDataUtf8()).isEqualTo("RESPONSE"); + } + + @Test + @Timeout(15_000L) + public void testZeroPayload() { + handler = + new Channel() { + @Override + public Flux requestStream(Payload payload) { + return Flux.just(EmptyPayload.INSTANCE); + } + }; + + Channel client = buildClient(); + + Payload result = client.requestStream(DefaultPayload.create("REQUEST", "META")).blockFirst(); + + assertThat(result.getDataUtf8()).isEmpty(); + } + + @Test + @Timeout(15_000L) + public void testRequestResponseErrors() { + handler = + new Channel() { + boolean first = true; + + @Override + public Mono requestResponse(Payload payload) { + if (first) { + first = false; + return Mono.error(new RuntimeException("EX")); + } + else { + return Mono.just(DefaultPayload.create("SUCCESS")); + } + } + }; + + Channel client = buildClient(); + + Payload response1 = + client + .requestResponse(DefaultPayload.create("REQUEST", "META")) + .onErrorReturn(DefaultPayload.create("ERROR")) + .block(); + Payload response2 = + client + .requestResponse(DefaultPayload.create("REQUEST", "META")) + .onErrorReturn(DefaultPayload.create("ERROR")) + .block(); + + assertThat(response1.getDataUtf8()).isEqualTo("ERROR"); + assertThat(response2.getDataUtf8()).isEqualTo("SUCCESS"); + } + + @Test + @Timeout(15_000L) + public void testTwoConcurrentStreams() throws InterruptedException { + ConcurrentHashMap> map = new ConcurrentHashMap<>(); + Sinks.Many processor1 = Sinks.many().unicast().onBackpressureBuffer(); + map.put("REQUEST1", processor1); + Sinks.Many processor2 = Sinks.many().unicast().onBackpressureBuffer(); + map.put("REQUEST2", processor2); + + handler = new Channel() { + @Override + public Flux requestStream(Payload payload) { + return map.get(payload.getDataUtf8()).asFlux(); + } + }; + + Channel client = buildClient(); + + Flux response1 = client.requestStream(DefaultPayload.create("REQUEST1")); + Flux response2 = client.requestStream(DefaultPayload.create("REQUEST2")); + + CountDownLatch nextCountdown = new CountDownLatch(2); + CountDownLatch completeCountdown = new CountDownLatch(2); + + response1 + .subscribeOn(Schedulers.newSingle("1")) + .subscribe(c -> nextCountdown.countDown(), t -> { }, completeCountdown::countDown); + + response2 + .subscribeOn(Schedulers.newSingle("2")) + .subscribe(c -> nextCountdown.countDown(), t -> { }, completeCountdown::countDown); + + processor1.tryEmitNext(DefaultPayload.create("RESPONSE1A")); + processor2.tryEmitNext(DefaultPayload.create("RESPONSE2A")); + + nextCountdown.await(); + + processor1.tryEmitComplete(); + processor2.tryEmitComplete(); + + completeCountdown.await(); + } +} diff --git a/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/observation/ByteBufGetter.java b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/observation/ByteBufGetter.java new file mode 100644 index 0000000..3b8ce33 --- /dev/null +++ b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/observation/ByteBufGetter.java @@ -0,0 +1,37 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.integration.observation; + +import java.util.Map; + +import io.micrometer.tracing.propagation.Propagator; +import io.netty.buffer.ByteBuf; + +public class ByteBufGetter implements Propagator.Getter { + + private final Map map; + + public ByteBufGetter(Map map) { + this.map = map; + } + + @Override + public String get(ByteBuf carrier, String key) { + return map.get(key); + } + +} diff --git a/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/observation/ByteBufSetter.java b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/observation/ByteBufSetter.java new file mode 100644 index 0000000..90ceed7 --- /dev/null +++ b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/observation/ByteBufSetter.java @@ -0,0 +1,34 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.integration.observation; + +import java.util.HashMap; +import java.util.Map; + +import io.micrometer.tracing.propagation.Propagator; +import io.netty.buffer.ByteBuf; + +public class ByteBufSetter implements Propagator.Setter { + + final Map map = new HashMap<>(); + + @Override + public void set(ByteBuf carrier, String key, String value) { + map.put(key, value); + } + +} diff --git a/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/observation/ObservationIntegrationTests.java b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/observation/ObservationIntegrationTests.java new file mode 100644 index 0000000..26e2c36 --- /dev/null +++ b/today-remoting-micrometer/src/test/java/infra/remoting/micrometer/integration/observation/ObservationIntegrationTests.java @@ -0,0 +1,232 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.micrometer.integration.observation; + +import org.awaitility.Awaitility; +import org.junit.jupiter.api.AfterEach; +import org.reactivestreams.Publisher; + +import java.time.Duration; +import java.util.Deque; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiConsumer; + +import infra.remoting.Channel; +import infra.remoting.Payload; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.micrometer.observation.ChannelRequesterTracingObservationHandler; +import infra.remoting.micrometer.observation.ChannelResponderTracingObservationHandler; +import infra.remoting.micrometer.observation.ObservationRequesterChannel; +import infra.remoting.micrometer.observation.ObservationResponderChannel; +import infra.remoting.plugins.ChannelDecorator; +import infra.remoting.transport.netty.client.TcpClientTransport; +import infra.remoting.transport.netty.server.CloseableChannel; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.remoting.util.DefaultPayload; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import io.micrometer.core.tck.MeterRegistryAssert; +import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationHandler; +import io.micrometer.observation.ObservationRegistry; +import io.micrometer.tracing.test.SampleTestRunner; +import io.micrometer.tracing.test.reporter.BuildingBlocks; +import io.micrometer.tracing.test.simple.SpansAssert; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ObservationIntegrationTests extends SampleTestRunner { + private static final MeterRegistry registry = new SimpleMeterRegistry(); + private static final ObservationRegistry observationRegistry = ObservationRegistry.create(); + + static { + observationRegistry + .observationConfig() + .observationHandler(new DefaultMeterObservationHandler(registry)); + } + + private final ChannelDecorator requesterInterceptor; + private final ChannelDecorator responderInterceptor; + + ObservationIntegrationTests() { + super(SampleRunnerConfig.builder().build()); + requesterInterceptor = channel -> new ObservationRequesterChannel(channel, observationRegistry); + responderInterceptor = channel -> new ObservationResponderChannel(channel, observationRegistry); + } + + private CloseableChannel server; + private Channel client; + private AtomicInteger counter; + + @Override + public BiConsumer>> customizeObservationHandlers() { + return (buildingBlocks, observationHandlers) -> { + ByteBufSetter setter = new ByteBufSetter(); + observationHandlers.addFirst( + new ChannelRequesterTracingObservationHandler( + buildingBlocks.getTracer(), + buildingBlocks.getPropagator(), + setter + )); + observationHandlers.addFirst( + new ChannelResponderTracingObservationHandler( + buildingBlocks.getTracer(), + buildingBlocks.getPropagator(), + new ByteBufGetter(setter.map))); + }; + } + + @AfterEach + public void teardown() { + if (server != null) { + server.dispose(); + } + } + + private void testRequest() { + counter.set(0); + client.requestResponse(DefaultPayload.create("REQUEST", "META")).block(); + assertThat(counter).as("Server did not see the request.").hasValue(1); + } + + private void testStream() { + counter.set(0); + client.requestStream(DefaultPayload.create("start")).blockLast(); + + assertThat(counter).as("Server did not see the request.").hasValue(1); + } + + private void testRequestChannel() { + counter.set(0); + client.requestChannel(Mono.just(DefaultPayload.create("start"))).blockFirst(); + assertThat(counter).as("Server did not see the request.").hasValue(1); + } + + private void testFireAndForget() { + counter.set(0); + client.fireAndForget(DefaultPayload.create("start")).subscribe(); + Awaitility.await().atMost(Duration.ofSeconds(50)).until(() -> counter.get() == 1); + assertThat(counter).as("Server did not see the request.").hasValue(1); + } + + @Override + public SampleTestRunnerConsumer yourCode() { + return (bb, meterRegistry) -> { + counter = new AtomicInteger(); + server = RemotingServer.create((setup, channel) -> { + channel.onClose().subscribe(); + + return Mono.just( + new Channel() { + @Override + public Mono requestResponse(Payload payload) { + payload.release(); + counter.incrementAndGet(); + return Mono.just(DefaultPayload.create("RESPONSE", "METADATA")); + } + + @Override + public Flux requestStream(Payload payload) { + payload.release(); + counter.incrementAndGet(); + return Flux.range(1, 10_000) + .map(i -> DefaultPayload.create("data -> " + i)); + } + + @Override + public Flux requestChannel(Publisher payloads) { + counter.incrementAndGet(); + return Flux.from(payloads); + } + + @Override + public Mono fireAndForget(Payload payload) { + payload.release(); + counter.incrementAndGet(); + return Mono.empty(); + } + }); + }) + .interceptors(registry -> registry.forResponder(responderInterceptor)) + .bind(TcpServerTransport.create("localhost", 0)) + .block(); + + client = ChannelConnector.create() + .interceptors(registry -> registry.forRequester(requesterInterceptor)) + .connect(TcpClientTransport.create(server.address())) + .block(); + + testRequest(); + + testStream(); + + testRequestChannel(); + + testFireAndForget(); + + SpansAssert.assertThat(bb.getFinishedSpans()) + .haveSameTraceId() + // "request_*" + "handle" x 4 + .hasNumberOfSpansEqualTo(8) + .hasNumberOfSpansWithNameEqualTo("handle", 4) + .forAllSpansWithNameEqualTo("handle", span -> span.hasTagWithKey("infra.remoting.request-type")) + .hasASpanWithNameIgnoreCase("request_stream") + .thenASpanWithNameEqualToIgnoreCase("request_stream") + .hasTag("infra.remoting.request-type", "REQUEST_STREAM") + .backToSpans() + .hasASpanWithNameIgnoreCase("request_channel") + .thenASpanWithNameEqualToIgnoreCase("request_channel") + .hasTag("infra.remoting.request-type", "REQUEST_CHANNEL") + .backToSpans() + .hasASpanWithNameIgnoreCase("request_fnf") + .thenASpanWithNameEqualToIgnoreCase("request_fnf") + .hasTag("infra.remoting.request-type", "REQUEST_FNF") + .backToSpans() + .hasASpanWithNameIgnoreCase("request_response") + .thenASpanWithNameEqualToIgnoreCase("request_response") + .hasTag("infra.remoting.request-type", "REQUEST_RESPONSE"); + + MeterRegistryAssert.assertThat(registry) + .hasTimerWithNameAndTags("infra.remoting.response", + Tags.of(Tag.of("error", "none"), Tag.of("infra.remoting.request-type", "REQUEST_RESPONSE"))) + .hasTimerWithNameAndTags("infra.remoting.fnf", + Tags.of(Tag.of("error", "none"), Tag.of("infra.remoting.request-type", "REQUEST_FNF"))) + .hasTimerWithNameAndTags("infra.remoting.request", + Tags.of(Tag.of("error", "none"), Tag.of("infra.remoting.request-type", "REQUEST_RESPONSE"))) + .hasTimerWithNameAndTags("infra.remoting.channel", + Tags.of(Tag.of("error", "none"), Tag.of("infra.remoting.request-type", "REQUEST_CHANNEL"))) + .hasTimerWithNameAndTags("infra.remoting.stream", + Tags.of(Tag.of("error", "none"), Tag.of("infra.remoting.request-type", "REQUEST_STREAM"))); + }; + } + + @Override + protected MeterRegistry getMeterRegistry() { + return registry; + } + + @Override + protected ObservationRegistry getObservationRegistry() { + return observationRegistry; + } +} diff --git a/today-remoting-micrometer/src/test/resources/logback-test.xml b/today-remoting-micrometer/src/test/resources/logback-test.xml new file mode 100644 index 0000000..2a208a6 --- /dev/null +++ b/today-remoting-micrometer/src/test/resources/logback-test.xml @@ -0,0 +1,17 @@ + + + + + + + %date{HH:mm:ss.SSS} %-10thread %-42logger %msg%n + + + + + + + + + + diff --git a/today-remoting-transport-local/build.gradle b/today-remoting-transport-local/build.gradle index bc8b32d..1f945d3 100644 --- a/today-remoting-transport-local/build.gradle +++ b/today-remoting-transport-local/build.gradle @@ -28,18 +28,8 @@ dependencies { api project(':today-remoting') testImplementation 'io.projectreactor:reactor-test' - testImplementation 'org.assertj:assertj-core' - testImplementation 'org.junit.jupiter:junit-jupiter-api' testRuntimeOnly 'ch.qos.logback:logback-classic' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' testImplementation testFixtures(project(":today-remoting")) } - -jar { - manifest { - attributes("Automatic-Module-Name": "infra.remoting.transport.local") - } -} - diff --git a/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalClientTransport.java b/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalClientTransport.java index e1f2045..dca4c21 100644 --- a/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalClientTransport.java +++ b/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalClientTransport.java @@ -1,25 +1,24 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.local; import java.util.Objects; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.internal.UnboundedProcessor; import infra.remoting.transport.ClientTransport; import infra.remoting.transport.ConnectionAcceptor; @@ -72,7 +71,7 @@ public static LocalClientTransport create(String name, ByteBufAllocator allocato } @Override - public Mono connect() { + public Mono connect() { return Mono.defer(() -> { ConnectionAcceptor server = LocalServerTransport.findServer(name); if (server == null) { @@ -86,10 +85,10 @@ public Mono connect() { Mono onClose = inSink.asMono().and(outSink.asMono()); - server.accept(new LocalDuplexConnection(name, allocator, out, in, onClose)).subscribe(); + server.accept(new LocalConnection(name, allocator, out, in, onClose)).subscribe(); - return Mono.just( - new LocalDuplexConnection(name, allocator, in, out, onClose)); + return Mono.just( + new LocalConnection(name, allocator, in, out, onClose)); }); } } diff --git a/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalDuplexConnection.java b/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalConnection.java similarity index 77% rename from today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalDuplexConnection.java rename to today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalConnection.java index 8132eda..cabc322 100644 --- a/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalDuplexConnection.java +++ b/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalConnection.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.local; @@ -22,7 +21,7 @@ import java.net.SocketAddress; import java.util.Objects; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.ProtocolErrorException; import infra.remoting.frame.ErrorFrameCodec; import infra.remoting.internal.UnboundedProcessor; @@ -34,8 +33,8 @@ import reactor.core.publisher.Mono; import reactor.core.publisher.Operators; -/** An implementation of {@link DuplexConnection} that connects inside the same JVM. */ -final class LocalDuplexConnection implements DuplexConnection { +/** An implementation of {@link Connection} that connects inside the same JVM. */ +final class LocalConnection implements Connection { private final LocalSocketAddress address; private final ByteBufAllocator allocator; @@ -54,7 +53,7 @@ final class LocalDuplexConnection implements DuplexConnection { * @param onClose the closing notifier * @throws NullPointerException if {@code in}, {@code out}, or {@code onClose} are {@code null} */ - LocalDuplexConnection(String name, ByteBufAllocator allocator, + LocalConnection(String name, ByteBufAllocator allocator, UnboundedProcessor in, UnboundedProcessor out, Mono onClose) { this.address = new LocalSocketAddress(name); this.allocator = Objects.requireNonNull(allocator, "allocator is required"); @@ -112,19 +111,19 @@ public SocketAddress remoteAddress() { @Override public String toString() { - return "LocalDuplexConnection{" + "address=" + address + "hash=" + hashCode() + '}'; + return "LocalConnection{" + "address=" + address + "hash=" + hashCode() + '}'; } static class ByteBufReleaserOperator implements CoreSubscriber, Subscription, Fuseable.QueueSubscription { final CoreSubscriber actual; - final LocalDuplexConnection parent; + final LocalConnection parent; Subscription s; public ByteBufReleaserOperator( - CoreSubscriber actual, LocalDuplexConnection parent) { + CoreSubscriber actual, LocalConnection parent) { this.actual = actual; this.parent = parent; } diff --git a/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalServerTransport.java b/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalServerTransport.java index 3776cca..2ef33be 100644 --- a/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalServerTransport.java +++ b/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalServerTransport.java @@ -1,22 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.local; +import org.jspecify.annotations.Nullable; + import java.util.Objects; import java.util.Set; import java.util.UUID; @@ -24,9 +25,8 @@ import java.util.concurrent.ConcurrentMap; import java.util.stream.Collectors; -import infra.lang.Nullable; import infra.remoting.Closeable; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.transport.ClientTransport; import infra.remoting.transport.ConnectionAcceptor; import infra.remoting.transport.ServerTransport; @@ -131,7 +131,7 @@ static class ServerCloseableAcceptor implements ConnectionAcceptor, Closeable { private final ConnectionAcceptor acceptor; - private final Set activeConnections = ConcurrentHashMap.newKeySet(); + private final Set activeConnections = ConcurrentHashMap.newKeySet(); private final Sinks.Empty onClose = Sinks.unsafe().empty(); @@ -142,13 +142,13 @@ static class ServerCloseableAcceptor implements ConnectionAcceptor, Closeable { } @Override - public Mono accept(DuplexConnection duplexConnection) { - activeConnections.add(duplexConnection); - duplexConnection + public Mono accept(Connection connection) { + activeConnections.add(connection); + connection .onClose() - .doFinally(__ -> activeConnections.remove(duplexConnection)) + .doFinally(__ -> activeConnections.remove(connection)) .subscribe(); - return acceptor.accept(duplexConnection); + return acceptor.accept(connection); } @Override @@ -158,8 +158,8 @@ public void dispose() { return; } - Mono.whenDelayError(activeConnections.stream().peek(DuplexConnection::dispose) - .map(DuplexConnection::onClose).collect(Collectors.toList())) + Mono.whenDelayError(activeConnections.stream().peek(Connection::dispose) + .map(Connection::onClose).collect(Collectors.toList())) .subscribe(null, onClose::tryEmitError, onClose::tryEmitEmpty); } diff --git a/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalSocketAddress.java b/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalSocketAddress.java index 9aa5098..902afd7 100644 --- a/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalSocketAddress.java +++ b/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/LocalSocketAddress.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.local; diff --git a/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/package-info.java b/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/package-info.java index 8cf3471..8c33711 100644 --- a/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/package-info.java +++ b/today-remoting-transport-local/src/main/java/infra/remoting/transport/local/package-info.java @@ -1,26 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /** * The local remoting transport implementation. */ -@NonNullApi -@NonNullFields +@NullMarked package infra.remoting.transport.local; -import infra.lang.NonNullApi; -import infra.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalClientTransportTests.java b/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalClientTransportTests.java index 7f02ea4..da37baf 100644 --- a/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalClientTransportTests.java +++ b/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalClientTransportTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.local; diff --git a/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalPingPong.java b/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalPingPong.java index 22b0279..20a56a4 100644 --- a/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalPingPong.java +++ b/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalPingPong.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.local; diff --git a/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalResumableTransportTests.java b/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalResumableTransportTests.java index ca8444b..f2bd5f5 100644 --- a/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalResumableTransportTests.java +++ b/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalResumableTransportTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.local; diff --git a/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalResumableWithFragmentationTransportTests.java b/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalResumableWithFragmentationTransportTests.java index f478b34..0a59c4f 100644 --- a/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalResumableWithFragmentationTransportTests.java +++ b/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalResumableWithFragmentationTransportTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.local; diff --git a/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalServerTransportTests.java b/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalServerTransportTests.java index b713b86..3d6cf53 100644 --- a/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalServerTransportTests.java +++ b/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalServerTransportTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.local; diff --git a/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalSocketAddressTests.java b/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalSocketAddressTests.java index fde758f..9a3ea3a 100644 --- a/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalSocketAddressTests.java +++ b/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalSocketAddressTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.local; diff --git a/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalTransportTests.java b/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalTransportTests.java index 623ef52..27d0253 100644 --- a/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalTransportTests.java +++ b/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalTransportTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.local; diff --git a/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalTransportWithFragmentationTests.java b/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalTransportWithFragmentationTests.java index aed5c90..1fe2311 100644 --- a/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalTransportWithFragmentationTests.java +++ b/today-remoting-transport-local/src/test/java/infra/remoting/transport/local/LocalTransportWithFragmentationTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.local; diff --git a/today-remoting-transport-local/src/test/resources/logback-test.xml b/today-remoting-transport-local/src/test/resources/logback-test.xml index 24b1089..0cea20c 100644 --- a/today-remoting-transport-local/src/test/resources/logback-test.xml +++ b/today-remoting-transport-local/src/test/resources/logback-test.xml @@ -1,21 +1,4 @@ - - @@ -34,12 +17,12 @@ - - + + - + diff --git a/today-remoting-transport-tcp/build.gradle b/today-remoting-transport-tcp/build.gradle index 9689833..d8aa2e8 100644 --- a/today-remoting-transport-tcp/build.gradle +++ b/today-remoting-transport-tcp/build.gradle @@ -15,7 +15,6 @@ dependencies { api(project(":today-remoting")) api "io.projectreactor.netty:reactor-netty-core" - api "io.projectreactor.netty:reactor-netty-http" optional 'io.netty:netty-transport' optional 'io.netty:netty-codec' @@ -28,15 +27,8 @@ dependencies { testRuntimeOnly 'org.bouncycastle:bcpkix-jdk15on:1.70' testRuntimeOnly 'ch.qos.logback:logback-classic' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' testRuntimeOnly 'io.netty:netty-tcnative-boringssl-static' + os_suffix testImplementation testFixtures(project(":today-remoting")) } - -jar { - manifest { - attributes("Automatic-Module-Name": "infra.remoting.transport.tcp") - } -} \ No newline at end of file diff --git a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/ProtocolFrameLengthCodec.java b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/ProtocolFrameLengthCodec.java index 85f62d8..eab2ccf 100644 --- a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/ProtocolFrameLengthCodec.java +++ b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/ProtocolFrameLengthCodec.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; @@ -38,7 +37,7 @@ public ProtocolFrameLengthCodec() { } /** - * Creates a new instance of the decoder, specifying the RSocket frame length header size. + * Creates a new instance of the decoder, specifying the Protocol frame length header size. * * @param maxFrameLength maximum allowed frame length for incoming protocol frames */ diff --git a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/TcpDuplexConnection.java b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/TcpConnection.java similarity index 56% rename from today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/TcpDuplexConnection.java rename to today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/TcpConnection.java index 6851922..1ebe582 100644 --- a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/TcpDuplexConnection.java +++ b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/TcpConnection.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; @@ -20,37 +19,37 @@ import java.net.SocketAddress; import java.util.Objects; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.ProtocolErrorException; import infra.remoting.frame.ErrorFrameCodec; import infra.remoting.frame.FrameLengthCodec; -import infra.remoting.internal.BaseDuplexConnection; +import infra.remoting.internal.BaseConnection; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import reactor.netty.Connection; -/** An implementation of {@link DuplexConnection} that connects via TCP. */ -public final class TcpDuplexConnection extends BaseDuplexConnection { +/** An implementation of {@link Connection} that connects via TCP. */ +public final class TcpConnection extends BaseConnection { + private final String side; - private final Connection connection; + private final reactor.netty.Connection connection; /** * Creates a new instance * - * @param connection the {@link Connection} for managing the server + * @param connection the {@link reactor.netty.Connection} for managing the server */ - public TcpDuplexConnection(Connection connection) { + public TcpConnection(reactor.netty.Connection connection) { this("unknown", connection); } /** * Creates a new instance * - * @param connection the {@link Connection} for managing the server + * @param connection the {@link reactor.netty.Connection} for managing the server */ - public TcpDuplexConnection(String side, Connection connection) { + public TcpConnection(String side, reactor.netty.Connection connection) { this.connection = Objects.requireNonNull(connection, "connection is required"); this.side = side; @@ -95,7 +94,7 @@ public void sendFrame(int streamId, ByteBuf frame) { @Override public String toString() { - return "TcpDuplexConnection{side='%s', connection=%s}".formatted(side, connection); + return "TcpConnection{side='%s', connection=%s}".formatted(side, connection); } } diff --git a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/client/TcpClientTransport.java b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/client/TcpClientTransport.java index 8366c91..61430f9 100644 --- a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/client/TcpClientTransport.java +++ b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/client/TcpClientTransport.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty.client; @@ -20,11 +19,11 @@ import java.net.InetSocketAddress; import java.util.Objects; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.transport.ClientTransport; import infra.remoting.transport.ServerTransport; import infra.remoting.transport.netty.ProtocolFrameLengthCodec; -import infra.remoting.transport.netty.TcpDuplexConnection; +import infra.remoting.transport.netty.TcpConnection; import reactor.core.publisher.Mono; import reactor.netty.tcp.TcpClient; @@ -114,10 +113,10 @@ public int getMaxFrameLength() { } @Override - public Mono connect() { + public Mono connect() { return client .doOnConnected(c -> c.addHandlerLast(new ProtocolFrameLengthCodec(maxFrameLength))) .connect() - .map(connection -> new TcpDuplexConnection("client", connection)); + .map(connection -> new TcpConnection("client", connection)); } } diff --git a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/client/package-info.java b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/client/package-info.java index eff2e01..f1bebbd 100644 --- a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/client/package-info.java +++ b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/client/package-info.java @@ -1,22 +1,21 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ -/** The Netty-based RSocket client transport implementations. */ -@NonNullApi +/** The Netty-based client transport implementations. */ +@NullMarked package infra.remoting.transport.netty.client; -import infra.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/package-info.java b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/package-info.java index 891de8b..70c5ee2 100644 --- a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/package-info.java +++ b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/package-info.java @@ -1,26 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /** * The Netty-based remoting transport implementations. */ -@NonNullApi -@NonNullFields +@NullMarked package infra.remoting.transport.netty; -import infra.lang.NonNullApi; -import infra.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/CloseableChannel.java b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/CloseableChannel.java index 2c3d306..a03d349 100644 --- a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/CloseableChannel.java +++ b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/CloseableChannel.java @@ -1,33 +1,31 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty.server; -import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.util.Objects; +import infra.core.FutureMono; import infra.remoting.Closeable; -import infra.remoting.util.FutureMono; import io.netty.channel.Channel; import reactor.core.publisher.Mono; import reactor.netty.DisposableChannel; -import static infra.remoting.transport.tcp.PromiseAdapter.adapt; +import static infra.remoting.util.PromiseAdapter.adapt; /** * An implementation of {@link Closeable} that wraps a {@link DisposableChannel}, enabling @@ -35,18 +33,6 @@ */ public final class CloseableChannel implements Closeable { - /** For forward compatibility: remove when RSocket compiles against Reactor 1.0. */ - private static final Method channelAddressMethod; - - static { - try { - channelAddressMethod = DisposableChannel.class.getMethod("address"); - } - catch (NoSuchMethodException ex) { - throw new IllegalStateException("Expected address method", ex); - } - } - private final Channel channel; /** @@ -76,17 +62,7 @@ public CloseableChannel(Channel channel) { * @see DisposableChannel#address() */ public InetSocketAddress address() { - try { - return (InetSocketAddress) channel.localAddress(); - } - catch (ClassCastException | NoSuchMethodError e) { - try { - return (InetSocketAddress) channelAddressMethod.invoke(this.channel); - } - catch (Exception ex) { - throw new IllegalStateException("Unable to obtain address", ex); - } - } + return (InetSocketAddress) channel.localAddress(); } @Override diff --git a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/TcpServerTransport.java b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/TcpServerTransport.java index 2cf8812..3f0aca8 100644 --- a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/TcpServerTransport.java +++ b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/TcpServerTransport.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty.server; @@ -24,7 +23,7 @@ import infra.remoting.transport.ConnectionAcceptor; import infra.remoting.transport.ServerTransport; import infra.remoting.transport.netty.ProtocolFrameLengthCodec; -import infra.remoting.transport.netty.TcpDuplexConnection; +import infra.remoting.transport.netty.TcpConnection; import reactor.core.publisher.Mono; import reactor.netty.tcp.TcpServer; @@ -115,7 +114,7 @@ public Mono start(ConnectionAcceptor acceptor) { Objects.requireNonNull(acceptor, "acceptor is required"); return server.doOnConnection(c -> { c.addHandlerLast(new ProtocolFrameLengthCodec(maxFrameLength)); - acceptor.accept(new TcpDuplexConnection("server", c)) + acceptor.accept(new TcpConnection("server", c)) .then(Mono.never()) .subscribe(c.disposeSubscriber()); }) diff --git a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/package-info.java b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/package-info.java index ede97a7..bead30e 100644 --- a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/package-info.java +++ b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/package-info.java @@ -1,22 +1,21 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ -/** The Netty-based RSocket server transport implementations. */ -@NonNullApi +/** The Netty-based server transport implementations. */ +@NullMarked package infra.remoting.transport.netty.server; -import infra.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/tcp/TCPServerTransport.java b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/tcp/TCPServerTransport.java index 9393d7c..119f884 100644 --- a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/tcp/TCPServerTransport.java +++ b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/tcp/TCPServerTransport.java @@ -1,34 +1,34 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.tcp; +import org.jspecify.annotations.Nullable; + import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.concurrent.TimeUnit; +import infra.core.FutureMono; import infra.lang.Assert; -import infra.lang.Nullable; import infra.logging.Logger; import infra.logging.LoggerFactory; import infra.remoting.transport.ConnectionAcceptor; import infra.remoting.transport.ServerTransport; import infra.remoting.transport.netty.server.CloseableChannel; -import infra.remoting.util.FutureMono; import infra.util.ClassUtils; import infra.util.concurrent.Future; import io.netty.bootstrap.ServerBootstrap; @@ -53,7 +53,7 @@ import io.netty.util.concurrent.DefaultThreadFactory; import reactor.core.publisher.Mono; -import static infra.remoting.transport.tcp.PromiseAdapter.adapt; +import static infra.remoting.util.PromiseAdapter.adapt; /** * Netty TCP server transport @@ -359,7 +359,7 @@ protected void initChannel(Channel ch) throws Exception { 0, FRAME_LENGTH_SIZE, 0, FRAME_LENGTH_SIZE)) .addLast("channel-transport", transport); -// acceptor.accept(new TcpDuplexConnection(ch)); +// acceptor.accept(new TcpConnection(ch)); } } diff --git a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/tcp/package-info.java b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/tcp/package-info.java index fc1aec3..ac3b380 100644 --- a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/tcp/package-info.java +++ b/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/tcp/package-info.java @@ -1,26 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /** * The Netty-based TCP transport implementations. */ -@NonNullApi -@NonNullFields +@NullMarked package infra.remoting.transport.tcp; -import infra.lang.NonNullApi; -import infra.lang.NonNullFields; \ No newline at end of file +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/integration/FragmentTests.java b/today-remoting-transport-tcp/src/test/java/infra/remoting/integration/FragmentTests.java index b21ebda..58fcd79 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/integration/FragmentTests.java +++ b/today-remoting-transport-tcp/src/test/java/infra/remoting/integration/FragmentTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.integration; @@ -25,15 +24,15 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Stream; -import infra.remoting.Payload; import infra.remoting.Channel; +import infra.remoting.DecoratingChannel; +import infra.remoting.Payload; import infra.remoting.core.ChannelConnector; import infra.remoting.core.RemotingServer; import infra.remoting.transport.netty.client.TcpClientTransport; import infra.remoting.transport.netty.server.CloseableChannel; import infra.remoting.transport.netty.server.TcpServerTransport; import infra.remoting.util.DefaultPayload; -import infra.remoting.util.ChannelDecorator; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -66,7 +65,7 @@ public void startup(int frameSize) { TcpServerTransport serverTransport = TcpServerTransport.create("localhost", randomPort); server = - RemotingServer.create((setup, sendingSocket) -> Mono.just(new ChannelDecorator(handler))) + RemotingServer.create((setup, channel) -> Mono.just(new DecoratingChannel(handler))) .fragment(frameSize) .bind(serverTransport) .block(); diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/integration/KeepaliveTests.java b/today-remoting-transport-tcp/src/test/java/infra/remoting/integration/KeepaliveTests.java index 6b22928..9397a06 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/integration/KeepaliveTests.java +++ b/today-remoting-transport-tcp/src/test/java/infra/remoting/integration/KeepaliveTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.integration; @@ -65,13 +64,13 @@ void tearDown() { @Test void keepAliveTest() { - RemotingClient rsocketClient = createClient(); + RemotingClient client = createClient(); int expectedCount = 4; AtomicBoolean sleepOnce = new AtomicBoolean(true); StepVerifier.create(Flux.range(0, expectedCount) .delayElements(Duration.ofMillis(2000)) - .concatMap(i -> rsocketClient + .concatMap(i -> client .requestResponse(Mono.just(DefaultPayload.create(""))) .doOnNext(__ -> { if (sleepOnce.getAndSet(false)) { @@ -94,13 +93,13 @@ void keepAliveTest() { @Test void keepAliveTestLazy() { - Mono rsocketMono = createClientLazy(); + Mono channelMono = createClientLazy(); int expectedCount = 4; AtomicBoolean sleepOnce = new AtomicBoolean(true); StepVerifier.create(Flux.range(0, expectedCount) .delayElements(Duration.ofMillis(2000)) - .concatMap(i -> rsocketMono.flatMap(rsocket -> rsocket + .concatMap(i -> channelMono.flatMap(channel -> channel .requestResponse(DefaultPayload.create("")) .doOnNext(__ -> { if (sleepOnce.getAndSet(false)) { @@ -126,18 +125,18 @@ private static Mono createServer() { TcpServer tcpServer = TcpServer.create().host("localhost").port(PORT); - return RemotingServer.create((setupPayload, rSocket) -> { - rSocket + return RemotingServer.create((setupPayload, channel) -> { + channel .onClose() .doFirst(() -> LOG.info("Connected on server side.")) .doOnTerminate(() -> LOG.info("Connection closed on server side.")) .subscribe(); - return Mono.just(new MyServerRsocket()); + return Mono.just(new MyServerChannel()); }) .payloadDecoder(PayloadDecoder.ZERO_COPY) .bind(TcpServerTransport.create(tcpServer)) - .doOnNext(closeableChannel -> LOG.info("RSocket server started.")); + .doOnNext(closeableChannel -> LOG.info("server started.")); } private static RemotingClient createClient() { @@ -147,14 +146,14 @@ private static RemotingClient createClient() { Retry.backoff(Long.MAX_VALUE, Duration.ofSeconds(10L)) .doBeforeRetry(retrySignal -> LOG.info("Reconnecting. Reason: {}", reason)); - Mono rsocketMono = + Mono channelMono = ChannelConnector.create() .fragment(16384) .reconnect(reconnectSpec.apply("connector-close")) .keepAlive(Duration.ofMillis(100L), Duration.ofMillis(900L)) .connect(TcpClientTransport.create(TcpClient.create().host("localhost").port(PORT))); - RemotingClient client = RemotingClient.from(rsocketMono); + RemotingClient client = RemotingClient.from(channelMono); client.source() .doOnNext(r -> LOG.info("Got")) @@ -184,7 +183,7 @@ private static Mono createClientLazy() { .connect(TcpClientTransport.create(TcpClient.create().host("localhost").port(PORT))); } - public static class MyServerRsocket implements Channel { + public static class MyServerChannel implements Channel { @Override public Mono requestResponse(Payload payload) { diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/ChannelFactoryNettyTransportFragmentationTests.java b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/ChannelFactoryNettyTransportFragmentationTests.java index 589dd7f..cca0964 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/ChannelFactoryNettyTransportFragmentationTests.java +++ b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/ChannelFactoryNettyTransportFragmentationTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; @@ -32,14 +31,15 @@ import infra.remoting.transport.netty.client.TcpClientTransport; import infra.remoting.transport.netty.server.CloseableChannel; import infra.remoting.transport.netty.server.TcpServerTransport; -import infra.remoting.transport.netty.server.WebsocketServerTransport; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; class ChannelFactoryNettyTransportFragmentationTests { static Stream> arguments() { - return Stream.of(TcpServerTransport.create(0), WebsocketServerTransport.create(0)); + return Stream.of(TcpServerTransport.create(0) +// , WebsocketServerTransport.create(0) + ); } @ParameterizedTest @@ -71,12 +71,12 @@ void clientSucceedsWithEnabledFragmentationOnSufficientMtu( CloseableChannel server = RemotingServer.create(mockAcceptor()).fragment(100).bind(serverTransport).block(); - Mono rSocket = + Mono channel = ChannelConnector.create() .fragment(100) .connect(TcpClientTransport.create(server.address())) .doFinally(s -> server.dispose()); - StepVerifier.create(rSocket).expectNextCount(1).expectComplete().verify(Duration.ofSeconds(5)); + StepVerifier.create(channel).expectNextCount(1).expectComplete().verify(Duration.ofSeconds(5)); } @ParameterizedTest @@ -84,10 +84,10 @@ void clientSucceedsWithEnabledFragmentationOnSufficientMtu( void clientSucceedsWithDisabledFragmentation(ServerTransport serverTransport) { CloseableChannel server = RemotingServer.create(mockAcceptor()).bind(serverTransport).block(); - Mono rSocket = + Mono channel = ChannelConnector.connectWith(TcpClientTransport.create(server.address())) .doFinally(s -> server.dispose()); - StepVerifier.create(rSocket).expectNextCount(1).expectComplete().verify(Duration.ofSeconds(5)); + StepVerifier.create(channel).expectNextCount(1).expectComplete().verify(Duration.ofSeconds(5)); } private ChannelAcceptor mockAcceptor() { diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpFragmentationTransportTests.java b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpFragmentationTransportTests.java index 8346a7e..9511411 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpFragmentationTransportTests.java +++ b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpFragmentationTransportTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; @@ -22,11 +21,11 @@ import java.net.InetSocketAddress; import java.time.Duration; -import io.netty.channel.ChannelOption; import infra.remoting.test.TransportPair; import infra.remoting.test.TransportTest; import infra.remoting.transport.netty.client.TcpClientTransport; import infra.remoting.transport.netty.server.TcpServerTransport; +import io.netty.channel.ChannelOption; import reactor.netty.tcp.TcpClient; import reactor.netty.tcp.TcpServer; diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpPingTests.java b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpPingTests.java index acd0694..51ee0b3 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpPingTests.java +++ b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpPingTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; @@ -35,7 +34,7 @@ @PerfTest class TcpPingTests { private static final int INTERACTIONS_COUNT = 1_000_000_000; - private static final int port = Integer.valueOf(System.getProperty("RSOCKET_TEST_PORT", "7878")); + private static final int port = Integer.valueOf(System.getProperty("REMOTING_TEST_PORT", "7878")); @BeforeEach void setUp() { @@ -89,12 +88,12 @@ private static PingClient newPingClient(boolean isResumable) { if (isResumable) { connector.resume(new Resume()); } - Mono rSocket = + Mono channel = connector .payloadDecoder(PayloadDecoder.ZERO_COPY) .keepAlive(Duration.ofMinutes(1), Duration.ofMinutes(30)) .connect(TcpClientTransport.create(port)); - return new PingClient(rSocket); + return new PingClient(channel); } } diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpPongServer.java b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpPongServer.java index 844c2e3..20d4327 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpPongServer.java +++ b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpPongServer.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; @@ -25,8 +24,8 @@ public final class TcpPongServer { private static final boolean isResume = - Boolean.valueOf(System.getProperty("RSOCKET_TEST_RESUME", "false")); - private static final int port = Integer.valueOf(System.getProperty("RSOCKET_TEST_PORT", "7878")); + Boolean.valueOf(System.getProperty("REMOTING_TEST_RESUME", "false")); + private static final int port = Integer.valueOf(System.getProperty("REMOTING_TEST_PORT", "7878")); public static void main(String... args) { System.out.println("Starting TCP ping-pong server"); diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpResumableTransportTests.java b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpResumableTransportTests.java index 46f722c..54dc18e 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpResumableTransportTests.java +++ b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpResumableTransportTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; @@ -22,11 +21,11 @@ import java.net.InetSocketAddress; import java.time.Duration; -import io.netty.channel.ChannelOption; import infra.remoting.test.TransportPair; import infra.remoting.test.TransportTest; import infra.remoting.transport.netty.client.TcpClientTransport; import infra.remoting.transport.netty.server.TcpServerTransport; +import io.netty.channel.ChannelOption; import reactor.netty.tcp.TcpClient; import reactor.netty.tcp.TcpServer; diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpResumableWithFragmentationTransportTests.java b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpResumableWithFragmentationTransportTests.java index 0c5c7b2..d683f97 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpResumableWithFragmentationTransportTests.java +++ b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpResumableWithFragmentationTransportTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; @@ -22,11 +21,11 @@ import java.net.InetSocketAddress; import java.time.Duration; -import io.netty.channel.ChannelOption; import infra.remoting.test.TransportPair; import infra.remoting.test.TransportTest; import infra.remoting.transport.netty.client.TcpClientTransport; import infra.remoting.transport.netty.server.TcpServerTransport; +import io.netty.channel.ChannelOption; import reactor.netty.tcp.TcpClient; import reactor.netty.tcp.TcpServer; diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpSecureTransportTests.java b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpSecureTransportTests.java index f6f4350..b119251 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpSecureTransportTests.java +++ b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpSecureTransportTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; @@ -25,14 +24,14 @@ import javax.net.ssl.SSLException; -import io.netty.channel.ChannelOption; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.handler.ssl.util.SelfSignedCertificate; import infra.remoting.test.TransportPair; import infra.remoting.test.TransportTest; import infra.remoting.transport.netty.client.TcpClientTransport; import infra.remoting.transport.netty.server.TcpServerTransport; +import io.netty.channel.ChannelOption; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; +import io.netty.handler.ssl.util.SelfSignedCertificate; import reactor.core.Exceptions; import reactor.netty.tcp.TcpClient; import reactor.netty.tcp.TcpServer; diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpTransportTests.java b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpTransportTests.java index 99d12c2..fbbe0cd 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpTransportTests.java +++ b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/TcpTransportTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; @@ -22,11 +21,11 @@ import java.net.InetSocketAddress; import java.time.Duration; -import io.netty.channel.ChannelOption; import infra.remoting.test.TransportPair; import infra.remoting.test.TransportTest; import infra.remoting.transport.netty.client.TcpClientTransport; import infra.remoting.transport.netty.server.TcpServerTransport; +import io.netty.channel.ChannelOption; import reactor.netty.tcp.TcpClient; import reactor.netty.tcp.TcpServer; diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketPongServer.java b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketPongServer.java deleted file mode 100644 index f3abe8c..0000000 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketPongServer.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.transport.netty; - -import infra.remoting.core.RemotingServer; -import infra.remoting.frame.decoder.PayloadDecoder; -import infra.remoting.test.PingHandler; -import infra.remoting.transport.netty.server.WebsocketServerTransport; - -public final class WebsocketPongServer { - - public static void main(String... args) { - RemotingServer.create(new PingHandler()) - .payloadDecoder(PayloadDecoder.ZERO_COPY) - .bind(WebsocketServerTransport.create(7878)) - .block() - .onClose() - .block(); - } -} diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketResumableWithFragmentationTransportTests.java b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketResumableWithFragmentationTransportTests.java deleted file mode 100644 index 5772e3e..0000000 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketResumableWithFragmentationTransportTests.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.transport.netty; - -import org.junit.jupiter.api.BeforeEach; - -import java.net.InetSocketAddress; -import java.time.Duration; - -import io.netty.channel.ChannelOption; -import infra.remoting.test.TransportPair; -import infra.remoting.test.TransportTest; -import infra.remoting.transport.netty.client.WebsocketClientTransport; -import infra.remoting.transport.netty.server.WebsocketServerTransport; -import reactor.netty.http.client.HttpClient; -import reactor.netty.http.server.HttpServer; - -final class WebsocketResumableWithFragmentationTransportTests implements TransportTest { - private TransportPair transportPair; - - @BeforeEach - void createTestPair() { - transportPair = - new TransportPair<>( - () -> InetSocketAddress.createUnresolved("localhost", 0), - (address, server, allocator) -> - WebsocketClientTransport.create( - HttpClient.create() - .host(server.address().getHostName()) - .port(server.address().getPort()) - .option(ChannelOption.ALLOCATOR, allocator), - ""), - (address, allocator) -> { - return WebsocketServerTransport.create( - HttpServer.create() - .host(address.getHostName()) - .port(address.getPort()) - .option(ChannelOption.ALLOCATOR, allocator)); - }, - true, - true); - } - - @Override - public Duration getTimeout() { - return Duration.ofMinutes(3); - } - - @Override - public TransportPair getTransportPair() { - return transportPair; - } -} diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketTransportTests.java b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketTransportTests.java deleted file mode 100644 index 20b115a..0000000 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketTransportTests.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.transport.netty; - -import org.junit.jupiter.api.BeforeEach; - -import java.net.InetSocketAddress; -import java.time.Duration; - -import io.netty.channel.ChannelOption; -import infra.remoting.test.TransportPair; -import infra.remoting.test.TransportTest; -import infra.remoting.transport.netty.client.WebsocketClientTransport; -import infra.remoting.transport.netty.server.WebsocketServerTransport; -import reactor.netty.http.client.HttpClient; -import reactor.netty.http.server.HttpServer; - -final class WebsocketTransportTests implements TransportTest { - private TransportPair transportPair; - - @BeforeEach - void createTestPair() { - transportPair = - new TransportPair<>( - () -> InetSocketAddress.createUnresolved("localhost", 0), - (address, server, allocator) -> - WebsocketClientTransport.create( - HttpClient.create() - .host(server.address().getHostName()) - .port(server.address().getPort()) - .option(ChannelOption.ALLOCATOR, allocator), - ""), - (address, allocator) -> { - return WebsocketServerTransport.create( - HttpServer.create() - .host(address.getHostName()) - .port(address.getPort()) - .option(ChannelOption.ALLOCATOR, allocator)); - }); - } - - @Override - public Duration getTimeout() { - return Duration.ofMinutes(3); - } - - @Override - public TransportPair getTransportPair() { - return transportPair; - } -} diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/client/TcpClientTransportTests.java b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/client/TcpClientTransportTests.java index 2e3c26c..da489f4 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/client/TcpClientTransportTests.java +++ b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/client/TcpClientTransportTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty.client; diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/server/CloseableChannelTests.java b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/server/CloseableChannelTests.java index 54bc5a1..15d54c7 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/server/CloseableChannelTests.java +++ b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/server/CloseableChannelTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty.server; diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/server/TcpServerTransportTests.java b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/server/TcpServerTransportTests.java index 9f2fea2..3e7c8e5 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/server/TcpServerTransportTests.java +++ b/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/server/TcpServerTransportTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty.server; diff --git a/today-remoting-transport-tcp/src/test/resources/logback-test.xml b/today-remoting-transport-tcp/src/test/resources/logback-test.xml index 5e8b4cd..7022e97 100644 --- a/today-remoting-transport-tcp/src/test/resources/logback-test.xml +++ b/today-remoting-transport-tcp/src/test/resources/logback-test.xml @@ -1,21 +1,4 @@ - - @@ -26,14 +9,14 @@ - + - - + + - + diff --git a/today-remoting-transport-websocket/build.gradle b/today-remoting-transport-websocket/build.gradle new file mode 100644 index 0000000..a2929b1 --- /dev/null +++ b/today-remoting-transport-websocket/build.gradle @@ -0,0 +1,26 @@ +description = "TODAY Remoting Transport Websocket based on netty" + + +dependencies { + api(project(":today-remoting")) + + api "io.projectreactor.netty:reactor-netty-core" + api "io.projectreactor.netty:reactor-netty-http" + + optional 'io.netty:netty-transport' + optional 'io.netty:netty-codec' + optional 'io.netty:netty-handler' + optional 'io.netty:netty-transport-classes-epoll' + + testImplementation 'io.projectreactor:reactor-test' + testImplementation project(":today-remoting-transport-tcp") + + testRuntimeOnly 'org.bouncycastle:bcpkix-jdk15on:1.70' + testRuntimeOnly 'ch.qos.logback:logback-classic' +// testRuntimeOnly 'io.netty:netty-tcnative-boringssl-static' + os_suffix + + testImplementation testFixtures(project(":today-remoting")) + + +} + diff --git a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/BaseWebsocketServerTransport.java b/today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/BaseWebsocketServerTransport.java similarity index 70% rename from today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/BaseWebsocketServerTransport.java rename to today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/BaseWebsocketServerTransport.java index 89de061..80f3697 100644 --- a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/BaseWebsocketServerTransport.java +++ b/today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/BaseWebsocketServerTransport.java @@ -1,21 +1,20 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty.server; +package infra.remoting.transport.websocket; import java.util.function.Consumer; import java.util.function.Function; @@ -23,6 +22,7 @@ import infra.logging.Logger; import infra.logging.LoggerFactory; import infra.remoting.Closeable; +import infra.remoting.frame.FrameLengthCodec; import infra.remoting.transport.ServerTransport; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; @@ -32,7 +32,6 @@ import reactor.netty.http.server.HttpServer; import reactor.netty.http.server.WebsocketServerSpec; -import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; import static io.netty.channel.ChannelHandler.Sharable; abstract class BaseWebsocketServerTransport, T extends Closeable> implements ServerTransport { @@ -45,7 +44,7 @@ abstract class BaseWebsocketServerTransport server.doOnConnection(connection -> connection.addHandlerLast(pongHandler)); final WebsocketServerSpec.Builder specBuilder = - WebsocketServerSpec.builder().maxFramePayloadLength(FRAME_LENGTH_MASK); + WebsocketServerSpec.builder().maxFramePayloadLength(FrameLengthCodec.FRAME_LENGTH_MASK); /** * Provide a consumer to customize properties of the {@link WebsocketServerSpec} to use for diff --git a/today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/CloseableChannel.java b/today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/CloseableChannel.java new file mode 100644 index 0000000..2cd4edf --- /dev/null +++ b/today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/CloseableChannel.java @@ -0,0 +1,83 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.transport.websocket; + +import java.net.InetSocketAddress; +import java.util.Objects; + +import infra.core.FutureMono; +import infra.remoting.Closeable; +import io.netty.channel.Channel; +import reactor.core.publisher.Mono; +import reactor.netty.DisposableChannel; + +import static infra.remoting.util.PromiseAdapter.adapt; + +/** + * An implementation of {@link Closeable} that wraps a {@link DisposableChannel}, enabling + * close-ability and exposing the {@link DisposableChannel}'s address. + */ +public final class CloseableChannel implements Closeable { + + private final Channel channel; + + /** + * Creates a new instance + * + * @param channel the {@link DisposableChannel} to wrap + * @throws NullPointerException if {@code context} is {@code null} + */ + CloseableChannel(DisposableChannel channel) { + this.channel = Objects.requireNonNull(channel, "channel is required").channel(); + } + + /** + * Creates a new instance + * + * @param channel the {@link DisposableChannel} to wrap + * @throws NullPointerException if {@code context} is {@code null} + */ + public CloseableChannel(Channel channel) { + this.channel = Objects.requireNonNull(channel, "channel is required"); + } + + /** + * Return local server selector channel address. + * + * @return local {@link InetSocketAddress} + * @see DisposableChannel#address() + */ + public InetSocketAddress address() { + return (InetSocketAddress) channel.localAddress(); + } + + @Override + public void dispose() { + channel.close(); + } + + @Override + public boolean isDisposed() { + return !channel.isActive(); + } + + @Override + public Mono onClose() { + return FutureMono.of(adapt(channel.closeFuture())); + } + +} diff --git a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/client/WebsocketClientTransport.java b/today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/WebsocketClientTransport.java similarity index 81% rename from today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/client/WebsocketClientTransport.java rename to today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/WebsocketClientTransport.java index 955bc38..c14c173 100644 --- a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/client/WebsocketClientTransport.java +++ b/today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/WebsocketClientTransport.java @@ -1,21 +1,20 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty.client; +package infra.remoting.transport.websocket; import java.net.InetSocketAddress; import java.net.URI; @@ -23,10 +22,10 @@ import java.util.Objects; import java.util.function.Consumer; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; +import infra.remoting.frame.FrameLengthCodec; import infra.remoting.transport.ClientTransport; import infra.remoting.transport.ServerTransport; -import infra.remoting.transport.netty.WebsocketDuplexConnection; import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.HttpHeaders; import reactor.core.publisher.Mono; @@ -34,8 +33,6 @@ import reactor.netty.http.client.WebsocketClientSpec; import reactor.netty.tcp.TcpClient; -import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; - /** * An implementation of {@link ClientTransport} that connects to a {@link ServerTransport} over * WebSocket. @@ -51,7 +48,7 @@ public final class WebsocketClientTransport implements ClientTransport { private HttpHeaders headers = new DefaultHttpHeaders(); private final WebsocketClientSpec.Builder specBuilder = - WebsocketClientSpec.builder().maxFramePayloadLength(FRAME_LENGTH_MASK); + WebsocketClientSpec.builder().maxFramePayloadLength(FrameLengthCodec.FRAME_LENGTH_MASK); private WebsocketClientTransport(HttpClient client, String path) { Objects.requireNonNull(client, "HttpClient is required"); @@ -166,12 +163,12 @@ public int getMaxFrameLength() { } @Override - public Mono connect() { + public Mono connect() { return client .headers(headers -> headers.add(this.headers)) .websocket(specBuilder.build()) .uri(path) .connect() - .map(connection -> new WebsocketDuplexConnection("client", connection)); + .map(connection -> new WebsocketConnection("client", connection)); } } diff --git a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/WebsocketDuplexConnection.java b/today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/WebsocketConnection.java similarity index 54% rename from today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/WebsocketDuplexConnection.java rename to today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/WebsocketConnection.java index b511743..fac8846 100644 --- a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/WebsocketDuplexConnection.java +++ b/today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/WebsocketConnection.java @@ -1,62 +1,60 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; +package infra.remoting.transport.websocket; import java.net.SocketAddress; import java.util.Objects; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.ProtocolErrorException; import infra.remoting.frame.ErrorFrameCodec; -import infra.remoting.internal.BaseDuplexConnection; +import infra.remoting.internal.BaseConnection; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import reactor.netty.Connection; /** - * An implementation of {@link DuplexConnection} that connects via a Websocket. + * An implementation of {@link Connection} that connects via a Websocket. * *

strongly assumes that each ByteBuf is encoded with the length. This is not true * for message oriented transports so this must be specifically dropped from Frames sent and * stitched back on for frames received. */ -public final class WebsocketDuplexConnection extends BaseDuplexConnection { +public final class WebsocketConnection extends BaseConnection { private final String side; - private final Connection connection; + private final reactor.netty.Connection connection; /** * Creates a new instance * - * @param connection the {@link Connection} to for managing the server + * @param connection the {@link reactor.netty.Connection} to for managing the server */ - public WebsocketDuplexConnection(Connection connection) { + public WebsocketConnection(reactor.netty.Connection connection) { this("unknown", connection); } /** * Creates a new instance * - * @param connection the {@link Connection} to for managing the server + * @param connection the {@link reactor.netty.Connection} to for managing the server */ - public WebsocketDuplexConnection(String side, Connection connection) { + public WebsocketConnection(String side, reactor.netty.Connection connection) { this.connection = Objects.requireNonNull(connection, "connection is required"); this.side = side; @@ -101,12 +99,6 @@ public void sendErrorAndClose(ProtocolErrorException e) { @Override public String toString() { - return "WebsocketDuplexConnection{" - + "side='" - + side - + '\'' - + ", connection=" - + connection - + '}'; + return "WebsocketConnection{side='%s', connection=%s}".formatted(side, connection); } } diff --git a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/WebsocketRouteTransport.java b/today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/WebsocketRouteTransport.java similarity index 51% rename from today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/WebsocketRouteTransport.java rename to today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/WebsocketRouteTransport.java index de4a895..5c4cc9e 100644 --- a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/WebsocketRouteTransport.java +++ b/today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/WebsocketRouteTransport.java @@ -1,21 +1,20 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty.server; +package infra.remoting.transport.websocket; import org.reactivestreams.Publisher; @@ -26,7 +25,6 @@ import infra.remoting.Closeable; import infra.remoting.transport.ConnectionAcceptor; import infra.remoting.transport.ServerTransport; -import infra.remoting.transport.netty.WebsocketDuplexConnection; import reactor.core.publisher.Mono; import reactor.netty.Connection; import reactor.netty.http.server.HttpServer; @@ -38,8 +36,7 @@ * An implementation of {@link ServerTransport} that connects via Websocket and listens on specified * routes. */ -public final class WebsocketRouteTransport - extends BaseWebsocketServerTransport { +public final class WebsocketRouteTransport extends BaseWebsocketServerTransport { private final String path; @@ -54,9 +51,8 @@ public final class WebsocketRouteTransport * @param routesBuilder the builder for the routes that will be listened on * @param path the path foe each route */ - public WebsocketRouteTransport( - HttpServer server, Consumer routesBuilder, String path) { - this.server = serverConfigurer.apply(Objects.requireNonNull(server, "server is required")); + public WebsocketRouteTransport(HttpServer server, Consumer routesBuilder, String path) { + this.server = BaseWebsocketServerTransport.serverConfigurer.apply(Objects.requireNonNull(server, "server is required")); this.routesBuilder = Objects.requireNonNull(routesBuilder, "routesBuilder is required"); this.path = Objects.requireNonNull(path, "path is required"); } @@ -64,12 +60,10 @@ public WebsocketRouteTransport( @Override public Mono start(ConnectionAcceptor acceptor) { Objects.requireNonNull(acceptor, "acceptor is required"); - return server - .route( - routes -> { - routesBuilder.accept(routes); - routes.ws(path, newHandler(acceptor), specBuilder.build()); - }) + return server.route(routes -> { + routesBuilder.accept(routes); + routes.ws(path, newHandler(acceptor), specBuilder.build()); + }) .bind() .map(CloseableChannel::new); } @@ -81,11 +75,8 @@ public Mono start(ConnectionAcceptor acceptor) { * @return a new Websocket handler * @throws NullPointerException if {@code acceptor} is {@code null} */ - public static BiFunction> newHandler( - ConnectionAcceptor acceptor) { - return (in, out) -> - acceptor - .accept(new WebsocketDuplexConnection("server", (Connection) in)) - .then(out.neverComplete()); + public static BiFunction> newHandler(ConnectionAcceptor acceptor) { + return (in, out) -> acceptor.accept(new WebsocketConnection("server", (Connection) in)) + .then(out.neverComplete()); } } diff --git a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/WebsocketServerTransport.java b/today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/WebsocketServerTransport.java similarity index 75% rename from today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/WebsocketServerTransport.java rename to today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/WebsocketServerTransport.java index 4fd2fc9..9f0aecd 100644 --- a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/netty/server/WebsocketServerTransport.java +++ b/today-remoting-transport-websocket/src/main/java/infra/remoting/transport/websocket/WebsocketServerTransport.java @@ -1,21 +1,20 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty.server; +package infra.remoting.transport.websocket; import java.net.InetSocketAddress; import java.util.Arrays; @@ -24,7 +23,6 @@ import infra.remoting.transport.ClientTransport; import infra.remoting.transport.ConnectionAcceptor; import infra.remoting.transport.ServerTransport; -import infra.remoting.transport.netty.WebsocketDuplexConnection; import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.HttpHeaders; import reactor.core.publisher.Mono; @@ -39,10 +37,10 @@ public final class WebsocketServerTransport extends BaseWebsocketServerTransport private final HttpServer server; - private HttpHeaders headers = new DefaultHttpHeaders(); + private final HttpHeaders headers = new DefaultHttpHeaders(); private WebsocketServerTransport(HttpServer server) { - this.server = serverConfigurer.apply(Objects.requireNonNull(server, "server is required")); + this.server = BaseWebsocketServerTransport.serverConfigurer.apply(Objects.requireNonNull(server, "server is required")); } /** @@ -116,7 +114,7 @@ public Mono start(ConnectionAcceptor acceptor) { return response.sendWebsocket( (in, out) -> acceptor - .accept(new WebsocketDuplexConnection("server", (Connection) in)) + .accept(new WebsocketConnection("server", (Connection) in)) .then(out.neverComplete()), specBuilder.build()); }) diff --git a/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/ChannelFactoryNettyTransportFragmentationTests.java b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/ChannelFactoryNettyTransportFragmentationTests.java new file mode 100644 index 0000000..dbc2632 --- /dev/null +++ b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/ChannelFactoryNettyTransportFragmentationTests.java @@ -0,0 +1,94 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.transport.websocket; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; + +import java.time.Duration; +import java.util.stream.Stream; + +import infra.remoting.Channel; +import infra.remoting.ChannelAcceptor; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.transport.ServerTransport; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +class ChannelFactoryNettyTransportFragmentationTests { + + static Stream> arguments() { + return Stream.of(WebsocketServerTransport.create(0)); + } + + @ParameterizedTest + @MethodSource("arguments") + void serverSucceedsWithEnabledFragmentationOnSufficientMtu( + ServerTransport serverTransport) { + Mono server = + RemotingServer.create(mockAcceptor()) + .fragment(100) + .bind(serverTransport) + .doOnNext(CloseableChannel::dispose); + StepVerifier.create(server).expectNextCount(1).expectComplete().verify(Duration.ofSeconds(5)); + } + + @ParameterizedTest + @MethodSource("arguments") + void serverSucceedsWithDisabledFragmentation(ServerTransport serverTransport) { + Mono server = + RemotingServer.create(mockAcceptor()) + .bind(serverTransport) + .doOnNext(CloseableChannel::dispose); + StepVerifier.create(server).expectNextCount(1).expectComplete().verify(Duration.ofSeconds(5)); + } + + @ParameterizedTest + @MethodSource("arguments") + void clientSucceedsWithEnabledFragmentationOnSufficientMtu( + ServerTransport serverTransport) { + CloseableChannel server = + RemotingServer.create(mockAcceptor()).fragment(100).bind(serverTransport).block(); + + Mono channel = + ChannelConnector.create() + .fragment(100) + .connect(WebsocketClientTransport.create(server.address())) + .doFinally(s -> server.dispose()); + StepVerifier.create(channel).expectNextCount(1).expectComplete().verify(Duration.ofSeconds(5)); + } + + @ParameterizedTest + @MethodSource("arguments") + void clientSucceedsWithDisabledFragmentation(ServerTransport serverTransport) { + CloseableChannel server = RemotingServer.create(mockAcceptor()).bind(serverTransport).block(); + + Mono channel = + ChannelConnector.connectWith(WebsocketClientTransport.create(server.address())) + .doFinally(s -> server.dispose()); + StepVerifier.create(channel).expectNextCount(1).expectComplete().verify(Duration.ofSeconds(5)); + } + + private ChannelAcceptor mockAcceptor() { + ChannelAcceptor mock = Mockito.mock(ChannelAcceptor.class); + Mockito.when(mock.accept(Mockito.any(), Mockito.any())) + .thenReturn(Mono.just(Mockito.mock(Channel.class))); + return mock; + } +} diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/SetupRejectionTests.java b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/SetupRejectionTests.java similarity index 71% rename from today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/SetupRejectionTests.java rename to today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/SetupRejectionTests.java index 0ae2fb2..59feb7d 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/SetupRejectionTests.java +++ b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/SetupRejectionTests.java @@ -1,20 +1,19 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; +package infra.remoting.transport.websocket; import org.junit.jupiter.params.provider.Arguments; @@ -24,19 +23,16 @@ import java.util.function.Function; import java.util.stream.Stream; -import infra.remoting.ConnectionSetupPayload; import infra.remoting.Channel; import infra.remoting.ChannelAcceptor; +import infra.remoting.ConnectionSetupPayload; import infra.remoting.core.ChannelConnector; import infra.remoting.core.RemotingServer; -import infra.remoting.exceptions.RejectedSetupException; +import infra.remoting.error.RejectedSetupException; import infra.remoting.transport.ClientTransport; import infra.remoting.transport.ServerTransport; import infra.remoting.transport.netty.client.TcpClientTransport; -import infra.remoting.transport.netty.client.WebsocketClientTransport; -import infra.remoting.transport.netty.server.CloseableChannel; import infra.remoting.transport.netty.server.TcpServerTransport; -import infra.remoting.transport.netty.server.WebsocketServerTransport; import infra.remoting.util.DefaultPayload; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -48,7 +44,7 @@ public class SetupRejectionTests { /* TODO Fix this test @DisplayName( - "Rejecting setup by server causes requester RSocket disposal and RejectedSetupException") + "Rejecting setup by server causes requester Channel disposal and RejectedSetupException") @ParameterizedTest @MethodSource(value = "transports")*/ void rejectSetupTcp( @@ -57,7 +53,7 @@ void rejectSetupTcp( String errorMessage = "error"; RejectingAcceptor acceptor = new RejectingAcceptor(errorMessage); - Mono serverRequester = acceptor.requesterRSocket(); + Mono serverRequester = acceptor.requesterChannel(); CloseableChannel channel = RemotingServer.create(acceptor) @@ -79,7 +75,7 @@ void rejectSetupTcp( StepVerifier.create(clientRequester.onClose()).expectComplete().verify(Duration.ofSeconds(5)); - StepVerifier.create(serverRequester.flatMap(socket -> socket.onClose())) + StepVerifier.create(serverRequester.flatMap(Channel::onClose)) .expectComplete() .verify(Duration.ofSeconds(5)); @@ -92,10 +88,8 @@ void rejectSetupTcp( } static Stream transports() { - Function> tcpServer = - TcpServerTransport::create; - Function> wsServer = - WebsocketServerTransport::create; + Function> tcpServer = TcpServerTransport::create; + Function> wsServer = WebsocketServerTransport::create; Function tcpClient = TcpClientTransport::create; Function wsClient = WebsocketClientTransport::create; @@ -124,12 +118,12 @@ public RejectingAcceptor(String msg) { } @Override - public Mono accept(ConnectionSetupPayload setup, Channel sendingSocket) { - requesters.tryEmitNext(sendingSocket); + public Mono accept(ConnectionSetupPayload setup, Channel channel) { + requesters.tryEmitNext(channel); return Mono.error(new RuntimeException(msg)); } - public Mono requesterRSocket() { + public Mono requesterChannel() { return requesters.asFlux().next(); } } diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebSocketClient.java b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebSocketClient.java similarity index 88% rename from today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebSocketClient.java rename to today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebSocketClient.java index d4b5b7a..b95d701 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebSocketClient.java +++ b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebSocketClient.java @@ -1,21 +1,20 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; +package infra.remoting.transport.websocket; import java.io.BufferedReader; import java.io.InputStreamReader; diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebSocketClientHandler.java b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebSocketClientHandler.java similarity index 80% rename from today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebSocketClientHandler.java rename to today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebSocketClientHandler.java index eb90ae4..2946450 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebSocketClientHandler.java +++ b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebSocketClientHandler.java @@ -1,21 +1,20 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; +package infra.remoting.transport.websocket; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebSocketTransportIntegrationTests.java b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebSocketTransportIntegrationTests.java similarity index 57% rename from today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebSocketTransportIntegrationTests.java rename to today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebSocketTransportIntegrationTests.java index e8086ff..ede85ed 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebSocketTransportIntegrationTests.java +++ b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebSocketTransportIntegrationTests.java @@ -1,29 +1,26 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; +package infra.remoting.transport.websocket; import org.junit.jupiter.api.Test; import java.net.URI; import java.time.Duration; -import infra.remoting.transport.netty.client.WebsocketClientTransport; -import infra.remoting.transport.netty.server.WebsocketRouteTransport; import infra.remoting.Channel; import infra.remoting.ChannelAcceptor; import infra.remoting.core.ChannelConnector; @@ -49,10 +46,10 @@ public void sendStreamOfDataWithExternalHttpServerTest() { .route(router -> router.ws("/test", WebsocketRouteTransport.newHandler(acceptor))) .bindNow(); - Channel rsocket = ChannelConnector.connectWith(WebsocketClientTransport.create(URI.create("ws://" + server.host() + ":" + server.port() + "/test"))) + Channel channel = ChannelConnector.connectWith(WebsocketClientTransport.create(URI.create("ws://" + server.host() + ":" + server.port() + "/test"))) .block(); - StepVerifier.create(rsocket.requestStream(EmptyPayload.INSTANCE)) + StepVerifier.create(channel.requestStream(EmptyPayload.INSTANCE)) .expectSubscription() .expectNextCount(10) .expectComplete() diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/client/WebsocketClientTransportTests.java b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketClientTransportTests.java similarity index 84% rename from today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/client/WebsocketClientTransportTests.java rename to today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketClientTransportTests.java index 3bf9840..214778f 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/client/WebsocketClientTransportTests.java +++ b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketClientTransportTests.java @@ -1,21 +1,20 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty.client; +package infra.remoting.transport.websocket; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -25,7 +24,6 @@ import java.net.InetSocketAddress; import java.net.URI; -import infra.remoting.transport.netty.server.WebsocketServerTransport; import reactor.core.publisher.Mono; import reactor.netty.http.client.HttpClient; import reactor.test.StepVerifier; diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketPing.java b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketPing.java similarity index 52% rename from today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketPing.java rename to today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketPing.java index 6e14aae..5e976a0 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketPing.java +++ b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketPing.java @@ -1,21 +1,20 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; +package infra.remoting.transport.websocket; import org.HdrHistogram.Recorder; @@ -25,7 +24,6 @@ import infra.remoting.core.ChannelConnector; import infra.remoting.frame.decoder.PayloadDecoder; import infra.remoting.test.PingClient; -import infra.remoting.transport.netty.client.WebsocketClientTransport; import reactor.core.publisher.Mono; public final class WebsocketPing { diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketPingPongIntegrationTests.java b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketPingPongIntegrationTests.java similarity index 84% rename from today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketPingPongIntegrationTests.java rename to today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketPingPongIntegrationTests.java index 46d304c..4a8a638 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketPingPongIntegrationTests.java +++ b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketPingPongIntegrationTests.java @@ -1,20 +1,19 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; +package infra.remoting.transport.websocket; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.params.ParameterizedTest; @@ -25,6 +24,13 @@ import java.time.Duration; import java.util.stream.Stream; +import infra.remoting.Channel; +import infra.remoting.ChannelAcceptor; +import infra.remoting.Closeable; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.transport.ServerTransport; +import infra.remoting.util.DefaultPayload; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; @@ -32,16 +38,6 @@ import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketFrame; import io.netty.util.ReferenceCountUtil; -import infra.remoting.Closeable; -import infra.remoting.Channel; -import infra.remoting.ChannelAcceptor; -import infra.remoting.core.ChannelConnector; -import infra.remoting.core.RemotingServer; -import infra.remoting.transport.ServerTransport; -import infra.remoting.transport.netty.client.WebsocketClientTransport; -import infra.remoting.transport.netty.server.WebsocketRouteTransport; -import infra.remoting.transport.netty.server.WebsocketServerTransport; -import infra.remoting.util.DefaultPayload; import reactor.core.Scannable; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; diff --git a/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketPongServer.java b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketPongServer.java new file mode 100644 index 0000000..2b9f8a7 --- /dev/null +++ b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketPongServer.java @@ -0,0 +1,33 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.transport.websocket; + +import infra.remoting.core.RemotingServer; +import infra.remoting.frame.decoder.PayloadDecoder; +import infra.remoting.test.PingHandler; + +public final class WebsocketPongServer { + + public static void main(String... args) { + RemotingServer.create(new PingHandler()) + .payloadDecoder(PayloadDecoder.ZERO_COPY) + .bind(WebsocketServerTransport.create(7878)) + .block() + .onClose() + .block(); + } +} diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketResumableTransportTests.java b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketResumableTransportTests.java similarity index 66% rename from today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketResumableTransportTests.java rename to today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketResumableTransportTests.java index dcc5415..ddbfa53 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketResumableTransportTests.java +++ b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketResumableTransportTests.java @@ -1,32 +1,29 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; +package infra.remoting.transport.websocket; import org.junit.jupiter.api.BeforeEach; import java.net.InetSocketAddress; import java.time.Duration; -import io.netty.channel.ChannelOption; import infra.remoting.test.TransportPair; import infra.remoting.test.TransportTest; -import infra.remoting.transport.netty.client.WebsocketClientTransport; -import infra.remoting.transport.netty.server.WebsocketServerTransport; +import io.netty.channel.ChannelOption; import reactor.netty.http.client.HttpClient; import reactor.netty.http.server.HttpServer; diff --git a/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketResumableWithFragmentationTransportTests.java b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketResumableWithFragmentationTransportTests.java new file mode 100644 index 0000000..80480c5 --- /dev/null +++ b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketResumableWithFragmentationTransportTests.java @@ -0,0 +1,64 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.transport.websocket; + +import org.junit.jupiter.api.BeforeEach; + +import java.net.InetSocketAddress; +import java.time.Duration; + +import infra.remoting.test.TransportPair; +import infra.remoting.test.TransportTest; +import io.netty.channel.ChannelOption; +import reactor.netty.http.client.HttpClient; +import reactor.netty.http.server.HttpServer; + +final class WebsocketResumableWithFragmentationTransportTests implements TransportTest { + private TransportPair transportPair; + + @BeforeEach + void createTestPair() { + transportPair = new TransportPair<>( + () -> InetSocketAddress.createUnresolved("localhost", 0), + (address, server, allocator) -> + WebsocketClientTransport.create( + HttpClient.create() + .host(server.address().getHostName()) + .port(server.address().getPort()) + .option(ChannelOption.ALLOCATOR, allocator), + ""), + (address, allocator) -> { + return WebsocketServerTransport.create( + HttpServer.create() + .host(address.getHostName()) + .port(address.getPort()) + .option(ChannelOption.ALLOCATOR, allocator)); + }, + true, + true); + } + + @Override + public Duration getTimeout() { + return Duration.ofMinutes(3); + } + + @Override + public TransportPair getTransportPair() { + return transportPair; + } +} diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/server/WebsocketRouteTransportTests.java b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketRouteTransportTests.java similarity index 74% rename from today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/server/WebsocketRouteTransportTests.java rename to today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketRouteTransportTests.java index e705b7b..4386c08 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/server/WebsocketRouteTransportTests.java +++ b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketRouteTransportTests.java @@ -1,21 +1,20 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty.server; +package infra.remoting.transport.websocket; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketSecureTransportTests.java b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketSecureTransportTests.java similarity index 77% rename from today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketSecureTransportTests.java rename to today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketSecureTransportTests.java index ee6ec2c..762b387 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/WebsocketSecureTransportTests.java +++ b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketSecureTransportTests.java @@ -1,21 +1,20 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty; +package infra.remoting.transport.websocket; import org.junit.jupiter.api.BeforeEach; @@ -25,14 +24,12 @@ import javax.net.ssl.SSLException; +import infra.remoting.test.TransportPair; +import infra.remoting.test.TransportTest; import io.netty.channel.ChannelOption; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.SelfSignedCertificate; -import infra.remoting.test.TransportPair; -import infra.remoting.test.TransportTest; -import infra.remoting.transport.netty.client.WebsocketClientTransport; -import infra.remoting.transport.netty.server.WebsocketServerTransport; import reactor.core.Exceptions; import reactor.netty.http.client.HttpClient; import reactor.netty.http.server.HttpServer; diff --git a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/server/WebsocketServerTransportTests.java b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketServerTransportTests.java similarity index 85% rename from today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/server/WebsocketServerTransportTests.java rename to today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketServerTransportTests.java index a1cc03b..df729f3 100644 --- a/today-remoting-transport-tcp/src/test/java/infra/remoting/transport/netty/server/WebsocketServerTransportTests.java +++ b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketServerTransportTests.java @@ -1,21 +1,20 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.netty.server; +package infra.remoting.transport.websocket; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketTransportTests.java b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketTransportTests.java new file mode 100644 index 0000000..6af6f55 --- /dev/null +++ b/today-remoting-transport-websocket/src/test/java/infra/remoting/transport/websocket/WebsocketTransportTests.java @@ -0,0 +1,63 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.transport.websocket; + +import org.junit.jupiter.api.BeforeEach; + +import java.net.InetSocketAddress; +import java.time.Duration; + +import infra.remoting.test.TransportPair; +import infra.remoting.test.TransportTest; +import io.netty.channel.ChannelOption; +import reactor.netty.http.client.HttpClient; +import reactor.netty.http.server.HttpServer; + +final class WebsocketTransportTests implements TransportTest { + + private TransportPair transportPair; + + @BeforeEach + void createTestPair() { + transportPair = new TransportPair<>( + () -> InetSocketAddress.createUnresolved("localhost", 0), + (address, server, allocator) -> + WebsocketClientTransport.create( + HttpClient.create() + .host(server.address().getHostName()) + .port(server.address().getPort()) + .option(ChannelOption.ALLOCATOR, allocator), + ""), + (address, allocator) -> { + return WebsocketServerTransport.create( + HttpServer.create() + .host(address.getHostName()) + .port(address.getPort()) + .option(ChannelOption.ALLOCATOR, allocator)); + }); + } + + @Override + public Duration getTimeout() { + return Duration.ofMinutes(3); + } + + @Override + public TransportPair getTransportPair() { + return transportPair; + } +} diff --git a/today-remoting/Protocol.md b/today-remoting/Protocol.md index fa2ffb1..d89ca90 100644 --- a/today-remoting/Protocol.md +++ b/today-remoting/Protocol.md @@ -2,7 +2,7 @@ This is an application protocol that provides [Reactive Streams](http://www.reactive-streams.org/) semantics across a network, asynchronous, binary boundary. -For more information, please see [rsocket.io](http://rsocket.io/). +原始协议参见:[rsocket.io](http://rsocket.io/). It assumes an operating paradigm. These assumptions are: - one-to-one communication @@ -95,7 +95,7 @@ Protocol as specified here has been designed for and tested with TCP, WebSocket, ### Framing Protocol Usage -Some supported transport protocols for RSocket may not support specific framing that preserves message boundaries. For these protocols, a framing protocol MUST be used with the RSocket frame that prepends the RSocket Frame Length. +Some supported transport protocols for this may not support specific framing that preserves message boundaries. For these protocols, a framing protocol MUST be used with the protocol frame that prepends the protocol Frame Length. The frame length field MUST be omitted if the transport protocol preserves message boundaries e.g. provides compatible framing. If, however, the transport protocol only provides a stream abstraction or can merge messages without preserving boundaries, or multiple transport protocols may be used, then the frame header MUST be used. @@ -108,7 +108,7 @@ The frame length field MUST be omitted if the transport protocol preserves messa ### Framing Format -When using a transport protocol providing framing, the RSocket frame is simply encapsulated into the transport protocol messages directly. +When using a transport protocol providing framing, the protocol frame is simply encapsulated into the transport protocol messages directly. ``` +-----------------------------------------------+ @@ -117,7 +117,7 @@ When using a transport protocol providing framing, the RSocket frame is simply e +-----------------------------------------------+ ``` -When using a transport protocol that does not provide compatible framing, the Frame Length MUST be prepended to the RSocket Frame. +When using a transport protocol that does not provide compatible framing, the Frame Length MUST be prepended to the protocol Frame. ``` 0 1 2 @@ -851,7 +851,7 @@ The requirements for the Resume Identification Token are implementation dependen * Tokens may be generated by the client. * Tokens may be generated outside the client and the server and managed externally to the protocol. -* Tokens should uniquely identify a connection on the server. The server should not assume a generation method of the token and should consider the token opaque. This allows a client to be compatible with any RSocket implementation that supports resuming operation and allows the client full control of Identification Token generation. +* Tokens should uniquely identify a connection on the server. The server should not assume a generation method of the token and should consider the token opaque. This allows a client to be compatible with any implementation that supports resuming operation and allows the client full control of Identification Token generation. * Tokens MUST be valid for the lifetime of an individual Protocol including possible resumption. * A server should not accept a SETUP with a Token that is currently already being used * Tokens should be resilient to replay attacks and thus should only be valid for the lifetime of an individual connection @@ -1196,7 +1196,7 @@ Credits are cumulative. Once credits are granted from Requester to Responder, th Please note that this explicitly does NOT follow rule number 17 in https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.0/README.md#3-subscription-code -While Reactive Streams support a demand of up to 2^63-1, and treats 2^63-1 as a magic number signaling to not track demand, this is not the case for RSocket. RSocket prioritizes byte size and only uses 4 bytes instead of 8 so the magic number is unavailable. +While Reactive Streams support a demand of up to 2^63-1, and treats 2^63-1 as a magic number signaling to not track demand, this is not the case. prioritizes byte size and only uses 4 bytes instead of 8 so the magic number is unavailable. The Requester and the Responder MUST respect the Reactive Streams semantics. diff --git a/today-remoting/README.md b/today-remoting/README.md index bde6291..7fb3193 100644 --- a/today-remoting/README.md +++ b/today-remoting/README.md @@ -2,7 +2,7 @@ 使用 RSocket 协议 -原始代码来自 https://github.com/rsocket/rsocket-java.git cff5cdbb16da6393efc04d8f0b80793e54f79026 +原始代码来自 https://github.com/channel/rsocket-java.git cff5cdbb16da6393efc04d8f0b80793e54f79026 1.1.5 版本 It enables the following symmetric interaction models via async message passing over a single connection: diff --git a/today-remoting/build.gradle b/today-remoting/build.gradle index e3f031c..34cc56b 100644 --- a/today-remoting/build.gradle +++ b/today-remoting/build.gradle @@ -4,16 +4,22 @@ description = "TODAY Cloud Remoting" dependencies { - api "cn.taketoday:today-core" + api "cn.taketoday:infra-core" api 'io.netty:netty-buffer' api 'io.projectreactor:reactor-core' testImplementation(project(":today-remoting-transport-local")) + testImplementation(project(":today-remoting-transport-tcp")) + testImplementation(project(":today-remoting-transport-websocket")) + testImplementation 'io.projectreactor:reactor-test' testImplementation 'org.awaitility:awaitility' + testImplementation 'com.netflix.concurrency-limits:concurrency-limits-core:0.3.6' + + testRuntimeOnly 'ch.qos.logback:logback-classic' - testFixturesApi "cn.taketoday:today-core" + testFixturesApi "cn.taketoday:infra-core" testFixturesApi 'org.assertj:assertj-core' testFixturesApi 'org.junit.jupiter:junit-jupiter-api' testFixturesApi 'org.junit.jupiter:junit-jupiter-params' @@ -22,5 +28,4 @@ dependencies { testFixturesApi 'org.hdrhistogram:HdrHistogram:2.1.12' - } diff --git a/today-remoting/src/main/java/infra/remoting/AbortedException.java b/today-remoting/src/main/java/infra/remoting/AbortedException.java deleted file mode 100644 index 27a93a0..0000000 --- a/today-remoting/src/main/java/infra/remoting/AbortedException.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ -package infra.remoting; - -import java.io.IOException; -import java.io.Serial; -import java.net.SocketException; - -/** - * An exception marking prematurely or unexpectedly closed inbound. - */ -public class AbortedException extends RuntimeException { - - @Serial - private static final long serialVersionUID = 6091789064032301718L; - - static final String CONNECTION_CLOSED_BEFORE_SEND = "Connection has been closed BEFORE send operation"; - - public AbortedException(String message) { - super(message); - } - - public AbortedException(Throwable throwable) { - super(throwable); - } - - /** - * Return true if connection has been simply aborted on a tcp level by verifying if - * the given inbound error. - * - * @param err an inbound exception - * @return true if connection has been simply aborted on a tcp level - */ - public static boolean isConnectionReset(Throwable err) { - return (err instanceof AbortedException && CONNECTION_CLOSED_BEFORE_SEND.equals(err.getMessage())) || - (err instanceof IOException && (err.getMessage() == null || err.getMessage() - .contains("Broken pipe") || - err.getMessage().contains("Connection reset by peer"))) || - (err instanceof SocketException && err.getMessage() != null && err.getMessage().contains("Connection reset by peer")); - } - - public static AbortedException beforeSend() { - return new AbortedException(CONNECTION_CLOSED_BEFORE_SEND); - } - -} diff --git a/today-remoting/src/main/java/infra/remoting/Availability.java b/today-remoting/src/main/java/infra/remoting/Availability.java index 44e5dc7..d8fba94 100644 --- a/today-remoting/src/main/java/infra/remoting/Availability.java +++ b/today-remoting/src/main/java/infra/remoting/Availability.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting; diff --git a/today-remoting/src/main/java/infra/remoting/Channel.java b/today-remoting/src/main/java/infra/remoting/Channel.java index 15c2020..01e9222 100644 --- a/today-remoting/src/main/java/infra/remoting/Channel.java +++ b/today-remoting/src/main/java/infra/remoting/Channel.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting; @@ -24,11 +23,11 @@ /** * A contract providing different interaction models for RSocket protocol. + * href="https://github.com/today-tech/today-cloud/blob/master/today-remoting/Protocol.md">protocol. * * @author 海子 Yang */ -public interface Channel extends Availability, Closeable, RemotingOperations { +public interface Channel extends Availability, Closeable { /** * Fire and Forget interaction model of protocol. @@ -89,11 +88,7 @@ default double availability() { } @Override - default void dispose() { } - - @Override - default boolean isDisposed() { - return false; + default void dispose() { } @Override diff --git a/today-remoting/src/main/java/infra/remoting/ChannelAcceptor.java b/today-remoting/src/main/java/infra/remoting/ChannelAcceptor.java index 88c6acb..33c3b0f 100644 --- a/today-remoting/src/main/java/infra/remoting/ChannelAcceptor.java +++ b/today-remoting/src/main/java/infra/remoting/ChannelAcceptor.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting; @@ -21,7 +20,7 @@ import java.util.function.Function; -import infra.remoting.exceptions.SetupException; +import infra.remoting.error.SetupException; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -38,20 +37,20 @@ public interface ChannelAcceptor { * * @param setup the {@code setup} received from a client in a server scenario, or in a client * scenario this is the setup about to be sent to the server. - * @param sendingSocket socket for sending requests to the remote peer. + * @param channel channel for sending requests to the remote peer. * @return {@code Channel} to accept requests with. - * @throws SetupException If the acceptor needs to reject the setup of this socket. + * @throws SetupException If the acceptor needs to reject the setup of this channel. */ - Mono accept(ConnectionSetupPayload setup, Channel sendingSocket); + Mono accept(ConnectionSetupPayload setup, Channel channel); /** - * Create a {@code SocketAcceptor} that handles requests with the given {@code Channel}. + * Create a {@code ChannelAcceptor} that handles requests with the given {@code Channel}. */ - static ChannelAcceptor with(Channel rsocket) { - return (setup, sendingChannel) -> Mono.just(rsocket); + static ChannelAcceptor with(Channel channel) { + return (setup, sendingChannel) -> Mono.just(channel); } - /** Create a {@code SocketAcceptor} for fire-and-forget interactions with the given handler. */ + /** Create a {@code ChannelAcceptor} for fire-and-forget interactions with the given handler. */ static ChannelAcceptor forFireAndForget(Function> handler) { return with( new Channel() { @@ -62,7 +61,7 @@ public Mono fireAndForget(Payload payload) { }); } - /** Create a {@code SocketAcceptor} for request-response interactions with the given handler. */ + /** Create a {@code ChannelAcceptor} for request-response interactions with the given handler. */ static ChannelAcceptor forRequestResponse(Function> handler) { return with( new Channel() { @@ -73,7 +72,7 @@ public Mono requestResponse(Payload payload) { }); } - /** Create a {@code SocketAcceptor} for request-stream interactions with the given handler. */ + /** Create a {@code ChannelAcceptor} for request-stream interactions with the given handler. */ static ChannelAcceptor forRequestStream(Function> handler) { return with( new Channel() { @@ -84,7 +83,7 @@ public Flux requestStream(Payload payload) { }); } - /** Create a {@code SocketAcceptor} for request-channel interactions with the given handler. */ + /** Create a {@code ChannelAcceptor} for request-channel interactions with the given handler. */ static ChannelAcceptor forRequestChannel(Function, Flux> handler) { return with( new Channel() { diff --git a/today-remoting/src/main/java/infra/remoting/ChannelAdapter.java b/today-remoting/src/main/java/infra/remoting/ChannelAdapter.java index 98e8f3f..4c3ed49 100644 --- a/today-remoting/src/main/java/infra/remoting/ChannelAdapter.java +++ b/today-remoting/src/main/java/infra/remoting/ChannelAdapter.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting; diff --git a/today-remoting/src/main/java/infra/remoting/Closeable.java b/today-remoting/src/main/java/infra/remoting/Closeable.java index fc14f2f..147d778 100644 --- a/today-remoting/src/main/java/infra/remoting/Closeable.java +++ b/today-remoting/src/main/java/infra/remoting/Closeable.java @@ -1,38 +1,37 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting; -import org.reactivestreams.Subscriber; - import reactor.core.Disposable; import reactor.core.publisher.Mono; -/** An interface which allows listening to when a specific instance of this interface is closed */ +/** + * An interface which allows listening to when a specific instance of this interface is closed + */ public interface Closeable extends Disposable { /** * Returns a {@link Mono} that terminates when the instance is terminated by any reason. Note, in * case of error termination, the cause of error will be propagated as an error signal through * {@link org.reactivestreams.Subscriber#onError(Throwable)}. Otherwise, {@link - * Subscriber#onComplete()} will be called. + * org.reactivestreams.Subscriber#onComplete()} will be called. * * @return a {@link Mono} to track completion with success or error of the underlying resource. - * When the underlying resource is an `RSocket`, the {@code Mono} exposes stream 0 (i.e. + * When the underlying resource is an `Channel`, the {@code Mono} exposes stream 0 (i.e. * connection level) errors. */ Mono onClose(); diff --git a/today-remoting/src/main/java/infra/remoting/DuplexConnection.java b/today-remoting/src/main/java/infra/remoting/Connection.java similarity index 74% rename from today-remoting/src/main/java/infra/remoting/DuplexConnection.java rename to today-remoting/src/main/java/infra/remoting/Connection.java index 7e7a830..54175fa 100644 --- a/today-remoting/src/main/java/infra/remoting/DuplexConnection.java +++ b/today-remoting/src/main/java/infra/remoting/Connection.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting; @@ -29,7 +28,7 @@ /** * Represents a connection with input/output that the protocol uses. */ -public interface DuplexConnection extends Availability, Closeable { +public interface Connection extends Availability, Closeable { /** * Delivers the given frame to the underlying transport connection. This method is non-blocking @@ -65,7 +64,7 @@ public interface DuplexConnection extends Availability, Closeable { *

Multiple Subscriptions * *

Returned {@code Publisher} is not required to support multiple concurrent subscriptions. - * RSocket will never have multiple subscriptions to this source. Implementations MUST + * Channel will never have multiple subscriptions to this source. Implementations MUST * emit an {@link IllegalStateException} for subsequent concurrent subscriptions, if they do not * support multiple concurrent subscriptions. * diff --git a/today-remoting/src/main/java/infra/remoting/ConnectionSetupPayload.java b/today-remoting/src/main/java/infra/remoting/ConnectionSetupPayload.java index b838ef5..f6765bf 100644 --- a/today-remoting/src/main/java/infra/remoting/ConnectionSetupPayload.java +++ b/today-remoting/src/main/java/infra/remoting/ConnectionSetupPayload.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; + import io.netty.buffer.ByteBuf; import io.netty.util.AbstractReferenceCounted; diff --git a/today-remoting/src/main/java/infra/remoting/util/ChannelDecorator.java b/today-remoting/src/main/java/infra/remoting/DecoratingChannel.java similarity index 56% rename from today-remoting/src/main/java/infra/remoting/util/ChannelDecorator.java rename to today-remoting/src/main/java/infra/remoting/DecoratingChannel.java index b722b34..670e9ec 100644 --- a/today-remoting/src/main/java/infra/remoting/util/ChannelDecorator.java +++ b/today-remoting/src/main/java/infra/remoting/DecoratingChannel.java @@ -1,38 +1,37 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.util; +package infra.remoting; import org.reactivestreams.Publisher; -import infra.remoting.Channel; -import infra.remoting.Payload; +import java.util.Objects; + import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** * Wrapper for a Channel. This is useful when we want to override a specific method. */ -public class ChannelDecorator implements Channel { +public class DecoratingChannel implements Channel { protected final Channel delegate; - public ChannelDecorator(Channel delegate) { - this.delegate = delegate; + public DecoratingChannel(Channel delegate) { + this.delegate = Objects.requireNonNull(delegate, "delegate is required"); } @Override @@ -79,4 +78,5 @@ public boolean isDisposed() { public Mono onClose() { return delegate.onClose(); } + } diff --git a/today-remoting/src/main/java/infra/remoting/DuplexConnectionWrapper.java b/today-remoting/src/main/java/infra/remoting/DecoratingConnection.java similarity index 51% rename from today-remoting/src/main/java/infra/remoting/DuplexConnectionWrapper.java rename to today-remoting/src/main/java/infra/remoting/DecoratingConnection.java index d2f0598..4732846 100644 --- a/today-remoting/src/main/java/infra/remoting/DuplexConnectionWrapper.java +++ b/today-remoting/src/main/java/infra/remoting/DecoratingConnection.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting; import java.net.SocketAddress; +import java.util.Objects; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; @@ -25,15 +25,20 @@ import reactor.core.publisher.Mono; /** + * A decorator for {@link Connection} that delegates all method calls to the underlying connection. + *

+ * This class can be extended to add additional behavior or monitoring to a connection without + * modifying the original implementation. + * * @author 海子 Yang * @since 1.0 2025/8/3 11:25 */ -public class DuplexConnectionWrapper implements DuplexConnection { +public class DecoratingConnection implements Connection { - private final DuplexConnection delegate; + protected final Connection delegate; - public DuplexConnectionWrapper(DuplexConnection delegate) { - this.delegate = delegate; + public DecoratingConnection(Connection delegate) { + this.delegate = Objects.requireNonNull(delegate, "delegate is required"); } @Override diff --git a/today-remoting/src/main/java/infra/remoting/Payload.java b/today-remoting/src/main/java/infra/remoting/Payload.java index 3105ef3..c30533f 100644 --- a/today-remoting/src/main/java/infra/remoting/Payload.java +++ b/today-remoting/src/main/java/infra/remoting/Payload.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting; @@ -24,8 +23,11 @@ import io.netty.util.ReferenceCounted; import io.netty.util.ResourceLeakDetector; -/** Payload of a Frame . */ +/** + * Payload of a Frame. + */ public interface Payload extends ReferenceCounted { + /** * Returns whether the payload has metadata, useful for tell if metadata is empty or not present. * @@ -88,6 +90,13 @@ public interface Payload extends ReferenceCounted { @Override Payload touch(Object hint); + /** + * Readable bytes + */ + default int length() { + return metadata().readableBytes() + data().readableBytes(); + } + default ByteBuffer getMetadata() { return sliceMetadata().nioBuffer(); } diff --git a/today-remoting/src/main/java/infra/remoting/ProtocolErrorException.java b/today-remoting/src/main/java/infra/remoting/ProtocolErrorException.java index 949dad7..b2df2a7 100644 --- a/today-remoting/src/main/java/infra/remoting/ProtocolErrorException.java +++ b/today-remoting/src/main/java/infra/remoting/ProtocolErrorException.java @@ -1,23 +1,22 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Exception that represents a protocol error. @@ -37,7 +36,7 @@ public class ProtocolErrorException extends RemotingException { /** * Constructor with a protocol error code and a message. * - * @param errorCode the RSocket protocol error code + * @param errorCode the protocol error code * @param message error explanation */ public ProtocolErrorException(int errorCode, String message) { @@ -47,7 +46,7 @@ public ProtocolErrorException(int errorCode, String message) { /** * Alternative to {@link #ProtocolErrorException(int, String)} with a root cause. * - * @param errorCode the RSocket protocol error code + * @param errorCode the protocol error code * @param message error explanation * @param cause a root cause for the error */ diff --git a/today-remoting/src/main/java/infra/remoting/RemotingException.java b/today-remoting/src/main/java/infra/remoting/RemotingException.java index 06be903..f53cfc7 100644 --- a/today-remoting/src/main/java/infra/remoting/RemotingException.java +++ b/today-remoting/src/main/java/infra/remoting/RemotingException.java @@ -1,25 +1,24 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting; -import java.io.Serial; +import org.jspecify.annotations.Nullable; -import infra.lang.Nullable; +import java.io.Serial; /** * @author 海子 Yang @@ -30,13 +29,6 @@ public class RemotingException extends RuntimeException { @Serial private static final long serialVersionUID = 1L; - public RemotingException() { - } - - public RemotingException(@Nullable String message) { - super(message); - } - public RemotingException(@Nullable String message, @Nullable Throwable cause) { super(message, cause); } diff --git a/today-remoting/src/main/java/infra/remoting/RemotingOperations.java b/today-remoting/src/main/java/infra/remoting/RemotingOperations.java index 4057f90..69b913e 100644 --- a/today-remoting/src/main/java/infra/remoting/RemotingOperations.java +++ b/today-remoting/src/main/java/infra/remoting/RemotingOperations.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting; @@ -23,52 +22,42 @@ import reactor.core.publisher.Mono; /** + * Operations for performing remoting interactions including Fire-and-Forget, + * Request-Response, Request-Stream, Request-Channel, and Metadata Push. + * * @author 海子 Yang * @since 1.0 2025/8/2 23:10 */ public interface RemotingOperations { /** - * Fire and Forget interaction model of protocol. - * - * @param payload Request payload. - * @return {@code Publisher} that completes when the passed {@code payload} is successfully - * handled, otherwise errors. + * Perform a Fire-and-Forget interaction via {@link Channel#fireAndForget(Payload)}. Allows + * multiple subscriptions and performs a request per subscriber. */ - Mono fireAndForget(Payload payload); + Mono fireAndForget(Mono payloadMono); /** - * Request-Response interaction model of protocol. - * - * @param payload Request payload. - * @return {@code Publisher} containing at most a single {@code Payload} representing the - * response. + * Perform a Request-Response interaction via {@link Channel#requestResponse(Payload)}. Allows + * multiple subscriptions and performs a request per subscriber. */ - Mono requestResponse(Payload payload); + Mono requestResponse(Mono payloadMono); /** - * Request-Stream interaction model of protocol. - * - * @param payload Request payload. - * @return {@code Publisher} containing the stream of {@code Payload}s representing the response. + * Perform a Request-Stream interaction via {@link Channel#requestStream(Payload)}. Allows + * multiple subscriptions and performs a request per subscriber. */ - Flux requestStream(Payload payload); + Flux requestStream(Mono payloadMono); /** - * Request-Channel interaction model of protocol. - * - * @param payloads Stream of request payloads. - * @return Stream of response payloads. + * Perform a Request-Channel interaction via {@link Channel#requestChannel(Publisher)}. Allows + * multiple subscriptions and performs a request per subscriber. */ Flux requestChannel(Publisher payloads); /** - * Metadata-Push interaction model of protocol. - * - * @param payload Request payloads. - * @return {@code Publisher} that completes when the passed {@code payload} is successfully - * handled, otherwise errors. + * Perform a Metadata Push via {@link Channel#metadataPush(Payload)}. Allows multiple + * subscriptions and performs a request per subscriber. */ - Mono metadataPush(Payload payload); + Mono metadataPush(Mono payloadMono); } diff --git a/today-remoting/src/main/java/infra/remoting/core/ChannelConnector.java b/today-remoting/src/main/java/infra/remoting/core/ChannelConnector.java index d64a964..8af85e7 100644 --- a/today-remoting/src/main/java/infra/remoting/core/ChannelConnector.java +++ b/today-remoting/src/main/java/infra/remoting/core/ChannelConnector.java @@ -1,45 +1,45 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; + import java.time.Duration; import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Supplier; -import infra.lang.Nullable; import infra.remoting.Channel; import infra.remoting.ChannelAcceptor; +import infra.remoting.Closeable; +import infra.remoting.Connection; import infra.remoting.ConnectionSetupPayload; -import infra.remoting.DuplexConnection; import infra.remoting.Payload; import infra.remoting.frame.SetupFrameCodec; import infra.remoting.frame.decoder.PayloadDecoder; import infra.remoting.keepalive.KeepAliveHandler; import infra.remoting.lease.TrackingLeaseSender; -import infra.remoting.plugins.ConnectionInterceptor; +import infra.remoting.plugins.ConnectionDecorator; import infra.remoting.plugins.InitializingInterceptorRegistry; import infra.remoting.plugins.InterceptorRegistry; -import infra.remoting.plugins.RateLimitInterceptor; +import infra.remoting.plugins.RateLimitDecorator; import infra.remoting.resume.ClientChannelSession; -import infra.remoting.resume.ResumableDuplexConnection; -import infra.remoting.resume.ResumableFramesStore; +import infra.remoting.resume.ResumableConnection; import infra.remoting.transport.ClientTransport; import infra.remoting.transport.Transport; import infra.remoting.util.ByteBufPayload; @@ -47,7 +47,6 @@ import infra.remoting.util.EmptyPayload; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import reactor.core.Disposable; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; import reactor.util.function.Tuples; @@ -58,27 +57,24 @@ import static infra.remoting.core.ReassemblyUtils.assertInboundPayloadSize; /** - * The main class to use to establish a connection to an RSocket server. + * The main class to use to establish a connection to a server. * *

For using TCP using default settings: * *

{@code
- * import io.rsocket.transport.netty.client.TcpClientTransport;
+ * import infra.remoting.transport.netty.client.TcpClientTransport;
  *
- * Mono source =
+ * Mono source =
  *         ChannelConnector.connectWith(TcpClientTransport.create("localhost", 7000));
- * RSocketClient client = RSocketClient.from(source);
+ * RemotingClient client = RemotingClient.from(source);
  * }
* *

To customize connection settings before connecting: * *

{@code
- * Mono source =
- *         ChannelConnector.create()
- *                 .metadataMimeType("message/x.rsocket.composite-metadata.v0")
- *                 .dataMimeType("application/cbor")
- *                 .connect(TcpClientTransport.create("localhost", 7000));
- * RSocketClient client = RSocketClient.from(source);
+ * Mono source = ChannelConnector.create()
+ *      .connect(TcpClientTransport.create("localhost", 7000));
+ * RemotingClient client = RemotingClient.from(source);
  * }
*/ public class ChannelConnector { @@ -88,27 +84,27 @@ public class ChannelConnector { private static final BiConsumer INVALIDATE_FUNCTION = (r, i) -> r.onClose().subscribe(null, __ -> i.invalidate(), i::invalidate); + private final InitializingInterceptorRegistry interceptors = new InitializingInterceptorRegistry(); + private Mono setupPayloadMono = Mono.empty(); + @Deprecated private String metadataMimeType = "application/binary"; + @Deprecated private String dataMimeType = "application/binary"; private Duration keepAliveInterval = Duration.ofSeconds(20); private Duration keepAliveMaxLifeTime = Duration.ofSeconds(90); - @Nullable - private ChannelAcceptor acceptor; + private @Nullable ChannelAcceptor acceptor; - private final InitializingInterceptorRegistry interceptors = new InitializingInterceptorRegistry(); + private @Nullable Retry retrySpec; - private Retry retrySpec; + private @Nullable Resume resume; - private Resume resume; - - @Nullable - private Consumer leaseConfigurer; + private @Nullable Consumer leaseConfigurer; private int mtu = 0; @@ -119,28 +115,6 @@ public class ChannelConnector { private ChannelConnector() { } - /** - * Static factory method to create an {@code ChannelConnector} instance and customize default - * settings before connecting. To connect only, use {@link #connectWith(ClientTransport)}. - */ - public static ChannelConnector create() { - return new ChannelConnector(); - } - - /** - * Static factory method to connect with default settings, effectively a shortcut for: - * - *
-   * ChannelConnector.create().connect(transport);
-   * 
- * - * @param transport the transport of choice to connect with - * @return a {@code Mono} with the connected RSocket - */ - public static Mono connectWith(ClientTransport transport) { - return ChannelConnector.create().connect(() -> transport); - } - /** * Provide a {@code Mono} from which to obtain the {@code Payload} for the initial SETUP frame. * Data and metadata should be formatted according to the MIME types specified via {@link @@ -199,14 +173,6 @@ public ChannelConnector dataMimeType(String dataMimeType) { * Set the MIME type to use for formatting payload metadata on the established connection. This is * set in the initial {@code SETUP} frame sent to the server. * - *

For metadata encoding, consider using one of the following encoders: - * - *

    - *
  • {@link io.rsocket.metadata.CompositeMetadataCodec Composite Metadata} - *
  • {@link io.rsocket.metadata.TaggingMetadataCodec Routing} - *
  • {@link io.rsocket.metadata.AuthMetadataCodec Authentication} - *
- * * @param metadataMimeType the MIME type to be used for payload metadata * @return the same instance for method chaining * @see SETUP @@ -265,7 +231,7 @@ public ChannelConnector keepAlive(Duration interval, Duration maxLifeTime) { * * @param configurer a configurer to customize interception with. * @return the same instance for method chaining - * @see RateLimitInterceptor + * @see RateLimitDecorator */ public ChannelConnector interceptors(Consumer configurer) { configurer.accept(this.interceptors); @@ -275,77 +241,77 @@ public ChannelConnector interceptors(Consumer configurer) { /** * Configure a client-side {@link ChannelAcceptor} for responding to requests from the server. * - *

A full-form example with access to the {@code SETUP} frame and the "sending" RSocket (the + *

A full-form example with access to the {@code SETUP} frame and the "sending" Channel (the * same as the one returned from {@link #connect(ClientTransport)}): * *

{@code
-   * Mono rsocketMono =
+   * Mono channelMono =
    *     ChannelConnector.create()
-   *             .acceptor((setup, sendingRSocket) -> Mono.just(new RSocket() {...}))
+   *             .acceptor((setup, sending) -> Mono.just(new Channel() {...}))
    *             .connect(transport);
    * }
* - *

A shortcut example with just the handling RSocket: + *

A shortcut example with just the handling Channel: * *

{@code
-   * Mono rsocketMono =
+   * Mono channelMono =
    *     ChannelConnector.create()
-   *             .acceptor(SocketAcceptor.with(new RSocket() {...})))
+   *             .acceptor(ChannelAcceptor.with(new Channel() {...})))
    *             .connect(transport);
    * }
* *

A shortcut example handling only request-response: * *

{@code
-   * Mono rsocketMono =
+   * Mono channelMono =
    *     ChannelConnector.create()
-   *             .acceptor(SocketAcceptor.forRequestResponse(payload -> ...))
+   *             .acceptor(ChannelAcceptor.forRequestResponse(payload -> ...))
    *             .connect(transport);
    * }
* - *

By default, {@code new RSocket(){}} is used which rejects all requests from the server with + *

By default, {@code new Channel(){}} is used which rejects all requests from the server with * {@link UnsupportedOperationException}. * * @param acceptor the acceptor to use for responding to server requests * @return the same instance for method chaining */ - public ChannelConnector acceptor(ChannelAcceptor acceptor) { + public ChannelConnector acceptor(@Nullable ChannelAcceptor acceptor) { this.acceptor = acceptor; return this; } /** - * When this is enabled, the connect methods of this class return a special {@code Mono} - * that maintains a single, shared {@code RSocket} for all subscribers: + * When this is enabled, the connect methods of this class return a special {@code Mono} + * that maintains a single, shared {@code Channel} for all subscribers: * *

{@code
-   * Mono rsocketMono =
+   * Mono channelMono =
    *   ChannelConnector.create()
    *           .reconnect(Retry.fixedDelay(3, Duration.ofSeconds(1)))
    *           .connect(transport);
    *
-   *  RSocket r1 = rsocketMono.block();
-   *  RSocket r2 = rsocketMono.block();
+   *  Channel r1 = channelMono.block();
+   *  Channel r2 = channelMono.block();
    *
    *  assert r1 == r2;
    * }
* - *

The {@code RSocket} remains cached until the connection is lost and after that, new attempts - * to subscribe or re-subscribe trigger a reconnect and result in a new shared {@code RSocket}: + *

The {@code Channel} remains cached until the connection is lost and after that, new attempts + * to subscribe or re-subscribe trigger a reconnect and result in a new shared {@code Channel}: * *

{@code
-   * Mono rsocketMono =
+   * Mono channelMono =
    *   ChannelConnector.create()
    *           .reconnect(Retry.fixedDelay(3, Duration.ofSeconds(1)))
    *           .connect(transport);
    *
-   *  RSocket r1 = rsocketMono.block();
-   *  RSocket r2 = rsocketMono.block();
+   *  Channel r1 = channelMono.block();
+   *  Channel r2 = channelMono.block();
    *
    *  r1.dispose();
    *
-   *  RSocket r3 = rsocketMono.block();
-   *  RSocket r4 = rsocketMono.block();
+   *  Channel r3 = channelMono.block();
+   *  Channel r4 = channelMono.block();
    *
    *  assert r1 == r2;
    *  assert r3 == r4;
@@ -357,12 +323,12 @@ public ChannelConnector acceptor(ChannelAcceptor acceptor) {
    * if or when failed requests should be retried which in turn triggers the shared reconnect:
    *
    * 
{@code
-   * Mono rocketMono =
+   * Mono rocketMono =
    *   ChannelConnector.create()
    *           .reconnect(Retry.fixedDelay(3, Duration.ofSeconds(1)))
    *           .connect(transport);
    *
-   *  rsocketMono.flatMap(rsocket -> rsocket.requestResponse(...))
+   *  channelMono.flatMap(channel -> channel.requestResponse(...))
    *           .retryWhen(Retry.fixedDelay(1, Duration.ofSeconds(5)))
    *           .subscribe()
    * }
@@ -383,7 +349,7 @@ public ChannelConnector reconnect(Retry retry) { } /** - * Enables the Resume capability of the RSocket protocol where if the client gets disconnected, + * Enables the Resume capability of the protocol where if the client gets disconnected, * the connection is re-acquired and any interrupted streams are resumed automatically. For this * to work the server must also support and have the Resume capability enabled. * @@ -408,13 +374,13 @@ public ChannelConnector resume(Resume resume) { } /** - * Enables the Lease feature of the RSocket protocol where the number of requests that can be + * Enables the Lease feature of the protocol where the number of requests that can be * performed from either side are rationed via {@code LEASE} frames from the responder side. * *

Example usage: * *

{@code
-   * Mono rocketMono =
+   * Mono rocketMono =
    *         ChannelConnector.create()
    *                         .lease()
    *                         .connect(transport);
@@ -431,13 +397,13 @@ public ChannelConnector lease() {
   }
 
   /**
-   * Enables the Lease feature of the RSocket protocol where the number of requests that can be
+   * Enables the Lease feature of the protocol where the number of requests that can be
    * performed from either side are rationed via {@code LEASE} frames from the responder side.
    *
    * 

Example usage: * *

{@code
-   * Mono rocketMono =
+   * Mono rocketMono =
    *         ChannelConnector.create()
    *                         .lease(spec -> spec.maxPendingRequests(128))
    *                         .connect(transport);
@@ -523,7 +489,7 @@ public ChannelConnector payloadDecoder(PayloadDecoder decoder) {
    * #reconnect(Retry) reconnect} nor {@link #resume(Resume)} are enabled.
    *
    * @param transport the transport of choice to connect with
-   * @return a {@code Mono} with the connected RSocket
+   * @return a {@code Mono} with the connected Channel
    */
   public Mono connect(ClientTransport transport) {
     return connect(() -> transport);
@@ -536,18 +502,18 @@ public Mono connect(ClientTransport transport) {
    * 

* * @param transportSupplier supplier for the transport to connect with - * @return a {@code Mono} with the connected RSocket + * @return a {@code Mono} with the connected Channel */ public Mono connect(Supplier transportSupplier) { return Mono.fromSupplier(transportSupplier).flatMap(ct -> { int maxFrameLength = ct.getMaxFrameLength(); - Mono connectionMono = Mono.fromCallable(() -> { + Mono connectionMono = Mono.fromCallable(() -> { assertValidateSetup(maxFrameLength, maxInboundPayloadSize, mtu); return ct; }) .flatMap(transport -> transport.connect()) - .map(sourceConnection -> interceptors.initConnection(ConnectionInterceptor.Type.SOURCE, sourceConnection)) - .map(con -> LoggingDuplexConnection.wrapIfEnabled(con)); + .map(sourceConnection -> interceptors.initConnection(ConnectionDecorator.Type.SOURCE, sourceConnection)) + .map(con -> LoggingConnection.wrapIfEnabled(con)); return connectionMono .flatMap(connection -> setupPayloadMono @@ -556,7 +522,7 @@ public Mono connect(Supplier transportSupplier) { .doOnError(ex -> connection.dispose()) .doOnCancel(connection::dispose)) .flatMap(tuple2 -> { - DuplexConnection sourceConnection = tuple2.getT1(); + Connection sourceConnection = tuple2.getT1(); Payload setupPayload = tuple2.getT2(); boolean leaseEnabled = leaseConfigurer != null; boolean resumeEnabled = resume != null; @@ -565,44 +531,39 @@ public Mono connect(Supplier transportSupplier) { ByteBuf resumeToken; if (resumeEnabled) { - resumeToken = resume.getTokenSupplier().get(); + resumeToken = resume.tokenGenerator.generate(); } else { resumeToken = Unpooled.EMPTY_BUFFER; } - ByteBuf setupFrame = SetupFrameCodec.encode(sourceConnection.alloc(), - leaseEnabled, - (int) keepAliveInterval.toMillis(), - (int) keepAliveMaxLifeTime.toMillis(), - resumeToken, - metadataMimeType, - dataMimeType, - setupPayload); + ByteBuf setupFrame = SetupFrameCodec.encode(sourceConnection.alloc(), leaseEnabled, + (int) keepAliveInterval.toMillis(), (int) keepAliveMaxLifeTime.toMillis(), + resumeToken, metadataMimeType, dataMimeType, setupPayload); sourceConnection.sendFrame(0, setupFrame.retainedSlice()); return clientSetup.init(sourceConnection).flatMap(tuple -> { - final DuplexConnection clientServerConnection = tuple.getT2(); + final Connection clientServerConnection = tuple.getT2(); + final Connection wrappedConnection; final KeepAliveHandler keepAliveHandler; - final DuplexConnection wrappedConnection; - final InitializingInterceptorRegistry interceptors = this.interceptors; if (resumeEnabled) { - final ResumableClientSetup resumableClientSetup = new ResumableClientSetup(); - final ResumableFramesStore resumableFramesStore = resume.getStoreFactory(CLIENT_TAG).apply(resumeToken); - final ResumableDuplexConnection resumableDuplexConnection = new ResumableDuplexConnection(CLIENT_TAG, resumeToken, clientServerConnection, resumableFramesStore); - final ClientChannelSession session = new ClientChannelSession(resumeToken, resumableDuplexConnection, connectionMono, resumableClientSetup::init, - resumableFramesStore, resume.getSessionDuration(), resume.getRetry(), resume.isCleanupStoreOnKeepAlive()); - - keepAliveHandler = new KeepAliveHandler.ResumableKeepAliveHandler(resumableDuplexConnection, session, session); - wrappedConnection = resumableDuplexConnection; + final var resumableClientSetup = new ResumableClientSetup(); + final var resumableFramesStore = resume.getStoreFactory(CLIENT_TAG).create(resumeToken); + final var resumableConnection = new ResumableConnection(CLIENT_TAG, resumeToken, clientServerConnection, resumableFramesStore); + final var session = new ClientChannelSession(resumeToken, resumableConnection, connectionMono, resumableClientSetup::init, + resumableFramesStore, resume.sessionDuration, resume.retry, resume.cleanupStoreOnKeepAlive); + + keepAliveHandler = new KeepAliveHandler.ResumableKeepAliveHandler(resumableConnection, session, session); + wrappedConnection = resumableConnection; } else { keepAliveHandler = new KeepAliveHandler.DefaultKeepAliveHandler(); wrappedConnection = clientServerConnection; } + final InitializingInterceptorRegistry interceptors = this.interceptors; var multiplexer = new ClientServerInputMultiplexer(wrappedConnection, interceptors, true); final LeaseSpec leases; @@ -620,7 +581,7 @@ public Mono connect(Supplier transportSupplier) { final Sinks.Empty requesterOnAllClosedSink = Sinks.unsafe().empty(); final Sinks.Empty responderOnAllClosedSink = Sinks.unsafe().empty(); - Channel channelRequester = new ChannelRequester(multiplexer.asClientConnection(), payloadDecoder, + Channel channelRequester = new RequesterChannel(multiplexer.asClientConnection(), payloadDecoder, StreamIdProvider.forClient(), mtu, maxFrameLength, maxInboundPayloadSize, (int) keepAliveInterval.toMillis(), (int) keepAliveMaxLifeTime.toMillis(), keepAliveHandler, interceptors::initRequesterRequestInterceptor, requesterLeaseTracker, requesterOnAllClosedSink, @@ -633,18 +594,17 @@ public Mono connect(Supplier transportSupplier) { return interceptors.decorateAcceptor(acceptor) .accept(setup, wrappedChannelRequester) - .map(rSocketHandler -> { - Channel wrappedChannelHandler = interceptors.decorateResponder(rSocketHandler); + .map(channelHandler -> { + Channel wrappedChannelHandler = interceptors.decorateResponder(channelHandler); ResponderLeaseTracker responderLeaseTracker = leaseEnabled ? new ResponderLeaseTracker(CLIENT_TAG, wrappedConnection, leases.sender) : null; - Channel channelResponder = new ChannelResponder(multiplexer.asServerConnection(), wrappedChannelHandler, - payloadDecoder, responderLeaseTracker, mtu, maxFrameLength, maxInboundPayloadSize, - leaseEnabled && leases.sender instanceof TrackingLeaseSender - ? rSocket -> interceptors.initResponderRequestInterceptor(rSocket, (TrackingLeaseSender) leases.sender) - : interceptors::initResponderRequestInterceptor, responderOnAllClosedSink); + new ResponderChannel(multiplexer.asServerConnection(), wrappedChannelHandler, payloadDecoder, responderLeaseTracker, + mtu, maxFrameLength, maxInboundPayloadSize, leaseEnabled && leases.sender instanceof TrackingLeaseSender + ? channel -> interceptors.initResponderRequestInterceptor(channel, (TrackingLeaseSender) leases.sender) + : interceptors::initResponderRequestInterceptor, responderOnAllClosedSink); return wrappedChannelRequester; }) @@ -654,11 +614,34 @@ public Mono connect(Supplier transportSupplier) { }).as(source -> { if (retrySpec != null) { return new ReconnectMono<>( - source.retryWhen(retrySpec), Disposable::dispose, INVALIDATE_FUNCTION); + source.retryWhen(retrySpec), Closeable::dispose, INVALIDATE_FUNCTION); } else { return source; } }); } + + /** + * Static factory method to create an {@code ChannelConnector} instance and customize default + * settings before connecting. To connect only, use {@link #connectWith(ClientTransport)}. + */ + public static ChannelConnector create() { + return new ChannelConnector(); + } + + /** + * Static factory method to connect with default settings, effectively a shortcut for: + * + *

{@code
+   * ChannelConnector.create().connect(transport);
+   * }
+ * + * @param transport the transport of choice to connect with + * @return a {@code Mono} with the connected Channel + */ + public static Mono connectWith(ClientTransport transport) { + return ChannelConnector.create().connect(() -> transport); + } + } diff --git a/today-remoting/src/main/java/infra/remoting/core/RequesterResponderSupport.java b/today-remoting/src/main/java/infra/remoting/core/ChannelSupport.java similarity index 67% rename from today-remoting/src/main/java/infra/remoting/core/RequesterResponderSupport.java rename to today-remoting/src/main/java/infra/remoting/core/ChannelSupport.java index 10fc23c..7c3845e 100644 --- a/today-remoting/src/main/java/infra/remoting/core/RequesterResponderSupport.java +++ b/today-remoting/src/main/java/infra/remoting/core/ChannelSupport.java @@ -1,59 +1,57 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; + import java.util.Objects; import java.util.function.Function; -import infra.lang.Nullable; import infra.remoting.Channel; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.frame.decoder.PayloadDecoder; import infra.remoting.plugins.RequestInterceptor; import io.netty.buffer.ByteBufAllocator; import io.netty.util.collection.IntObjectHashMap; import io.netty.util.collection.IntObjectMap; -class RequesterResponderSupport { +class ChannelSupport implements Channel { - protected final int mtu; + public final int mtu; - protected final int maxFrameLength; + public final int maxFrameLength; - protected final int maxInboundPayloadSize; + public final int maxInboundPayloadSize; - protected final PayloadDecoder payloadDecoder; + public final PayloadDecoder payloadDecoder; - protected final ByteBufAllocator allocator; + public final ByteBufAllocator allocator; - protected final DuplexConnection connection; + public final Connection connection; - @Nullable - protected final RequestInterceptor requestInterceptor; + public final @Nullable RequestInterceptor requestInterceptor; - @Nullable - protected final StreamIdProvider streamIdProvider; + protected final @Nullable StreamIdProvider streamIdProvider; protected final IntObjectMap activeStreams; - public RequesterResponderSupport(int mtu, int maxFrameLength, int maxInboundPayloadSize, - PayloadDecoder payloadDecoder, DuplexConnection connection, @Nullable StreamIdProvider streamIdProvider, - Function requestInterceptorFunction) { + public ChannelSupport(int mtu, int maxFrameLength, int maxInboundPayloadSize, + PayloadDecoder payloadDecoder, Connection connection, @Nullable StreamIdProvider streamIdProvider, + Function requestInterceptorFunction) { this.activeStreams = new IntObjectHashMap<>(); this.mtu = mtu; @@ -63,7 +61,7 @@ public RequesterResponderSupport(int mtu, int maxFrameLength, int maxInboundPayl this.allocator = connection.alloc(); this.streamIdProvider = streamIdProvider; this.connection = connection; - this.requestInterceptor = requestInterceptorFunction.apply((Channel) this); + this.requestInterceptor = requestInterceptorFunction.apply(this); } public int getMtu() { @@ -86,7 +84,7 @@ public ByteBufAllocator getAllocator() { return allocator; } - public DuplexConnection getDuplexConnection() { + public Connection getConnection() { return connection; } @@ -104,7 +102,7 @@ public RequestInterceptor getRequestInterceptor() { * Issues next {@code streamId} * * @return issued {@code streamId} - * @throws RuntimeException if the {@link RequesterResponderSupport} is terminated for any reason + * @throws RuntimeException if the {@link ChannelSupport} is terminated for any reason */ public int getNextStreamId() { final StreamIdProvider streamIdProvider = this.streamIdProvider; @@ -123,7 +121,7 @@ public int getNextStreamId() { * * @param frameHandler to store * @return issued {@code streamId} - * @throws RuntimeException if the {@link RequesterResponderSupport} is terminated for any reason + * @throws RuntimeException if the {@link ChannelSupport} is terminated for any reason */ public int addAndGetNextStreamId(FrameHandler frameHandler) { final StreamIdProvider streamIdProvider = this.streamIdProvider; @@ -157,8 +155,7 @@ public synchronized boolean add(int streamId, FrameHandler frameHandler) { * @param streamId used to resolve {@link FrameHandler} * @return {@link FrameHandler} or {@code null} */ - @Nullable - public synchronized FrameHandler get(int streamId) { + public synchronized @Nullable FrameHandler get(int streamId) { return this.activeStreams.get(streamId); } diff --git a/today-remoting/src/main/java/infra/remoting/core/ClientServerInputMultiplexer.java b/today-remoting/src/main/java/infra/remoting/core/ClientServerInputMultiplexer.java index 1bfca28..fdbd4a8 100644 --- a/today-remoting/src/main/java/infra/remoting/core/ClientServerInputMultiplexer.java +++ b/today-remoting/src/main/java/infra/remoting/core/ClientServerInputMultiplexer.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -23,10 +22,10 @@ import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import infra.remoting.Closeable; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.ProtocolErrorException; import infra.remoting.frame.FrameHeaderCodec; -import infra.remoting.plugins.ConnectionInterceptor.Type; +import infra.remoting.plugins.ConnectionDecorator.Type; import infra.remoting.plugins.InitializingInterceptorRegistry; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; @@ -36,7 +35,7 @@ import reactor.core.publisher.Operators; /** - * {@link DuplexConnection#receive()} is a single stream on which the following type of frames + * {@link Connection#receive()} is a single stream on which the following type of frames * arrive: * *
    @@ -50,11 +49,11 @@ */ class ClientServerInputMultiplexer implements CoreSubscriber, Closeable { - private final InternalDuplexConnection serverReceiver; - private final InternalDuplexConnection clientReceiver; - private final DuplexConnection serverConnection; - private final DuplexConnection clientConnection; - private final DuplexConnection source; + private final InternalConnection serverReceiver; + private final InternalConnection clientReceiver; + private final Connection serverConnection; + private final Connection clientConnection; + private final Connection source; private final boolean isClient; private Subscription s; @@ -65,22 +64,21 @@ class ClientServerInputMultiplexer implements CoreSubscriber, Closeable private static final AtomicIntegerFieldUpdater STATE = AtomicIntegerFieldUpdater.newUpdater(ClientServerInputMultiplexer.class, "state"); - public ClientServerInputMultiplexer( - DuplexConnection source, InitializingInterceptorRegistry registry, boolean isClient) { + public ClientServerInputMultiplexer(Connection source, InitializingInterceptorRegistry registry, boolean isClient) { this.source = source; this.isClient = isClient; - this.serverReceiver = new InternalDuplexConnection(Type.SERVER, this, source); - this.clientReceiver = new InternalDuplexConnection(Type.CLIENT, this, source); + this.serverReceiver = new InternalConnection(Type.SERVER, this, source); + this.clientReceiver = new InternalConnection(Type.CLIENT, this, source); this.serverConnection = registry.initConnection(Type.SERVER, serverReceiver); this.clientConnection = registry.initConnection(Type.CLIENT, clientReceiver); } - DuplexConnection asServerConnection() { + Connection asServerConnection() { return serverConnection; } - DuplexConnection asClientConnection() { + Connection asClientConnection() { return clientConnection; } @@ -224,22 +222,19 @@ public String toString() { + '}'; } - private static class InternalDuplexConnection extends Flux - implements Subscription, DuplexConnection { + private static class InternalConnection extends Flux implements Subscription, Connection { + private final Type type; + private final Connection source; private final ClientServerInputMultiplexer clientServerInputMultiplexer; - private final DuplexConnection source; private volatile int state; - static final AtomicIntegerFieldUpdater STATE = - AtomicIntegerFieldUpdater.newUpdater(InternalDuplexConnection.class, "state"); + static final AtomicIntegerFieldUpdater STATE = + AtomicIntegerFieldUpdater.newUpdater(InternalConnection.class, "state"); CoreSubscriber actual; - public InternalDuplexConnection( - Type type, - ClientServerInputMultiplexer clientServerInputMultiplexer, - DuplexConnection source) { + public InternalConnection(Type type, ClientServerInputMultiplexer clientServerInputMultiplexer, Connection source) { this.type = type; this.clientServerInputMultiplexer = clientServerInputMultiplexer; this.source = source; @@ -252,9 +247,7 @@ public void subscribe(CoreSubscriber actual) { actual.onSubscribe(this); } else { - Operators.error( - actual, - new IllegalStateException("InternalDuplexConnection allows only single subscription")); + Operators.error(actual, new IllegalStateException("InternalConnection allows only single subscription")); } } @@ -342,14 +335,7 @@ public double availability() { @Override public String toString() { - return "InternalDuplexConnection{" - + "type=" - + type - + ", source=" - + source - + ", state=" - + state - + '}'; + return "InternalConnection{type=%s, source=%s, state=%d}".formatted(type, source, state); } } } diff --git a/today-remoting/src/main/java/infra/remoting/core/ClientSetup.java b/today-remoting/src/main/java/infra/remoting/core/ClientSetup.java index a0336ee..32623a4 100644 --- a/today-remoting/src/main/java/infra/remoting/core/ClientSetup.java +++ b/today-remoting/src/main/java/infra/remoting/core/ClientSetup.java @@ -1,25 +1,24 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; import java.nio.channels.ClosedChannelException; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import reactor.core.Disposable; @@ -29,13 +28,13 @@ abstract class ClientSetup { - abstract Mono> init(DuplexConnection connection); + abstract Mono> init(Connection connection); } class DefaultClientSetup extends ClientSetup { @Override - Mono> init(DuplexConnection connection) { + Mono> init(Connection connection) { return Mono.create(sink -> sink.onRequest(__ -> sink.success(Tuples.of(Unpooled.EMPTY_BUFFER, connection)))); } } @@ -43,9 +42,9 @@ Mono> init(DuplexConnection connection) { class ResumableClientSetup extends ClientSetup { @Override - Mono> init(DuplexConnection connection) { + Mono> init(Connection connection) { return Mono.create(sink -> { - sink.onRequest(__ -> new SetupHandlingDuplexConnection(connection, sink)); + sink.onRequest(__ -> new SetupHandlingConnection(connection, sink)); Disposable subscribe = connection.onClose() .doFinally(__ -> sink.error(new ClosedChannelException())) diff --git a/today-remoting/src/main/java/infra/remoting/core/DefaultConnectionSetupPayload.java b/today-remoting/src/main/java/infra/remoting/core/DefaultConnectionSetupPayload.java index a8618c3..d1c4fbd 100644 --- a/today-remoting/src/main/java/infra/remoting/core/DefaultConnectionSetupPayload.java +++ b/today-remoting/src/main/java/infra/remoting/core/DefaultConnectionSetupPayload.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -25,7 +24,7 @@ /** * Default implementation of {@link ConnectionSetupPayload}. Primarily for internal use within - * RSocket Java but may be created in an application, e.g. for testing purposes. + * Java but may be created in an application, e.g. for testing purposes. */ public class DefaultConnectionSetupPayload extends ConnectionSetupPayload { diff --git a/today-remoting/src/main/java/infra/remoting/core/DefaultRemotingClient.java b/today-remoting/src/main/java/infra/remoting/core/DefaultRemotingClient.java index 98ce442..ba347a4 100644 --- a/today-remoting/src/main/java/infra/remoting/core/DefaultRemotingClient.java +++ b/today-remoting/src/main/java/infra/remoting/core/DefaultRemotingClient.java @@ -1,21 +1,21 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; @@ -26,7 +26,6 @@ import java.util.function.Consumer; import java.util.stream.Stream; -import infra.lang.Nullable; import infra.remoting.Channel; import infra.remoting.Payload; import infra.remoting.frame.FrameType; @@ -45,8 +44,7 @@ /** * Default implementation of {@link RemotingClient} */ -class DefaultRemotingClient extends ResolvingOperator - implements CoreSubscriber, CorePublisher, RemotingClient { +class DefaultRemotingClient extends ResolvingOperator implements CoreSubscriber, CorePublisher, RemotingClient { static final Consumer DISCARD_ELEMENTS_CONSUMER = data -> { if (data instanceof ReferenceCounted rc) { @@ -61,12 +59,8 @@ class DefaultRemotingClient extends ResolvingOperator } }; - static final Object ON_DISCARD_KEY; - - static { - Context discardAwareContext = Operators.enableOnDiscard(null, DISCARD_ELEMENTS_CONSUMER); - ON_DISCARD_KEY = discardAwareContext.stream().findFirst().get().getKey(); - } + static final Object ON_DISCARD_KEY = Operators.enableOnDiscard(null, DISCARD_ELEMENTS_CONSUMER) + .stream().findFirst().get().getKey(); final Mono source; @@ -83,12 +77,12 @@ class DefaultRemotingClient extends ResolvingOperator } private Mono unwrapReconnectMono(Mono source) { - return source instanceof ReconnectMono ? ((ReconnectMono) source).getSource() : source; + return source instanceof ReconnectMono ? ((ReconnectMono) source).source : source; } @Override public Mono onClose() { - return this.onDisposeSink.asMono(); + return onDisposeSink.asMono(); } @Override @@ -98,27 +92,27 @@ public Mono source() { @Override public Mono fireAndForget(Mono payloadMono) { - return new RSocketClientMonoOperator<>(this, FrameType.REQUEST_FNF, payloadMono); + return new ChannelClientMonoOperator<>(this, FrameType.REQUEST_FNF, payloadMono); } @Override public Mono requestResponse(Mono payloadMono) { - return new RSocketClientMonoOperator<>(this, FrameType.REQUEST_RESPONSE, payloadMono); + return new ChannelClientMonoOperator<>(this, FrameType.REQUEST_RESPONSE, payloadMono); } @Override public Flux requestStream(Mono payloadMono) { - return new RSocketClientFluxOperator<>(this, FrameType.REQUEST_STREAM, payloadMono); + return new ChannelClientFluxOperator<>(this, FrameType.REQUEST_STREAM, payloadMono); } @Override public Flux requestChannel(Publisher payloads) { - return new RSocketClientFluxOperator<>(this, FrameType.REQUEST_CHANNEL, payloads); + return new ChannelClientFluxOperator<>(this, FrameType.REQUEST_CHANNEL, payloads); } @Override public Mono metadataPush(Mono payloadMono) { - return new RSocketClientMonoOperator<>(this, FrameType.METADATA_PUSH, payloadMono); + return new ChannelClientMonoOperator<>(this, FrameType.METADATA_PUSH, payloadMono); } @Override @@ -128,7 +122,7 @@ public void subscribe(CoreSubscriber actual) { new ResolvingOperator.MonoDeferredResolutionOperator<>(this, actual); actual.onSubscribe(inner); - this.observe(inner); + observe(inner); } @Override @@ -149,15 +143,15 @@ public void onComplete() { final Channel value = this.value; if (s == Operators.cancelledSubscription() || !S.compareAndSet(this, s, null)) { - this.doFinally(); + doFinally(); return; } if (value == null) { - this.terminate(new IllegalStateException("Source completed empty")); + terminate(new IllegalStateException("Source completed empty")); } else { - this.complete(value); + complete(value); } } @@ -168,36 +162,36 @@ public void onError(Throwable t) { if (s == Operators.cancelledSubscription() || S.getAndSet(this, Operators.cancelledSubscription()) == Operators.cancelledSubscription()) { - this.doFinally(); + doFinally(); Operators.onErrorDropped(t, Context.empty()); return; } - this.doFinally(); + doFinally(); // terminate upstream which means retryBackoff has exhausted - this.terminate(t); + terminate(t); } @Override public void onNext(Channel value) { if (this.s == Operators.cancelledSubscription()) { - this.doOnValueExpired(value); + doOnValueExpired(value); return; } this.value = value; // volatile write and check on racing - this.doFinally(); + doFinally(); } @Override protected void doSubscribe() { - this.source.subscribe(this); + source.subscribe(this); } @Override protected void doOnValueResolved(Channel value) { - value.onClose().subscribe(null, t -> this.invalidate(), this::invalidate); + value.onClose().subscribe(null, t -> invalidate(), this::invalidate); } @Override @@ -219,17 +213,16 @@ protected void doOnDispose() { static final class FlatMapMain implements CoreSubscriber, Context, Scannable { - final DefaultRemotingClient parent; - final CoreSubscriber actual; + private final DefaultRemotingClient parent; + private final CoreSubscriber actual; - final FlattingInner second; + private final FlattingInner second; - Subscription s; + private Subscription s; - boolean done; + private boolean done; - FlatMapMain( - DefaultRemotingClient parent, CoreSubscriber actual, FrameType requestType) { + FlatMapMain(DefaultRemotingClient parent, CoreSubscriber actual, FrameType requestType) { this.parent = parent; this.actual = actual; this.second = new FlattingInner<>(parent, this, actual, requestType); @@ -242,18 +235,18 @@ public Context currentContext() { @Override public Stream inners() { - return Stream.of(this.second); + return Stream.of(second); } @Override @Nullable public Object scanUnsafe(Attr key) { if (key == Attr.PARENT) - return this.s; + return s; if (key == Attr.CANCELLED) - return this.second.isCancelled(); + return second.isCancelled(); if (key == Attr.TERMINATED) - return this.done; + return done; return null; } @@ -262,13 +255,13 @@ public Object scanUnsafe(Attr key) { public void onSubscribe(Subscription s) { if (Operators.validate(this.s, s)) { this.s = s; - this.actual.onSubscribe(this.second); + actual.onSubscribe(second); } } @Override public void onNext(Payload payload) { - if (this.done) { + if (done) { if (payload.refCnt() > 0) { try { payload.release(); @@ -311,18 +304,18 @@ public void onNext(Payload payload) { return; } - this.parent.observe(inner); + parent.observe(inner); } @Override public void onError(Throwable t) { - if (this.done) { - Operators.onErrorDropped(t, this.actual.currentContext()); + if (done) { + Operators.onErrorDropped(t, actual.currentContext()); return; } this.done = true; - this.actual.onError(t); + actual.onError(t); } @Override @@ -332,7 +325,7 @@ public void onComplete() { } this.done = true; - this.actual.onComplete(); + actual.onComplete(); } void request(long n) { @@ -349,7 +342,7 @@ public K get(Object key) { if (key == ON_DISCARD_KEY) { return (K) DISCARD_ELEMENTS_CONSUMER; } - return this.actual.currentContext().get(key); + return actual.currentContext().get(key); } @Override @@ -357,12 +350,12 @@ public boolean hasKey(Object key) { if (key == ON_DISCARD_KEY) { return true; } - return this.actual.currentContext().hasKey(key); + return actual.currentContext().hasKey(key); } @Override public Context put(Object key, Object value) { - return this.actual + return actual .currentContext() .put(ON_DISCARD_KEY, DISCARD_ELEMENTS_CONSUMER) .put(key, value); @@ -370,7 +363,7 @@ public Context put(Object key, Object value) { @Override public Context delete(Object key) { - return this.actual + return actual .currentContext() .put(ON_DISCARD_KEY, DISCARD_ELEMENTS_CONSUMER) .delete(key); @@ -378,34 +371,31 @@ public Context delete(Object key) { @Override public int size() { - return this.actual.currentContext().size() + 1; + return actual.currentContext().size() + 1; } @Override public Stream> stream() { return Stream.concat( - Stream.of( - new AbstractMap.SimpleImmutableEntry<>(ON_DISCARD_KEY, DISCARD_ELEMENTS_CONSUMER)), - this.actual.currentContext().stream()); + Stream.of(new AbstractMap.SimpleImmutableEntry<>(ON_DISCARD_KEY, DISCARD_ELEMENTS_CONSUMER)), + actual.currentContext().stream()); } } static final class FlattingInner extends DeferredResolution { - final FlatMapMain main; - final FrameType interactionType; + private final FlatMapMain main; + + private final FrameType interactionType; - volatile Payload payload; + private volatile Payload payload; @SuppressWarnings("rawtypes") static final AtomicReferenceFieldUpdater PAYLOAD = AtomicReferenceFieldUpdater.newUpdater(FlattingInner.class, Payload.class, "payload"); - FlattingInner( - DefaultRemotingClient parent, - FlatMapMain main, - CoreSubscriber actual, - FrameType interactionType) { + FlattingInner(DefaultRemotingClient parent, FlatMapMain main, + CoreSubscriber actual, FrameType interactionType) { super(parent, actual); this.main = main; @@ -414,8 +404,8 @@ static final class FlattingInner extends DeferredResolution { @Override @SuppressWarnings({ "unchecked", "rawtypes" }) - public void accept(Channel channel, Throwable t) { - if (this.isCancelled()) { + public void accept(Channel channel, @Nullable Throwable t) { + if (isCancelled()) { return; } @@ -440,7 +430,7 @@ public void accept(Channel channel, Throwable t) { } CorePublisher source; - switch (this.interactionType) { + switch (interactionType) { case REQUEST_FNF: source = channel.fireAndForget(payload); break; @@ -454,7 +444,7 @@ public void accept(Channel channel, Throwable t) { source = channel.metadataPush(payload); break; default: - this.onError(new IllegalStateException("Should never happen")); + onError(new IllegalStateException("Should never happen")); return; } @@ -464,7 +454,7 @@ public void accept(Channel channel, Throwable t) { @Override public void request(long n) { super.request(n); - this.main.request(n); + main.request(n); } public void cancel() { @@ -473,13 +463,13 @@ public void cancel() { return; } - this.main.cancel(); + main.cancel(); if (state == STATE_SUBSCRIBED) { - this.s.cancel(); + s.cancel(); } else { - this.parent.remove(this); + parent.remove(this); Payload payload = PAYLOAD.getAndSet(this, null); if (payload != null) { payload.release(); @@ -490,14 +480,12 @@ public void cancel() { static final class RequestChannelInner extends DeferredResolution { - final FrameType interactionType; - final Publisher upstream; + private final FrameType interactionType; - RequestChannelInner( - DefaultRemotingClient parent, - Publisher upstream, - CoreSubscriber actual, - FrameType interactionType) { + private final Publisher upstream; + + RequestChannelInner(DefaultRemotingClient parent, Publisher upstream, + CoreSubscriber actual, FrameType interactionType) { super(parent, actual); this.upstream = upstream; @@ -505,8 +493,8 @@ static final class RequestChannelInner extends DeferredResolution source; - if (this.interactionType == FrameType.REQUEST_CHANNEL) { - source = channel.requestChannel(this.upstream); + if (interactionType == FrameType.REQUEST_CHANNEL) { + channel.requestChannel(upstream).subscribe(this); } else { - this.onError(new IllegalStateException("Should never happen")); - return; + onError(new IllegalStateException("Should never happen")); } - - source.subscribe(this); } } - static class RSocketClientMonoOperator extends MonoOperator { + static class ChannelClientMonoOperator extends MonoOperator { final DefaultRemotingClient parent; + final FrameType requestType; - public RSocketClientMonoOperator( - DefaultRemotingClient parent, FrameType requestType, Mono source) { + public ChannelClientMonoOperator(DefaultRemotingClient parent, FrameType requestType, Mono source) { super(source); this.parent = parent; this.requestType = requestType; @@ -542,18 +526,19 @@ public RSocketClientMonoOperator( @Override public void subscribe(CoreSubscriber actual) { - this.source.subscribe(new FlatMapMain(this.parent, actual, this.requestType)); + source.subscribe(new FlatMapMain(parent, actual, requestType)); } } - static class RSocketClientFluxOperator> extends Flux { + static class ChannelClientFluxOperator> extends Flux { final DefaultRemotingClient parent; + final FrameType requestType; + final ST source; - public RSocketClientFluxOperator( - DefaultRemotingClient parent, FrameType requestType, ST source) { + public ChannelClientFluxOperator(DefaultRemotingClient parent, FrameType requestType, ST source) { this.parent = parent; this.requestType = requestType; this.source = source; @@ -562,13 +547,12 @@ public RSocketClientFluxOperator( @Override public void subscribe(CoreSubscriber actual) { if (requestType == FrameType.REQUEST_CHANNEL) { - RequestChannelInner inner = - new RequestChannelInner(this.parent, source, actual, requestType); + RequestChannelInner inner = new RequestChannelInner(parent, source, actual, requestType); actual.onSubscribe(inner); - this.parent.observe(inner); + parent.observe(inner); } else { - this.source.subscribe(new FlatMapMain<>(this.parent, actual, this.requestType)); + source.subscribe(new FlatMapMain<>(parent, actual, requestType)); } } } diff --git a/today-remoting/src/main/java/infra/remoting/core/FireAndForgetRequesterMono.java b/today-remoting/src/main/java/infra/remoting/core/FireAndForgetRequesterMono.java index 0f2268c..b6eee67 100644 --- a/today-remoting/src/main/java/infra/remoting/core/FireAndForgetRequesterMono.java +++ b/today-remoting/src/main/java/infra/remoting/core/FireAndForgetRequesterMono.java @@ -1,33 +1,29 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Subscription; import java.time.Duration; import java.util.concurrent.atomic.AtomicLongFieldUpdater; -import infra.lang.NonNull; -import infra.lang.Nullable; -import infra.remoting.DuplexConnection; import infra.remoting.Payload; import infra.remoting.frame.FrameType; import infra.remoting.plugins.RequestInterceptor; -import io.netty.buffer.ByteBufAllocator; import io.netty.util.IllegalReferenceCountException; import reactor.core.CoreSubscriber; import reactor.core.Exceptions; @@ -53,23 +49,11 @@ final class FireAndForgetRequesterMono extends Mono implements Subscriptio final Payload payload; - final ByteBufAllocator allocator; - final int mtu; - final int maxFrameLength; - final RequesterResponderSupport requesterResponderSupport; - final DuplexConnection connection; + final ChannelSupport channel; - @Nullable - final RequestInterceptor requestInterceptor; - - FireAndForgetRequesterMono(Payload payload, RequesterResponderSupport requesterResponderSupport) { - this.allocator = requesterResponderSupport.getAllocator(); + FireAndForgetRequesterMono(Payload payload, ChannelSupport channel) { this.payload = payload; - this.mtu = requesterResponderSupport.getMtu(); - this.maxFrameLength = requesterResponderSupport.getMaxFrameLength(); - this.requesterResponderSupport = requesterResponderSupport; - this.connection = requesterResponderSupport.getDuplexConnection(); - this.requestInterceptor = requesterResponderSupport.getRequestInterceptor(); + this.channel = channel; } @Override @@ -79,7 +63,7 @@ public void subscribe(CoreSubscriber actual) { final IllegalStateException e = new IllegalStateException("FireAndForgetMono allows only a single Subscriber"); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_FNF, null); } @@ -91,15 +75,14 @@ public void subscribe(CoreSubscriber actual) { actual.onSubscribe(this); final Payload p = this.payload; - int mtu = this.mtu; + int mtu = channel.mtu; try { - if (!isValid(mtu, this.maxFrameLength, p, false)) { + if (!isValid(mtu, channel.maxFrameLength, p, false)) { lazyTerminate(STATE, this); - final IllegalArgumentException e = - new IllegalArgumentException( - String.format(INVALID_PAYLOAD_ERROR_MESSAGE, this.maxFrameLength)); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final IllegalArgumentException e = new IllegalArgumentException( + String.format(INVALID_PAYLOAD_ERROR_MESSAGE, channel.maxFrameLength)); + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_FNF, p.metadata()); } @@ -113,7 +96,7 @@ public void subscribe(CoreSubscriber actual) { catch (IllegalReferenceCountException e) { lazyTerminate(STATE, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_FNF, null); } @@ -124,13 +107,13 @@ public void subscribe(CoreSubscriber actual) { final int streamId; try { - streamId = this.requesterResponderSupport.getNextStreamId(); + streamId = this.channel.getNextStreamId(); } catch (Throwable t) { lazyTerminate(STATE, this); final Throwable ut = Exceptions.unwrap(t); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(ut, FrameType.REQUEST_FNF, p.metadata()); } @@ -141,7 +124,7 @@ public void subscribe(CoreSubscriber actual) { return; } - final RequestInterceptor interceptor = this.requestInterceptor; + final RequestInterceptor interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onStart(streamId, FrameType.REQUEST_FNF, p.metadata()); } @@ -157,8 +140,8 @@ public void subscribe(CoreSubscriber actual) { return; } - sendReleasingPayload( - streamId, FrameType.REQUEST_FNF, mtu, p, this.connection, this.allocator, true); + sendReleasingPayload(streamId, FrameType.REQUEST_FNF, mtu, p, + channel.connection, channel.allocator, true); } catch (Throwable e) { lazyTerminate(STATE, this); @@ -206,9 +189,8 @@ public Void block(Duration m) { public Void block() { long previousState = markSubscribed(STATE, this); if (isSubscribedOrTerminated(previousState)) { - final IllegalStateException e = - new IllegalStateException("FireAndForgetMono allows only a single Subscriber"); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final IllegalStateException e = new IllegalStateException("FireAndForgetMono allows only a single Subscriber"); + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_FNF, null); } @@ -217,14 +199,13 @@ public Void block() { final Payload p = this.payload; try { - if (!isValid(this.mtu, this.maxFrameLength, p, false)) { + if (!isValid(channel.mtu, channel.maxFrameLength, p, false)) { lazyTerminate(STATE, this); - final IllegalArgumentException e = - new IllegalArgumentException( - String.format(INVALID_PAYLOAD_ERROR_MESSAGE, this.maxFrameLength)); + final IllegalArgumentException e = new IllegalArgumentException( + String.format(INVALID_PAYLOAD_ERROR_MESSAGE, channel.maxFrameLength)); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_FNF, p.metadata()); } @@ -237,7 +218,7 @@ public Void block() { catch (IllegalReferenceCountException e) { lazyTerminate(STATE, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_FNF, null); } @@ -247,12 +228,12 @@ public Void block() { final int streamId; try { - streamId = this.requesterResponderSupport.getNextStreamId(); + streamId = this.channel.getNextStreamId(); } catch (Throwable t) { lazyTerminate(STATE, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(Exceptions.unwrap(t), FrameType.REQUEST_FNF, p.metadata()); } @@ -262,20 +243,14 @@ public Void block() { throw Exceptions.propagate(t); } - final RequestInterceptor interceptor = this.requestInterceptor; + final RequestInterceptor interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onStart(streamId, FrameType.REQUEST_FNF, p.metadata()); } try { - sendReleasingPayload( - streamId, - FrameType.REQUEST_FNF, - this.mtu, - this.payload, - this.connection, - this.allocator, - true); + sendReleasingPayload(streamId, FrameType.REQUEST_FNF, channel.mtu, this.payload, + channel.connection, channel.allocator, true); } catch (Throwable e) { lazyTerminate(STATE, this); @@ -296,14 +271,15 @@ public Void block() { return null; } + @Nullable @Override public Object scanUnsafe(Scannable.Attr key) { return null; // no particular key to be represented, still useful in hooks } @Override - @NonNull public String stepName() { return "source(FireAndForgetMono)"; } + } diff --git a/today-remoting/src/main/java/infra/remoting/core/FireAndForgetResponderSubscriber.java b/today-remoting/src/main/java/infra/remoting/core/FireAndForgetResponderSubscriber.java index a1b903b..24462aa 100644 --- a/today-remoting/src/main/java/infra/remoting/core/FireAndForgetResponderSubscriber.java +++ b/today-remoting/src/main/java/infra/remoting/core/FireAndForgetResponderSubscriber.java @@ -1,51 +1,47 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Subscription; -import infra.lang.Nullable; import infra.logging.Logger; import infra.logging.LoggerFactory; import infra.remoting.Channel; import infra.remoting.Payload; import infra.remoting.frame.FrameType; -import infra.remoting.frame.decoder.PayloadDecoder; import infra.remoting.plugins.RequestInterceptor; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.CompositeByteBuf; import io.netty.util.ReferenceCountUtil; import reactor.core.CoreSubscriber; import reactor.core.publisher.Mono; -final class FireAndForgetResponderSubscriber - implements CoreSubscriber, ResponderFrameHandler { +final class FireAndForgetResponderSubscriber implements CoreSubscriber, ResponderFrameHandler { static final Logger logger = LoggerFactory.getLogger(FireAndForgetResponderSubscriber.class); static final FireAndForgetResponderSubscriber INSTANCE = new FireAndForgetResponderSubscriber(); final int streamId; - final ByteBufAllocator allocator; - final PayloadDecoder payloadDecoder; - final RequesterResponderSupport requesterResponderSupport; + + final ChannelSupport channel; + final Channel handler; - final int maxInboundPayloadSize; @Nullable final RequestInterceptor requestInterceptor; @@ -54,43 +50,28 @@ final class FireAndForgetResponderSubscriber private FireAndForgetResponderSubscriber() { this.streamId = 0; - this.allocator = null; - this.payloadDecoder = null; - this.maxInboundPayloadSize = 0; - this.requesterResponderSupport = null; + this.channel = null; this.handler = null; this.requestInterceptor = null; this.frames = null; } - FireAndForgetResponderSubscriber( - int streamId, RequesterResponderSupport requesterResponderSupport) { + FireAndForgetResponderSubscriber(int streamId, ChannelSupport channel) { this.streamId = streamId; - this.allocator = null; - this.payloadDecoder = null; - this.maxInboundPayloadSize = 0; - this.requesterResponderSupport = null; + this.channel = null; this.handler = null; - this.requestInterceptor = requesterResponderSupport.getRequestInterceptor(); + this.requestInterceptor = channel.getRequestInterceptor(); this.frames = null; } - FireAndForgetResponderSubscriber( - int streamId, - ByteBuf firstFrame, - RequesterResponderSupport requesterResponderSupport, - Channel handler) { + FireAndForgetResponderSubscriber(int streamId, ByteBuf firstFrame, ChannelSupport channel, Channel handler) { this.streamId = streamId; - this.allocator = requesterResponderSupport.getAllocator(); - this.payloadDecoder = requesterResponderSupport.getPayloadDecoder(); - this.maxInboundPayloadSize = requesterResponderSupport.getMaxInboundPayloadSize(); - this.requesterResponderSupport = requesterResponderSupport; + this.channel = channel; this.handler = handler; - this.requestInterceptor = requesterResponderSupport.getRequestInterceptor(); + this.requestInterceptor = channel.getRequestInterceptor(); - this.frames = - ReassemblyUtils.addFollowingFrame( - allocator.compositeBuffer(), firstFrame, true, maxInboundPayloadSize); + this.frames = ReassemblyUtils.addFollowingFrame( + channel.allocator.compositeBuffer(), firstFrame, true, channel.maxInboundPayloadSize); } @Override @@ -122,14 +103,14 @@ public void onComplete() { @Override public void handleNext(ByteBuf followingFrame, boolean hasFollows, boolean isLastPayload) { final CompositeByteBuf frames = this.frames; - + final ChannelSupport channel = this.channel; try { ReassemblyUtils.addFollowingFrame( - frames, followingFrame, hasFollows, this.maxInboundPayloadSize); + frames, followingFrame, hasFollows, channel.maxInboundPayloadSize); } catch (IllegalStateException t) { final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + channel.remove(streamId, this); this.frames = null; frames.release(); @@ -144,12 +125,12 @@ public void handleNext(ByteBuf followingFrame, boolean hasFollows, boolean isLas } if (!hasFollows) { - this.requesterResponderSupport.remove(this.streamId, this); + channel.remove(this.streamId, this); this.frames = null; Payload payload; try { - payload = this.payloadDecoder.apply(frames); + payload = channel.payloadDecoder.decode(frames); frames.release(); } catch (Throwable t) { @@ -174,7 +155,7 @@ public final void handleCancel() { final CompositeByteBuf frames = this.frames; if (frames != null) { final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + this.channel.remove(streamId, this); this.frames = null; frames.release(); diff --git a/today-remoting/src/main/java/infra/remoting/core/FragmentationUtils.java b/today-remoting/src/main/java/infra/remoting/core/FragmentationUtils.java index 775c825..4a0873c 100644 --- a/today-remoting/src/main/java/infra/remoting/core/FragmentationUtils.java +++ b/today-remoting/src/main/java/infra/remoting/core/FragmentationUtils.java @@ -1,22 +1,22 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; + import infra.remoting.frame.FrameHeaderCodec; import infra.remoting.frame.FrameLengthCodec; import infra.remoting.frame.FrameType; @@ -37,9 +37,9 @@ class FragmentationUtils { static final int FRAME_OFFSET = // 9 bytes in total FrameLengthCodec.FRAME_LENGTH_SIZE // includes encoded frame length bytes size + FrameHeaderCodec.size(); // includes encoded frame headers info bytes size + static final int FRAME_OFFSET_WITH_METADATA = // 12 bytes in total - FRAME_OFFSET - + FrameLengthCodec.FRAME_LENGTH_SIZE; // include encoded metadata length bytes size + FRAME_OFFSET + FrameLengthCodec.FRAME_LENGTH_SIZE; // include encoded metadata length bytes size static final int FRAME_OFFSET_WITH_INITIAL_REQUEST_N = // 13 bytes in total FRAME_OFFSET + Integer.BYTES; // includes extra space for initialRequestN bytes size @@ -47,36 +47,24 @@ class FragmentationUtils { FRAME_OFFSET_WITH_METADATA + Integer.BYTES; // includes extra space for initialRequestN bytes size - static boolean isFragmentable( - int mtu, ByteBuf data, @Nullable ByteBuf metadata, boolean hasInitialRequestN) { + static boolean isFragmentable(int mtu, ByteBuf data, @Nullable ByteBuf metadata, boolean hasInitialRequestN) { if (mtu == 0) { return false; } if (metadata != null) { - int remaining = - mtu - - (hasInitialRequestN - ? FRAME_OFFSET_WITH_METADATA_AND_INITIAL_REQUEST_N - : FRAME_OFFSET_WITH_METADATA); + int remaining = mtu - (hasInitialRequestN + ? FRAME_OFFSET_WITH_METADATA_AND_INITIAL_REQUEST_N + : FRAME_OFFSET_WITH_METADATA); return (metadata.readableBytes() + data.readableBytes()) > remaining; } - else { - int remaining = - mtu - (hasInitialRequestN ? FRAME_OFFSET_WITH_INITIAL_REQUEST_N : FRAME_OFFSET); - - return data.readableBytes() > remaining; - } + int remaining = mtu - (hasInitialRequestN ? FRAME_OFFSET_WITH_INITIAL_REQUEST_N : FRAME_OFFSET); + return data.readableBytes() > remaining; } - static ByteBuf encodeFollowsFragment( - ByteBufAllocator allocator, - int mtu, - int streamId, - boolean complete, - ByteBuf metadata, - ByteBuf data) { + static ByteBuf encodeFollowsFragment(ByteBufAllocator allocator, + int mtu, int streamId, boolean complete, ByteBuf metadata, ByteBuf data) { // subtract the header bytes + frame length size int remaining = mtu - FRAME_OFFSET; @@ -108,14 +96,8 @@ static ByteBuf encodeFollowsFragment( allocator, streamId, follows, (!follows && complete), true, metadataFragment, dataFragment); } - static ByteBuf encodeFirstFragment( - ByteBufAllocator allocator, - int mtu, - FrameType frameType, - int streamId, - boolean hasMetadata, - ByteBuf metadata, - ByteBuf data) { + static ByteBuf encodeFirstFragment(ByteBufAllocator allocator, int mtu, + FrameType frameType, int streamId, boolean hasMetadata, ByteBuf metadata, ByteBuf data) { // subtract the header bytes + frame length size int remaining = mtu - FRAME_OFFSET; @@ -144,37 +126,24 @@ static ByteBuf encodeFirstFragment( throw e; } - switch (frameType) { - case REQUEST_FNF: - return RequestFireAndForgetFrameCodec.encode( - allocator, streamId, true, metadataFragment, dataFragment); - case REQUEST_RESPONSE: - return RequestResponseFrameCodec.encode( - allocator, streamId, true, metadataFragment, dataFragment); + return switch (frameType) { + case REQUEST_FNF -> RequestFireAndForgetFrameCodec.encode( + allocator, streamId, true, metadataFragment, dataFragment); + case REQUEST_RESPONSE -> RequestResponseFrameCodec.encode( + allocator, streamId, true, metadataFragment, dataFragment); // Payload and synthetic types from the responder side - case PAYLOAD: - return PayloadFrameCodec.encode( - allocator, streamId, true, false, false, metadataFragment, dataFragment); - case NEXT: - // see https://github.com/today-tech/today-cloud/blob/master/today-remoting/Protocol.md#handling-the-unexpected - // point 7 - case NEXT_COMPLETE: - return PayloadFrameCodec.encode( - allocator, streamId, true, false, true, metadataFragment, dataFragment); - default: - throw new IllegalStateException("unsupported fragment type: " + frameType); - } + case PAYLOAD -> PayloadFrameCodec.encode( + allocator, streamId, true, false, false, metadataFragment, dataFragment); + // see https://github.com/today-tech/today-cloud/blob/master/today-remoting/Protocol.md#handling-the-unexpected + // point 7 + case NEXT, NEXT_COMPLETE -> PayloadFrameCodec.encode( + allocator, streamId, true, false, true, metadataFragment, dataFragment); + default -> throw new IllegalStateException("unsupported fragment type: " + frameType); + }; } - static ByteBuf encodeFirstFragment( - ByteBufAllocator allocator, - int mtu, - long initialRequestN, - FrameType frameType, - int streamId, - boolean hasMetadata, - ByteBuf metadata, - ByteBuf data) { + static ByteBuf encodeFirstFragment(ByteBufAllocator allocator, int mtu, long initialRequestN, + FrameType frameType, int streamId, boolean hasMetadata, ByteBuf metadata, ByteBuf data) { // subtract the header bytes + frame length bytes + initial requestN bytes int remaining = mtu - FRAME_OFFSET_WITH_INITIAL_REQUEST_N; @@ -203,25 +172,20 @@ static ByteBuf encodeFirstFragment( throw e; } - switch (frameType) { + return switch (frameType) { // Requester Side - case REQUEST_STREAM: - return RequestStreamFrameCodec.encode( - allocator, streamId, true, initialRequestN, metadataFragment, dataFragment); - case REQUEST_CHANNEL: - return RequestChannelFrameCodec.encode( - allocator, streamId, true, false, initialRequestN, metadataFragment, dataFragment); - default: - throw new IllegalStateException("unsupported fragment type: " + frameType); - } + case REQUEST_STREAM -> RequestStreamFrameCodec.encode( + allocator, streamId, true, initialRequestN, metadataFragment, dataFragment); + case REQUEST_CHANNEL -> RequestChannelFrameCodec.encode( + allocator, streamId, true, false, initialRequestN, metadataFragment, dataFragment); + default -> throw new IllegalStateException("unsupported fragment type: " + frameType); + }; } static int assertMtu(int mtu) { if (mtu > 0 && mtu < MIN_MTU_SIZE || mtu < 0) { - String msg = - String.format( - "The smallest allowed mtu size is %d bytes, provided: %d", MIN_MTU_SIZE, mtu); - throw new IllegalArgumentException(msg); + throw new IllegalArgumentException("The smallest allowed mtu size is %d bytes, provided: %d" + .formatted(MIN_MTU_SIZE, mtu)); } else { return mtu; diff --git a/today-remoting/src/main/java/infra/remoting/core/FrameHandler.java b/today-remoting/src/main/java/infra/remoting/core/FrameHandler.java index 5dd0d06..1e060b1 100644 --- a/today-remoting/src/main/java/infra/remoting/core/FrameHandler.java +++ b/today-remoting/src/main/java/infra/remoting/core/FrameHandler.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; diff --git a/today-remoting/src/main/java/infra/remoting/core/LeasePermitHandler.java b/today-remoting/src/main/java/infra/remoting/core/LeasePermitHandler.java index 56f81b2..66ba671 100644 --- a/today-remoting/src/main/java/infra/remoting/core/LeasePermitHandler.java +++ b/today-remoting/src/main/java/infra/remoting/core/LeasePermitHandler.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; diff --git a/today-remoting/src/main/java/infra/remoting/core/LeaseSpec.java b/today-remoting/src/main/java/infra/remoting/core/LeaseSpec.java index a56dafc..edb59eb 100644 --- a/today-remoting/src/main/java/infra/remoting/core/LeaseSpec.java +++ b/today-remoting/src/main/java/infra/remoting/core/LeaseSpec.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; diff --git a/today-remoting/src/main/java/infra/remoting/core/LoggingDuplexConnection.java b/today-remoting/src/main/java/infra/remoting/core/LoggingConnection.java similarity index 58% rename from today-remoting/src/main/java/infra/remoting/core/LoggingDuplexConnection.java rename to today-remoting/src/main/java/infra/remoting/core/LoggingConnection.java index 8b9047d..e7284c3 100644 --- a/today-remoting/src/main/java/infra/remoting/core/LoggingDuplexConnection.java +++ b/today-remoting/src/main/java/infra/remoting/core/LoggingConnection.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -21,7 +20,7 @@ import infra.logging.Logger; import infra.logging.LoggerFactory; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.ProtocolErrorException; import infra.remoting.frame.FrameUtil; import io.netty.buffer.ByteBuf; @@ -29,12 +28,13 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -class LoggingDuplexConnection implements DuplexConnection { +final class LoggingConnection implements Connection { + private static final Logger LOGGER = LoggerFactory.getLogger("infra.remoting.FrameLogger"); - final DuplexConnection source; + private final Connection source; - LoggingDuplexConnection(DuplexConnection source) { + LoggingConnection(Connection source) { this.source = source; } @@ -77,11 +77,10 @@ public SocketAddress remoteAddress() { return source.remoteAddress(); } - static DuplexConnection wrapIfEnabled(DuplexConnection source) { + static Connection wrapIfEnabled(Connection source) { if (LOGGER.isDebugEnabled()) { - return new LoggingDuplexConnection(source); + return new LoggingConnection(source); } - return source; } } diff --git a/today-remoting/src/main/java/infra/remoting/core/MetadataPushRequesterMono.java b/today-remoting/src/main/java/infra/remoting/core/MetadataPushRequesterMono.java index 84a35b6..39c914b 100644 --- a/today-remoting/src/main/java/infra/remoting/core/MetadataPushRequesterMono.java +++ b/today-remoting/src/main/java/infra/remoting/core/MetadataPushRequesterMono.java @@ -1,31 +1,28 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; + import java.time.Duration; import java.util.concurrent.atomic.AtomicLongFieldUpdater; -import infra.lang.NonNull; -import infra.lang.Nullable; -import infra.remoting.DuplexConnection; import infra.remoting.Payload; import infra.remoting.frame.MetadataPushFrameCodec; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; import io.netty.util.IllegalReferenceCountException; import reactor.core.CoreSubscriber; import reactor.core.Scannable; @@ -44,16 +41,13 @@ final class MetadataPushRequesterMono extends Mono implements Scannable { static final AtomicLongFieldUpdater STATE = AtomicLongFieldUpdater.newUpdater(MetadataPushRequesterMono.class, "state"); - final ByteBufAllocator allocator; final Payload payload; - final int maxFrameLength; - final DuplexConnection connection; - MetadataPushRequesterMono(Payload payload, RequesterResponderSupport requesterResponderSupport) { - this.allocator = requesterResponderSupport.getAllocator(); + private final ChannelSupport channel; + + MetadataPushRequesterMono(Payload payload, ChannelSupport channel) { this.payload = payload; - this.maxFrameLength = requesterResponderSupport.getMaxFrameLength(); - this.connection = requesterResponderSupport.getDuplexConnection(); + this.channel = channel; } @Override @@ -78,13 +72,13 @@ public void subscribe(CoreSubscriber actual) { new IllegalArgumentException("Metadata push should have metadata field present")); return; } - if (!isValidMetadata(this.maxFrameLength, metadata)) { + if (!isValidMetadata(channel.maxFrameLength, metadata)) { lazyTerminate(STATE, this); p.release(); Operators.error( actual, new IllegalArgumentException( - String.format(INVALID_PAYLOAD_ERROR_MESSAGE, this.maxFrameLength))); + String.format(INVALID_PAYLOAD_ERROR_MESSAGE, channel.maxFrameLength))); return; } } @@ -115,8 +109,8 @@ public void subscribe(CoreSubscriber actual) { } final ByteBuf requestFrame = - MetadataPushFrameCodec.encode(this.allocator, metadataRetainedSlice); - this.connection.sendFrame(0, requestFrame); + MetadataPushFrameCodec.encode(channel.allocator, metadataRetainedSlice); + channel.connection.sendFrame(0, requestFrame); Operators.complete(actual); } @@ -150,11 +144,11 @@ public Void block() { p.release(); throw new IllegalArgumentException("Metadata push should have metadata field present"); } - if (!isValidMetadata(this.maxFrameLength, metadata)) { + if (!isValidMetadata(channel.maxFrameLength, metadata)) { lazyTerminate(STATE, this); p.release(); throw new IllegalArgumentException( - String.format(INVALID_PAYLOAD_ERROR_MESSAGE, this.maxFrameLength)); + String.format(INVALID_PAYLOAD_ERROR_MESSAGE, channel.maxFrameLength)); } } catch (IllegalReferenceCountException e) { @@ -180,20 +174,19 @@ public Void block() { throw e; } - final ByteBuf requestFrame = - MetadataPushFrameCodec.encode(this.allocator, metadataRetainedSlice); - this.connection.sendFrame(0, requestFrame); + final ByteBuf requestFrame = MetadataPushFrameCodec.encode(channel.allocator, metadataRetainedSlice); + channel.connection.sendFrame(0, requestFrame); return null; } + @Nullable @Override public Object scanUnsafe(Attr key) { return null; // no particular key to be represented, still useful in hooks } @Override - @NonNull public String stepName() { return "source(MetadataPushMono)"; } diff --git a/today-remoting/src/main/java/infra/remoting/core/MetadataPushResponderSubscriber.java b/today-remoting/src/main/java/infra/remoting/core/MetadataPushResponderSubscriber.java index a0e9c0f..a3b9149 100644 --- a/today-remoting/src/main/java/infra/remoting/core/MetadataPushResponderSubscriber.java +++ b/today-remoting/src/main/java/infra/remoting/core/MetadataPushResponderSubscriber.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; diff --git a/today-remoting/src/main/java/infra/remoting/core/PayloadValidationUtils.java b/today-remoting/src/main/java/infra/remoting/core/PayloadValidationUtils.java index a4bba7f..56926dd 100644 --- a/today-remoting/src/main/java/infra/remoting/core/PayloadValidationUtils.java +++ b/today-remoting/src/main/java/infra/remoting/core/PayloadValidationUtils.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -27,11 +26,11 @@ import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; final class PayloadValidationUtils { + static final String INVALID_PAYLOAD_ERROR_MESSAGE = "The payload is too big to be send as a single frame with a max frame length %s. Consider enabling fragmentation."; static boolean isValid(int mtu, int maxFrameLength, Payload payload, boolean hasInitialRequestN) { - if (mtu > 0) { return true; } @@ -42,18 +41,12 @@ static boolean isValid(int mtu, int maxFrameLength, Payload payload, boolean has int unitSize; if (hasMetadata) { final ByteBuf metadata = payload.metadata(); - unitSize = - (hasInitialRequestN - ? FRAME_OFFSET_WITH_METADATA_AND_INITIAL_REQUEST_N - : FRAME_OFFSET_WITH_METADATA) - + metadata.readableBytes() - + // metadata payload bytes - data.readableBytes(); // data payload bytes + unitSize = (hasInitialRequestN ? FRAME_OFFSET_WITH_METADATA_AND_INITIAL_REQUEST_N : FRAME_OFFSET_WITH_METADATA) + + metadata.readableBytes()// metadata payload bytes + + data.readableBytes(); // data payload bytes } else { - unitSize = - (hasInitialRequestN ? FRAME_OFFSET_WITH_INITIAL_REQUEST_N : FRAME_OFFSET) - + data.readableBytes(); // data payload bytes + unitSize = (hasInitialRequestN ? FRAME_OFFSET_WITH_INITIAL_REQUEST_N : FRAME_OFFSET) + data.readableBytes(); // data payload bytes } return unitSize <= maxFrameLength; diff --git a/today-remoting/src/main/java/infra/remoting/core/ReassemblyUtils.java b/today-remoting/src/main/java/infra/remoting/core/ReassemblyUtils.java index e563238..25e5a6e 100644 --- a/today-remoting/src/main/java/infra/remoting/core/ReassemblyUtils.java +++ b/today-remoting/src/main/java/infra/remoting/core/ReassemblyUtils.java @@ -1,19 +1,19 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; import org.reactivestreams.Subscription; @@ -40,8 +40,8 @@ import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; class ReassemblyUtils { - static final String ILLEGAL_REASSEMBLED_PAYLOAD_SIZE = - "Reassembled payload size went out of allowed %s bytes"; + + static final String ILLEGAL_REASSEMBLED_PAYLOAD_SIZE = "Reassembled payload size went out of allowed %s bytes"; @SuppressWarnings("ConstantConditions") static void release(RequesterFrameHandler framesHolder, long state) { @@ -52,7 +52,7 @@ static void release(RequesterFrameHandler framesHolder, long state) { } } - @SuppressWarnings({ "ConstantConditions", "SynchronizationOnLocalVariableOrMethodParameter" }) + @SuppressWarnings({ "ConstantConditions" }) static void synchronizedRelease(RequesterFrameHandler framesHolder, long state) { if (isReassembling(state)) { final CompositeByteBuf frames = framesHolder.getFrames(); @@ -64,17 +64,9 @@ static void synchronizedRelease(RequesterFrameHandler framesHolder, long state) } } - static void handleNextSupport( - AtomicLongFieldUpdater updater, - T instance, - Subscription subscription, - CoreSubscriber inboundSubscriber, - PayloadDecoder payloadDecoder, - ByteBufAllocator allocator, - int maxInboundPayloadSize, - ByteBuf frame, - boolean hasFollows, - boolean isLastPayload) { + static void handleNextSupport(AtomicLongFieldUpdater updater, + T instance, Subscription subscription, CoreSubscriber inboundSubscriber, PayloadDecoder payloadDecoder, + ByteBufAllocator allocator, int maxInboundPayloadSize, ByteBuf frame, boolean hasFollows, boolean isLastPayload) { long state = updater.get(instance); if (isTerminated(state)) { @@ -84,7 +76,7 @@ static void handleNextSupport( if (!hasFollows && !isReassembling(state)) { Payload payload; try { - payload = payloadDecoder.apply(frame); + payload = payloadDecoder.decode(frame); } catch (Throwable t) { // sends cancel frame to prevent any further frames @@ -104,9 +96,7 @@ static void handleNextSupport( CompositeByteBuf frames = instance.getFrames(); if (frames == null) { - frames = - ReassemblyUtils.addFollowingFrame( - allocator.compositeBuffer(), frame, hasFollows, maxInboundPayloadSize); + frames = ReassemblyUtils.addFollowingFrame(allocator.compositeBuffer(), frame, hasFollows, maxInboundPayloadSize); instance.setFrames(frames); long previousState = markReassembling(updater, instance); @@ -118,8 +108,7 @@ static void handleNextSupport( } else { try { - frames = - ReassemblyUtils.addFollowingFrame(frames, frame, hasFollows, maxInboundPayloadSize); + frames = ReassemblyUtils.addFollowingFrame(frames, frame, hasFollows, maxInboundPayloadSize); } catch (IllegalStateException t) { if (isTerminated(updater.get(instance))) { @@ -145,7 +134,7 @@ static void handleNextSupport( Payload payload; try { - payload = payloadDecoder.apply(frames); + payload = payloadDecoder.decode(frames); frames.release(); } catch (Throwable t) { @@ -167,18 +156,13 @@ static void handleNextSupport( } } - static CompositeByteBuf addFollowingFrame( - CompositeByteBuf frames, - ByteBuf followingFrame, - boolean hasFollows, - int maxInboundPayloadSize) { + static CompositeByteBuf addFollowingFrame(CompositeByteBuf frames, ByteBuf followingFrame, boolean hasFollows, int maxInboundPayloadSize) { int readableBytes = frames.readableBytes(); if (readableBytes == 0) { return frames.addComponent(true, followingFrame.retain()); } else if (maxInboundPayloadSize != Integer.MAX_VALUE - && readableBytes + followingFrame.readableBytes() - FrameHeaderCodec.size() - > maxInboundPayloadSize) { + && readableBytes + followingFrame.readableBytes() - FrameHeaderCodec.size() > maxInboundPayloadSize) { throw new IllegalStateException( String.format(ILLEGAL_REASSEMBLED_PAYLOAD_SIZE, maxInboundPayloadSize)); } @@ -196,8 +180,7 @@ else if (followingFrame.readableBytes() < MIN_MTU_SIZE - 3 && hasFollows) { // CompositeByteBuf if (hasMetadata) { final FrameType frameType = FrameHeaderCodec.frameType(frames); - final int lengthFieldPosition = - FrameHeaderCodec.size() + (frameType.hasInitialRequestN() ? Integer.BYTES : 0); + final int lengthFieldPosition = FrameHeaderCodec.size() + (frameType.hasInitialRequestN() ? Integer.BYTES : 0); frames.markReaderIndex(); frames.skipBytes(lengthFieldPosition); @@ -245,11 +228,8 @@ private static int decodeLength(final ByteBuf byteBuf) { static int assertInboundPayloadSize(int inboundPayloadSize) { if (inboundPayloadSize < MIN_MTU_SIZE) { - String msg = - String.format( - "The min allowed inboundPayloadSize size is %d bytes, provided: %d", - FrameLengthCodec.FRAME_LENGTH_MASK, inboundPayloadSize); - throw new IllegalArgumentException(msg); + throw new IllegalArgumentException("The min allowed inboundPayloadSize size is %d bytes, provided: %d" + .formatted(FrameLengthCodec.FRAME_LENGTH_MASK, inboundPayloadSize)); } else { return inboundPayloadSize; diff --git a/today-remoting/src/main/java/infra/remoting/core/ReconnectMono.java b/today-remoting/src/main/java/infra/remoting/core/ReconnectMono.java index fe9eda5..15896c2 100644 --- a/today-remoting/src/main/java/infra/remoting/core/ReconnectMono.java +++ b/today-remoting/src/main/java/infra/remoting/core/ReconnectMono.java @@ -1,22 +1,22 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Subscription; import java.time.Duration; @@ -26,7 +26,6 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; -import infra.lang.Nullable; import reactor.core.CoreSubscriber; import reactor.core.Disposable; import reactor.core.Scannable; @@ -36,14 +35,13 @@ final class ReconnectMono extends Mono implements Invalidatable, Disposable, Scannable { - final Mono source; + public final Mono source; + final BiConsumer onValueReceived; final Consumer onValueExpired; final ResolvingInner resolvingInner; - ReconnectMono( - Mono source, - Consumer onValueExpired, + ReconnectMono(Mono source, Consumer onValueExpired, BiConsumer onValueReceived) { this.source = source; this.onValueExpired = onValueExpired; @@ -51,10 +49,7 @@ final class ReconnectMono extends Mono implements Invalidatable, Disposabl this.resolvingInner = new ResolvingInner<>(this); } - public Mono getSource() { - return source; - } - + @Nullable @Override public Object scanUnsafe(Attr key) { if (key == Attr.PARENT) @@ -269,6 +264,7 @@ protected void doSubscribe() { this.parent.source.subscribe(this.mainSubscriber); } + @Nullable @Override public Object scanUnsafe(Attr key) { if (key == Attr.PARENT) diff --git a/today-remoting/src/main/java/infra/remoting/core/RemotingClient.java b/today-remoting/src/main/java/infra/remoting/core/RemotingClient.java index 7aaec90..574b41e 100644 --- a/today-remoting/src/main/java/infra/remoting/core/RemotingClient.java +++ b/today-remoting/src/main/java/infra/remoting/core/RemotingClient.java @@ -1,86 +1,85 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; import org.reactivestreams.Publisher; +import java.util.List; + import infra.remoting.Channel; import infra.remoting.Closeable; import infra.remoting.Payload; +import infra.remoting.RemotingOperations; import infra.remoting.lb.LoadBalanceRemotingClient; +import infra.remoting.lb.LoadBalanceTarget; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** - * Contract for performing RSocket requests. + * Contract for performing protocol requests. * *

    {@link RemotingClient} differs from {@link Channel} in a number of ways: * *

      - *
    • {@code RSocket} represents a "live" connection that is transient and needs to be obtained - * typically from a {@code Mono} source via {@code flatMap} or block. By contrast, - * {@code RSocketClient} is a higher level layer that contains such a {@link #source() source} + *
    • {@code Channel} represents a "live" connection that is transient and needs to be obtained + * typically from a {@code Mono} source via {@code flatMap} or block. By contrast, + * {@code RemotingClient} is a higher level layer that contains such a {@link #source() source} * of connections and transparently obtains and re-obtains a shared connection as needed when - * requests are made concurrently. That means an {@code RSocketClient} can simply be created + * requests are made concurrently. That means an {@code RemotingClient} can simply be created * once, even before a connection is established, and shared as a singleton across multiple * places as you would with any other client. - *
    • For request input {@code RSocket} accepts an instance of {@code Payload} and does not allow + *
    • For request input {@code Channel} accepts an instance of {@code Payload} and does not allow * more than one subscription per request because there is no way to safely re-use that input. - * By contrast {@code RSocketClient} accepts {@code Publisher} and allow + * By contrast {@code RemotingClient} accepts {@code Publisher} and allow * re-subscribing which repeats the request. - *
    • {@code RSocket} can be used for sending and it can also be implemented for receiving. By - * contrast {@code RSocketClient} is used only for sending, typically from the client side + *
    • {@code Channel} can be used for sending and it can also be implemented for receiving. By + * contrast {@code RemotingClient} is used only for sending, typically from the client side * which allows obtaining and re-obtaining connections from a source as needed. However it can * also be used from the server side by {@link #from(Channel) wrapping} the "live" {@code - * RSocket} for a given connection. + * Channel} for a given connection. *
    * - *

    The example below shows how to create an {@code RSocketClient}: + *

    The example below shows how to create an {@code RemotingClient}: * - *

    {@code
    - * Mono source =
    - *         RSocketConnector.create()
    - *                 .metadataMimeType("message/x.rsocket.composite-metadata.v0")
    - *                 .dataMimeType("application/cbor")
    - *                 .connect(TcpClientTransport.create("localhost", 7000));
    + * 
    {@code
    + * Mono source = ChannelConnector.create()
    + *      .connect(TcpClientTransport.create("localhost", 7000));
      *
    - * RSocketClient client = RSocketClient.from(source);
    + * RemotingClient client = RemotingClient.from(source);
      * }
    * - *

    The below configures retry logic to use when a shared {@code RSocket} connection is obtained: + *

    The below configures retry logic to use when a shared {@code Channel} connection is obtained: * - *

    {@code
    - * Mono source =
    - *         RSocketConnector.create()
    - *                 .metadataMimeType("message/x.rsocket.composite-metadata.v0")
    - *                 .dataMimeType("application/cbor")
    + * 
    {@code
    + * Mono source =
    + *         ChannelConnector.create()
      *                 .reconnect(Retry.fixedDelay(3, Duration.ofSeconds(1)))
      *                 .connect(TcpClientTransport.create("localhost", 7000));
    - *
    - * RSocketClient client = RSocketClient.from(source);
    + * RemotingClient client = RemotingClient.from(source);
      * }
    * + * @author
    海子 Yang * @see LoadBalanceRemotingClient */ -public interface RemotingClient extends Closeable { +public interface RemotingClient extends Closeable, RemotingOperations { /** - * Connect to the remote rsocket endpoint, if not yet connected. This method is a shortcut for - * {@code RSocketClient#source().subscribe()}. + * Connect to the remote endpoint, if not yet connected. This method is a shortcut for + * {@code RemotingClient#source().subscribe()}. * * @return {@code true} if an attempt to connect was triggered or if already connected, or {@code * false} if the client is terminated. @@ -102,35 +101,40 @@ default Mono onClose() { * Perform a Fire-and-Forget interaction via {@link Channel#fireAndForget(Payload)}. Allows * multiple subscriptions and performs a request per subscriber. */ + @Override Mono fireAndForget(Mono payloadMono); /** * Perform a Request-Response interaction via {@link Channel#requestResponse(Payload)}. Allows * multiple subscriptions and performs a request per subscriber. */ + @Override Mono requestResponse(Mono payloadMono); /** * Perform a Request-Stream interaction via {@link Channel#requestStream(Payload)}. Allows * multiple subscriptions and performs a request per subscriber. */ + @Override Flux requestStream(Mono payloadMono); /** * Perform a Request-Channel interaction via {@link Channel#requestChannel(Publisher)}. Allows * multiple subscriptions and performs a request per subscriber. */ + @Override Flux requestChannel(Publisher payloads); /** * Perform a Metadata Push via {@link Channel#metadataPush(Payload)}. Allows multiple * subscriptions and performs a request per subscriber. */ + @Override Mono metadataPush(Mono payloadMono); /** * Create an {@link RemotingClient} that obtains shared connections as needed, when requests are - * made, from the given {@code Mono} source. + * made, from the given {@code Mono} source. * * @param source the source for connections, typically prepared via {@link ChannelConnector}. * @return the created client instance @@ -141,16 +145,45 @@ static RemotingClient from(Mono source) { /** * Adapt the given {@link Channel} to use as {@link RemotingClient}. This is useful to wrap the - * sending {@code RSocket} in a server. + * sending {@code Channel} in a server. * - *

    Note: unlike an {@code RSocketClient} created via {@link + *

    Note: unlike an {@code RemotingClient} created via {@link * RemotingClient#from(Mono)}, the instance returned from this factory method can only perform - * requests for as long as the given {@code RSocket} remains "live". + * requests for as long as the given {@code Channel} remains "live". * - * @param rsocket the {@code RSocket} to perform requests with + * @param channel the {@code Channel} to perform requests with * @return the created client instance */ - static RemotingClient from(Channel rsocket) { - return new RemotingClientAdapter(rsocket); + static RemotingClient from(Channel channel) { + return new RemotingClientAdapter(channel); } + + /** + * Shortcut to create an {@link LoadBalanceRemotingClient} with round-robin load balancing. + * Effectively a shortcut for: + * + *

    {@code
    +   * LoadBalanceRemotingClient.builder(targetPublisher)
    +   *    .connector(ChannelConnector.create())
    +   *    .build();
    +   * }
    + * + * @param connector a "template" for connecting to load balance targets + * @param targetPublisher refreshes the list of load balance targets periodically + * @return the created client instance + */ + static LoadBalanceRemotingClient forLoadBalance(ChannelConnector connector, Publisher> targetPublisher) { + return forLoadBalance(targetPublisher).connector(connector).build(); + } + + /** + * Return a builder for a {@link LoadBalanceRemotingClient}. + * + * @param targetPublisher refreshes the list of load balance targets periodically + * @return the created builder + */ + static LoadBalanceRemotingClient.Builder forLoadBalance(Publisher> targetPublisher) { + return LoadBalanceRemotingClient.builder(targetPublisher); + } + } diff --git a/today-remoting/src/main/java/infra/remoting/core/RemotingClientAdapter.java b/today-remoting/src/main/java/infra/remoting/core/RemotingClientAdapter.java index 6aec05e..ee05c37 100644 --- a/today-remoting/src/main/java/infra/remoting/core/RemotingClientAdapter.java +++ b/today-remoting/src/main/java/infra/remoting/core/RemotingClientAdapter.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -31,58 +30,58 @@ */ class RemotingClientAdapter implements RemotingClient { - private final Channel rsocket; + private final Channel channel; - public RemotingClientAdapter(Channel rsocket) { - this.rsocket = rsocket; + public RemotingClientAdapter(Channel channel) { + this.channel = channel; } - public Channel rsocket() { - return rsocket; + public Channel channel() { + return channel; } @Override public boolean connect() { - throw new UnsupportedOperationException("Connect does not apply to a server side RSocket"); + throw new UnsupportedOperationException("Connect does not apply to a server side Channel"); } @Override public Mono source() { - return Mono.just(rsocket); + return Mono.just(channel); } @Override public Mono onClose() { - return rsocket.onClose(); + return channel.onClose(); } @Override public Mono fireAndForget(Mono payloadMono) { - return payloadMono.flatMap(rsocket::fireAndForget); + return payloadMono.flatMap(channel::fireAndForget); } @Override public Mono requestResponse(Mono payloadMono) { - return payloadMono.flatMap(rsocket::requestResponse); + return payloadMono.flatMap(channel::requestResponse); } @Override public Flux requestStream(Mono payloadMono) { - return payloadMono.flatMapMany(rsocket::requestStream); + return payloadMono.flatMapMany(channel::requestStream); } @Override public Flux requestChannel(Publisher payloads) { - return rsocket.requestChannel(payloads); + return channel.requestChannel(payloads); } @Override public Mono metadataPush(Mono payloadMono) { - return payloadMono.flatMap(rsocket::metadataPush); + return payloadMono.flatMap(channel::metadataPush); } @Override public void dispose() { - rsocket.dispose(); + channel.dispose(); } } diff --git a/today-remoting/src/main/java/infra/remoting/core/RemotingServer.java b/today-remoting/src/main/java/infra/remoting/core/RemotingServer.java index 7c3c881..39791aa 100644 --- a/today-remoting/src/main/java/infra/remoting/core/RemotingServer.java +++ b/today-remoting/src/main/java/infra/remoting/core/RemotingServer.java @@ -1,22 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; + import java.time.Duration; import java.util.Objects; import java.util.function.Consumer; @@ -25,20 +26,20 @@ import infra.remoting.Channel; import infra.remoting.ChannelAcceptor; import infra.remoting.Closeable; +import infra.remoting.Connection; import infra.remoting.ConnectionSetupPayload; -import infra.remoting.DuplexConnection; import infra.remoting.Payload; import infra.remoting.ProtocolErrorException; -import infra.remoting.exceptions.InvalidSetupException; -import infra.remoting.exceptions.RejectedSetupException; +import infra.remoting.error.InvalidSetupException; +import infra.remoting.error.RejectedSetupException; import infra.remoting.frame.FrameHeaderCodec; import infra.remoting.frame.SetupFrameCodec; import infra.remoting.frame.decoder.PayloadDecoder; import infra.remoting.lease.TrackingLeaseSender; -import infra.remoting.plugins.ConnectionInterceptor; +import infra.remoting.plugins.ConnectionDecorator; import infra.remoting.plugins.InitializingInterceptorRegistry; import infra.remoting.plugins.InterceptorRegistry; -import infra.remoting.plugins.RateLimitInterceptor; +import infra.remoting.plugins.RateLimitDecorator; import infra.remoting.resume.SessionManager; import infra.remoting.transport.ConnectionAcceptor; import infra.remoting.transport.ServerTransport; @@ -46,6 +47,7 @@ import io.netty.buffer.ByteBuf; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; +import reactor.core.scheduler.Schedulers; import static infra.remoting.core.FragmentationUtils.assertMtu; import static infra.remoting.core.PayloadValidationUtils.assertValidateSetup; @@ -53,13 +55,13 @@ import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; /** - * The main class for starting an RSocket server. + * The main class for starting a server. * *

    For example: * *

    {@code
      * CloseableChannel closeable =
    - *         RemotingServer.create(SocketAcceptor.with(new RSocket() {...}))
    + *         RemotingServer.create(ChannelAcceptor.with(new Channel() {...}))
      *                 .bind(TcpServerTransport.create("localhost", 7000))
      *                 .block();
      * }
    @@ -72,9 +74,9 @@ public final class RemotingServer { private final InitializingInterceptorRegistry interceptors = new InitializingInterceptorRegistry(); - private Resume resume; + private @Nullable Resume resume; - private Consumer leaseConfigurer = null; + private @Nullable Consumer leaseConfigurer = null; private int mtu = 0; @@ -87,43 +89,22 @@ public final class RemotingServer { private RemotingServer() { } - /** Static factory method to create an {@code RemotingServer}. */ - public static RemotingServer create() { - return new RemotingServer(); - } - - /** - * Static factory method to create an {@code RemotingServer} instance with the given {@code - * SocketAcceptor}. Effectively a shortcut for: - * - *
    -   * RemotingServer.create().acceptor(...);
    -   * 
    - * - * @param acceptor the acceptor to handle connections with - * @return the same instance for method chaining - * @see #acceptor(ChannelAcceptor) - */ - public static RemotingServer create(ChannelAcceptor acceptor) { - return RemotingServer.create().acceptor(acceptor); - } - /** * Set the acceptor to handle incoming connections and handle requests. * - *

    An example with access to the {@code SETUP} frame and sending RSocket for performing + *

    An example with access to the {@code SETUP} frame and sending Channel for performing * requests back to the client if needed: * *

    {@code
    -   * RemotingServer.create((setup, sendingRSocket) -> Mono.just(new RSocket() {...}))
    +   * RemotingServer.create((setup, sending) -> Mono.just(new Channel() {...}))
        *         .bind(TcpServerTransport.create("localhost", 7000))
        *         .subscribe();
        * }
    * - *

    A shortcut to provide the handling RSocket only: + *

    A shortcut to provide the handling Channel only: * *

    {@code
    -   * RemotingServer.create(SocketAcceptor.with(new RSocket() {...}))
    +   * RemotingServer.create(ChannelAcceptor.with(new Channel() {...}))
        *         .bind(TcpServerTransport.create("localhost", 7000))
        *         .subscribe();
        * }
    @@ -131,12 +112,12 @@ public static RemotingServer create(ChannelAcceptor acceptor) { *

    A shortcut to handle request-response interactions only: * *

    {@code
    -   * RemotingServer.create(SocketAcceptor.forRequestResponse(payload -> ...))
    +   * RemotingServer.create(ChannelAcceptor.forRequestResponse(payload -> ...))
        *         .bind(TcpServerTransport.create("localhost", 7000))
        *         .subscribe();
        * }
    * - *

    By default, {@code new RSocket(){}} is used for handling which rejects requests from the + *

    By default, {@code new Channel(){}} is used for handling which rejects requests from the * client with {@link UnsupportedOperationException}. * * @param acceptor the acceptor to handle incoming connections and requests with @@ -160,7 +141,7 @@ public RemotingServer acceptor(ChannelAcceptor acceptor) { * * @param configurer a configurer to customize interception with. * @return the same instance for method chaining - * @see RateLimitInterceptor + * @see RateLimitDecorator */ public RemotingServer interceptors(Consumer configurer) { configurer.accept(this.interceptors); @@ -168,14 +149,14 @@ public RemotingServer interceptors(Consumer configurer) { } /** - * Enables the Resume capability of the RSocket protocol where if the client gets disconnected, + * Enables the Resume capability of the protocol where if the client gets disconnected, * the connection is re-acquired and any interrupted streams are transparently resumed. For this * to work clients must also support and request to enable this when connecting. * *

    Use the {@link Resume} argument to customize the Resume session duration, storage, retry * logic, and others. * - *

    By default this is not enabled. + *

    By default, this is not enabled. * * @param resume configuration for the Resume capability * @return the same instance for method chaining @@ -183,20 +164,20 @@ public RemotingServer interceptors(Consumer configurer) { * href="https://github.com/today-tech/today-cloud/blob/master/today-remoting/Protocol.md#resuming-operation">Resuming * Operation */ - public RemotingServer resume(Resume resume) { + public RemotingServer resume(@Nullable Resume resume) { this.resume = resume; return this; } /** - * Enables the Lease feature of the RSocket protocol where the number of requests that can be + * Enables the Lease feature of the protocol where the number of requests that can be * performed from either side are rationed via {@code LEASE} frames from the responder side. For * this to work clients must also support and request to enable this when connecting. * *

    Example usage: * *

    {@code
    -   * RemotingServer.create(SocketAcceptor.with(new RSocket() {...}))
    +   * RemotingServer.create(ChannelAcceptor.with(new Channel() {...}))
        *         .lease(spec ->
        *            spec.sender(() -> Flux.interval(ofSeconds(1))
        *                                  .map(__ -> Lease.create(ofSeconds(1), 1)))
    @@ -212,7 +193,7 @@ public RemotingServer resume(Resume resume) {
        * @see Lease
        * Semantics
        */
    -  public RemotingServer lease(Consumer leaseConfigurer) {
    +  public RemotingServer lease(@Nullable Consumer leaseConfigurer) {
         this.leaseConfigurer = leaseConfigurer;
         return this;
       }
    @@ -241,7 +222,7 @@ public RemotingServer maxInboundPayloadSize(int maxInboundPayloadSize) {
        * Specify the max time to wait for the first frame (e.g. {@code SETUP}) on an accepted
        * connection.
        *
    -   * 

    By default this is set to 1 minute. + *

    By default, this is set to 1 minute. * * @param timeout duration * @return the same instance for method chaining @@ -258,7 +239,7 @@ public RemotingServer maxTimeToFirstFrame(Duration timeout) { * When this is set, frames larger than the given maximum transmission unit (mtu) size value are * fragmented. * - *

    By default this is not set in which case payloads are sent whole up to the maximum frame + *

    By default, this is not set in which case payloads are sent whole up to the maximum frame * size of 16,777,215 bytes. * * @param mtu the threshold size for fragmentation, must be no less than 64 @@ -306,15 +287,15 @@ public RemotingServer payloadDecoder(PayloadDecoder decoder) { */ public Mono bind(ServerTransport transport) { return Mono.defer(new Supplier>() { - private final ServerSetup serverSetup = serverSetup(timeout); + private final ServerSetup serverSetup = createSetup(timeout); @Override public Mono get() { int maxFrameLength = transport.getMaxFrameLength(); assertValidateSetup(maxFrameLength, maxInboundPayloadSize, mtu); return transport - .start(duplexConnection -> acceptor(serverSetup, duplexConnection, maxFrameLength)) -// .publishOn(Schedulers.boundedElastic()) + .start(connection -> acceptor(serverSetup, connection, maxFrameLength)) + .publishOn(Schedulers.boundedElastic()) .doOnNext(c -> c.onClose().doFinally(v -> serverSetup.dispose()).subscribe()); } }); @@ -329,7 +310,7 @@ public T bindNow(ServerTransport transport) { } /** - * An alternative to {@link #bind(ServerTransport)} that is useful for installing RSocket on a + * An alternative to {@link #bind(ServerTransport)} that is useful for installing Channel on a * server that is started independently. */ public ConnectionAcceptor asConnectionAcceptor() { @@ -337,38 +318,38 @@ public ConnectionAcceptor asConnectionAcceptor() { } /** - * An alternative to {@link #bind(ServerTransport)} that is useful for installing RSocket on a + * An alternative to {@link #bind(ServerTransport)} that is useful for installing Channel on a * server that is started independently. */ public ConnectionAcceptor asConnectionAcceptor(int maxFrameLength) { assertValidateSetup(maxFrameLength, maxInboundPayloadSize, mtu); return new ConnectionAcceptor() { - private final ServerSetup serverSetup = serverSetup(timeout); + private final ServerSetup serverSetup = createSetup(timeout); @Override - public Mono accept(DuplexConnection connection) { + public Mono accept(Connection connection) { return acceptor(serverSetup, connection, maxFrameLength); } }; } - private Mono acceptor(ServerSetup serverSetup, DuplexConnection sourceConnection, int maxFrameLength) { - final DuplexConnection interceptedConnection = interceptors.initConnection(ConnectionInterceptor.Type.SOURCE, sourceConnection); + private Mono acceptor(ServerSetup serverSetup, Connection sourceConnection, int maxFrameLength) { + final Connection interceptedConnection = interceptors.initConnection(ConnectionDecorator.Type.SOURCE, sourceConnection); return serverSetup - .init(LoggingDuplexConnection.wrapIfEnabled(interceptedConnection)) + .init(LoggingConnection.wrapIfEnabled(interceptedConnection)) .flatMap(tuple2 -> { final ByteBuf startFrame = tuple2.getT1(); - final DuplexConnection clientServerConnection = tuple2.getT2(); + final Connection clientServerConnection = tuple2.getT2(); return accept(serverSetup, startFrame, clientServerConnection, maxFrameLength); }); } - private Mono acceptResume(ServerSetup serverSetup, ByteBuf resumeFrame, DuplexConnection clientServerConnection) { - return serverSetup.acceptRSocketResume(resumeFrame, clientServerConnection); + private Mono acceptResume(ServerSetup serverSetup, ByteBuf resumeFrame, Connection clientServerConnection) { + return serverSetup.acceptChannelResume(resumeFrame, clientServerConnection); } - private Mono accept(ServerSetup serverSetup, ByteBuf startFrame, DuplexConnection clientServerConnection, int maxFrameLength) { + private Mono accept(ServerSetup serverSetup, ByteBuf startFrame, Connection clientServerConnection, int maxFrameLength) { return switch (FrameHeaderCodec.frameType(startFrame)) { case SETUP -> acceptSetup(serverSetup, startFrame, clientServerConnection, maxFrameLength); case RESUME -> acceptResume(serverSetup, startFrame, clientServerConnection); @@ -379,7 +360,7 @@ private Mono accept(ServerSetup serverSetup, ByteBuf startFrame, DuplexCon }; } - private Mono acceptSetup(ServerSetup serverSetup, ByteBuf setupFrame, DuplexConnection clientServerConnection, int maxFrameLength) { + private Mono acceptSetup(ServerSetup serverSetup, ByteBuf setupFrame, Connection clientServerConnection, int maxFrameLength) { if (!SetupFrameCodec.isSupportedVersion(setupFrame)) { serverSetup.sendError(clientServerConnection, new InvalidSetupException( "Unsupported version: " + SetupFrameCodec.humanReadableVersion(setupFrame))); @@ -392,10 +373,10 @@ private Mono acceptSetup(ServerSetup serverSetup, ByteBuf setupFrame, Dupl return clientServerConnection.onClose(); } - return serverSetup.acceptRSocketSetup(setupFrame, clientServerConnection, (keepAliveHandler, wrappedDuplexConnection) -> { + return serverSetup.acceptChannelSetup(setupFrame, clientServerConnection, (keepAliveHandler, wrappedConnection) -> { final InitializingInterceptorRegistry interceptors = this.interceptors; final ConnectionSetupPayload setupPayload = new DefaultConnectionSetupPayload(setupFrame.retain()); - final ClientServerInputMultiplexer multiplexer = new ClientServerInputMultiplexer(wrappedDuplexConnection, interceptors, false); + final ClientServerInputMultiplexer multiplexer = new ClientServerInputMultiplexer(wrappedConnection, interceptors, false); final LeaseSpec leases; final RequesterLeaseTracker requesterLeaseTracker; if (leaseEnabled) { @@ -411,46 +392,43 @@ private Mono acceptSetup(ServerSetup serverSetup, ByteBuf setupFrame, Dupl final Sinks.Empty requesterOnAllClosedSink = Sinks.unsafe().empty(); final Sinks.Empty responderOnAllClosedSink = Sinks.unsafe().empty(); - Channel channelRequester = new ChannelRequester(multiplexer.asServerConnection(), payloadDecoder, StreamIdProvider.forServer(), + Channel requesterChannel = new RequesterChannel(multiplexer.asServerConnection(), payloadDecoder, StreamIdProvider.forServer(), mtu, maxFrameLength, maxInboundPayloadSize, setupPayload.keepAliveInterval(), setupPayload.keepAliveMaxLifetime(), keepAliveHandler, interceptors::initRequesterRequestInterceptor, requesterLeaseTracker, requesterOnAllClosedSink, Mono.whenDelayError(responderOnAllClosedSink.asMono(), requesterOnAllClosedSink.asMono())); - Channel wrappedChannelRequester = interceptors.decorateRequester(channelRequester); + Channel wrappedChannelRequester = interceptors.decorateRequester(requesterChannel); return interceptors .decorateAcceptor(acceptor) .accept(setupPayload, wrappedChannelRequester) - .onErrorResume(err -> Mono.fromRunnable(() -> serverSetup.sendError( - wrappedDuplexConnection, rejectedSetupError(err))) - .then(wrappedDuplexConnection.onClose()) + .onErrorResume(err -> Mono.fromRunnable(() -> serverSetup.sendError(wrappedConnection, rejectedSetupError(err))) + .then(wrappedConnection.onClose()) .then(Mono.error(err))) - .doOnNext(rSocketHandler -> { - Channel wrappedChannelHandler = interceptors.decorateResponder(rSocketHandler); - DuplexConnection clientConnection = multiplexer.asClientConnection(); + .doOnNext(channelHandler -> { + Channel wrappedChannelHandler = interceptors.decorateResponder(channelHandler); + Connection clientConnection = multiplexer.asClientConnection(); ResponderLeaseTracker responderLeaseTracker = leaseEnabled ? new ResponderLeaseTracker(SERVER_TAG, clientConnection, leases.sender) : null; - Channel channelResponder = new ChannelResponder(clientConnection, wrappedChannelHandler, payloadDecoder, responderLeaseTracker, - mtu, maxFrameLength, maxInboundPayloadSize, - leaseEnabled && leases.sender instanceof TrackingLeaseSender - ? rSocket -> interceptors.initResponderRequestInterceptor(rSocket, (TrackingLeaseSender) leases.sender) - : interceptors::initResponderRequestInterceptor, responderOnAllClosedSink); + new ResponderChannel(clientConnection, wrappedChannelHandler, payloadDecoder, responderLeaseTracker, + mtu, maxFrameLength, maxInboundPayloadSize, leaseEnabled && leases.sender instanceof TrackingLeaseSender + ? channel -> interceptors.initResponderRequestInterceptor(channel, (TrackingLeaseSender) leases.sender) + : interceptors::initResponderRequestInterceptor, responderOnAllClosedSink); }) .doFinally(signalType -> setupPayload.release()) .then(); }); } - private ServerSetup serverSetup(Duration timeout) { - return resume != null ? createSetup(timeout) : new ServerSetup.DefaultServerSetup(timeout); - } - - ServerSetup createSetup(Duration timeout) { - return new ServerSetup.ResumableServerSetup(timeout, new SessionManager(), resume.getSessionDuration(), - resume.getStreamTimeout(), resume.getStoreFactory(SERVER_TAG), resume.isCleanupStoreOnKeepAlive()); + private ServerSetup createSetup(Duration timeout) { + if (resume == null) { + return new ServerSetup.DefaultServerSetup(timeout); + } + return new ServerSetup.ResumableServerSetup(timeout, new SessionManager(), resume.sessionDuration, + resume.streamTimeout, resume.getStoreFactory(SERVER_TAG), resume.cleanupStoreOnKeepAlive); } private ProtocolErrorException rejectedSetupError(Throwable err) { @@ -458,4 +436,31 @@ private ProtocolErrorException rejectedSetupError(Throwable err) { return new RejectedSetupException(msg == null ? "rejected by server acceptor" : msg); } + //--------------------------------------------------------------------- + // Static Factory Methods + //--------------------------------------------------------------------- + + /** + * Static factory method to create an {@code RemotingServer}. + */ + public static RemotingServer create() { + return new RemotingServer(); + } + + /** + * Static factory method to create an {@code RemotingServer} instance with the given {@code + * ChannelAcceptor}. Effectively a shortcut for: + * + *

    {@code
    +   * RemotingServer.create().acceptor(...);
    +   * }
    + * + * @param acceptor the acceptor to handle connections with + * @return the same instance for method chaining + * @see #acceptor(ChannelAcceptor) + */ + public static RemotingServer create(ChannelAcceptor acceptor) { + return RemotingServer.create().acceptor(acceptor); + } + } diff --git a/today-remoting/src/main/java/infra/remoting/core/RequestChannelRequesterFlux.java b/today-remoting/src/main/java/infra/remoting/core/RequestChannelRequesterFlux.java index 0af1515..d493b31 100644 --- a/today-remoting/src/main/java/infra/remoting/core/RequestChannelRequesterFlux.java +++ b/today-remoting/src/main/java/infra/remoting/core/RequestChannelRequesterFlux.java @@ -1,21 +1,22 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import org.reactivestreams.Subscription; @@ -23,16 +24,13 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.atomic.AtomicLongFieldUpdater; -import infra.lang.NonNull; -import infra.lang.Nullable; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.Payload; import infra.remoting.frame.CancelFrameCodec; import infra.remoting.frame.ErrorFrameCodec; import infra.remoting.frame.FrameType; import infra.remoting.frame.PayloadFrameCodec; import infra.remoting.frame.RequestNFrameCodec; -import infra.remoting.frame.decoder.PayloadDecoder; import infra.remoting.plugins.RequestInterceptor; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; @@ -71,26 +69,14 @@ import static infra.remoting.core.StateUtils.markTerminated; final class RequestChannelRequesterFlux extends Flux - implements RequesterFrameHandler, - LeasePermitHandler, - CoreSubscriber, - Subscription, - Scannable { - - final ByteBufAllocator allocator; - final int mtu; - final int maxFrameLength; - final int maxInboundPayloadSize; - final RequesterResponderSupport requesterResponderSupport; - final DuplexConnection connection; - final PayloadDecoder payloadDecoder; + implements RequesterFrameHandler, LeasePermitHandler, CoreSubscriber, Subscription, Scannable { + + final ChannelSupport channel; final Publisher payloadsPublisher; @Nullable final RequesterLeaseTracker requesterLeaseTracker; - @Nullable - final RequestInterceptor requestInterceptor; volatile long state; static final AtomicLongFieldUpdater STATE = @@ -113,18 +99,10 @@ final class RequestChannelRequesterFlux extends Flux CompositeByteBuf frames; - RequestChannelRequesterFlux( - Publisher payloadsPublisher, RequesterResponderSupport requesterResponderSupport) { - this.allocator = requesterResponderSupport.getAllocator(); + RequestChannelRequesterFlux(Publisher payloadsPublisher, ChannelSupport channel) { this.payloadsPublisher = payloadsPublisher; - this.mtu = requesterResponderSupport.getMtu(); - this.maxFrameLength = requesterResponderSupport.getMaxFrameLength(); - this.maxInboundPayloadSize = requesterResponderSupport.getMaxInboundPayloadSize(); - this.requesterResponderSupport = requesterResponderSupport; - this.connection = requesterResponderSupport.getDuplexConnection(); - this.payloadDecoder = requesterResponderSupport.getPayloadDecoder(); - this.requesterLeaseTracker = requesterResponderSupport.getRequesterLeaseTracker(); - this.requestInterceptor = requesterResponderSupport.getRequestInterceptor(); + this.channel = channel; + this.requesterLeaseTracker = channel.getRequesterLeaseTracker(); } @Override @@ -135,7 +113,7 @@ public void subscribe(CoreSubscriber actual) { if (isSubscribedOrTerminated(previousState)) { final IllegalStateException e = new IllegalStateException("RequestChannelFlux allows only a single Subscriber"); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_CHANNEL, null); } @@ -173,8 +151,8 @@ public final void request(long n) { if (isFirstFrameSent(previousState) && !isMaxAllowedRequestN(extractRequestN(previousState))) { final int streamId = this.streamId; - final ByteBuf requestNFrame = RequestNFrameCodec.encode(this.allocator, streamId, n); - this.connection.sendFrame(streamId, requestNFrame); + final ByteBuf requestNFrame = RequestNFrameCodec.encode(channel.allocator, streamId, n); + channel.connection.sendFrame(streamId, requestNFrame); } return; } @@ -240,9 +218,9 @@ public boolean handlePermit() { } void sendFirstPayload(Payload firstPayload, long initialRequestN, boolean completed) { - int mtu = this.mtu; + int mtu = channel.mtu; try { - if (!isValid(mtu, this.maxFrameLength, firstPayload, true)) { + if (!isValid(mtu, channel.maxFrameLength, firstPayload, true)) { final long previousState = markTerminated(STATE, this); if (isTerminated(previousState)) { @@ -253,10 +231,8 @@ void sendFirstPayload(Payload firstPayload, long initialRequestN, boolean comple this.outboundSubscription.cancel(); } - final IllegalArgumentException e = - new IllegalArgumentException( - String.format(INVALID_PAYLOAD_ERROR_MESSAGE, this.maxFrameLength)); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final IllegalArgumentException e = new IllegalArgumentException(String.format(INVALID_PAYLOAD_ERROR_MESSAGE, channel.maxFrameLength)); + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_CHANNEL, firstPayload.metadata()); } @@ -280,7 +256,7 @@ void sendFirstPayload(Payload firstPayload, long initialRequestN, boolean comple this.outboundSubscription.cancel(); } - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_CHANNEL, null); } @@ -290,13 +266,13 @@ void sendFirstPayload(Payload firstPayload, long initialRequestN, boolean comple return; } - final RequesterResponderSupport sm = this.requesterResponderSupport; - final DuplexConnection connection = this.connection; - final ByteBufAllocator allocator = this.allocator; + final ChannelSupport channel = this.channel; + final Connection connection = channel.connection; + final ByteBufAllocator allocator = channel.allocator; final int streamId; try { - streamId = sm.addAndGetNextStreamId(this); + streamId = channel.addAndGetNextStreamId(this); this.streamId = streamId; } catch (Throwable t) { @@ -314,7 +290,7 @@ void sendFirstPayload(Payload firstPayload, long initialRequestN, boolean comple } final Throwable ut = Exceptions.unwrap(t); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = this.channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(ut, FrameType.REQUEST_CHANNEL, firstPayload.metadata()); } @@ -325,7 +301,7 @@ void sendFirstPayload(Payload firstPayload, long initialRequestN, boolean comple return; } - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = this.channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onStart(streamId, FrameType.REQUEST_CHANNEL, firstPayload.metadata()); } @@ -351,7 +327,7 @@ void sendFirstPayload(Payload firstPayload, long initialRequestN, boolean comple return; } - sm.remove(streamId, this); + channel.remove(streamId, this); if (!isOutboundTerminated(previousState)) { this.outboundSubscription.cancel(); @@ -377,7 +353,7 @@ void sendFirstPayload(Payload firstPayload, long initialRequestN, boolean comple return; } - sm.remove(streamId, this); + channel.remove(streamId, this); // 2) SendFirst is called asynchronously on the connection event-loop. Thus, we // need to check if outbound error is present. Note, we check outboundError since @@ -411,7 +387,7 @@ void sendFirstPayload(Payload firstPayload, long initialRequestN, boolean comple } if (!completed && isOutboundTerminated(previousState)) { - final ByteBuf completeFrame = PayloadFrameCodec.encodeComplete(this.allocator, streamId); + final ByteBuf completeFrame = PayloadFrameCodec.encodeComplete(this.channel.allocator, streamId); connection.sendFrame(streamId, completeFrame); } @@ -435,15 +411,13 @@ void sendFirstPayload(Payload firstPayload, long initialRequestN, boolean comple final void sendFollowingPayload(Payload followingPayload) { int streamId = this.streamId; - int mtu = this.mtu; + int mtu = channel.mtu; try { - if (!isValid(mtu, this.maxFrameLength, followingPayload, true)) { + if (!isValid(mtu, channel.maxFrameLength, followingPayload, true)) { followingPayload.release(); - final IllegalArgumentException e = - new IllegalArgumentException( - String.format(INVALID_PAYLOAD_ERROR_MESSAGE, this.maxFrameLength)); + final IllegalArgumentException e = new IllegalArgumentException(String.format(INVALID_PAYLOAD_ERROR_MESSAGE, channel.maxFrameLength)); if (!this.tryCancel()) { Operators.onErrorDropped(e, this.inboundSubscriber.currentContext()); return; @@ -473,8 +447,8 @@ final void sendFollowingPayload(Payload followingPayload) { FrameType.NEXT, mtu, followingPayload, - this.connection, - allocator, + channel.connection, + channel.allocator, true); } catch (Throwable e) { @@ -493,7 +467,7 @@ void propagateErrorSafely(Throwable t) { if (!this.inboundDone) { synchronized(this) { if (!this.inboundDone) { - final RequestInterceptor interceptor = requestInterceptor; + final RequestInterceptor interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(this.streamId, FrameType.REQUEST_CHANNEL, t); } @@ -517,7 +491,7 @@ public final void cancel() { return; } - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onCancel(this.streamId, FrameType.REQUEST_CHANNEL); } @@ -546,10 +520,11 @@ boolean tryCancel() { final boolean firstFrameSent = isFirstFrameSent(previousState); if (firstFrameSent) { final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + final ChannelSupport channel = this.channel; + channel.remove(streamId, this); - final ByteBuf cancelFrame = CancelFrameCodec.encode(this.allocator, streamId); - this.connection.sendFrame(streamId, cancelFrame); + final ByteBuf cancelFrame = CancelFrameCodec.encode(channel.allocator, streamId); + channel.connection.sendFrame(streamId, cancelFrame); } return firstFrameSent; @@ -595,16 +570,17 @@ else if (!isReadyToSendFirstFrame(previousState)) { if (isFirstFrameSent(previousState)) { final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + final ChannelSupport channel = this.channel; + channel.remove(streamId, this); // propagates error to remote responder - final ByteBuf errorFrame = ErrorFrameCodec.encode(this.allocator, streamId, t); - this.connection.sendFrame(streamId, errorFrame); + final ByteBuf errorFrame = ErrorFrameCodec.encode(channel.allocator, streamId, t); + channel.connection.sendFrame(streamId, errorFrame); if (!isInboundTerminated(previousState)) { // FIXME: must be scheduled on the connection event-loop to achieve serial // behaviour on the inbound subscriber synchronized(this) { - final RequestInterceptor interceptor = requestInterceptor; + final RequestInterceptor interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(streamId, FrameType.REQUEST_CHANNEL, t); } @@ -641,14 +617,15 @@ public void onComplete() { } final int streamId = this.streamId; - final ByteBuf completeFrame = PayloadFrameCodec.encodeComplete(this.allocator, streamId); + final ChannelSupport channel = this.channel; + final ByteBuf completeFrame = PayloadFrameCodec.encodeComplete(channel.allocator, streamId); - this.connection.sendFrame(streamId, completeFrame); + channel.connection.sendFrame(streamId, completeFrame); if (isInboundTerminated(previousState)) { - this.requesterResponderSupport.remove(streamId, this); + channel.remove(streamId, this); - final RequestInterceptor interceptor = requestInterceptor; + final RequestInterceptor interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(streamId, FrameType.REQUEST_CHANNEL, null); } @@ -669,9 +646,9 @@ public final void handleComplete() { } if (isOutboundTerminated(previousState)) { - this.requesterResponderSupport.remove(this.streamId, this); + this.channel.remove(this.streamId, this); - final RequestInterceptor interceptor = this.requestInterceptor; + final RequestInterceptor interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(this.streamId, FrameType.REQUEST_CHANNEL, null); } @@ -695,7 +672,7 @@ public final void handlePermitError(Throwable cause) { } final Payload p = this.firstPayload; - final RequestInterceptor interceptor = requestInterceptor; + final RequestInterceptor interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onReject(cause, FrameType.REQUEST_CHANNEL, p.metadata()); } @@ -726,9 +703,9 @@ public final void handleError(Throwable cause) { ReassemblyUtils.release(this, previousState); final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + this.channel.remove(streamId, this); - final RequestInterceptor interceptor = requestInterceptor; + final RequestInterceptor interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(streamId, FrameType.REQUEST_CHANNEL, cause); } @@ -754,7 +731,7 @@ public final void handlePayload(Payload value) { final Throwable cause = Exceptions.failWithOverflow( "The number of messages received exceeds the number requested"); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onTerminate(streamId, FrameType.REQUEST_CHANNEL, cause); } @@ -787,13 +764,13 @@ public void handleCancel() { final boolean inboundTerminated = isInboundTerminated(previousState); if (inboundTerminated) { - this.requesterResponderSupport.remove(this.streamId, this); + this.channel.remove(this.streamId, this); } this.outboundSubscription.cancel(); if (inboundTerminated) { - final RequestInterceptor interceptor = requestInterceptor; + final RequestInterceptor interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(this.streamId, FrameType.REQUEST_CHANNEL, null); } @@ -807,16 +784,15 @@ public void handleNext(ByteBuf frame, boolean hasFollows, boolean isLastPayload) this, this, this.inboundSubscriber, - this.payloadDecoder, - this.allocator, - this.maxInboundPayloadSize, + channel.payloadDecoder, + channel.allocator, + channel.maxInboundPayloadSize, frame, hasFollows, isLastPayload); } @Override - @NonNull public Context currentContext() { long state = this.state; @@ -858,7 +834,6 @@ public Object scanUnsafe(Attr key) { } @Override - @NonNull public String stepName() { return "source(RequestChannelFlux)"; } diff --git a/today-remoting/src/main/java/infra/remoting/core/RequestChannelResponderSubscriber.java b/today-remoting/src/main/java/infra/remoting/core/RequestChannelResponderSubscriber.java index 62f516a..790c10c 100644 --- a/today-remoting/src/main/java/infra/remoting/core/RequestChannelResponderSubscriber.java +++ b/today-remoting/src/main/java/infra/remoting/core/RequestChannelResponderSubscriber.java @@ -1,19 +1,19 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; import org.reactivestreams.Subscription; @@ -22,20 +22,15 @@ import java.util.concurrent.atomic.AtomicLongFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import infra.lang.Nullable; -import infra.logging.Logger; -import infra.logging.LoggerFactory; import infra.remoting.Channel; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.Payload; -import infra.remoting.exceptions.CanceledException; +import infra.remoting.error.CanceledException; import infra.remoting.frame.CancelFrameCodec; import infra.remoting.frame.ErrorFrameCodec; import infra.remoting.frame.FrameType; import infra.remoting.frame.PayloadFrameCodec; import infra.remoting.frame.RequestNFrameCodec; -import infra.remoting.frame.decoder.PayloadDecoder; -import infra.remoting.plugins.RequestInterceptor; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.CompositeByteBuf; @@ -69,23 +64,15 @@ import static infra.remoting.core.StateUtils.markTerminated; import static reactor.core.Exceptions.TERMINATED; -final class RequestChannelResponderSubscriber extends Flux - implements ResponderFrameHandler, Subscription, CoreSubscriber { - - static final Logger logger = LoggerFactory.getLogger(RequestChannelResponderSubscriber.class); +final class RequestChannelResponderSubscriber extends Flux implements ResponderFrameHandler, Subscription, CoreSubscriber { final int streamId; - final ByteBufAllocator allocator; - final PayloadDecoder payloadDecoder; - final int mtu; - final int maxFrameLength; - final int maxInboundPayloadSize; - final RequesterResponderSupport requesterResponderSupport; - final DuplexConnection connection; - final long firstRequest; - @Nullable - final RequestInterceptor requestInterceptor; + final ChannelSupport channel; + + final Connection connection; + + final long firstRequest; final Channel handler; @@ -111,44 +98,24 @@ final class RequestChannelResponderSubscriber extends Flux long requested; long produced; - public RequestChannelResponderSubscriber( - int streamId, - long firstRequestN, - ByteBuf firstFrame, - RequesterResponderSupport requesterResponderSupport, - Channel handler) { + public RequestChannelResponderSubscriber(int streamId, long firstRequestN, + ByteBuf firstFrame, ChannelSupport channel, Channel handler) { this.streamId = streamId; - this.allocator = requesterResponderSupport.getAllocator(); - this.mtu = requesterResponderSupport.getMtu(); - this.maxFrameLength = requesterResponderSupport.getMaxFrameLength(); - this.maxInboundPayloadSize = requesterResponderSupport.getMaxInboundPayloadSize(); - this.requesterResponderSupport = requesterResponderSupport; - this.connection = requesterResponderSupport.getDuplexConnection(); - this.payloadDecoder = requesterResponderSupport.getPayloadDecoder(); - this.requestInterceptor = requesterResponderSupport.getRequestInterceptor(); + this.channel = channel; + this.connection = channel.getConnection(); this.handler = handler; this.firstRequest = firstRequestN; - this.frames = - ReassemblyUtils.addFollowingFrame( - allocator.compositeBuffer(), firstFrame, true, maxInboundPayloadSize); + this.frames = ReassemblyUtils.addFollowingFrame( + channel.allocator.compositeBuffer(), firstFrame, true, channel.maxInboundPayloadSize); STATE.lazySet(this, REASSEMBLING_FLAG); } - public RequestChannelResponderSubscriber( - int streamId, - long firstRequestN, - Payload firstPayload, - RequesterResponderSupport requesterResponderSupport) { + public RequestChannelResponderSubscriber(int streamId, long firstRequestN, + Payload firstPayload, ChannelSupport channel) { this.streamId = streamId; - this.allocator = requesterResponderSupport.getAllocator(); - this.mtu = requesterResponderSupport.getMtu(); - this.maxFrameLength = requesterResponderSupport.getMaxFrameLength(); - this.maxInboundPayloadSize = requesterResponderSupport.getMaxInboundPayloadSize(); - this.requesterResponderSupport = requesterResponderSupport; - this.connection = requesterResponderSupport.getDuplexConnection(); - this.payloadDecoder = requesterResponderSupport.getPayloadDecoder(); - this.requestInterceptor = requesterResponderSupport.getRequestInterceptor(); + this.channel = channel; + this.connection = channel.getConnection(); this.firstRequest = firstRequestN; this.firstPayload = firstPayload; @@ -168,16 +135,13 @@ public void subscribe(CoreSubscriber actual) { Operators.error(actual, t); } else { - Operators.error( - actual, - new CancellationException("RequestChannelSubscriber has already been terminated")); + Operators.error(actual, new CancellationException("RequestChannelSubscriber has already been terminated")); } return; } if (isSubscribed(previousState)) { - Operators.error( - actual, new IllegalStateException("RequestChannelSubscriber allows only one Subscriber")); + Operators.error(actual, new IllegalStateException("RequestChannelSubscriber allows only one Subscriber")); return; } @@ -259,7 +223,7 @@ public void request(long n) { if (isFirstFrameSent(previousState) && !isMaxAllowedRequestN(StateUtils.extractRequestN(previousState))) { final int streamId = this.streamId; - final ByteBuf requestNFrame = RequestNFrameCodec.encode(this.allocator, streamId, n); + final ByteBuf requestNFrame = RequestNFrameCodec.encode(channel.allocator, streamId, n); this.connection.sendFrame(streamId, requestNFrame); } return; @@ -305,7 +269,7 @@ else if (this.inboundDone) { long requestN = StateUtils.extractRequestN(previousState); if (isMaxAllowedRequestN(requestN)) { final int streamId = this.streamId; - final ByteBuf requestNFrame = RequestNFrameCodec.encode(allocator, streamId, requestN); + final ByteBuf requestNFrame = RequestNFrameCodec.encode(channel.allocator, streamId, requestN); this.connection.sendFrame(streamId, requestNFrame); } else { @@ -313,7 +277,7 @@ else if (this.inboundDone) { if (firstRequestN > 0) { final int streamId = this.streamId; final ByteBuf requestNFrame = - RequestNFrameCodec.encode(this.allocator, streamId, firstRequestN); + RequestNFrameCodec.encode(channel.allocator, streamId, firstRequestN); this.connection.sendFrame(streamId, requestNFrame); } } @@ -338,14 +302,14 @@ public void cancel() { final boolean isOutboundTerminated = isOutboundTerminated(previousState); if (isOutboundTerminated) { - this.requesterResponderSupport.remove(streamId, this); + channel.remove(streamId, this); } - final ByteBuf cancelFrame = CancelFrameCodec.encode(this.allocator, streamId); + final ByteBuf cancelFrame = CancelFrameCodec.encode(channel.allocator, streamId); this.connection.sendFrame(streamId, cancelFrame); if (isOutboundTerminated) { - final RequestInterceptor interceptor = requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(streamId, FrameType.REQUEST_CHANNEL, null); } @@ -360,7 +324,7 @@ public final void handleCancel() { // and fragmentation of the first frame was cancelled before lazyTerminate(STATE, this); - this.requesterResponderSupport.remove(this.streamId, this); + this.channel.remove(this.streamId, this); final CompositeByteBuf frames = this.frames; if (frames != null) { @@ -373,7 +337,7 @@ public final void handleCancel() { firstPayload.release(); } - final RequestInterceptor interceptor = this.requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onCancel(this.streamId, FrameType.REQUEST_CHANNEL); } @@ -385,7 +349,7 @@ public final void handleCancel() { return; } - final RequestInterceptor interceptor = this.requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onCancel(this.streamId, FrameType.REQUEST_CHANNEL); } @@ -400,7 +364,7 @@ final long tryTerminate(boolean isFromInbound) { return previousState; } - this.requesterResponderSupport.remove(this.streamId, this); + this.channel.remove(this.streamId, this); if (isReassembling(previousState)) { final CompositeByteBuf frames = this.frames; @@ -475,12 +439,12 @@ final void handlePayload(Payload p) { return; } - this.requesterResponderSupport.remove(this.streamId, this); + this.channel.remove(this.streamId, this); this.connection.sendFrame( streamId, ErrorFrameCodec.encode( - this.allocator, streamId, new CanceledException(cause.getMessage()))); + channel.allocator, streamId, new CanceledException(cause.getMessage()))); if (!isSubscribed(previousState)) { final Payload firstPayload = this.firstPayload; @@ -500,7 +464,7 @@ else if (isFirstFrameSent(previousState) && !isInboundTerminated(previousState)) // needs for disconnected upstream and downstream case this.outboundSubscription.cancel(); - final RequestInterceptor interceptor = requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(this.streamId, FrameType.REQUEST_CHANNEL, cause); } @@ -531,7 +495,7 @@ public final void handleError(Throwable t) { return; } - this.requesterResponderSupport.remove(this.streamId, this); + this.channel.remove(this.streamId, this); if (isReassembling(previousState)) { final CompositeByteBuf frames = this.frames; @@ -557,7 +521,7 @@ else if (isFirstFrameSent(previousState) && !isInboundTerminated(previousState)) // needs for disconnected upstream and downstream case this.outboundSubscription.cancel(); - final RequestInterceptor interceptor = requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(this.streamId, FrameType.REQUEST_CHANNEL, t); } @@ -575,7 +539,7 @@ public void handleComplete() { final boolean isOutboundTerminated = isOutboundTerminated(previousState); if (isOutboundTerminated) { - this.requesterResponderSupport.remove(this.streamId, this); + this.channel.remove(this.streamId, this); } if (isFirstFrameSent(previousState)) { @@ -583,7 +547,7 @@ public void handleComplete() { } if (isOutboundTerminated) { - final RequestInterceptor interceptor = this.requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(this.streamId, FrameType.REQUEST_CHANNEL, null); } @@ -600,7 +564,7 @@ public void handleNext(ByteBuf frame, boolean hasFollows, boolean isLastPayload) if (!hasFollows && !isReassembling(state)) { Payload payload; try { - payload = this.payloadDecoder.apply(frame); + payload = channel.payloadDecoder.decode(frame); } catch (Throwable t) { long previousState = this.tryTerminate(true); @@ -609,7 +573,7 @@ public void handleNext(ByteBuf frame, boolean hasFollows, boolean isLastPayload) return; } else if (isOutboundTerminated(previousState)) { - final RequestInterceptor interceptor = this.requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(this.streamId, FrameType.REQUEST_CHANNEL, t); } @@ -622,10 +586,10 @@ else if (isOutboundTerminated(previousState)) { // send error to terminate interaction final int streamId = this.streamId; final ByteBuf errorFrame = - ErrorFrameCodec.encode(this.allocator, streamId, new CanceledException(t.getMessage())); + ErrorFrameCodec.encode(channel.allocator, streamId, new CanceledException(t.getMessage())); this.connection.sendFrame(streamId, errorFrame); - final RequestInterceptor interceptor = requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(streamId, FrameType.REQUEST_CHANNEL, t); } @@ -641,9 +605,8 @@ else if (isOutboundTerminated(previousState)) { CompositeByteBuf frames = this.frames; if (frames == null) { - frames = - ReassemblyUtils.addFollowingFrame( - this.allocator.compositeBuffer(), frame, hasFollows, this.maxInboundPayloadSize); + frames = ReassemblyUtils.addFollowingFrame( + channel.allocator.compositeBuffer(), frame, hasFollows, channel.maxInboundPayloadSize); this.frames = frames; long previousState = markReassembling(STATE, this); @@ -655,9 +618,7 @@ else if (isOutboundTerminated(previousState)) { } else { try { - frames = - ReassemblyUtils.addFollowingFrame( - frames, frame, hasFollows, this.maxInboundPayloadSize); + frames = ReassemblyUtils.addFollowingFrame(frames, frame, hasFollows, channel.maxInboundPayloadSize); } catch (IllegalStateException e) { if (isTerminated(this.state)) { @@ -670,7 +631,7 @@ else if (isOutboundTerminated(previousState)) { return; } else if (isOutboundTerminated(previousState)) { - final RequestInterceptor interceptor = this.requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(this.streamId, FrameType.REQUEST_CHANNEL, e); } @@ -682,14 +643,11 @@ else if (isOutboundTerminated(previousState)) { this.outboundDone = true; // send error to terminate interaction final int streamId = this.streamId; - final ByteBuf errorFrame = - ErrorFrameCodec.encode( - this.allocator, - streamId, - new CanceledException("Failed to reassemble payload. Cause: " + e.getMessage())); - this.connection.sendFrame(streamId, errorFrame); + final ByteBuf errorFrame = ErrorFrameCodec.encode(channel.allocator, streamId, + new CanceledException("Failed to reassemble payload. Cause: " + e.getMessage())); + connection.sendFrame(streamId, errorFrame); - final RequestInterceptor interceptor = this.requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(streamId, FrameType.REQUEST_CHANNEL, e); } @@ -708,19 +666,19 @@ else if (isOutboundTerminated(previousState)) { Payload payload; try { - payload = this.payloadDecoder.apply(frames); + payload = channel.payloadDecoder.decode(frames); frames.release(); } catch (Throwable t) { ReferenceCountUtil.safeRelease(frames); - previousState = this.tryTerminate(true); + previousState = tryTerminate(true); if (isTerminated(previousState)) { Operators.onErrorDropped(t, this.inboundSubscriber.currentContext()); return; } else if (isOutboundTerminated(previousState)) { - final RequestInterceptor interceptor = this.requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(this.streamId, FrameType.REQUEST_CHANNEL, t); } @@ -731,14 +689,11 @@ else if (isOutboundTerminated(previousState)) { // send error to terminate interaction final int streamId = this.streamId; - final ByteBuf errorFrame = - ErrorFrameCodec.encode( - this.allocator, - streamId, - new CanceledException("Failed to reassemble payload. Cause: " + t.getMessage())); + final ByteBuf errorFrame = ErrorFrameCodec.encode(channel.allocator, streamId, + new CanceledException("Failed to reassemble payload. Cause: " + t.getMessage())); this.connection.sendFrame(streamId, errorFrame); - final RequestInterceptor interceptor = requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(streamId, FrameType.REQUEST_CHANNEL, t); } @@ -769,12 +724,12 @@ public void onNext(Payload p) { } final int streamId = this.streamId; - final DuplexConnection connection = this.connection; - final ByteBufAllocator allocator = this.allocator; + final Connection connection = channel.connection; + final ByteBufAllocator allocator = channel.allocator; - final int mtu = this.mtu; + final int mtu = channel.mtu; try { - if (!isValid(mtu, this.maxFrameLength, p, false)) { + if (!isValid(mtu, channel.maxFrameLength, p, false)) { p.release(); // FIXME: must be scheduled on the connection event-loop to achieve serial @@ -783,16 +738,16 @@ public void onNext(Payload p) { if (isTerminated(previousState)) { Operators.onErrorDropped( new IllegalArgumentException( - String.format(INVALID_PAYLOAD_ERROR_MESSAGE, this.maxFrameLength)), + String.format(INVALID_PAYLOAD_ERROR_MESSAGE, channel.maxFrameLength)), this.inboundSubscriber.currentContext()); return; } else if (isOutboundTerminated(previousState)) { final IllegalArgumentException e = new IllegalArgumentException( - String.format(INVALID_PAYLOAD_ERROR_MESSAGE, this.maxFrameLength)); + String.format(INVALID_PAYLOAD_ERROR_MESSAGE, channel.maxFrameLength)); - final RequestInterceptor interceptor = this.requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(streamId, FrameType.REQUEST_CHANNEL, e); } @@ -803,11 +758,11 @@ else if (isOutboundTerminated(previousState)) { final CanceledException e = new CanceledException( - String.format(INVALID_PAYLOAD_ERROR_MESSAGE, this.maxFrameLength)); + String.format(INVALID_PAYLOAD_ERROR_MESSAGE, channel.maxFrameLength)); final ByteBuf errorFrame = ErrorFrameCodec.encode(allocator, streamId, e); connection.sendFrame(streamId, errorFrame); - final RequestInterceptor interceptor = this.requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(streamId, FrameType.REQUEST_CHANNEL, e); } @@ -824,7 +779,7 @@ else if (isOutboundTerminated(previousState)) { return; } else if (isOutboundTerminated(previousState)) { - final RequestInterceptor interceptor = this.requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(streamId, FrameType.REQUEST_CHANNEL, e); } @@ -833,14 +788,11 @@ else if (isOutboundTerminated(previousState)) { return; } - final ByteBuf errorFrame = - ErrorFrameCodec.encode( - allocator, - streamId, - new CanceledException("Failed to validate payload. Cause:" + e.getMessage())); + final ByteBuf errorFrame = ErrorFrameCodec.encode(allocator, streamId, + new CanceledException("Failed to validate payload. Cause:" + e.getMessage())); connection.sendFrame(streamId, errorFrame); - final RequestInterceptor interceptor = requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(streamId, FrameType.REQUEST_CHANNEL, e); } @@ -854,7 +806,7 @@ else if (isOutboundTerminated(previousState)) { // FIXME: must be scheduled on the connection event-loop to achieve serial // behaviour on the inbound subscriber long previousState = this.tryTerminate(false); - final RequestInterceptor interceptor = requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null && !isTerminated(previousState)) { interceptor.onTerminate(streamId, FrameType.REQUEST_CHANNEL, t); } @@ -868,11 +820,8 @@ public void onError(Throwable t) { return; } - boolean wasThrowableAdded = - Exceptions.addThrowable( - INBOUND_ERROR, - this, - new CancellationException("Outbound has terminated with an error")); + boolean wasThrowableAdded = Exceptions.addThrowable(INBOUND_ERROR, this, + new CancellationException("Outbound has terminated with an error")); this.outboundDone = true; long previousState = markTerminated(STATE, this); @@ -883,7 +832,7 @@ public void onError(Throwable t) { final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + this.channel.remove(streamId, this); if (isReassembling(previousState)) { final CompositeByteBuf frames = this.frames; @@ -913,10 +862,10 @@ && isFirstFrameSent(previousState) } } - final ByteBuf errorFrame = ErrorFrameCodec.encode(this.allocator, streamId, t); + final ByteBuf errorFrame = ErrorFrameCodec.encode(channel.allocator, streamId, t); this.connection.sendFrame(streamId, errorFrame); - final RequestInterceptor interceptor = this.requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(streamId, FrameType.REQUEST_CHANNEL, t); } @@ -939,14 +888,14 @@ public void onComplete() { final boolean isInboundTerminated = isInboundTerminated(previousState); if (isInboundTerminated) { - this.requesterResponderSupport.remove(streamId, this); + this.channel.remove(streamId, this); } - final ByteBuf completeFrame = PayloadFrameCodec.encodeComplete(this.allocator, streamId); - this.connection.sendFrame(streamId, completeFrame); + final ByteBuf completeFrame = PayloadFrameCodec.encodeComplete(channel.allocator, streamId); + connection.sendFrame(streamId, completeFrame); if (isInboundTerminated) { - final RequestInterceptor interceptor = this.requestInterceptor; + final var interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onTerminate(streamId, FrameType.REQUEST_CHANNEL, null); } diff --git a/today-remoting/src/main/java/infra/remoting/core/RequestResponseRequesterMono.java b/today-remoting/src/main/java/infra/remoting/core/RequestResponseRequesterMono.java index a1219e3..83d6256 100644 --- a/today-remoting/src/main/java/infra/remoting/core/RequestResponseRequesterMono.java +++ b/today-remoting/src/main/java/infra/remoting/core/RequestResponseRequesterMono.java @@ -1,32 +1,29 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Subscription; import java.util.concurrent.atomic.AtomicLongFieldUpdater; -import infra.lang.NonNull; -import infra.lang.Nullable; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.Payload; import infra.remoting.frame.CancelFrameCodec; import infra.remoting.frame.FrameType; -import infra.remoting.frame.decoder.PayloadDecoder; import infra.remoting.plugins.RequestInterceptor; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; @@ -57,19 +54,12 @@ final class RequestResponseRequesterMono extends Mono implements RequesterFrameHandler, LeasePermitHandler, Subscription, Scannable { - final ByteBufAllocator allocator; final Payload payload; - final int mtu; - final int maxFrameLength; - final int maxInboundPayloadSize; - final RequesterResponderSupport requesterResponderSupport; - final DuplexConnection connection; - final PayloadDecoder payloadDecoder; + + final ChannelSupport channel; @Nullable final RequesterLeaseTracker requesterLeaseTracker; - @Nullable - final RequestInterceptor requestInterceptor; volatile long state; static final AtomicLongFieldUpdater STATE = @@ -80,29 +70,20 @@ final class RequestResponseRequesterMono extends Mono CompositeByteBuf frames; boolean done; - RequestResponseRequesterMono( - Payload payload, RequesterResponderSupport requesterResponderSupport) { - - this.allocator = requesterResponderSupport.getAllocator(); + RequestResponseRequesterMono(Payload payload, ChannelSupport channel) { this.payload = payload; - this.mtu = requesterResponderSupport.getMtu(); - this.maxFrameLength = requesterResponderSupport.getMaxFrameLength(); - this.maxInboundPayloadSize = requesterResponderSupport.getMaxInboundPayloadSize(); - this.requesterResponderSupport = requesterResponderSupport; - this.connection = requesterResponderSupport.getDuplexConnection(); - this.payloadDecoder = requesterResponderSupport.getPayloadDecoder(); - this.requesterLeaseTracker = requesterResponderSupport.getRequesterLeaseTracker(); - this.requestInterceptor = requesterResponderSupport.getRequestInterceptor(); + this.channel = channel; + this.requesterLeaseTracker = channel.getRequesterLeaseTracker(); } @Override public void subscribe(CoreSubscriber actual) { - + ChannelSupport channel = this.channel; long previousState = markSubscribed(STATE, this); if (isSubscribedOrTerminated(previousState)) { final IllegalStateException e = new IllegalStateException("RequestResponseMono allows only a single " + "Subscriber"); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_RESPONSE, null); } @@ -113,13 +94,11 @@ public void subscribe(CoreSubscriber actual) { final Payload p = this.payload; try { - if (!isValid(this.mtu, this.maxFrameLength, p, false)) { + if (!isValid(channel.mtu, channel.maxFrameLength, p, false)) { lazyTerminate(STATE, this); - final IllegalArgumentException e = - new IllegalArgumentException( - String.format(INVALID_PAYLOAD_ERROR_MESSAGE, this.maxFrameLength)); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final IllegalArgumentException e = new IllegalArgumentException(String.format(INVALID_PAYLOAD_ERROR_MESSAGE, channel.maxFrameLength)); + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_RESPONSE, p.metadata()); } @@ -133,7 +112,7 @@ public void subscribe(CoreSubscriber actual) { catch (IllegalReferenceCountException e) { lazyTerminate(STATE, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_RESPONSE, null); } @@ -182,9 +161,9 @@ public boolean handlePermit() { void sendFirstPayload(Payload payload) { - final RequesterResponderSupport sm = this.requesterResponderSupport; - final DuplexConnection connection = this.connection; - final ByteBufAllocator allocator = this.allocator; + final ChannelSupport sm = this.channel; + final Connection connection = sm.connection; + final ByteBufAllocator allocator = sm.allocator; final int streamId; try { @@ -196,7 +175,7 @@ void sendFirstPayload(Payload payload) { final long previousState = markTerminated(STATE, this); final Throwable ut = Exceptions.unwrap(t); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(ut, FrameType.REQUEST_RESPONSE, payload.metadata()); } @@ -209,14 +188,14 @@ void sendFirstPayload(Payload payload) { return; } - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onStart(streamId, FrameType.REQUEST_RESPONSE, payload.metadata()); } try { sendReleasingPayload( - streamId, FrameType.REQUEST_RESPONSE, this.mtu, payload, connection, allocator, true); + streamId, FrameType.REQUEST_RESPONSE, sm.mtu, payload, connection, allocator, true); } catch (Throwable e) { this.done = true; @@ -258,13 +237,14 @@ public final void cancel() { if (isFirstFrameSent(previousState)) { final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + ChannelSupport channel = this.channel; + channel.remove(streamId, this); ReassemblyUtils.synchronizedRelease(this, previousState); - this.connection.sendFrame(streamId, CancelFrameCodec.encode(this.allocator, streamId)); + channel.connection.sendFrame(streamId, CancelFrameCodec.encode(channel.allocator, streamId)); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onCancel(streamId, FrameType.REQUEST_RESPONSE); } @@ -290,9 +270,9 @@ public final void handlePayload(Payload value) { } final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + this.channel.remove(streamId, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, null); } @@ -316,9 +296,9 @@ public final void handleComplete() { } final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + this.channel.remove(streamId, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, null); } @@ -337,7 +317,7 @@ public final void handlePermitError(Throwable cause) { } final Payload p = this.payload; - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(cause, FrameType.REQUEST_RESPONSE, p.metadata()); } @@ -364,9 +344,9 @@ public final void handleError(Throwable cause) { ReassemblyUtils.synchronizedRelease(this, previousState); final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + this.channel.remove(streamId, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, cause); } @@ -376,14 +356,13 @@ public final void handleError(Throwable cause) { @Override public void handleNext(ByteBuf frame, boolean hasFollows, boolean isLastPayload) { - handleNextSupport( - STATE, + handleNextSupport(STATE, this, this, this.actual, - this.payloadDecoder, - this.allocator, - this.maxInboundPayloadSize, + channel.payloadDecoder, + channel.allocator, + channel.maxInboundPayloadSize, frame, hasFollows, isLastPayload); @@ -395,7 +374,7 @@ public CompositeByteBuf getFrames() { } @Override - public void setFrames(CompositeByteBuf byteBuf) { + public void setFrames(@Nullable CompositeByteBuf byteBuf) { this.frames = byteBuf; } @@ -414,7 +393,6 @@ public Object scanUnsafe(Attr key) { } @Override - @NonNull public String stepName() { return "source(RequestResponseMono)"; } diff --git a/today-remoting/src/main/java/infra/remoting/core/RequestResponseResponderSubscriber.java b/today-remoting/src/main/java/infra/remoting/core/RequestResponseResponderSubscriber.java index e972f88..4d30603 100644 --- a/today-remoting/src/main/java/infra/remoting/core/RequestResponseResponderSubscriber.java +++ b/today-remoting/src/main/java/infra/remoting/core/RequestResponseResponderSubscriber.java @@ -1,36 +1,35 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Subscription; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import infra.lang.Nullable; import infra.logging.Logger; import infra.logging.LoggerFactory; import infra.remoting.Channel; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.Payload; -import infra.remoting.exceptions.CanceledException; +import infra.remoting.error.CanceledException; import infra.remoting.frame.ErrorFrameCodec; import infra.remoting.frame.FrameType; import infra.remoting.frame.PayloadFrameCodec; -import infra.remoting.frame.decoder.PayloadDecoder; import infra.remoting.plugins.RequestInterceptor; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; @@ -52,17 +51,10 @@ final class RequestResponseResponderSubscriber static final Logger logger = LoggerFactory.getLogger(RequestResponseResponderSubscriber.class); final int streamId; - final ByteBufAllocator allocator; - final PayloadDecoder payloadDecoder; - final int mtu; - final int maxFrameLength; - final int maxInboundPayloadSize; - final RequesterResponderSupport requesterResponderSupport; - final DuplexConnection connection; - final Channel handler; - @Nullable - final RequestInterceptor requestInterceptor; + final ChannelSupport channel; + + final Channel handler; boolean done; CompositeByteBuf frames; @@ -72,39 +64,18 @@ final class RequestResponseResponderSubscriber AtomicReferenceFieldUpdater.newUpdater( RequestResponseResponderSubscriber.class, Subscription.class, "s"); - public RequestResponseResponderSubscriber( - int streamId, - ByteBuf firstFrame, - RequesterResponderSupport requesterResponderSupport, - Channel handler) { + public RequestResponseResponderSubscriber(int streamId, ByteBuf firstFrame, ChannelSupport channel, Channel handler) { this.streamId = streamId; - this.allocator = requesterResponderSupport.getAllocator(); - this.mtu = requesterResponderSupport.getMtu(); - this.maxFrameLength = requesterResponderSupport.getMaxFrameLength(); - this.maxInboundPayloadSize = requesterResponderSupport.getMaxInboundPayloadSize(); - this.requesterResponderSupport = requesterResponderSupport; - this.connection = requesterResponderSupport.getDuplexConnection(); - this.payloadDecoder = requesterResponderSupport.getPayloadDecoder(); - this.requestInterceptor = requesterResponderSupport.getRequestInterceptor(); + this.channel = channel; this.handler = handler; - this.frames = - ReassemblyUtils.addFollowingFrame( - allocator.compositeBuffer(), firstFrame, true, maxInboundPayloadSize); + this.frames = ReassemblyUtils.addFollowingFrame( + channel.allocator.compositeBuffer(), firstFrame, true, channel.maxInboundPayloadSize); } - public RequestResponseResponderSubscriber( - int streamId, RequesterResponderSupport requesterResponderSupport) { + public RequestResponseResponderSubscriber(int streamId, ChannelSupport channel) { this.streamId = streamId; - this.allocator = requesterResponderSupport.getAllocator(); - this.mtu = requesterResponderSupport.getMtu(); - this.maxFrameLength = requesterResponderSupport.getMaxFrameLength(); - this.maxInboundPayloadSize = requesterResponderSupport.getMaxInboundPayloadSize(); - this.requesterResponderSupport = requesterResponderSupport; - this.connection = requesterResponderSupport.getDuplexConnection(); - this.requestInterceptor = requesterResponderSupport.getRequestInterceptor(); - - this.payloadDecoder = null; + this.channel = channel; this.handler = null; this.frames = null; } @@ -138,38 +109,37 @@ public void onNext(@Nullable Payload p) { this.done = true; final int streamId = this.streamId; - final DuplexConnection connection = this.connection; - final ByteBufAllocator allocator = this.allocator; + final ChannelSupport channel = this.channel; + final Connection connection = channel.connection; + final ByteBufAllocator allocator = channel.allocator; - this.requesterResponderSupport.remove(streamId, this); + channel.remove(streamId, this); if (p == null) { final ByteBuf completeFrame = PayloadFrameCodec.encodeComplete(allocator, streamId); connection.sendFrame(streamId, completeFrame); - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, null); + final RequestInterceptor interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, null); } return; } - final int mtu = this.mtu; + final int mtu = channel.mtu; try { - if (!isValid(mtu, this.maxFrameLength, p, false)) { + if (!isValid(mtu, channel.maxFrameLength, p, false)) { currentSubscription.cancel(); p.release(); - final CanceledException e = - new CanceledException( - String.format(INVALID_PAYLOAD_ERROR_MESSAGE, this.maxFrameLength)); + final CanceledException e = new CanceledException(String.format(INVALID_PAYLOAD_ERROR_MESSAGE, channel.maxFrameLength)); final ByteBuf errorFrame = ErrorFrameCodec.encode(allocator, streamId, e); connection.sendFrame(streamId, errorFrame); - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, e); + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, e); } return; } @@ -177,16 +147,13 @@ public void onNext(@Nullable Payload p) { catch (IllegalReferenceCountException e) { currentSubscription.cancel(); - final ByteBuf errorFrame = - ErrorFrameCodec.encode( - allocator, - streamId, - new CanceledException("Failed to validate payload. Cause" + e.getMessage())); + final ByteBuf errorFrame = ErrorFrameCodec.encode(allocator, streamId, + new CanceledException("Failed to validate payload. Cause" + e.getMessage())); connection.sendFrame(streamId, errorFrame); - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, e); + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, e); } return; } @@ -194,17 +161,17 @@ public void onNext(@Nullable Payload p) { try { sendReleasingPayload(streamId, FrameType.NEXT_COMPLETE, mtu, p, connection, allocator, false); - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, null); + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, null); } } catch (Throwable t) { currentSubscription.cancel(); - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, t); + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, t); } } } @@ -226,15 +193,16 @@ public void onError(Throwable t) { this.done = true; final int streamId = this.streamId; + final ChannelSupport channel = this.channel; - this.requesterResponderSupport.remove(streamId, this); + channel.remove(streamId, this); - final ByteBuf errorFrame = ErrorFrameCodec.encode(this.allocator, streamId, t); - this.connection.sendFrame(streamId, errorFrame); + final ByteBuf errorFrame = ErrorFrameCodec.encode(channel.allocator, streamId, t); + channel.connection.sendFrame(streamId, errorFrame); - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, t); + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, t); } } @@ -250,13 +218,14 @@ public void handleCancel() { return; } + final ChannelSupport channel = this.channel; if (currentSubscription == null) { // if subscription is null, it means that streams has not yet reassembled all the fragments // and fragmentation of the first frame was cancelled before S.lazySet(this, Operators.cancelledSubscription()); final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + channel.remove(streamId, this); final CompositeByteBuf frames = this.frames; if (frames != null) { @@ -264,9 +233,9 @@ public void handleCancel() { frames.release(); } - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onCancel(streamId, FrameType.REQUEST_RESPONSE); + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onCancel(streamId, FrameType.REQUEST_RESPONSE); } return; } @@ -276,13 +245,13 @@ public void handleCancel() { } final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + channel.remove(streamId, this); currentSubscription.cancel(); - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onCancel(streamId, FrameType.REQUEST_RESPONSE); + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onCancel(streamId, FrameType.REQUEST_RESPONSE); } } @@ -293,13 +262,14 @@ public void handleNext(ByteBuf frame, boolean hasFollows, boolean isLastPayload) return; } + final ChannelSupport channel = this.channel; try { - ReassemblyUtils.addFollowingFrame(frames, frame, hasFollows, this.maxInboundPayloadSize); + ReassemblyUtils.addFollowingFrame(frames, frame, hasFollows, channel.maxInboundPayloadSize); } catch (IllegalStateException t) { S.lazySet(this, Operators.cancelledSubscription()); - this.requesterResponderSupport.remove(this.streamId, this); + channel.remove(this.streamId, this); this.frames = null; frames.release(); @@ -308,16 +278,13 @@ public void handleNext(ByteBuf frame, boolean hasFollows, boolean isLastPayload) // sends error frame from the responder side to tell that something went wrong final int streamId = this.streamId; - final ByteBuf errorFrame = - ErrorFrameCodec.encode( - this.allocator, - streamId, - new CanceledException("Failed to reassemble payload. Cause: " + t.getMessage())); - this.connection.sendFrame(streamId, errorFrame); - - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, t); + final ByteBuf errorFrame = ErrorFrameCodec.encode(channel.allocator, streamId, + new CanceledException("Failed to reassemble payload. Cause: " + t.getMessage())); + channel.connection.sendFrame(streamId, errorFrame); + + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, t); } return; } @@ -326,30 +293,27 @@ public void handleNext(ByteBuf frame, boolean hasFollows, boolean isLastPayload) this.frames = null; Payload payload; try { - payload = this.payloadDecoder.apply(frames); + payload = channel.payloadDecoder.decode(frames); frames.release(); } catch (Throwable t) { S.lazySet(this, Operators.cancelledSubscription()); final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + channel.remove(streamId, this); ReferenceCountUtil.safeRelease(frames); logger.debug("Reassembly has failed", t); // sends error frame from the responder side to tell that something went wrong - final ByteBuf errorFrame = - ErrorFrameCodec.encode( - this.allocator, - streamId, - new CanceledException("Failed to reassemble payload. Cause: " + t.getMessage())); - this.connection.sendFrame(streamId, errorFrame); - - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, t); + final ByteBuf errorFrame = ErrorFrameCodec.encode(channel.allocator, streamId, + new CanceledException("Failed to reassemble payload. Cause: " + t.getMessage())); + channel.connection.sendFrame(streamId, errorFrame); + + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onTerminate(streamId, FrameType.REQUEST_RESPONSE, t); } return; } diff --git a/today-remoting/src/main/java/infra/remoting/core/RequestStreamRequesterFlux.java b/today-remoting/src/main/java/infra/remoting/core/RequestStreamRequesterFlux.java index 81d6c14..d7346a9 100644 --- a/today-remoting/src/main/java/infra/remoting/core/RequestStreamRequesterFlux.java +++ b/today-remoting/src/main/java/infra/remoting/core/RequestStreamRequesterFlux.java @@ -1,33 +1,30 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Subscription; import java.util.concurrent.atomic.AtomicLongFieldUpdater; -import infra.lang.NonNull; -import infra.lang.Nullable; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.Payload; import infra.remoting.frame.CancelFrameCodec; import infra.remoting.frame.FrameType; import infra.remoting.frame.RequestNFrameCodec; -import infra.remoting.frame.decoder.PayloadDecoder; import infra.remoting.plugins.RequestInterceptor; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; @@ -60,19 +57,12 @@ final class RequestStreamRequesterFlux extends Flux implements RequesterFrameHandler, LeasePermitHandler, Subscription, Scannable { - final ByteBufAllocator allocator; final Payload payload; - final int mtu; - final int maxFrameLength; - final int maxInboundPayloadSize; - final RequesterResponderSupport requesterResponderSupport; - final DuplexConnection connection; - final PayloadDecoder payloadDecoder; + + final ChannelSupport channel; @Nullable final RequesterLeaseTracker requesterLeaseTracker; - @Nullable - final RequestInterceptor requestInterceptor; volatile long state; static final AtomicLongFieldUpdater STATE = @@ -85,17 +75,10 @@ final class RequestStreamRequesterFlux extends Flux long requested; long produced; - RequestStreamRequesterFlux(Payload payload, RequesterResponderSupport requesterResponderSupport) { - this.allocator = requesterResponderSupport.getAllocator(); + RequestStreamRequesterFlux(Payload payload, ChannelSupport channel) { this.payload = payload; - this.mtu = requesterResponderSupport.getMtu(); - this.maxFrameLength = requesterResponderSupport.getMaxFrameLength(); - this.maxInboundPayloadSize = requesterResponderSupport.getMaxInboundPayloadSize(); - this.requesterResponderSupport = requesterResponderSupport; - this.connection = requesterResponderSupport.getDuplexConnection(); - this.payloadDecoder = requesterResponderSupport.getPayloadDecoder(); - this.requesterLeaseTracker = requesterResponderSupport.getRequesterLeaseTracker(); - this.requestInterceptor = requesterResponderSupport.getRequestInterceptor(); + this.channel = channel; + this.requesterLeaseTracker = channel.getRequesterLeaseTracker(); } @Override @@ -104,7 +87,7 @@ public void subscribe(CoreSubscriber actual) { if (isSubscribedOrTerminated(previousState)) { final IllegalStateException e = new IllegalStateException("RequestStreamFlux allows only a single Subscriber"); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_STREAM, null); } @@ -115,13 +98,13 @@ public void subscribe(CoreSubscriber actual) { final Payload p = this.payload; try { - if (!isValid(this.mtu, this.maxFrameLength, p, false)) { + if (!isValid(channel.mtu, channel.maxFrameLength, p, false)) { lazyTerminate(STATE, this); final IllegalArgumentException e = new IllegalArgumentException( - String.format(INVALID_PAYLOAD_ERROR_MESSAGE, this.maxFrameLength)); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + String.format(INVALID_PAYLOAD_ERROR_MESSAGE, channel.maxFrameLength)); + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_STREAM, p.metadata()); } @@ -135,7 +118,7 @@ public void subscribe(CoreSubscriber actual) { catch (IllegalReferenceCountException e) { lazyTerminate(STATE, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_STREAM, null); } @@ -167,8 +150,8 @@ public final void request(long n) { if (isFirstFrameSent(previousState) && !isMaxAllowedRequestN(extractRequestN(previousState))) { final int streamId = this.streamId; - final ByteBuf requestNFrame = RequestNFrameCodec.encode(this.allocator, streamId, n); - this.connection.sendFrame(streamId, requestNFrame); + final ByteBuf requestNFrame = RequestNFrameCodec.encode(channel.allocator, streamId, n); + channel.connection.sendFrame(streamId, requestNFrame); } return; } @@ -194,14 +177,13 @@ public boolean handlePermit() { } void sendFirstPayload(Payload payload, long initialRequestN) { - - final RequesterResponderSupport sm = this.requesterResponderSupport; - final DuplexConnection connection = this.connection; - final ByteBufAllocator allocator = this.allocator; + final ChannelSupport channel = this.channel; + final Connection connection = channel.connection; + final ByteBufAllocator allocator = channel.allocator; final int streamId; try { - streamId = sm.addAndGetNextStreamId(this); + streamId = channel.addAndGetNextStreamId(this); this.streamId = streamId; } catch (Throwable t) { @@ -209,7 +191,7 @@ void sendFirstPayload(Payload payload, long initialRequestN) { final long previousState = markTerminated(STATE, this); final Throwable ut = Exceptions.unwrap(t); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(ut, FrameType.REQUEST_STREAM, payload.metadata()); } @@ -222,7 +204,7 @@ void sendFirstPayload(Payload payload, long initialRequestN) { return; } - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onStart(streamId, FrameType.REQUEST_STREAM, payload.metadata()); } @@ -232,7 +214,7 @@ void sendFirstPayload(Payload payload, long initialRequestN) { streamId, FrameType.REQUEST_STREAM, initialRequestN, - this.mtu, + channel.mtu, payload, connection, allocator, @@ -242,7 +224,7 @@ void sendFirstPayload(Payload payload, long initialRequestN) { this.done = true; lazyTerminate(STATE, this); - sm.remove(streamId, this); + channel.remove(streamId, this); if (requestInterceptor != null) { requestInterceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, t); @@ -261,7 +243,7 @@ void sendFirstPayload(Payload payload, long initialRequestN) { final ByteBuf cancelFrame = CancelFrameCodec.encode(allocator, streamId); connection.sendFrame(streamId, cancelFrame); - sm.remove(streamId, this); + channel.remove(streamId, this); if (requestInterceptor != null) { requestInterceptor.onCancel(streamId, FrameType.REQUEST_STREAM); @@ -296,14 +278,14 @@ public final void cancel() { if (isFirstFrameSent(previousState)) { final int streamId = this.streamId; + final ChannelSupport channel = this.channel; ReassemblyUtils.synchronizedRelease(this, previousState); - this.connection.sendFrame(streamId, CancelFrameCodec.encode(this.allocator, streamId)); - - this.requesterResponderSupport.remove(streamId, this); + channel.connection.sendFrame(streamId, CancelFrameCodec.encode(channel.allocator, streamId)); + channel.remove(streamId, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onCancel(streamId, FrameType.REQUEST_STREAM); } @@ -331,15 +313,15 @@ public final void handlePayload(Payload p) { } final int streamId = this.streamId; + final ChannelSupport channel = this.channel; - final IllegalStateException cause = - Exceptions.failWithOverflow( - "The number of messages received exceeds the number requested"); - this.connection.sendFrame(streamId, CancelFrameCodec.encode(this.allocator, streamId)); + final IllegalStateException cause = Exceptions.failWithOverflow( + "The number of messages received exceeds the number requested"); + channel.connection.sendFrame(streamId, CancelFrameCodec.encode(channel.allocator, streamId)); - this.requesterResponderSupport.remove(streamId, this); + channel.remove(streamId, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, cause); } @@ -367,9 +349,9 @@ public final void handleComplete() { } final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + channel.remove(streamId, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, null); } @@ -388,7 +370,7 @@ public final void handlePermitError(Throwable cause) { } final Payload p = this.payload; - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(cause, FrameType.REQUEST_STREAM, p.metadata()); } @@ -413,11 +395,11 @@ public final void handleError(Throwable cause) { } final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + channel.remove(streamId, this); ReassemblyUtils.synchronizedRelease(this, previousState); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, cause); } @@ -432,9 +414,9 @@ public void handleNext(ByteBuf frame, boolean hasFollows, boolean isLastPayload) this, this, this.inboundSubscriber, - this.payloadDecoder, - this.allocator, - this.maxInboundPayloadSize, + channel.payloadDecoder, + channel.allocator, + channel.maxInboundPayloadSize, frame, hasFollows, isLastPayload); @@ -465,7 +447,6 @@ public Object scanUnsafe(Attr key) { } @Override - @NonNull public String stepName() { return "source(RequestStreamFlux)"; } diff --git a/today-remoting/src/main/java/infra/remoting/core/RequestStreamResponderSubscriber.java b/today-remoting/src/main/java/infra/remoting/core/RequestStreamResponderSubscriber.java index f94cd33..f765138 100644 --- a/today-remoting/src/main/java/infra/remoting/core/RequestStreamResponderSubscriber.java +++ b/today-remoting/src/main/java/infra/remoting/core/RequestStreamResponderSubscriber.java @@ -1,37 +1,34 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; import org.reactivestreams.Subscription; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import infra.lang.Nullable; import infra.logging.Logger; import infra.logging.LoggerFactory; import infra.remoting.Channel; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.Payload; -import infra.remoting.exceptions.CanceledException; +import infra.remoting.error.CanceledException; import infra.remoting.frame.ErrorFrameCodec; import infra.remoting.frame.FrameType; import infra.remoting.frame.PayloadFrameCodec; -import infra.remoting.frame.decoder.PayloadDecoder; -import infra.remoting.plugins.RequestInterceptor; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.CompositeByteBuf; @@ -46,23 +43,14 @@ import static infra.remoting.core.PayloadValidationUtils.isValid; import static infra.remoting.core.SendUtils.sendReleasingPayload; -final class RequestStreamResponderSubscriber - implements ResponderFrameHandler, CoreSubscriber { +final class RequestStreamResponderSubscriber implements ResponderFrameHandler, CoreSubscriber { static final Logger logger = LoggerFactory.getLogger(RequestStreamResponderSubscriber.class); final int streamId; final long firstRequest; - final ByteBufAllocator allocator; - final PayloadDecoder payloadDecoder; - final int mtu; - final int maxFrameLength; - final int maxInboundPayloadSize; - final RequesterResponderSupport requesterResponderSupport; - final DuplexConnection connection; - @Nullable - final RequestInterceptor requestInterceptor; + final ChannelSupport channel; final Channel handler; @@ -74,41 +62,21 @@ final class RequestStreamResponderSubscriber CompositeByteBuf frames; boolean done; - public RequestStreamResponderSubscriber( - int streamId, - long firstRequest, - ByteBuf firstFrame, - RequesterResponderSupport requesterResponderSupport, - Channel handler) { + public RequestStreamResponderSubscriber(int streamId, long firstRequest, + ByteBuf firstFrame, ChannelSupport channel, Channel handler) { this.streamId = streamId; this.firstRequest = firstRequest; - this.allocator = requesterResponderSupport.getAllocator(); - this.mtu = requesterResponderSupport.getMtu(); - this.maxFrameLength = requesterResponderSupport.getMaxFrameLength(); - this.maxInboundPayloadSize = requesterResponderSupport.getMaxInboundPayloadSize(); - this.requesterResponderSupport = requesterResponderSupport; - this.connection = requesterResponderSupport.getDuplexConnection(); - this.payloadDecoder = requesterResponderSupport.getPayloadDecoder(); - this.requestInterceptor = requesterResponderSupport.getRequestInterceptor(); + this.channel = channel; this.handler = handler; - this.frames = - ReassemblyUtils.addFollowingFrame( - allocator.compositeBuffer(), firstFrame, true, maxInboundPayloadSize); + this.frames = ReassemblyUtils.addFollowingFrame( + channel.allocator.compositeBuffer(), firstFrame, true, channel.maxInboundPayloadSize); } - public RequestStreamResponderSubscriber( - int streamId, long firstRequest, RequesterResponderSupport requesterResponderSupport) { + public RequestStreamResponderSubscriber(int streamId, long firstRequest, ChannelSupport channel) { this.streamId = streamId; this.firstRequest = firstRequest; - this.allocator = requesterResponderSupport.getAllocator(); - this.mtu = requesterResponderSupport.getMtu(); - this.maxFrameLength = requesterResponderSupport.getMaxFrameLength(); - this.maxInboundPayloadSize = requesterResponderSupport.getMaxInboundPayloadSize(); - this.requesterResponderSupport = requesterResponderSupport; - this.connection = requesterResponderSupport.getDuplexConnection(); - this.requestInterceptor = requesterResponderSupport.getRequestInterceptor(); - - this.payloadDecoder = null; + this.channel = channel; + this.handler = null; this.frames = null; } @@ -130,29 +98,28 @@ public void onNext(Payload p) { } final int streamId = this.streamId; - final DuplexConnection sender = this.connection; - final ByteBufAllocator allocator = this.allocator; + final Connection sender = channel.connection; + final ByteBufAllocator allocator = channel.allocator; - final int mtu = this.mtu; + final int mtu = channel.mtu; try { - if (!isValid(mtu, this.maxFrameLength, p, false)) { + if (!isValid(mtu, channel.maxFrameLength, p, false)) { p.release(); if (!this.tryTerminateOnError()) { return; } - final CanceledException e = - new CanceledException( - String.format(INVALID_PAYLOAD_ERROR_MESSAGE, this.maxFrameLength)); + final CanceledException e = new CanceledException( + String.format(INVALID_PAYLOAD_ERROR_MESSAGE, channel.maxFrameLength)); final ByteBuf errorFrame = ErrorFrameCodec.encode(allocator, streamId, e); sender.sendFrame(streamId, errorFrame); - this.requesterResponderSupport.remove(streamId, this); + this.channel.remove(streamId, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, e); + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, e); } return; } @@ -162,18 +129,15 @@ public void onNext(Payload p) { return; } - final ByteBuf errorFrame = - ErrorFrameCodec.encode( - allocator, - streamId, - new CanceledException("Failed to validate payload. Cause" + e.getMessage())); + final ByteBuf errorFrame = ErrorFrameCodec.encode(allocator, streamId, + new CanceledException("Failed to validate payload. Cause" + e.getMessage())); sender.sendFrame(streamId, errorFrame); - this.requesterResponderSupport.remove(streamId, this); + this.channel.remove(streamId, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, e); + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, e); } return; } @@ -186,11 +150,11 @@ public void onNext(Payload p) { return; } - this.requesterResponderSupport.remove(streamId, this); + this.channel.remove(streamId, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, t); + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, t); } } } @@ -233,14 +197,14 @@ public void onError(Throwable t) { final int streamId = this.streamId; - final ByteBuf errorFrame = ErrorFrameCodec.encode(this.allocator, streamId, t); - this.connection.sendFrame(streamId, errorFrame); + final ByteBuf errorFrame = ErrorFrameCodec.encode(channel.allocator, streamId, t); + channel.connection.sendFrame(streamId, errorFrame); - this.requesterResponderSupport.remove(streamId, this); + this.channel.remove(streamId, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, t); + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, t); } } @@ -258,14 +222,14 @@ public void onComplete() { final int streamId = this.streamId; - final ByteBuf completeFrame = PayloadFrameCodec.encodeComplete(this.allocator, streamId); - this.connection.sendFrame(streamId, completeFrame); + final ByteBuf completeFrame = PayloadFrameCodec.encodeComplete(channel.allocator, streamId); + channel.connection.sendFrame(streamId, completeFrame); - this.requesterResponderSupport.remove(streamId, this); + this.channel.remove(streamId, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, null); + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, null); } } @@ -287,7 +251,7 @@ public final void handleCancel() { S.lazySet(this, Operators.cancelledSubscription()); final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + this.channel.remove(streamId, this); final CompositeByteBuf frames = this.frames; if (frames != null) { @@ -295,9 +259,9 @@ public final void handleCancel() { frames.release(); } - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onCancel(streamId, FrameType.REQUEST_STREAM); + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onCancel(streamId, FrameType.REQUEST_STREAM); } return; } @@ -307,13 +271,13 @@ public final void handleCancel() { } final int streamId = this.streamId; - this.requesterResponderSupport.remove(streamId, this); + this.channel.remove(streamId, this); currentSubscription.cancel(); - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onCancel(streamId, FrameType.REQUEST_STREAM); + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onCancel(streamId, FrameType.REQUEST_STREAM); } } @@ -325,8 +289,7 @@ public void handleNext(ByteBuf followingFrame, boolean hasFollows, boolean isLas } try { - ReassemblyUtils.addFollowingFrame( - frames, followingFrame, hasFollows, this.maxInboundPayloadSize); + ReassemblyUtils.addFollowingFrame(frames, followingFrame, hasFollows, channel.maxInboundPayloadSize); } catch (IllegalStateException e) { // if subscription is null, it means that streams has not yet reassembled all the fragments @@ -339,18 +302,15 @@ public void handleNext(ByteBuf followingFrame, boolean hasFollows, boolean isLas frames.release(); // sends error frame from the responder side to tell that something went wrong - final ByteBuf errorFrame = - ErrorFrameCodec.encode( - this.allocator, - streamId, - new CanceledException("Failed to reassemble payload. Cause: " + e.getMessage())); - this.connection.sendFrame(streamId, errorFrame); - - this.requesterResponderSupport.remove(streamId, this); - - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, e); + final ByteBuf errorFrame = ErrorFrameCodec.encode(channel.allocator, streamId, + new CanceledException("Failed to reassemble payload. Cause: " + e.getMessage())); + channel.connection.sendFrame(streamId, errorFrame); + + this.channel.remove(streamId, this); + + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, e); } logger.debug("Reassembly has failed", e); @@ -361,7 +321,7 @@ public void handleNext(ByteBuf followingFrame, boolean hasFollows, boolean isLas this.frames = null; Payload payload; try { - payload = this.payloadDecoder.apply(frames); + payload = channel.payloadDecoder.decode(frames); frames.release(); } catch (Throwable t) { @@ -373,18 +333,15 @@ public void handleNext(ByteBuf followingFrame, boolean hasFollows, boolean isLas ReferenceCountUtil.safeRelease(frames); // sends error frame from the responder side to tell that something went wrong - final ByteBuf errorFrame = - ErrorFrameCodec.encode( - this.allocator, - streamId, - new CanceledException("Failed to reassemble payload. Cause: " + t.getMessage())); - this.connection.sendFrame(streamId, errorFrame); - - this.requesterResponderSupport.remove(streamId, this); - - final RequestInterceptor requestInterceptor = this.requestInterceptor; - if (requestInterceptor != null) { - requestInterceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, t); + final ByteBuf errorFrame = ErrorFrameCodec.encode(channel.allocator, streamId, + new CanceledException("Failed to reassemble payload. Cause: " + t.getMessage())); + channel.connection.sendFrame(streamId, errorFrame); + + this.channel.remove(streamId, this); + + final var interceptor = channel.requestInterceptor; + if (interceptor != null) { + interceptor.onTerminate(streamId, FrameType.REQUEST_STREAM, t); } logger.debug("Reassembly has failed", t); diff --git a/today-remoting/src/main/java/infra/remoting/core/ChannelRequester.java b/today-remoting/src/main/java/infra/remoting/core/RequesterChannel.java similarity index 86% rename from today-remoting/src/main/java/infra/remoting/core/ChannelRequester.java rename to today-remoting/src/main/java/infra/remoting/core/RequesterChannel.java index ed3cf5b..805e2bc 100644 --- a/today-remoting/src/main/java/infra/remoting/core/ChannelRequester.java +++ b/today-remoting/src/main/java/infra/remoting/core/RequesterChannel.java @@ -1,22 +1,22 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import java.nio.channels.ClosedChannelException; @@ -25,14 +25,13 @@ import java.util.function.Function; import java.util.function.Supplier; -import infra.lang.Nullable; import infra.logging.Logger; import infra.logging.LoggerFactory; import infra.remoting.Channel; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.Payload; -import infra.remoting.exceptions.ConnectionErrorException; -import infra.remoting.exceptions.Exceptions; +import infra.remoting.error.ConnectionErrorException; +import infra.remoting.error.Exceptions; import infra.remoting.frame.ErrorFrameCodec; import infra.remoting.frame.FrameHeaderCodec; import infra.remoting.frame.FrameType; @@ -51,11 +50,11 @@ import static infra.remoting.keepalive.KeepAliveSupport.ClientKeepAliveSupport; /** - * Requester Side of a RSocket socket. Sends {@link ByteBuf}s to a {@link ChannelResponder} of peer + * Requester Side of a Channel socket. Sends {@link ByteBuf}s to a {@link ResponderChannel} of peer */ -class ChannelRequester extends RequesterResponderSupport implements Channel { +class RequesterChannel extends ChannelSupport implements Channel { - private static final Logger LOGGER = LoggerFactory.getLogger(ChannelRequester.class); + private static final Logger LOGGER = LoggerFactory.getLogger(RequesterChannel.class); private static final Exception CLOSED_CHANNEL_EXCEPTION = new ClosedChannelException(); @@ -63,22 +62,22 @@ class ChannelRequester extends RequesterResponderSupport implements Channel { CLOSED_CHANNEL_EXCEPTION.setStackTrace(new StackTraceElement[0]); } - private volatile Throwable terminationError; - private static final AtomicReferenceFieldUpdater TERMINATION_ERROR = - AtomicReferenceFieldUpdater.newUpdater(ChannelRequester.class, Throwable.class, "terminationError"); + private volatile @Nullable Throwable terminationError; - @Nullable - private final RequesterLeaseTracker requesterLeaseTracker; + private static final AtomicReferenceFieldUpdater TERMINATION_ERROR = + AtomicReferenceFieldUpdater.newUpdater(RequesterChannel.class, Throwable.class, "terminationError"); - private final Sinks.Empty onThisSideClosedSink; + private final @Nullable RequesterLeaseTracker requesterLeaseTracker; + + private final @Nullable KeepAliveFramesAcceptor keepAliveFramesAcceptor; - private final KeepAliveFramesAcceptor keepAliveFramesAcceptor; + private final Sinks.Empty onThisSideClosedSink; private final Mono onAllClosed; - ChannelRequester(DuplexConnection connection, PayloadDecoder payloadDecoder, StreamIdProvider streamIdProvider, + RequesterChannel(Connection connection, PayloadDecoder payloadDecoder, StreamIdProvider streamIdProvider, int mtu, int maxFrameLength, int maxInboundPayloadSize, int keepAliveTickPeriod, int keepAliveAckTimeout, - @Nullable KeepAliveHandler keepAliveHandler, Function requestInterceptorFunction, + @Nullable KeepAliveHandler keepAliveHandler, Function requestInterceptorFunction, @Nullable RequesterLeaseTracker requesterLeaseTracker, Sinks.Empty onThisSideClosedSink, Mono onAllClosed) { super(mtu, maxFrameLength, maxInboundPayloadSize, payloadDecoder, connection, streamIdProvider, requestInterceptorFunction); @@ -97,7 +96,7 @@ class ChannelRequester extends RequesterResponderSupport implements Channel { keepAliveFrame -> connection.sendFrame(0, keepAliveFrame), this::tryTerminateOnKeepAlive); } else { - keepAliveFramesAcceptor = null; + this.keepAliveFramesAcceptor = null; } } @@ -137,9 +136,8 @@ public Mono metadataPush(Payload payload) { return new MetadataPushRequesterMono(payload, this); } - @Nullable @Override - public RequesterLeaseTracker getRequesterLeaseTracker() { + public @Nullable RequesterLeaseTracker getRequesterLeaseTracker() { return this.requesterLeaseTracker; } @@ -222,7 +220,9 @@ private void handleStreamZero(FrameType type, ByteBuf frame) { tryTerminateOnZeroError(frame); break; case LEASE: - requesterLeaseTracker.handleLeaseFrame(frame); + if (requesterLeaseTracker != null) { + requesterLeaseTracker.handleLeaseFrame(frame); + } break; case KEEPALIVE: if (keepAliveFramesAcceptor != null) { @@ -230,7 +230,7 @@ private void handleStreamZero(FrameType type, ByteBuf frame) { } break; default: - // Ignore unknown frames. Throwing an error will close the socket. + // Ignore unknown frames. Throwing an error will close the channel. if (LOGGER.isInfoEnabled()) { LOGGER.info("Requester received unsupported frame on stream 0: {}", frame.toString()); } diff --git a/today-remoting/src/main/java/infra/remoting/core/RequesterFrameHandler.java b/today-remoting/src/main/java/infra/remoting/core/RequesterFrameHandler.java index e73a84b..a1e6f8c 100644 --- a/today-remoting/src/main/java/infra/remoting/core/RequesterFrameHandler.java +++ b/today-remoting/src/main/java/infra/remoting/core/RequesterFrameHandler.java @@ -1,24 +1,24 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; + import java.util.concurrent.CancellationException; -import infra.lang.Nullable; import infra.remoting.Payload; import io.netty.buffer.CompositeByteBuf; diff --git a/today-remoting/src/main/java/infra/remoting/core/RequesterLeaseTracker.java b/today-remoting/src/main/java/infra/remoting/core/RequesterLeaseTracker.java index ede9ac0..0b3fbe4 100644 --- a/today-remoting/src/main/java/infra/remoting/core/RequesterLeaseTracker.java +++ b/today-remoting/src/main/java/infra/remoting/core/RequesterLeaseTracker.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; diff --git a/today-remoting/src/main/java/infra/remoting/core/ResolvingOperator.java b/today-remoting/src/main/java/infra/remoting/core/ResolvingOperator.java index d106be6..c164b15 100644 --- a/today-remoting/src/main/java/infra/remoting/core/ResolvingOperator.java +++ b/today-remoting/src/main/java/infra/remoting/core/ResolvingOperator.java @@ -1,21 +1,21 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Subscription; import java.time.Duration; @@ -25,7 +25,6 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.BiConsumer; -import infra.lang.Nullable; import reactor.core.CoreSubscriber; import reactor.core.Disposable; import reactor.core.Exceptions; @@ -33,8 +32,6 @@ import reactor.core.publisher.Operators; import reactor.util.context.Context; -// A copy of this class exists in io.rsocket.loadbalance - class ResolvingOperator implements Disposable { static final CancellationException ON_DISPOSE = new CancellationException("Disposed"); @@ -78,7 +75,7 @@ public ResolvingOperator() { @Override public final void dispose() { - this.terminate(ON_DISPOSE); + terminate(ON_DISPOSE); } @Override @@ -94,10 +91,7 @@ public final boolean isPending() { @Nullable public final T valueIfResolved() { if (this.subscribers == READY) { - T value = this.value; - if (value != null) { - return value; - } + return this.value; } return null; @@ -105,7 +99,7 @@ public final T valueIfResolved() { final void observe(BiConsumer actual) { for (; ; ) { - final int state = this.add(actual); + final int state = add(actual); T value = this.value; @@ -165,7 +159,7 @@ public T block(@Nullable Duration timeout) { // connect once if (subscribers == EMPTY_UNSUBSCRIBED && SUBSCRIBERS.compareAndSet(this, EMPTY_UNSUBSCRIBED, EMPTY_SUBSCRIBED)) { - this.doSubscribe(); + doSubscribe(); } long delay; @@ -201,7 +195,7 @@ public T block(@Nullable Duration timeout) { // connect again since invalidate() has happened in between if (subscribers == EMPTY_UNSUBSCRIBED && SUBSCRIBERS.compareAndSet(this, EMPTY_UNSUBSCRIBED, EMPTY_SUBSCRIBED)) { - this.doSubscribe(); + doSubscribe(); } Thread.sleep(1); @@ -230,9 +224,9 @@ final void terminate(Throwable t) { return; } - this.doOnDispose(); + doOnDispose(); - this.doFinally(); + doFinally(); for (BiConsumer consumer : subscribers) { consumer.accept(null, t); @@ -242,7 +236,7 @@ final void terminate(Throwable t) { final void complete(T value) { BiConsumer[] subscribers = this.subscribers; if (subscribers == TERMINATED) { - this.doOnValueExpired(value); + doOnValueExpired(value); return; } @@ -257,12 +251,12 @@ final void complete(T value) { subscribers = this.subscribers; if (subscribers == TERMINATED) { - this.doFinally(); + doFinally(); return; } } - this.doOnValueResolved(value); + doOnValueResolved(value); for (BiConsumer consumer : subscribers) { consumer.accept(value, null); @@ -285,7 +279,7 @@ final void doFinally() { value = this.value; if (value != null && isDisposed()) { this.value = null; - this.doOnValueExpired(value); + doOnValueExpired(value); return; } @@ -312,7 +306,7 @@ final void invalidate() { final T value = this.value; if (value != null) { this.value = null; - this.doOnValueExpired(value); + doOnValueExpired(value); } int m = 1; @@ -357,7 +351,7 @@ public final boolean connect() { } if (SUBSCRIBERS.compareAndSet(this, a, EMPTY_SUBSCRIBED)) { - this.doSubscribe(); + doSubscribe(); return true; } } @@ -383,7 +377,7 @@ final int add(BiConsumer ps) { if (SUBSCRIBERS.compareAndSet(this, a, b)) { if (a == EMPTY_UNSUBSCRIBED) { - this.doSubscribe(); + doSubscribe(); } return ADDED_STATE; } @@ -649,6 +643,7 @@ public void onError(Throwable t) { } } + @Nullable @Override public Object scanUnsafe(Attr key) { if (key == Attr.PARENT) diff --git a/today-remoting/src/main/java/infra/remoting/core/ChannelResponder.java b/today-remoting/src/main/java/infra/remoting/core/ResponderChannel.java similarity index 85% rename from today-remoting/src/main/java/infra/remoting/core/ChannelResponder.java rename to today-remoting/src/main/java/infra/remoting/core/ResponderChannel.java index 62da97c..54efeeb 100644 --- a/today-remoting/src/main/java/infra/remoting/core/ChannelResponder.java +++ b/today-remoting/src/main/java/infra/remoting/core/ResponderChannel.java @@ -1,22 +1,22 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import java.nio.channels.ClosedChannelException; @@ -26,14 +26,13 @@ import java.util.function.Function; import java.util.function.Supplier; -import infra.lang.Nullable; import infra.logging.Logger; import infra.logging.LoggerFactory; import infra.remoting.Channel; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.Payload; -import infra.remoting.exceptions.ConnectionErrorException; -import infra.remoting.exceptions.Exceptions; +import infra.remoting.error.ConnectionErrorException; +import infra.remoting.error.Exceptions; import infra.remoting.frame.ErrorFrameCodec; import infra.remoting.frame.FrameHeaderCodec; import infra.remoting.frame.FrameType; @@ -50,11 +49,11 @@ import reactor.core.publisher.Sinks; /** - * Responder side of RSocket. Receives {@link ByteBuf}s from a peer's {@link ChannelRequester} + * Responder side of Channel. Receives {@link ByteBuf}s from a peer's {@link RequesterChannel} */ -class ChannelResponder extends RequesterResponderSupport implements Channel { +class ResponderChannel extends ChannelSupport implements Channel { - private static final Logger LOGGER = LoggerFactory.getLogger(ChannelResponder.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ResponderChannel.class); private static final Exception CLOSED_CHANNEL_EXCEPTION = new ClosedChannelException(); @@ -62,16 +61,16 @@ class ChannelResponder extends RequesterResponderSupport implements Channel { private final Sinks.Empty onThisSideClosedSink; - @Nullable - private final ResponderLeaseTracker leaseHandler; + private final @Nullable ResponderLeaseTracker leaseHandler; - private volatile Throwable terminationError; - private static final AtomicReferenceFieldUpdater TERMINATION_ERROR = + private volatile @Nullable Throwable terminationError; + + private static final AtomicReferenceFieldUpdater TERMINATION_ERROR = AtomicReferenceFieldUpdater.newUpdater( - ChannelResponder.class, Throwable.class, "terminationError"); + ResponderChannel.class, Throwable.class, "terminationError"); - ChannelResponder(DuplexConnection connection, Channel requestHandler, PayloadDecoder payloadDecoder, @Nullable ResponderLeaseTracker leaseHandler, - int mtu, int maxFrameLength, int maxInboundPayloadSize, Function requestInterceptorFunction, + ResponderChannel(Connection connection, Channel requestHandler, PayloadDecoder payloadDecoder, @Nullable ResponderLeaseTracker leaseHandler, + int mtu, int maxFrameLength, int maxInboundPayloadSize, Function requestInterceptorFunction, Sinks.Empty onThisSideClosedSink) { super(mtu, maxFrameLength, maxInboundPayloadSize, payloadDecoder, connection, null, requestInterceptorFunction); this.leaseHandler = leaseHandler; @@ -230,7 +229,7 @@ final void handleFrame(ByteBuf frame) { streamId, frame, channelInitialRequestN, FrameHeaderCodec.hasComplete(frame)); break; case METADATA_PUSH: - handleMetadataPush(metadataPush(getPayloadDecoder().apply(frame))); + handleMetadataPush(metadataPush(payloadDecoder.decode(frame))); break; case CANCEL: receiver = get(streamId); @@ -280,7 +279,7 @@ final void handleFrame(ByteBuf frame) { case LEASE: default: connection.sendFrame(streamId, ErrorFrameCodec.encode( - allocator, streamId, new IllegalStateException("ServerRSocket: Unexpected frame type: " + frameType))); + allocator, streamId, new IllegalStateException("ServerChannel: Unexpected frame type: " + frameType))); break; } } @@ -313,11 +312,11 @@ final void handleFireAndForget(int streamId, ByteBuf frame) { requestInterceptor.onStart( streamId, FrameType.REQUEST_FNF, RequestFireAndForgetFrameCodec.metadata(frame)); - fireAndForget(getPayloadDecoder().apply(frame)) + fireAndForget(payloadDecoder.decode(frame)) .subscribe(new FireAndForgetResponderSubscriber(streamId, this)); } else { - fireAndForget(getPayloadDecoder().apply(frame)) + fireAndForget(payloadDecoder.decode(frame)) .subscribe(FireAndForgetResponderSubscriber.INSTANCE); } } @@ -346,7 +345,7 @@ final void handleRequestResponse(int streamId, ByteBuf frame) { else { var subscriber = new RequestResponseResponderSubscriber(streamId, this); if (add(streamId, subscriber)) { - requestResponse(getPayloadDecoder().apply(frame)).subscribe(subscriber); + requestResponse(payloadDecoder.decode(frame)).subscribe(subscriber); } } } @@ -377,7 +376,7 @@ final void handleStream(int streamId, ByteBuf frame, long initialRequestN) { new RequestStreamResponderSubscriber(streamId, initialRequestN, this); if (add(streamId, subscriber)) { - requestStream(getPayloadDecoder().apply(frame)).subscribe(subscriber); + requestStream(payloadDecoder.decode(frame)).subscribe(subscriber); } } } @@ -407,7 +406,7 @@ final void handleChannel(int streamId, ByteBuf frame, long initialRequestN, bool add(streamId, subscriber); } else { - final Payload firstPayload = getPayloadDecoder().apply(frame); + final Payload firstPayload = payloadDecoder.decode(frame); RequestChannelResponderSubscriber subscriber = new RequestChannelResponderSubscriber(streamId, initialRequestN, firstPayload, this); @@ -429,7 +428,7 @@ final void handleChannel(int streamId, ByteBuf frame, long initialRequestN, bool } private void sendLeaseRejection(int streamId, Throwable leaseError) { - connection.sendFrame(streamId, ErrorFrameCodec.encode(getAllocator(), streamId, leaseError)); + connection.sendFrame(streamId, ErrorFrameCodec.encode(allocator, streamId, leaseError)); } private void handleMetadataPush(Mono result) { diff --git a/today-remoting/src/main/java/infra/remoting/core/ResponderFrameHandler.java b/today-remoting/src/main/java/infra/remoting/core/ResponderFrameHandler.java index 2b21cad..242f89e 100644 --- a/today-remoting/src/main/java/infra/remoting/core/ResponderFrameHandler.java +++ b/today-remoting/src/main/java/infra/remoting/core/ResponderFrameHandler.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; diff --git a/today-remoting/src/main/java/infra/remoting/core/ResponderLeaseTracker.java b/today-remoting/src/main/java/infra/remoting/core/ResponderLeaseTracker.java index 1bd72b5..3e88a93 100644 --- a/today-remoting/src/main/java/infra/remoting/core/ResponderLeaseTracker.java +++ b/today-remoting/src/main/java/infra/remoting/core/ResponderLeaseTracker.java @@ -1,25 +1,25 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; + import infra.remoting.Availability; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.frame.LeaseFrameCodec; import infra.remoting.lease.Lease; import infra.remoting.lease.LeaseSender; @@ -32,12 +32,12 @@ final class ResponderLeaseTracker extends BaseSubscriber implements Dispo final String tag; final ByteBufAllocator allocator; - final DuplexConnection connection; + final Connection connection; @Nullable volatile MutableLease currentLease; - ResponderLeaseTracker(String tag, DuplexConnection connection, LeaseSender leaseSender) { + ResponderLeaseTracker(String tag, Connection connection, LeaseSender leaseSender) { this.tag = tag; this.connection = connection; this.allocator = connection.alloc(); diff --git a/today-remoting/src/main/java/infra/remoting/core/Resume.java b/today-remoting/src/main/java/infra/remoting/core/Resume.java index 86a2818..94ec566 100644 --- a/today-remoting/src/main/java/infra/remoting/core/Resume.java +++ b/today-remoting/src/main/java/infra/remoting/core/Resume.java @@ -1,60 +1,62 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; + import java.time.Duration; import java.util.Objects; -import java.util.function.Function; -import java.util.function.Supplier; import infra.logging.Logger; import infra.logging.LoggerFactory; -import infra.remoting.frame.ResumeFrameCodec; import infra.remoting.resume.InMemoryResumableFramesStore; -import infra.remoting.resume.ResumableFramesStore; -import io.netty.buffer.ByteBuf; +import infra.remoting.resume.InMemoryResumableFramesStoreFactory; +import infra.remoting.resume.RandomUUIDResumeTokenGenerator; +import infra.remoting.resume.ResumableFramesStoreFactory; +import infra.remoting.resume.ResumeTokenGenerator; import reactor.util.retry.Retry; /** - * Simple holder of configuration settings for the RSocket Resume capability. This can be used to + * Simple holder of configuration settings for the protocol Resume capability. This can be used to * configure an {@link ChannelConnector} or an {@link RemotingServer} except for {@link - * #retry(Retry)} and {@link #token(Supplier)} which apply only to the client side. + * #retry(Retry)} and {@link #token(ResumeTokenGenerator)} which apply only to the client side. */ public class Resume { private static final Logger logger = LoggerFactory.getLogger(Resume.class); - private Duration sessionDuration = Duration.ofMinutes(2); - - /* Storage */ - private boolean cleanupStoreOnKeepAlive; + @Nullable + private ResumableFramesStoreFactory storeFactory; - private Function storeFactory; + Duration sessionDuration = Duration.ofMinutes(2); - private Duration streamTimeout = Duration.ofSeconds(10); + Duration streamTimeout = Duration.ofSeconds(10); /* Client only */ - private Supplier tokenSupplier = ResumeFrameCodec::generateResumeToken; + ResumeTokenGenerator tokenGenerator = new RandomUUIDResumeTokenGenerator(); - private Retry retry = Retry.backoff(Long.MAX_VALUE, Duration.ofSeconds(1)) + Retry retry = Retry.backoff(Long.MAX_VALUE, Duration.ofSeconds(1)) .maxBackoff(Duration.ofSeconds(16)) .jitter(1.0) .doBeforeRetry(signal -> logger.debug("Connection error", signal.failure())); + /* Storage */ + boolean cleanupStoreOnKeepAlive; + public Resume() { } @@ -75,9 +77,9 @@ public Resume sessionDuration(Duration sessionDuration) { /** * When this property is enabled, hints from {@code KEEPALIVE} frames about how much data has been * received by the other side, is used to proactively clean frames from the {@link - * #storeFactory(Function) store}. + * Resume#storeFactory(ResumableFramesStoreFactory) store}. * - *

    By default this is set to {@code false} in which case information from {@code KEEPALIVE} is + *

    By default, this is set to {@code false} in which case information from {@code KEEPALIVE} is * ignored and old frames from the store are removed only when the store runs out of space. * * @return the same instance for method chaining @@ -87,6 +89,21 @@ public Resume cleanupStoreOnKeepAlive() { return this; } + /** + * When this property is enabled, hints from {@code KEEPALIVE} frames about how much data has been + * received by the other side, is used to proactively clean frames from the {@link + * Resume#storeFactory(ResumableFramesStoreFactory) store}. + * + *

    By default, this is set to {@code false} in which case information from {@code KEEPALIVE} is + * ignored and old frames from the store are removed only when the store runs out of space. + * + * @return the same instance for method chaining + */ + public Resume cleanupStoreOnKeepAlive(boolean cleanupStoreOnKeepAlive) { + this.cleanupStoreOnKeepAlive = cleanupStoreOnKeepAlive; + return this; + } + /** * Configure a factory to create the storage for buffering (or persisting) a window of frames that * may need to be sent again to resume after a dropped connection. @@ -98,17 +115,17 @@ public Resume cleanupStoreOnKeepAlive() { * @param storeFactory the factory to use to create the store * @return the same instance for method chaining */ - public Resume storeFactory(Function storeFactory) { + public Resume storeFactory(@Nullable ResumableFramesStoreFactory storeFactory) { this.storeFactory = storeFactory; return this; } /** * A {@link reactor.core.publisher.Flux#timeout(Duration) timeout} value to apply to the resumed - * session stream obtained from the {@link #storeFactory(Function) store} after a reconnect. The + * session stream obtained from the {@link #storeFactory(ResumableFramesStoreFactory) store} after a reconnect. The * resume stream must not take longer than the specified time to emit each frame. * - *

    By default this is set to 10 seconds. + *

    By default, this is set to 10 seconds. * * @param streamTimeout the timeout value for resuming a session stream * @return the same instance for method chaining @@ -142,41 +159,22 @@ public Resume retry(Retry retry) { * Customize the generation of the resume identification token used to resume. This setting is for * use with {@link ChannelConnector#resume(Resume)} on the client side only. * - *

    By default this is {@code ResumeFrameFlyweight::generateResumeToken}. + *

    By default, this is {@code ResumeFrameFlyweight::generateResumeToken}. * - * @param supplier a custom generator for a resume identification token + * @param generator a custom generator for a resume identification token * @return the same instance for method chaining */ - public Resume token(Supplier supplier) { - this.tokenSupplier = supplier; + public Resume token(ResumeTokenGenerator generator) { + this.tokenGenerator = generator; return this; } // Package private accessors - Duration getSessionDuration() { - return sessionDuration; - } - - boolean isCleanupStoreOnKeepAlive() { - return cleanupStoreOnKeepAlive; - } - - Function getStoreFactory(String tag) { + ResumableFramesStoreFactory getStoreFactory(String tag) { return storeFactory != null ? storeFactory - : token -> new InMemoryResumableFramesStore(tag, token, 100_000); - } - - Duration getStreamTimeout() { - return streamTimeout; + : new InMemoryResumableFramesStoreFactory(tag, 100_000); } - Retry getRetry() { - return retry; - } - - Supplier getTokenSupplier() { - return tokenSupplier; - } } diff --git a/today-remoting/src/main/java/infra/remoting/core/SendUtils.java b/today-remoting/src/main/java/infra/remoting/core/SendUtils.java index caaa657..ef1e653 100644 --- a/today-remoting/src/main/java/infra/remoting/core/SendUtils.java +++ b/today-remoting/src/main/java/infra/remoting/core/SendUtils.java @@ -1,26 +1,25 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; import java.util.function.Consumer; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.Payload; -import infra.remoting.exceptions.CanceledException; +import infra.remoting.error.CanceledException; import infra.remoting.frame.CancelFrameCodec; import infra.remoting.frame.ErrorFrameCodec; import infra.remoting.frame.FrameType; @@ -40,29 +39,22 @@ import static infra.remoting.core.FragmentationUtils.isFragmentable; final class SendUtils { - private static final Consumer DROPPED_ELEMENTS_CONSUMER = - data -> { - if (data instanceof ReferenceCounted) { - try { - ReferenceCounted referenceCounted = (ReferenceCounted) data; - referenceCounted.release(); - } - catch (Throwable e) { - // ignored - } - } - }; + + private static final Consumer DROPPED_ELEMENTS_CONSUMER = data -> { + if (data instanceof ReferenceCounted rc) { + try { + rc.release(); + } + catch (Throwable e) { + // ignored + } + } + }; static final Context DISCARD_CONTEXT = Operators.enableOnDiscard(null, DROPPED_ELEMENTS_CONSUMER); - static void sendReleasingPayload( - int streamId, - FrameType frameType, - int mtu, - Payload payload, - DuplexConnection connection, - ByteBufAllocator allocator, - boolean requester) { + static void sendReleasingPayload(int streamId, FrameType frameType, int mtu, Payload payload, + Connection connection, ByteBufAllocator allocator, boolean requester) { final boolean hasMetadata = payload.hasMetadata(); final ByteBuf metadata = hasMetadata ? payload.metadata() : null; @@ -83,9 +75,8 @@ static void sendReleasingPayload( final ByteBuf first; try { - first = - FragmentationUtils.encodeFirstFragment( - allocator, mtu, frameType, streamId, hasMetadata, slicedMetadata, slicedData); + first = FragmentationUtils.encodeFirstFragment( + allocator, mtu, frameType, streamId, hasMetadata, slicedMetadata, slicedData); } catch (IllegalReferenceCountException e) { sendTerminalFrame(streamId, frameType, connection, allocator, requester, false, e); @@ -98,9 +89,8 @@ static void sendReleasingPayload( while (slicedData.isReadable() || slicedMetadata.isReadable()) { final ByteBuf following; try { - following = - FragmentationUtils.encodeFollowsFragment( - allocator, mtu, streamId, complete, slicedMetadata, slicedData); + following = FragmentationUtils.encodeFollowsFragment( + allocator, mtu, streamId, complete, slicedMetadata, slicedData); } catch (IllegalReferenceCountException e) { sendTerminalFrame(streamId, frameType, connection, allocator, requester, true, e); @@ -144,48 +134,20 @@ static void sendReleasingPayload( throw e; } - final ByteBuf requestFrame; - switch (frameType) { - case REQUEST_FNF: - requestFrame = - RequestFireAndForgetFrameCodec.encode( - allocator, streamId, false, metadataRetainedSlice, dataRetainedSlice); - break; - case REQUEST_RESPONSE: - requestFrame = - RequestResponseFrameCodec.encode( - allocator, streamId, false, metadataRetainedSlice, dataRetainedSlice); - break; - case PAYLOAD: - case NEXT: - case NEXT_COMPLETE: - requestFrame = - PayloadFrameCodec.encode( - allocator, - streamId, - false, - frameType == FrameType.NEXT_COMPLETE, - frameType != FrameType.PAYLOAD, - metadataRetainedSlice, - dataRetainedSlice); - break; - default: - throw new IllegalArgumentException("Unsupported frame type " + frameType); - } + final ByteBuf requestFrame = switch (frameType) { + case REQUEST_FNF -> RequestFireAndForgetFrameCodec.encode(allocator, streamId, false, metadataRetainedSlice, dataRetainedSlice); + case REQUEST_RESPONSE -> RequestResponseFrameCodec.encode(allocator, streamId, false, metadataRetainedSlice, dataRetainedSlice); + case PAYLOAD, NEXT, NEXT_COMPLETE -> PayloadFrameCodec.encode(allocator, streamId, false, + frameType == FrameType.NEXT_COMPLETE, frameType != FrameType.PAYLOAD, metadataRetainedSlice, dataRetainedSlice); + default -> throw new IllegalArgumentException("Unsupported frame type " + frameType); + }; connection.sendFrame(streamId, requestFrame); } } - static void sendReleasingPayload( - int streamId, - FrameType frameType, - long initialRequestN, - int mtu, - Payload payload, - DuplexConnection connection, - ByteBufAllocator allocator, - boolean complete) { + static void sendReleasingPayload(int streamId, FrameType frameType, long initialRequestN, int mtu, + Payload payload, Connection connection, ByteBufAllocator allocator, boolean complete) { final boolean hasMetadata = payload.hasMetadata(); final ByteBuf metadata = hasMetadata ? payload.metadata() : null; @@ -206,16 +168,8 @@ static void sendReleasingPayload( final ByteBuf first; try { - first = - FragmentationUtils.encodeFirstFragment( - allocator, - mtu, - initialRequestN, - frameType, - streamId, - hasMetadata, - slicedMetadata, - slicedData); + first = FragmentationUtils.encodeFirstFragment(allocator, mtu, initialRequestN, + frameType, streamId, hasMetadata, slicedMetadata, slicedData); } catch (IllegalReferenceCountException e) { sendTerminalFrame(streamId, frameType, connection, allocator, true, false, e); @@ -227,9 +181,8 @@ static void sendReleasingPayload( while (slicedData.isReadable() || slicedMetadata.isReadable()) { final ByteBuf following; try { - following = - FragmentationUtils.encodeFollowsFragment( - allocator, mtu, streamId, complete, slicedMetadata, slicedData); + following = FragmentationUtils.encodeFollowsFragment( + allocator, mtu, streamId, complete, slicedMetadata, slicedData); } catch (IllegalReferenceCountException e) { sendTerminalFrame(streamId, frameType, connection, allocator, true, true, e); @@ -273,45 +226,20 @@ static void sendReleasingPayload( throw e; } - final ByteBuf requestFrame; - switch (frameType) { - case REQUEST_STREAM: - requestFrame = - RequestStreamFrameCodec.encode( - allocator, - streamId, - false, - initialRequestN, - metadataRetainedSlice, - dataRetainedSlice); - break; - case REQUEST_CHANNEL: - requestFrame = - RequestChannelFrameCodec.encode( - allocator, - streamId, - false, - complete, - initialRequestN, - metadataRetainedSlice, - dataRetainedSlice); - break; - default: - throw new IllegalArgumentException("Unsupported frame type " + frameType); - } + final ByteBuf requestFrame = switch (frameType) { + case REQUEST_STREAM -> RequestStreamFrameCodec.encode(allocator, streamId, false, initialRequestN, + metadataRetainedSlice, dataRetainedSlice); + case REQUEST_CHANNEL -> RequestChannelFrameCodec.encode(allocator, streamId, false, complete, + initialRequestN, metadataRetainedSlice, dataRetainedSlice); + default -> throw new IllegalArgumentException("Unsupported frame type " + frameType); + }; connection.sendFrame(streamId, requestFrame); } } - static void sendTerminalFrame( - int streamId, - FrameType frameType, - DuplexConnection connection, - ByteBufAllocator allocator, - boolean requester, - boolean onFollowingFrame, - Throwable t) { + static void sendTerminalFrame(int streamId, FrameType frameType, Connection connection, + ByteBufAllocator allocator, boolean requester, boolean onFollowingFrame, Throwable t) { if (onFollowingFrame) { if (requester) { @@ -319,15 +247,8 @@ static void sendTerminalFrame( connection.sendFrame(streamId, cancelFrame); } else { - final ByteBuf errorFrame = - ErrorFrameCodec.encode( - allocator, - streamId, - new CanceledException( - "Failed to encode fragmented " - + frameType - + " frame. Cause: " - + t.getMessage())); + final ByteBuf errorFrame = ErrorFrameCodec.encode(allocator, streamId, + new CanceledException("Failed to encode fragmented %s frame. Cause: %s".formatted(frameType, t.getMessage()))); connection.sendFrame(streamId, errorFrame); } } @@ -341,12 +262,8 @@ static void sendTerminalFrame( connection.sendFrame(streamId, cancelFrame); } else { - final ByteBuf errorFrame = - ErrorFrameCodec.encode( - allocator, - streamId, - new CanceledException( - "Failed to encode " + frameType + " frame. Cause: " + t.getMessage())); + final ByteBuf errorFrame = ErrorFrameCodec.encode(allocator, streamId, new CanceledException( + "Failed to encode %s frame. Cause: %s".formatted(frameType, t.getMessage()))); connection.sendFrame(streamId, errorFrame); } } diff --git a/today-remoting/src/main/java/infra/remoting/core/ServerSetup.java b/today-remoting/src/main/java/infra/remoting/core/ServerSetup.java index 00ff744..9fe1247 100644 --- a/today-remoting/src/main/java/infra/remoting/core/ServerSetup.java +++ b/today-remoting/src/main/java/infra/remoting/core/ServerSetup.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -20,17 +19,16 @@ import java.nio.channels.ClosedChannelException; import java.time.Duration; import java.util.function.BiFunction; -import java.util.function.Function; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.ProtocolErrorException; -import infra.remoting.exceptions.RejectedResumeException; -import infra.remoting.exceptions.UnsupportedSetupException; +import infra.remoting.error.RejectedResumeException; +import infra.remoting.error.UnsupportedSetupException; import infra.remoting.frame.ResumeFrameCodec; import infra.remoting.frame.SetupFrameCodec; import infra.remoting.keepalive.KeepAliveHandler; -import infra.remoting.resume.ResumableDuplexConnection; -import infra.remoting.resume.ResumableFramesStore; +import infra.remoting.resume.ResumableConnection; +import infra.remoting.resume.ResumableFramesStoreFactory; import infra.remoting.resume.ServerChannelSession; import infra.remoting.resume.SessionManager; import io.netty.buffer.ByteBuf; @@ -48,23 +46,24 @@ protected ServerSetup(Duration timeout) { this.timeout = timeout; } - Mono> init(DuplexConnection connection) { - return Mono.>create(sink -> sink.onRequest(__ -> new SetupHandlingDuplexConnection(connection, sink))) + Mono> init(Connection connection) { + return Mono.>create(sink -> sink.onRequest(__ -> new SetupHandlingConnection(connection, sink))) .timeout(this.timeout) .or(connection.onClose().then(Mono.error(ClosedChannelException::new))); } - abstract Mono acceptRSocketSetup(ByteBuf frame, - DuplexConnection clientServerConnection, - BiFunction> then); + abstract Mono acceptChannelSetup(ByteBuf frame, + Connection clientServerConnection, + BiFunction> then); - abstract Mono acceptRSocketResume(ByteBuf frame, DuplexConnection connection); + abstract Mono acceptChannelResume(ByteBuf frame, Connection connection); - void dispose() { } + void dispose() { + } - void sendError(DuplexConnection duplexConnection, ProtocolErrorException exception) { - duplexConnection.sendErrorAndClose(exception); - duplexConnection.receive().subscribe(); + void sendError(Connection connection, ProtocolErrorException exception) { + connection.sendErrorAndClose(exception); + connection.receive().subscribe(); } static class DefaultServerSetup extends ServerSetup { @@ -74,85 +73,76 @@ static class DefaultServerSetup extends ServerSetup { } @Override - public Mono acceptRSocketSetup(ByteBuf frame, DuplexConnection duplexConnection, - BiFunction> then) { + public Mono acceptChannelSetup(ByteBuf frame, Connection connection, + BiFunction> then) { if (SetupFrameCodec.resumeEnabled(frame)) { - sendError(duplexConnection, new UnsupportedSetupException("resume not supported")); - return duplexConnection.onClose(); + sendError(connection, new UnsupportedSetupException("resume not supported")); + return connection.onClose(); } else { - return then.apply(new DefaultKeepAliveHandler(), duplexConnection); + return then.apply(new DefaultKeepAliveHandler(), connection); } } @Override - public Mono acceptRSocketResume(ByteBuf frame, DuplexConnection duplexConnection) { - sendError(duplexConnection, new RejectedResumeException("resume not supported")); - return duplexConnection.onClose(); + public Mono acceptChannelResume(ByteBuf frame, Connection connection) { + sendError(connection, new RejectedResumeException("resume not supported")); + return connection.onClose(); } } static class ResumableServerSetup extends ServerSetup { + private final Duration resumeStreamTimeout; + private final SessionManager sessionManager; + private final Duration resumeSessionDuration; - private final Duration resumeStreamTimeout; - private final Function resumeStoreFactory; + private final boolean cleanupStoreOnKeepAlive; + private final ResumableFramesStoreFactory resumeStoreFactory; + ResumableServerSetup(Duration timeout, SessionManager sessionManager, Duration resumeSessionDuration, Duration resumeStreamTimeout, - Function resumeStoreFactory, boolean cleanupStoreOnKeepAlive) { + ResumableFramesStoreFactory resumeStoreFactory, boolean cleanupStoreOnKeepAlive) { super(timeout); this.sessionManager = sessionManager; - this.resumeSessionDuration = resumeSessionDuration; - this.resumeStreamTimeout = resumeStreamTimeout; this.resumeStoreFactory = resumeStoreFactory; + this.resumeStreamTimeout = resumeStreamTimeout; + this.resumeSessionDuration = resumeSessionDuration; this.cleanupStoreOnKeepAlive = cleanupStoreOnKeepAlive; } @Override - public Mono acceptRSocketSetup(ByteBuf frame, DuplexConnection duplexConnection, - BiFunction> then) { - + public Mono acceptChannelSetup(ByteBuf frame, Connection connection, BiFunction> then) { if (SetupFrameCodec.resumeEnabled(frame)) { ByteBuf resumeToken = SetupFrameCodec.resumeToken(frame); - final ResumableFramesStore resumableFramesStore = resumeStoreFactory.apply(resumeToken); - final ResumableDuplexConnection resumableDuplexConnection = - new ResumableDuplexConnection( - "server", resumeToken, duplexConnection, resumableFramesStore); - final ServerChannelSession serverRSocketSession = - new ServerChannelSession( - resumeToken, - resumableDuplexConnection, - duplexConnection, - resumableFramesStore, - resumeSessionDuration, - cleanupStoreOnKeepAlive); - - sessionManager.save(serverRSocketSession, resumeToken); - - return then.apply(new ResumableKeepAliveHandler( - resumableDuplexConnection, serverRSocketSession, serverRSocketSession), - resumableDuplexConnection); + var resumableFramesStore = resumeStoreFactory.create(resumeToken); + var resumableConnection = new ResumableConnection("server", resumeToken, connection, resumableFramesStore); + var serverChannelSession = new ServerChannelSession(resumeToken, resumableConnection, connection, + resumableFramesStore, resumeSessionDuration, cleanupStoreOnKeepAlive); + + sessionManager.save(serverChannelSession, resumeToken); + + return then.apply(new ResumableKeepAliveHandler(resumableConnection, serverChannelSession, serverChannelSession), resumableConnection); } else { - return then.apply(new DefaultKeepAliveHandler(), duplexConnection); + return then.apply(new DefaultKeepAliveHandler(), connection); } } @Override - public Mono acceptRSocketResume(ByteBuf frame, DuplexConnection duplexConnection) { + public Mono acceptChannelResume(ByteBuf frame, Connection connection) { ServerChannelSession session = sessionManager.get(ResumeFrameCodec.token(frame)); if (session != null) { - session.resumeWith(frame, duplexConnection); - return duplexConnection.onClose(); + session.resumeWith(frame, connection); } else { - sendError(duplexConnection, new RejectedResumeException("unknown resume token")); - return duplexConnection.onClose(); + sendError(connection, new RejectedResumeException("unknown resume token")); } + return connection.onClose(); } @Override diff --git a/today-remoting/src/main/java/infra/remoting/core/SetupHandlingDuplexConnection.java b/today-remoting/src/main/java/infra/remoting/core/SetupHandlingConnection.java similarity index 73% rename from today-remoting/src/main/java/infra/remoting/core/SetupHandlingDuplexConnection.java rename to today-remoting/src/main/java/infra/remoting/core/SetupHandlingConnection.java index 09a2bd0..5fb5048 100644 --- a/today-remoting/src/main/java/infra/remoting/core/SetupHandlingDuplexConnection.java +++ b/today-remoting/src/main/java/infra/remoting/core/SetupHandlingConnection.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -22,7 +21,7 @@ import java.net.SocketAddress; import java.nio.channels.ClosedChannelException; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.ProtocolErrorException; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; @@ -35,11 +34,11 @@ import reactor.util.function.Tuple2; import reactor.util.function.Tuples; -class SetupHandlingDuplexConnection extends Flux - implements DuplexConnection, CoreSubscriber, Subscription { +class SetupHandlingConnection extends Flux + implements Connection, CoreSubscriber, Subscription { - final DuplexConnection source; - final MonoSink> sink; + final Connection source; + final MonoSink> sink; Subscription s; boolean firstFrameReceived = false; @@ -49,8 +48,8 @@ class SetupHandlingDuplexConnection extends Flux boolean done; Throwable t; - SetupHandlingDuplexConnection( - DuplexConnection source, MonoSink> sink) { + SetupHandlingConnection( + Connection source, MonoSink> sink) { this.source = source; this.sink = sink; @@ -191,6 +190,6 @@ public ByteBufAllocator alloc() { @Override public String toString() { - return "SetupHandlingDuplexConnection{" + "source=" + source + ", done=" + done + '}'; + return "SetupHandlingConnection{" + "source=" + source + ", done=" + done + '}'; } } diff --git a/today-remoting/src/main/java/infra/remoting/core/SlowFireAndForgetRequesterMono.java b/today-remoting/src/main/java/infra/remoting/core/SlowFireAndForgetRequesterMono.java index accdfd6..b28e1cf 100644 --- a/today-remoting/src/main/java/infra/remoting/core/SlowFireAndForgetRequesterMono.java +++ b/today-remoting/src/main/java/infra/remoting/core/SlowFireAndForgetRequesterMono.java @@ -1,32 +1,28 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Subscription; import java.util.concurrent.atomic.AtomicLongFieldUpdater; -import infra.lang.NonNull; -import infra.lang.Nullable; -import infra.remoting.DuplexConnection; import infra.remoting.Payload; import infra.remoting.frame.FrameType; import infra.remoting.plugins.RequestInterceptor; -import io.netty.buffer.ByteBufAllocator; import io.netty.util.IllegalReferenceCountException; import reactor.core.CoreSubscriber; import reactor.core.Exceptions; @@ -55,29 +51,17 @@ final class SlowFireAndForgetRequesterMono extends Mono final Payload payload; - final ByteBufAllocator allocator; - final int mtu; - final int maxFrameLength; - final RequesterResponderSupport requesterResponderSupport; - final DuplexConnection connection; + final ChannelSupport channel; @Nullable final RequesterLeaseTracker requesterLeaseTracker; - @Nullable - final RequestInterceptor requestInterceptor; CoreSubscriber actual; - SlowFireAndForgetRequesterMono( - Payload payload, RequesterResponderSupport requesterResponderSupport) { - this.allocator = requesterResponderSupport.getAllocator(); + SlowFireAndForgetRequesterMono(Payload payload, ChannelSupport channel) { this.payload = payload; - this.mtu = requesterResponderSupport.getMtu(); - this.maxFrameLength = requesterResponderSupport.getMaxFrameLength(); - this.requesterResponderSupport = requesterResponderSupport; - this.connection = requesterResponderSupport.getDuplexConnection(); - this.requestInterceptor = requesterResponderSupport.getRequestInterceptor(); - this.requesterLeaseTracker = requesterResponderSupport.getRequesterLeaseTracker(); + this.channel = channel; + this.requesterLeaseTracker = channel.getRequesterLeaseTracker(); } @Override @@ -89,7 +73,7 @@ public void subscribe(CoreSubscriber actual) { final IllegalStateException e = new IllegalStateException("FireAndForgetMono allows only a single Subscriber"); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_FNF, null); } @@ -99,15 +83,14 @@ public void subscribe(CoreSubscriber actual) { } final Payload p = this.payload; - int mtu = this.mtu; try { - if (!isValid(mtu, this.maxFrameLength, p, false)) { + if (!isValid(channel.mtu, channel.maxFrameLength, p, false)) { lazyTerminate(STATE, this); final IllegalArgumentException e = new IllegalArgumentException( - String.format(INVALID_PAYLOAD_ERROR_MESSAGE, this.maxFrameLength)); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + String.format(INVALID_PAYLOAD_ERROR_MESSAGE, channel.maxFrameLength)); + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_FNF, p.metadata()); } @@ -121,7 +104,7 @@ public void subscribe(CoreSubscriber actual) { catch (IllegalReferenceCountException e) { lazyTerminate(STATE, this); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(e, FrameType.REQUEST_FNF, null); } @@ -157,13 +140,13 @@ void sendFirstFrame(Payload p) { final CoreSubscriber actual = this.actual; final int streamId; try { - streamId = this.requesterResponderSupport.getNextStreamId(); + streamId = this.channel.getNextStreamId(); } catch (Throwable t) { lazyTerminate(STATE, this); final Throwable ut = Exceptions.unwrap(t); - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(ut, FrameType.REQUEST_FNF, p.metadata()); } @@ -174,7 +157,7 @@ void sendFirstFrame(Payload p) { return; } - final RequestInterceptor interceptor = this.requestInterceptor; + final RequestInterceptor interceptor = channel.requestInterceptor; if (interceptor != null) { interceptor.onStart(streamId, FrameType.REQUEST_FNF, p.metadata()); } @@ -191,7 +174,7 @@ void sendFirstFrame(Payload p) { } sendReleasingPayload( - streamId, FrameType.REQUEST_FNF, mtu, p, this.connection, this.allocator, true); + streamId, FrameType.REQUEST_FNF, channel.mtu, p, channel.connection, channel.allocator, true); } catch (Throwable e) { lazyTerminate(STATE, this); @@ -240,7 +223,7 @@ public final void handlePermitError(Throwable cause) { } final Payload p = this.payload; - final RequestInterceptor requestInterceptor = this.requestInterceptor; + final RequestInterceptor requestInterceptor = channel.requestInterceptor; if (requestInterceptor != null) { requestInterceptor.onReject(cause, FrameType.REQUEST_RESPONSE, p.metadata()); } @@ -250,13 +233,13 @@ public final void handlePermitError(Throwable cause) { this.actual.onError(cause); } + @Nullable @Override public Object scanUnsafe(Attr key) { return null; // no particular key to be represented, still useful in hooks } @Override - @NonNull public String stepName() { return "source(FireAndForgetMono)"; } diff --git a/today-remoting/src/main/java/infra/remoting/core/StateUtils.java b/today-remoting/src/main/java/infra/remoting/core/StateUtils.java index 79a3624..e826236 100644 --- a/today-remoting/src/main/java/infra/remoting/core/StateUtils.java +++ b/today-remoting/src/main/java/infra/remoting/core/StateUtils.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; diff --git a/today-remoting/src/main/java/infra/remoting/core/StreamIdProvider.java b/today-remoting/src/main/java/infra/remoting/core/StreamIdProvider.java index b6c2780..338bc38 100644 --- a/today-remoting/src/main/java/infra/remoting/core/StreamIdProvider.java +++ b/today-remoting/src/main/java/infra/remoting/core/StreamIdProvider.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; diff --git a/today-remoting/src/main/java/infra/remoting/core/package-info.java b/today-remoting/src/main/java/infra/remoting/core/package-info.java index 064f588..054fe0b 100644 --- a/today-remoting/src/main/java/infra/remoting/core/package-info.java +++ b/today-remoting/src/main/java/infra/remoting/core/package-info.java @@ -1,29 +1,28 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /** - * Contains {@link infra.remoting.core.ChannelConnector RSocketConnector} and {@link - * infra.remoting.core.RemotingServer RemotingServer}, the main classes for connecting to or starting an - * RSocket server. + * Contains {@link infra.remoting.core.ChannelConnector ChannelConnector} and {@link + * infra.remoting.core.RemotingServer RemotingServer}, the main classes for connecting to or starting a + * protocol server. * *

    This package also contains a package private classes that implement support for the main - * RSocket interactions. + * interactions. */ -@NonNullApi +@NullMarked package infra.remoting.core; -import infra.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting/src/main/java/infra/remoting/exceptions/ApplicationErrorException.java b/today-remoting/src/main/java/infra/remoting/error/ApplicationErrorException.java similarity index 56% rename from today-remoting/src/main/java/infra/remoting/exceptions/ApplicationErrorException.java rename to today-remoting/src/main/java/infra/remoting/error/ApplicationErrorException.java index 32532bd..8977782 100644 --- a/today-remoting/src/main/java/infra/remoting/exceptions/ApplicationErrorException.java +++ b/today-remoting/src/main/java/infra/remoting/error/ApplicationErrorException.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.exceptions; +package infra.remoting.error; + +import org.jspecify.annotations.Nullable; -import infra.lang.Nullable; import infra.remoting.ProtocolErrorException; import infra.remoting.frame.ErrorFrameCodec; diff --git a/today-remoting/src/main/java/infra/remoting/exceptions/CanceledException.java b/today-remoting/src/main/java/infra/remoting/error/CanceledException.java similarity index 57% rename from today-remoting/src/main/java/infra/remoting/exceptions/CanceledException.java rename to today-remoting/src/main/java/infra/remoting/error/CanceledException.java index 1ccf54a..8d7a40d 100644 --- a/today-remoting/src/main/java/infra/remoting/exceptions/CanceledException.java +++ b/today-remoting/src/main/java/infra/remoting/error/CanceledException.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.exceptions; +package infra.remoting.error; + +import org.jspecify.annotations.Nullable; -import infra.lang.Nullable; import infra.remoting.ProtocolErrorException; import infra.remoting.frame.ErrorFrameCodec; diff --git a/today-remoting/src/main/java/infra/remoting/exceptions/ConnectionCloseException.java b/today-remoting/src/main/java/infra/remoting/error/ConnectionCloseException.java similarity index 58% rename from today-remoting/src/main/java/infra/remoting/exceptions/ConnectionCloseException.java rename to today-remoting/src/main/java/infra/remoting/error/ConnectionCloseException.java index e1c3576..9bd9605 100644 --- a/today-remoting/src/main/java/infra/remoting/exceptions/ConnectionCloseException.java +++ b/today-remoting/src/main/java/infra/remoting/error/ConnectionCloseException.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.exceptions; +package infra.remoting.error; + +import org.jspecify.annotations.Nullable; -import infra.lang.Nullable; import infra.remoting.ProtocolErrorException; import infra.remoting.frame.ErrorFrameCodec; diff --git a/today-remoting/src/main/java/infra/remoting/exceptions/ConnectionErrorException.java b/today-remoting/src/main/java/infra/remoting/error/ConnectionErrorException.java similarity index 58% rename from today-remoting/src/main/java/infra/remoting/exceptions/ConnectionErrorException.java rename to today-remoting/src/main/java/infra/remoting/error/ConnectionErrorException.java index 27f297d..a37bc7d 100644 --- a/today-remoting/src/main/java/infra/remoting/exceptions/ConnectionErrorException.java +++ b/today-remoting/src/main/java/infra/remoting/error/ConnectionErrorException.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.exceptions; +package infra.remoting.error; + +import org.jspecify.annotations.Nullable; -import infra.lang.Nullable; import infra.remoting.ProtocolErrorException; import infra.remoting.frame.ErrorFrameCodec; diff --git a/today-remoting/src/main/java/infra/remoting/exceptions/CustomProtocolException.java b/today-remoting/src/main/java/infra/remoting/error/CustomProtocolException.java similarity index 64% rename from today-remoting/src/main/java/infra/remoting/exceptions/CustomProtocolException.java rename to today-remoting/src/main/java/infra/remoting/error/CustomProtocolException.java index 1d66836..503f8bd 100644 --- a/today-remoting/src/main/java/infra/remoting/exceptions/CustomProtocolException.java +++ b/today-remoting/src/main/java/infra/remoting/error/CustomProtocolException.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.exceptions; +package infra.remoting.error; + +import org.jspecify.annotations.Nullable; -import infra.lang.Nullable; import infra.remoting.ProtocolErrorException; import infra.remoting.frame.ErrorFrameCodec; diff --git a/today-remoting/src/main/java/infra/remoting/exceptions/Exceptions.java b/today-remoting/src/main/java/infra/remoting/error/Exceptions.java similarity index 80% rename from today-remoting/src/main/java/infra/remoting/exceptions/Exceptions.java rename to today-remoting/src/main/java/infra/remoting/error/Exceptions.java index 04d83df..c5a46de 100644 --- a/today-remoting/src/main/java/infra/remoting/exceptions/Exceptions.java +++ b/today-remoting/src/main/java/infra/remoting/error/Exceptions.java @@ -1,21 +1,20 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.exceptions; +package infra.remoting.error; import java.util.Objects; diff --git a/today-remoting/src/main/java/infra/remoting/exceptions/InvalidException.java b/today-remoting/src/main/java/infra/remoting/error/InvalidException.java similarity index 54% rename from today-remoting/src/main/java/infra/remoting/exceptions/InvalidException.java rename to today-remoting/src/main/java/infra/remoting/error/InvalidException.java index fe338e0..059c4d9 100644 --- a/today-remoting/src/main/java/infra/remoting/exceptions/InvalidException.java +++ b/today-remoting/src/main/java/infra/remoting/error/InvalidException.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.exceptions; +package infra.remoting.error; + +import org.jspecify.annotations.Nullable; -import infra.lang.Nullable; import infra.remoting.ProtocolErrorException; import infra.remoting.frame.ErrorFrameCodec; diff --git a/today-remoting/src/main/java/infra/remoting/exceptions/InvalidSetupException.java b/today-remoting/src/main/java/infra/remoting/error/InvalidSetupException.java similarity index 55% rename from today-remoting/src/main/java/infra/remoting/exceptions/InvalidSetupException.java rename to today-remoting/src/main/java/infra/remoting/error/InvalidSetupException.java index 1830a0d..9e53dff 100644 --- a/today-remoting/src/main/java/infra/remoting/exceptions/InvalidSetupException.java +++ b/today-remoting/src/main/java/infra/remoting/error/InvalidSetupException.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.exceptions; +package infra.remoting.error; + +import org.jspecify.annotations.Nullable; -import infra.lang.Nullable; import infra.remoting.frame.ErrorFrameCodec; /** diff --git a/today-remoting/src/main/java/infra/remoting/exceptions/RejectedException.java b/today-remoting/src/main/java/infra/remoting/error/RejectedException.java similarity index 59% rename from today-remoting/src/main/java/infra/remoting/exceptions/RejectedException.java rename to today-remoting/src/main/java/infra/remoting/error/RejectedException.java index 79ffc0f..323dbd3 100644 --- a/today-remoting/src/main/java/infra/remoting/exceptions/RejectedException.java +++ b/today-remoting/src/main/java/infra/remoting/error/RejectedException.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.exceptions; +package infra.remoting.error; + +import org.jspecify.annotations.Nullable; -import infra.lang.Nullable; import infra.remoting.ProtocolErrorException; import infra.remoting.frame.ErrorFrameCodec; diff --git a/today-remoting/src/main/java/infra/remoting/exceptions/RejectedResumeException.java b/today-remoting/src/main/java/infra/remoting/error/RejectedResumeException.java similarity index 56% rename from today-remoting/src/main/java/infra/remoting/exceptions/RejectedResumeException.java rename to today-remoting/src/main/java/infra/remoting/error/RejectedResumeException.java index 1229166..c8a26ed 100644 --- a/today-remoting/src/main/java/infra/remoting/exceptions/RejectedResumeException.java +++ b/today-remoting/src/main/java/infra/remoting/error/RejectedResumeException.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.exceptions; +package infra.remoting.error; + +import org.jspecify.annotations.Nullable; -import infra.lang.Nullable; import infra.remoting.ProtocolErrorException; import infra.remoting.frame.ErrorFrameCodec; diff --git a/today-remoting/src/main/java/infra/remoting/exceptions/RejectedSetupException.java b/today-remoting/src/main/java/infra/remoting/error/RejectedSetupException.java similarity index 55% rename from today-remoting/src/main/java/infra/remoting/exceptions/RejectedSetupException.java rename to today-remoting/src/main/java/infra/remoting/error/RejectedSetupException.java index 1dd9b03..b6b2839 100644 --- a/today-remoting/src/main/java/infra/remoting/exceptions/RejectedSetupException.java +++ b/today-remoting/src/main/java/infra/remoting/error/RejectedSetupException.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.exceptions; +package infra.remoting.error; + +import org.jspecify.annotations.Nullable; -import infra.lang.Nullable; import infra.remoting.frame.ErrorFrameCodec; /** diff --git a/today-remoting/src/main/java/infra/remoting/error/Retryable.java b/today-remoting/src/main/java/infra/remoting/error/Retryable.java new file mode 100644 index 0000000..39d3002 --- /dev/null +++ b/today-remoting/src/main/java/infra/remoting/error/Retryable.java @@ -0,0 +1,25 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.error; + +/** + * Indicates that an exception is retryable. This interface is a marker and the strategy for + * retrying and operation that causes a {@link Retryable} to be thrown is not specified. + */ +public interface Retryable { + +} diff --git a/today-remoting/src/main/java/infra/remoting/error/SetupException.java b/today-remoting/src/main/java/infra/remoting/error/SetupException.java new file mode 100644 index 0000000..3e654a6 --- /dev/null +++ b/today-remoting/src/main/java/infra/remoting/error/SetupException.java @@ -0,0 +1,38 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.error; + +import org.jspecify.annotations.Nullable; + +import infra.remoting.ProtocolErrorException; + +/** The root of the setup exception hierarchy. */ +public abstract class SetupException extends ProtocolErrorException { + + private static final long serialVersionUID = -2928269501877732756L; + + /** + * Constructs a new exception with the specified error code, message and cause. + * + * @param errorCode the protocol code + * @param message the message + * @param cause the cause of this exception + */ + public SetupException(int errorCode, String message, @Nullable Throwable cause) { + super(errorCode, message, cause); + } +} diff --git a/today-remoting/src/main/java/infra/remoting/exceptions/UnsupportedSetupException.java b/today-remoting/src/main/java/infra/remoting/error/UnsupportedSetupException.java similarity index 55% rename from today-remoting/src/main/java/infra/remoting/exceptions/UnsupportedSetupException.java rename to today-remoting/src/main/java/infra/remoting/error/UnsupportedSetupException.java index 8159614..409da10 100644 --- a/today-remoting/src/main/java/infra/remoting/exceptions/UnsupportedSetupException.java +++ b/today-remoting/src/main/java/infra/remoting/error/UnsupportedSetupException.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.exceptions; +package infra.remoting.error; + +import org.jspecify.annotations.Nullable; -import infra.lang.Nullable; import infra.remoting.frame.ErrorFrameCodec; /** diff --git a/today-remoting/src/main/java/infra/remoting/error/package-info.java b/today-remoting/src/main/java/infra/remoting/error/package-info.java new file mode 100644 index 0000000..9c63461 --- /dev/null +++ b/today-remoting/src/main/java/infra/remoting/error/package-info.java @@ -0,0 +1,26 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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. + */ + +/** + * A hierarchy of exceptions that represent protocol error codes. + * + * @see Error + * Codes + */ +@NullMarked +package infra.remoting.error; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting/src/main/java/infra/remoting/exceptions/Retryable.java b/today-remoting/src/main/java/infra/remoting/exceptions/Retryable.java deleted file mode 100644 index 8d6f3c9..0000000 --- a/today-remoting/src/main/java/infra/remoting/exceptions/Retryable.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.exceptions; - -/** - * Indicates that an exception is retryable. This interface is a marker and the strategy for - * retrying and operation that causes a {@link Retryable} to be thrown is not specified. - */ -public interface Retryable { - -} diff --git a/today-remoting/src/main/java/infra/remoting/exceptions/SetupException.java b/today-remoting/src/main/java/infra/remoting/exceptions/SetupException.java deleted file mode 100644 index 8904793..0000000 --- a/today-remoting/src/main/java/infra/remoting/exceptions/SetupException.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.exceptions; - -import infra.lang.Nullable; -import infra.remoting.ProtocolErrorException; - -/** The root of the setup exception hierarchy. */ -public abstract class SetupException extends ProtocolErrorException { - - private static final long serialVersionUID = -2928269501877732756L; - - /** - * Constructs a new exception with the specified error code, message and cause. - * - * @param errorCode the RSocket protocol code - * @param message the message - * @param cause the cause of this exception - */ - public SetupException(int errorCode, String message, @Nullable Throwable cause) { - super(errorCode, message, cause); - } -} diff --git a/today-remoting/src/main/java/infra/remoting/exceptions/package-info.java b/today-remoting/src/main/java/infra/remoting/exceptions/package-info.java deleted file mode 100644 index 09f733a..0000000 --- a/today-remoting/src/main/java/infra/remoting/exceptions/package-info.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -/** - * A hierarchy of exceptions that represent RSocket protocol error codes. - * - * @see Error - * Codes - */ -@NonNullApi -package infra.remoting.exceptions; - -import infra.lang.NonNullApi; diff --git a/today-remoting/src/main/java/infra/remoting/frame/CancelFrameCodec.java b/today-remoting/src/main/java/infra/remoting/frame/CancelFrameCodec.java index 4ee059c..16f72ac 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/CancelFrameCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/CancelFrameCodec.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/main/java/infra/remoting/frame/ErrorFrameCodec.java b/today-remoting/src/main/java/infra/remoting/frame/ErrorFrameCodec.java index ff84222..b67cbdf 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/ErrorFrameCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/ErrorFrameCodec.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/main/java/infra/remoting/frame/ExtensionFrameCodec.java b/today-remoting/src/main/java/infra/remoting/frame/ExtensionFrameCodec.java index dd72620..78dd1ed 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/ExtensionFrameCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/ExtensionFrameCodec.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; + import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; diff --git a/today-remoting/src/main/java/infra/remoting/frame/FragmentationCodec.java b/today-remoting/src/main/java/infra/remoting/frame/FragmentationCodec.java deleted file mode 100644 index e74bef6..0000000 --- a/today-remoting/src/main/java/infra/remoting/frame/FragmentationCodec.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.frame; - -import infra.lang.Nullable; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; - -/** FragmentationFlyweight is used to re-assemble frames */ -public class FragmentationCodec { - public static ByteBuf encode(final ByteBufAllocator allocator, ByteBuf header, ByteBuf data) { - return encode(allocator, header, null, data); - } - - public static ByteBuf encode( - final ByteBufAllocator allocator, ByteBuf header, @Nullable ByteBuf metadata, ByteBuf data) { - - final boolean hasMetadata = metadata != null; - return FrameBodyCodec.encode(allocator, header, metadata, hasMetadata, data); - } -} diff --git a/today-remoting/src/main/java/infra/remoting/frame/FrameBodyCodec.java b/today-remoting/src/main/java/infra/remoting/frame/FrameBodyCodec.java index 66d68e5..ca0b2a2 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/FrameBodyCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/FrameBodyCodec.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; + import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; diff --git a/today-remoting/src/main/java/infra/remoting/frame/FrameHeaderCodec.java b/today-remoting/src/main/java/infra/remoting/frame/FrameHeaderCodec.java index 5c21f3b..ddbc2a2 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/FrameHeaderCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/FrameHeaderCodec.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; @@ -45,7 +44,7 @@ public final class FrameHeaderCodec { /** (N)ext: bit to indicate payload or metadata present ({@link Subscriber#onNext(Object)}) */ public static final int FLAGS_N = 0b00_0010_0000; - public static final String DISABLE_FRAME_TYPE_CHECK = "io.rsocket.frames.disableFrameTypeCheck"; + public static final String DISABLE_FRAME_TYPE_CHECK = "infra.remoting.frames.disableFrameTypeCheck"; private static final int FRAME_FLAGS_MASK = 0b0000_0011_1111_1111; private static final int FRAME_TYPE_BITS = 6; private static final int FRAME_TYPE_SHIFT = 16 - FRAME_TYPE_BITS; diff --git a/today-remoting/src/main/java/infra/remoting/frame/FrameLengthCodec.java b/today-remoting/src/main/java/infra/remoting/frame/FrameLengthCodec.java index 3d93d1d..1259a24 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/FrameLengthCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/FrameLengthCodec.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; @@ -21,14 +20,16 @@ import io.netty.buffer.ByteBufAllocator; /** - * Some transports like TCP aren't framed, and require a length. This is used by DuplexConnections + * Some transports like TCP aren't framed, and require a length. This is used by Connections * for transports that need to send length */ public class FrameLengthCodec { + public static final int FRAME_LENGTH_MASK = 0xFFFFFF; public static final int FRAME_LENGTH_SIZE = 3; - private FrameLengthCodec() { } + private FrameLengthCodec() { + } private static void encodeLength(final ByteBuf byteBuf, final int length) { if ((length & ~FRAME_LENGTH_MASK) != 0) { @@ -51,7 +52,8 @@ private static int decodeLength(final ByteBuf byteBuf) { public static ByteBuf encode(ByteBufAllocator allocator, int length, ByteBuf frame) { ByteBuf buffer = allocator.buffer(); encodeLength(buffer, length); - return allocator.compositeBuffer(2).addComponents(true, buffer, frame); + return allocator.compositeBuffer(2) + .addComponents(true, buffer, frame); } public static int length(ByteBuf byteBuf) { diff --git a/today-remoting/src/main/java/infra/remoting/frame/FrameType.java b/today-remoting/src/main/java/infra/remoting/frame/FrameType.java index 714d0e2..de71673 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/FrameType.java +++ b/today-remoting/src/main/java/infra/remoting/frame/FrameType.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/main/java/infra/remoting/frame/FrameUtil.java b/today-remoting/src/main/java/infra/remoting/frame/FrameUtil.java index f96f354..055b423 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/FrameUtil.java +++ b/today-remoting/src/main/java/infra/remoting/frame/FrameUtil.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; @@ -22,7 +21,8 @@ public class FrameUtil { - private FrameUtil() { } + private FrameUtil() { + } public static String toString(ByteBuf frame) { FrameType frameType = FrameHeaderCodec.frameType(frame); diff --git a/today-remoting/src/main/java/infra/remoting/frame/GenericFrameCodec.java b/today-remoting/src/main/java/infra/remoting/frame/GenericFrameCodec.java index a46313e..514b76e 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/GenericFrameCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/GenericFrameCodec.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; + import infra.remoting.Payload; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; diff --git a/today-remoting/src/main/java/infra/remoting/frame/KeepAliveFrameCodec.java b/today-remoting/src/main/java/infra/remoting/frame/KeepAliveFrameCodec.java index 40a1045..f1162c1 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/KeepAliveFrameCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/KeepAliveFrameCodec.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/main/java/infra/remoting/frame/LeaseFrameCodec.java b/today-remoting/src/main/java/infra/remoting/frame/LeaseFrameCodec.java index cd86870..ab2e8ef 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/LeaseFrameCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/LeaseFrameCodec.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; + import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; diff --git a/today-remoting/src/main/java/infra/remoting/frame/MetadataPushFrameCodec.java b/today-remoting/src/main/java/infra/remoting/frame/MetadataPushFrameCodec.java index d3cad41..d2e6496 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/MetadataPushFrameCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/MetadataPushFrameCodec.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/main/java/infra/remoting/frame/PayloadFrameCodec.java b/today-remoting/src/main/java/infra/remoting/frame/PayloadFrameCodec.java index 2ad68f6..d1124c8 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/PayloadFrameCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/PayloadFrameCodec.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; + import infra.remoting.Payload; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; diff --git a/today-remoting/src/main/java/infra/remoting/frame/RequestChannelFrameCodec.java b/today-remoting/src/main/java/infra/remoting/frame/RequestChannelFrameCodec.java index abcdf3e..cf1ad75 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/RequestChannelFrameCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/RequestChannelFrameCodec.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; + import infra.remoting.Payload; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; diff --git a/today-remoting/src/main/java/infra/remoting/frame/RequestFireAndForgetFrameCodec.java b/today-remoting/src/main/java/infra/remoting/frame/RequestFireAndForgetFrameCodec.java index c1761aa..709747b 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/RequestFireAndForgetFrameCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/RequestFireAndForgetFrameCodec.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; + import infra.remoting.Payload; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; diff --git a/today-remoting/src/main/java/infra/remoting/frame/RequestNFrameCodec.java b/today-remoting/src/main/java/infra/remoting/frame/RequestNFrameCodec.java index a7d01f2..3b3dffc 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/RequestNFrameCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/RequestNFrameCodec.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/main/java/infra/remoting/frame/RequestResponseFrameCodec.java b/today-remoting/src/main/java/infra/remoting/frame/RequestResponseFrameCodec.java index 174b0f5..9814fab 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/RequestResponseFrameCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/RequestResponseFrameCodec.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; + import infra.remoting.Payload; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; diff --git a/today-remoting/src/main/java/infra/remoting/frame/RequestStreamFrameCodec.java b/today-remoting/src/main/java/infra/remoting/frame/RequestStreamFrameCodec.java index 29d9281..7d771dd 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/RequestStreamFrameCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/RequestStreamFrameCodec.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; + import infra.remoting.Payload; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; diff --git a/today-remoting/src/main/java/infra/remoting/frame/ResumeFrameCodec.java b/today-remoting/src/main/java/infra/remoting/frame/ResumeFrameCodec.java index 63bbbff..cd84eea 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/ResumeFrameCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/ResumeFrameCodec.java @@ -1,36 +1,29 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; -import java.util.UUID; - import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; public class ResumeFrameCodec { static final int CURRENT_VERSION = SetupFrameCodec.CURRENT_VERSION; - public static ByteBuf encode( - ByteBufAllocator allocator, - ByteBuf token, - long lastReceivedServerPos, - long firstAvailableClientPos) { + public static ByteBuf encode(ByteBufAllocator allocator, ByteBuf token, + long lastReceivedServerPos, long firstAvailableClientPos) { ByteBuf byteBuf = FrameHeaderCodec.encodeStreamZero(allocator, FrameType.RESUME, 0); byteBuf.writeInt(CURRENT_VERSION); @@ -104,11 +97,4 @@ public static long firstAvailableClientPos(ByteBuf byteBuf) { return firstAvailableClientPos; } - public static ByteBuf generateResumeToken() { - UUID uuid = UUID.randomUUID(); - ByteBuf bb = Unpooled.buffer(16); - bb.writeLong(uuid.getMostSignificantBits()); - bb.writeLong(uuid.getLeastSignificantBits()); - return bb; - } } diff --git a/today-remoting/src/main/java/infra/remoting/frame/ResumeOkFrameCodec.java b/today-remoting/src/main/java/infra/remoting/frame/ResumeOkFrameCodec.java index 644dc83..00d1af7 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/ResumeOkFrameCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/ResumeOkFrameCodec.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/main/java/infra/remoting/frame/SetupFrameCodec.java b/today-remoting/src/main/java/infra/remoting/frame/SetupFrameCodec.java index b219db2..818b2d1 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/SetupFrameCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/SetupFrameCodec.java @@ -1,25 +1,25 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; +import org.jspecify.annotations.Nullable; + import java.nio.charset.StandardCharsets; -import infra.lang.Nullable; import infra.remoting.Payload; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; diff --git a/today-remoting/src/main/java/infra/remoting/frame/VersionCodec.java b/today-remoting/src/main/java/infra/remoting/frame/VersionCodec.java index 039b177..fae411b 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/VersionCodec.java +++ b/today-remoting/src/main/java/infra/remoting/frame/VersionCodec.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/main/java/infra/remoting/frame/decoder/DefaultPayloadDecoder.java b/today-remoting/src/main/java/infra/remoting/frame/decoder/DefaultPayloadDecoder.java index 3c1b604..9781717 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/decoder/DefaultPayloadDecoder.java +++ b/today-remoting/src/main/java/infra/remoting/frame/decoder/DefaultPayloadDecoder.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame.decoder; @@ -32,11 +31,13 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -/** Default Frame decoder that copies the frames contents for easy of use. */ +/** + * Default Frame decoder that copies the frames contents for easy of use. + */ class DefaultPayloadDecoder implements PayloadDecoder { @Override - public Payload apply(ByteBuf byteBuf) { + public Payload decode(ByteBuf byteBuf) { ByteBuf d; FrameType type = FrameHeaderCodec.frameType(byteBuf); ByteBuf m = switch (type) { diff --git a/today-remoting/src/main/java/infra/remoting/frame/decoder/PayloadDecoder.java b/today-remoting/src/main/java/infra/remoting/frame/decoder/PayloadDecoder.java index 25d0a6e..8b3eb24 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/decoder/PayloadDecoder.java +++ b/today-remoting/src/main/java/infra/remoting/frame/decoder/PayloadDecoder.java @@ -1,28 +1,30 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame.decoder; -import java.util.function.Function; - import infra.remoting.Payload; import io.netty.buffer.ByteBuf; -public interface PayloadDecoder extends Function { +public interface PayloadDecoder { + PayloadDecoder DEFAULT = new DefaultPayloadDecoder(); + PayloadDecoder ZERO_COPY = new ZeroCopyPayloadDecoder(); + + Payload decode(ByteBuf byteBuf); + } diff --git a/today-remoting/src/main/java/infra/remoting/frame/decoder/ZeroCopyPayloadDecoder.java b/today-remoting/src/main/java/infra/remoting/frame/decoder/ZeroCopyPayloadDecoder.java index d5c4f77..86fd1d4 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/decoder/ZeroCopyPayloadDecoder.java +++ b/today-remoting/src/main/java/infra/remoting/frame/decoder/ZeroCopyPayloadDecoder.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame.decoder; @@ -35,40 +34,39 @@ * for releasing the payload to free memory when they no long need it. */ public class ZeroCopyPayloadDecoder implements PayloadDecoder { + @Override - public Payload apply(ByteBuf byteBuf) { - ByteBuf m; - ByteBuf d; + public Payload decode(ByteBuf byteBuf) { FrameType type = FrameHeaderCodec.frameType(byteBuf); - switch (type) { - case REQUEST_FNF: + + ByteBuf d; + ByteBuf m = switch (type) { + case REQUEST_FNF -> { d = RequestFireAndForgetFrameCodec.data(byteBuf); - m = RequestFireAndForgetFrameCodec.metadata(byteBuf); - break; - case REQUEST_RESPONSE: + yield RequestFireAndForgetFrameCodec.metadata(byteBuf); + } + case REQUEST_RESPONSE -> { d = RequestResponseFrameCodec.data(byteBuf); - m = RequestResponseFrameCodec.metadata(byteBuf); - break; - case REQUEST_STREAM: + yield RequestResponseFrameCodec.metadata(byteBuf); + } + case REQUEST_STREAM -> { d = RequestStreamFrameCodec.data(byteBuf); - m = RequestStreamFrameCodec.metadata(byteBuf); - break; - case REQUEST_CHANNEL: + yield RequestStreamFrameCodec.metadata(byteBuf); + } + case REQUEST_CHANNEL -> { d = RequestChannelFrameCodec.data(byteBuf); - m = RequestChannelFrameCodec.metadata(byteBuf); - break; - case NEXT: - case NEXT_COMPLETE: + yield RequestChannelFrameCodec.metadata(byteBuf); + } + case NEXT, NEXT_COMPLETE -> { d = PayloadFrameCodec.data(byteBuf); - m = PayloadFrameCodec.metadata(byteBuf); - break; - case METADATA_PUSH: + yield PayloadFrameCodec.metadata(byteBuf); + } + case METADATA_PUSH -> { d = Unpooled.EMPTY_BUFFER; - m = MetadataPushFrameCodec.metadata(byteBuf); - break; - default: - throw new IllegalArgumentException("unsupported frame type: " + type); - } + yield MetadataPushFrameCodec.metadata(byteBuf); + } + default -> throw new IllegalArgumentException("unsupported frame type: " + type); + }; return ByteBufPayload.create(d.retain(), m != null ? m.retain() : null); } diff --git a/today-remoting/src/main/java/infra/remoting/frame/decoder/package-info.java b/today-remoting/src/main/java/infra/remoting/frame/decoder/package-info.java index f85bbae..14bd32a 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/decoder/package-info.java +++ b/today-remoting/src/main/java/infra/remoting/frame/decoder/package-info.java @@ -1,25 +1,24 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /** - * Support for encoding and decoding of RSocket frames to and from {@link infra.remoting.Payload + * Support for encoding and decoding of protocol frames to and from {@link infra.remoting.Payload * Payload}. */ -@NonNullApi +@NullMarked package infra.remoting.frame.decoder; -import infra.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting/src/main/java/infra/remoting/frame/package-info.java b/today-remoting/src/main/java/infra/remoting/frame/package-info.java index f411668..51715a5 100644 --- a/today-remoting/src/main/java/infra/remoting/frame/package-info.java +++ b/today-remoting/src/main/java/infra/remoting/frame/package-info.java @@ -1,25 +1,24 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /** - * Support for encoding and decoding of RSocket frames to and from {@link infra.remoting.Payload + * Support for encoding and decoding of protocol frames to and from {@link infra.remoting.Payload * Payload}. */ -@NonNullApi +@NullMarked package infra.remoting.frame; -import infra.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting/src/main/java/infra/remoting/internal/BaseDuplexConnection.java b/today-remoting/src/main/java/infra/remoting/internal/BaseConnection.java similarity index 52% rename from today-remoting/src/main/java/infra/remoting/internal/BaseDuplexConnection.java rename to today-remoting/src/main/java/infra/remoting/internal/BaseConnection.java index 28b57c7..181013e 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/BaseDuplexConnection.java +++ b/today-remoting/src/main/java/infra/remoting/internal/BaseConnection.java @@ -1,34 +1,33 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.internal; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import io.netty.buffer.ByteBuf; import reactor.core.Scannable; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; -public abstract class BaseDuplexConnection implements DuplexConnection { +public abstract class BaseConnection implements Connection { protected final Sinks.Empty onClose = Sinks.empty(); protected final UnboundedProcessor sender = new UnboundedProcessor(onClose::tryEmitEmpty); - public BaseDuplexConnection() { + public BaseConnection() { } @Override diff --git a/today-remoting/src/main/java/infra/remoting/internal/UnboundedProcessor.java b/today-remoting/src/main/java/infra/remoting/internal/UnboundedProcessor.java index 4cf46c2..3c7d820 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/UnboundedProcessor.java +++ b/today-remoting/src/main/java/infra/remoting/internal/UnboundedProcessor.java @@ -1,22 +1,22 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.internal; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Subscription; import java.util.Objects; @@ -26,7 +26,6 @@ import java.util.concurrent.atomic.AtomicLongFieldUpdater; import java.util.stream.Stream; -import infra.lang.Nullable; import infra.remoting.internal.jctools.queues.MpscUnboundedArrayQueue; import io.netty.buffer.ByteBuf; import reactor.core.CoreSubscriber; diff --git a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/BaseLinkedQueue.java b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/BaseLinkedQueue.java index ef88f3b..00287c3 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/BaseLinkedQueue.java +++ b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/BaseLinkedQueue.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /* diff --git a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/BaseMpscLinkedArrayQueue.java b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/BaseMpscLinkedArrayQueue.java index 1b5987c..593ae36 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/BaseMpscLinkedArrayQueue.java +++ b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/BaseMpscLinkedArrayQueue.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /* diff --git a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/IndexedQueueSizeUtil.java b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/IndexedQueueSizeUtil.java index 014567b..f078f60 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/IndexedQueueSizeUtil.java +++ b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/IndexedQueueSizeUtil.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /* diff --git a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/LinkedArrayQueueUtil.java b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/LinkedArrayQueueUtil.java index 57c32c4..065f83d 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/LinkedArrayQueueUtil.java +++ b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/LinkedArrayQueueUtil.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /* diff --git a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/LinkedQueueNode.java b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/LinkedQueueNode.java index b56a3ec..1e58bb8 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/LinkedQueueNode.java +++ b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/LinkedQueueNode.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /* diff --git a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/MessagePassingQueue.java b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/MessagePassingQueue.java index d6954bf..3f0690a 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/MessagePassingQueue.java +++ b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/MessagePassingQueue.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /* diff --git a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/MessagePassingQueueUtil.java b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/MessagePassingQueueUtil.java index c36a5fa..b97b548 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/MessagePassingQueueUtil.java +++ b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/MessagePassingQueueUtil.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /* diff --git a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/MpscUnboundedArrayQueue.java b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/MpscUnboundedArrayQueue.java index c2a3413..8e1f4ae 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/MpscUnboundedArrayQueue.java +++ b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/MpscUnboundedArrayQueue.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /* diff --git a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/PortableJvmInfo.java b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/PortableJvmInfo.java index 1c17ee7..ea425b8 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/PortableJvmInfo.java +++ b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/PortableJvmInfo.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /* diff --git a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/Pow2.java b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/Pow2.java index 17bc5a6..cb27084 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/Pow2.java +++ b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/Pow2.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /* diff --git a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/QueueProgressIndicators.java b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/QueueProgressIndicators.java index e5683eb..f0a9ac0 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/QueueProgressIndicators.java +++ b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/QueueProgressIndicators.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /* diff --git a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/RangeUtil.java b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/RangeUtil.java index 371970b..2ee9495 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/RangeUtil.java +++ b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/RangeUtil.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /* @@ -39,30 +38,6 @@ public static long checkPositive(long n, String name) { return n; } - public static int checkPositiveOrZero(int n, String name) { - if (n < 0) { - throw new IllegalArgumentException(name + ": " + n + " (expected: >= 0)"); - } - - return n; - } - - public static int checkLessThan(int n, int expected, String name) { - if (n >= expected) { - throw new IllegalArgumentException(name + ": " + n + " (expected: < " + expected + ')'); - } - - return n; - } - - public static int checkLessThanOrEqual(int n, long expected, String name) { - if (n > expected) { - throw new IllegalArgumentException(name + ": " + n + " (expected: <= " + expected + ')'); - } - - return n; - } - public static int checkGreaterThanOrEqual(int n, int expected, String name) { if (n < expected) { throw new IllegalArgumentException(name + ": " + n + " (expected: >= " + expected + ')'); diff --git a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/UnsafeAccess.java b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/UnsafeAccess.java index a425706..48d2e52 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/UnsafeAccess.java +++ b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/UnsafeAccess.java @@ -1,26 +1,11 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -/* * 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 + * https://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, @@ -28,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package infra.remoting.internal.jctools.queues; import java.lang.reflect.Constructor; @@ -52,14 +38,10 @@ * @author nitsanw */ class UnsafeAccess { - public static final boolean SUPPORTS_GET_AND_SET_REF; - public static final boolean SUPPORTS_GET_AND_ADD_LONG; public static final Unsafe UNSAFE; static { UNSAFE = getUnsafe(); - SUPPORTS_GET_AND_SET_REF = hasGetAndSetSupport(); - SUPPORTS_GET_AND_ADD_LONG = hasGetAndAddLongSupport(); } private static Unsafe getUnsafe() { @@ -87,26 +69,6 @@ private static Unsafe getUnsafe() { return instance; } - private static boolean hasGetAndSetSupport() { - try { - Unsafe.class.getMethod("getAndSetObject", Object.class, Long.TYPE, Object.class); - return true; - } - catch (Exception ignored) { - } - return false; - } - - private static boolean hasGetAndAddLongSupport() { - try { - Unsafe.class.getMethod("getAndAddLong", Object.class, Long.TYPE, Long.TYPE); - return true; - } - catch (Exception ignored) { - } - return false; - } - public static long fieldOffset(Class clz, String fieldName) throws RuntimeException { try { return UNSAFE.objectFieldOffset(clz.getDeclaredField(fieldName)); diff --git a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/UnsafeRefArrayAccess.java b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/UnsafeRefArrayAccess.java index 2bfca17..d790a44 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/UnsafeRefArrayAccess.java +++ b/today-remoting/src/main/java/infra/remoting/internal/jctools/queues/UnsafeRefArrayAccess.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /* diff --git a/today-remoting/src/main/java/infra/remoting/internal/package-info.java b/today-remoting/src/main/java/infra/remoting/internal/package-info.java index e2ea92f..3eee235 100644 --- a/today-remoting/src/main/java/infra/remoting/internal/package-info.java +++ b/today-remoting/src/main/java/infra/remoting/internal/package-info.java @@ -1,27 +1,24 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /** * Internal package and must not be used outside this project. There are no guarantees for * API compatibility. */ -@NonNullApi -@NonNullFields +@NullMarked package infra.remoting.internal; -import infra.lang.NonNullApi; -import infra.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting/src/main/java/infra/remoting/keepalive/KeepAliveFramesAcceptor.java b/today-remoting/src/main/java/infra/remoting/keepalive/KeepAliveFramesAcceptor.java index bf6cad9..08481a3 100644 --- a/today-remoting/src/main/java/infra/remoting/keepalive/KeepAliveFramesAcceptor.java +++ b/today-remoting/src/main/java/infra/remoting/keepalive/KeepAliveFramesAcceptor.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.keepalive; diff --git a/today-remoting/src/main/java/infra/remoting/keepalive/KeepAliveHandler.java b/today-remoting/src/main/java/infra/remoting/keepalive/KeepAliveHandler.java index 534bfd2..04744ba 100644 --- a/today-remoting/src/main/java/infra/remoting/keepalive/KeepAliveHandler.java +++ b/today-remoting/src/main/java/infra/remoting/keepalive/KeepAliveHandler.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.keepalive; @@ -21,7 +20,7 @@ import infra.remoting.keepalive.KeepAliveSupport.KeepAlive; import infra.remoting.resume.ChannelSession; -import infra.remoting.resume.ResumableDuplexConnection; +import infra.remoting.resume.ResumableConnection; import infra.remoting.resume.ResumeStateHolder; import io.netty.buffer.ByteBuf; @@ -44,15 +43,15 @@ public KeepAliveFramesAcceptor start(KeepAliveSupport keepAliveSupport, class ResumableKeepAliveHandler implements KeepAliveHandler { - private final ResumableDuplexConnection resumableDuplexConnection; + private final ResumableConnection resumableConnection; private final ChannelSession channelSession; private final ResumeStateHolder resumeStateHolder; - public ResumableKeepAliveHandler(ResumableDuplexConnection resumableDuplexConnection, + public ResumableKeepAliveHandler(ResumableConnection resumableConnection, ChannelSession channelSession, ResumeStateHolder resumeStateHolder) { - this.resumableDuplexConnection = resumableDuplexConnection; + this.resumableConnection = resumableConnection; this.channelSession = channelSession; this.resumeStateHolder = resumeStateHolder; } @@ -65,7 +64,7 @@ public KeepAliveFramesAcceptor start(KeepAliveSupport keepAliveSupport, return keepAliveSupport .resumeState(resumeStateHolder) .onSendKeepAliveFrame(onSendKeepAliveFrame) - .onTimeout(keepAlive -> resumableDuplexConnection.disconnect()) + .onTimeout(keepAlive -> resumableConnection.disconnect()) .start(); } } diff --git a/today-remoting/src/main/java/infra/remoting/keepalive/KeepAliveSupport.java b/today-remoting/src/main/java/infra/remoting/keepalive/KeepAliveSupport.java index 7642c44..db1e899 100644 --- a/today-remoting/src/main/java/infra/remoting/keepalive/KeepAliveSupport.java +++ b/today-remoting/src/main/java/infra/remoting/keepalive/KeepAliveSupport.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.keepalive; diff --git a/today-remoting/src/main/java/infra/remoting/keepalive/package-info.java b/today-remoting/src/main/java/infra/remoting/keepalive/package-info.java index aedaf34..6f94811 100644 --- a/today-remoting/src/main/java/infra/remoting/keepalive/package-info.java +++ b/today-remoting/src/main/java/infra/remoting/keepalive/package-info.java @@ -1,26 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /** * Support classes for sending and keeping track of KEEPALIVE frames from the remote. */ -@NonNullApi -@NonNullFields +@NullMarked package infra.remoting.keepalive; -import infra.lang.NonNullApi; -import infra.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting/src/main/java/infra/remoting/lb/BaseWeightedStats.java b/today-remoting/src/main/java/infra/remoting/lb/BaseWeightedStats.java index 95dae53..335242d 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/BaseWeightedStats.java +++ b/today-remoting/src/main/java/infra/remoting/lb/BaseWeightedStats.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; diff --git a/today-remoting/src/main/java/infra/remoting/lb/ChannelPool.java b/today-remoting/src/main/java/infra/remoting/lb/ChannelPool.java index 8dcb4f9..1b786ce 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/ChannelPool.java +++ b/today-remoting/src/main/java/infra/remoting/lb/ChannelPool.java @@ -1,21 +1,21 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import org.reactivestreams.Subscription; @@ -29,7 +29,6 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.stream.Collectors; -import infra.lang.Nullable; import infra.remoting.Channel; import infra.remoting.Closeable; import infra.remoting.Payload; @@ -44,8 +43,8 @@ class ChannelPool extends ResolvingOperator implements CoreSubscriber>, Closeable { - static final AtomicReferenceFieldUpdater ACTIVE_SOCKETS = - AtomicReferenceFieldUpdater.newUpdater(ChannelPool.class, PooledChannel[].class, "activeSockets"); + static final AtomicReferenceFieldUpdater ACTIVE_CHANNELS = + AtomicReferenceFieldUpdater.newUpdater(ChannelPool.class, PooledChannel[].class, "activeChannels"); static final PooledChannel[] EMPTY = new PooledChannel[0]; static final PooledChannel[] TERMINATED = new PooledChannel[0]; @@ -53,7 +52,7 @@ class ChannelPool extends ResolvingOperator implements CoreSubscriber
  • S = AtomicReferenceFieldUpdater.newUpdater(ChannelPool.class, Subscription.class, "s"); - final DeferredResolutionChannel deferredResolutionRSocket = new DeferredResolutionChannel(this); + final DeferredResolutionChannel deferredResolutionChannel = new DeferredResolutionChannel(this); final ChannelConnector connector; @@ -61,14 +60,14 @@ class ChannelPool extends ResolvingOperator implements CoreSubscriber
  • onAllClosedSink = Sinks.unsafe().empty(); - volatile PooledChannel[] activeSockets; + volatile PooledChannel[] activeChannels; volatile Subscription s; public ChannelPool(ChannelConnector connector, Publisher> targetPublisher, LoadBalanceStrategy loadbalanceStrategy) { this.connector = connector; this.loadbalanceStrategy = loadbalanceStrategy; - ACTIVE_SOCKETS.lazySet(this, EMPTY); + ACTIVE_CHANNELS.lazySet(this, EMPTY); targetPublisher.subscribe(this); } @@ -81,13 +80,13 @@ public Mono onClose() { protected void doOnDispose() { Operators.terminate(S, this); - Channel[] activeSockets = ACTIVE_SOCKETS.getAndSet(this, TERMINATED); - for (Channel channel : activeSockets) { + Channel[] activeChannels = ACTIVE_CHANNELS.getAndSet(this, TERMINATED); + for (Channel channel : activeChannels) { channel.dispose(); } - if (activeSockets.length > 0) { - Mono.whenDelayError(Arrays.stream(activeSockets).map(Channel::onClose).collect(Collectors.toList())) + if (activeChannels.length > 0) { + Mono.whenDelayError(Arrays.stream(activeChannels).map(Channel::onClose).collect(Collectors.toList())) .subscribe(null, onAllClosedSink::tryEmitError, onAllClosedSink::tryEmitEmpty); } else { @@ -111,77 +110,77 @@ public void onNext(List targets) { // This operation should happen less frequently than calls to select() (which are per request) // and therefore it is acceptable somewhat less efficient. - PooledChannel[] previouslyActiveSockets; - PooledChannel[] inactiveSockets; - PooledChannel[] socketsToUse; + PooledChannel[] previouslyActiveChannels; + PooledChannel[] inactiveChannels; + PooledChannel[] channelsToUse; for (; ; ) { - HashMap rSocketSuppliersCopy = new HashMap<>(targets.size()); + HashMap channelSuppliersCopy = new HashMap<>(targets.size()); int j = 0; for (LoadBalanceTarget target : targets) { - rSocketSuppliersCopy.put(target, j++); + channelSuppliersCopy.put(target, j++); } // Intersect current and new list of targets and find the ones to keep vs dispose - previouslyActiveSockets = this.activeSockets; - inactiveSockets = new PooledChannel[previouslyActiveSockets.length]; - PooledChannel[] nextActiveSockets = - new PooledChannel[previouslyActiveSockets.length + rSocketSuppliersCopy.size()]; - int activeSocketsPosition = 0; - int inactiveSocketsPosition = 0; - for (PooledChannel rSocket : previouslyActiveSockets) { - Integer index = rSocketSuppliersCopy.remove(rSocket.target()); + previouslyActiveChannels = this.activeChannels; + inactiveChannels = new PooledChannel[previouslyActiveChannels.length]; + PooledChannel[] nextActiveChannels = + new PooledChannel[previouslyActiveChannels.length + channelSuppliersCopy.size()]; + int activeChannelsPosition = 0; + int inactiveChannelsPosition = 0; + for (PooledChannel channel : previouslyActiveChannels) { + Integer index = channelSuppliersCopy.remove(channel.target()); if (index == null) { - // if one of the active rSockets is not included, we remove it and put in the + // if one of the active channels is not included, we remove it and put in the // pending removal - if (!rSocket.isDisposed()) { - inactiveSockets[inactiveSocketsPosition++] = rSocket; - // TODO: provide a meaningful algo for keeping removed rsocket in the list - // nextActiveSockets[position++] = rSocket; + if (!channel.isDisposed()) { + inactiveChannels[inactiveChannelsPosition++] = channel; + // TODO: provide a meaningful algo for keeping removed channel in the list + // nextActiveChannels[position++] = channel; } } else { - if (!rSocket.isDisposed()) { - // keep old RSocket instance - nextActiveSockets[activeSocketsPosition++] = rSocket; + if (!channel.isDisposed()) { + // keep old Channel instance + nextActiveChannels[activeChannelsPosition++] = channel; } else { - // put newly create RSocket instance + // put newly create Channel instance LoadBalanceTarget target = targets.get(index); - nextActiveSockets[activeSocketsPosition++] = + nextActiveChannels[activeChannelsPosition++] = new PooledChannel(this, this.connector.connect(target.getTransport()), target); } } } // The remainder are the brand new targets - for (LoadBalanceTarget target : rSocketSuppliersCopy.keySet()) { - nextActiveSockets[activeSocketsPosition++] = + for (LoadBalanceTarget target : channelSuppliersCopy.keySet()) { + nextActiveChannels[activeChannelsPosition++] = new PooledChannel(this, this.connector.connect(target.getTransport()), target); } - if (activeSocketsPosition == 0) { - socketsToUse = EMPTY; + if (activeChannelsPosition == 0) { + channelsToUse = EMPTY; } else { - socketsToUse = Arrays.copyOf(nextActiveSockets, activeSocketsPosition); + channelsToUse = Arrays.copyOf(nextActiveChannels, activeChannelsPosition); } - if (ACTIVE_SOCKETS.compareAndSet(this, previouslyActiveSockets, socketsToUse)) { + if (ACTIVE_CHANNELS.compareAndSet(this, previouslyActiveChannels, channelsToUse)) { break; } } - for (PooledChannel inactiveSocket : inactiveSockets) { - if (inactiveSocket == null) { + for (PooledChannel inactiveChannel : inactiveChannels) { + if (inactiveChannel == null) { break; } - inactiveSocket.dispose(); + inactiveChannel.dispose(); } if (isPending()) { // notifies that upstream is resolved - if (socketsToUse != EMPTY) { + if (channelsToUse != EMPTY) { //noinspection ConstantConditions complete(this); } @@ -204,7 +203,7 @@ public void onComplete() { public Channel select() { if (isDisposed()) { - return this.deferredResolutionRSocket; + return this.deferredResolutionChannel; } Channel selected = doSelect(); @@ -217,13 +216,13 @@ public Channel select() { invalidate(); // check since it is possible that between doSelect() and invalidate() we might - // have received new sockets + // have received new channels selected = doSelect(); if (selected != null) { return selected; } } - return this.deferredResolutionRSocket; + return this.deferredResolutionChannel; } return selected; @@ -231,13 +230,13 @@ public Channel select() { @Nullable public Channel doSelect() { - PooledChannel[] sockets = this.activeSockets; + PooledChannel[] channels = this.activeChannels; - if (sockets == EMPTY || sockets == TERMINATED) { + if (channels == EMPTY || channels == TERMINATED) { return null; } - return this.loadbalanceStrategy.select(WrappingList.wrap(sockets)); + return this.loadbalanceStrategy.select(WrappingList.wrap(channels)); } static class DeferredResolutionChannel implements Channel { @@ -397,50 +396,50 @@ static final class WrappingList implements List { static final ThreadLocal INSTANCE = ThreadLocal.withInitial(WrappingList::new); - private PooledChannel[] activeSockets; + private PooledChannel[] activeChannels; - static List wrap(PooledChannel[] activeSockets) { - final WrappingList sockets = INSTANCE.get(); - sockets.activeSockets = activeSockets; - return sockets; + static List wrap(PooledChannel[] activeChannels) { + final WrappingList channels = INSTANCE.get(); + channels.activeChannels = activeChannels; + return channels; } @Override public Channel get(int index) { - final PooledChannel socket = activeSockets[index]; + final PooledChannel channel = activeChannels[index]; - Channel realValue = socket.value; + Channel realValue = channel.value; if (realValue != null) { return realValue; } - realValue = socket.valueIfResolved(); + realValue = channel.valueIfResolved(); if (realValue != null) { return realValue; } - return socket; + return channel; } @Override public int size() { - return activeSockets.length; + return activeChannels.length; } @Override public boolean isEmpty() { - return activeSockets.length == 0; + return activeChannels.length == 0; } @Override public Object[] toArray() { - return activeSockets; + return activeChannels; } @Override @SuppressWarnings("unchecked") public T[] toArray(T[] a) { - return (T[]) activeSockets; + return (T[]) activeChannels; } @Override diff --git a/today-remoting/src/main/java/infra/remoting/lb/ClientLoadBalanceStrategy.java b/today-remoting/src/main/java/infra/remoting/lb/ClientLoadBalanceStrategy.java index 41d8cd5..bc4b8c4 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/ClientLoadBalanceStrategy.java +++ b/today-remoting/src/main/java/infra/remoting/lb/ClientLoadBalanceStrategy.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; diff --git a/today-remoting/src/main/java/infra/remoting/lb/Ewma.java b/today-remoting/src/main/java/infra/remoting/lb/Ewma.java index ccf5764..a50df86 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/Ewma.java +++ b/today-remoting/src/main/java/infra/remoting/lb/Ewma.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; diff --git a/today-remoting/src/main/java/infra/remoting/lb/FluxDeferredResolution.java b/today-remoting/src/main/java/infra/remoting/lb/FluxDeferredResolution.java index 76e70c5..430ae65 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/FluxDeferredResolution.java +++ b/today-remoting/src/main/java/infra/remoting/lb/FluxDeferredResolution.java @@ -1,27 +1,26 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Subscription; import java.util.concurrent.atomic.AtomicLongFieldUpdater; import java.util.function.BiConsumer; -import infra.lang.Nullable; import infra.remoting.Payload; import infra.remoting.frame.FrameType; import io.netty.util.ReferenceCountUtil; diff --git a/today-remoting/src/main/java/infra/remoting/lb/FrugalQuantile.java b/today-remoting/src/main/java/infra/remoting/lb/FrugalQuantile.java index 3f5b4a7..57c9ce2 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/FrugalQuantile.java +++ b/today-remoting/src/main/java/infra/remoting/lb/FrugalQuantile.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; diff --git a/today-remoting/src/main/java/infra/remoting/lb/Int2LongHashMap.java b/today-remoting/src/main/java/infra/remoting/lb/Int2LongHashMap.java index 44e94fe..e495c39 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/Int2LongHashMap.java +++ b/today-remoting/src/main/java/infra/remoting/lb/Int2LongHashMap.java @@ -1,21 +1,22 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; +import org.jspecify.annotations.Nullable; + import java.io.Serializable; import java.util.AbstractCollection; import java.util.AbstractSet; @@ -27,8 +28,6 @@ import java.util.function.Function; import java.util.function.IntToLongFunction; -import infra.lang.Nullable; - /** A open addressing with linear probing hash map specialised for primitive key and value pairs. */ class Int2LongHashMap implements Map, Serializable { static final float DEFAULT_LOAD_FACTOR = 0.55f; @@ -636,7 +635,6 @@ protected final void findNext() { throw new IllegalStateException(); } - /** {@inheritDoc} */ public void remove() { if (isPositionValid) { final int position = keyPosition(); diff --git a/today-remoting/src/main/java/infra/remoting/lb/LoadBalanceRemotingClient.java b/today-remoting/src/main/java/infra/remoting/lb/LoadBalanceRemotingClient.java index e174cd9..d8b9022 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/LoadBalanceRemotingClient.java +++ b/today-remoting/src/main/java/infra/remoting/lb/LoadBalanceRemotingClient.java @@ -1,27 +1,26 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import java.util.List; -import infra.lang.Nullable; import infra.remoting.Channel; import infra.remoting.Payload; import infra.remoting.core.ChannelConnector; @@ -31,8 +30,8 @@ import reactor.core.publisher.Mono; /** - * An implementation of {@link RemotingClient} backed by a pool of {@code RSocket} instances and - * using a {@link LoadBalanceStrategy} to select the {@code RSocket} to use for a given request. + * An implementation of {@link RemotingClient} backed by a pool of {@code Channel} instances and + * using a {@link LoadBalanceStrategy} to select the {@code Channel} to use for a given request. */ public class LoadBalanceRemotingClient implements RemotingClient { @@ -52,7 +51,9 @@ public boolean connect() { return channelPool.connect(); } - /** Return {@code Mono} that selects an RSocket from the underlying pool. */ + /** + * Return {@code Mono} that selects a Channel from the underlying pool. + */ @Override public Mono source() { return Mono.fromSupplier(channelPool::select); @@ -75,7 +76,7 @@ public Flux requestStream(Mono payloadMono) { @Override public Flux requestChannel(Publisher payloads) { - return source().flatMapMany(rSocket -> rSocket.requestChannel(payloads)); + return source().flatMapMany(channel -> channel.requestChannel(payloads)); } @Override @@ -88,24 +89,6 @@ public void dispose() { channelPool.dispose(); } - /** - * Shortcut to create an {@link LoadBalanceRemotingClient} with round-robin load balancing. - * Effectively a shortcut for: - * - *
    -   * LoadBalanceRemotingClient.builder(targetPublisher)
    -   *    .connector(RSocketConnector.create())
    -   *    .build();
    -   * 
    - * - * @param connector a "template" for connecting to load balance targets - * @param targetPublisher refreshes the list of load balance targets periodically - * @return the created client instance - */ - public static LoadBalanceRemotingClient create(ChannelConnector connector, Publisher> targetPublisher) { - return builder(targetPublisher).connector(connector).build(); - } - /** * Return a builder for a {@link LoadBalanceRemotingClient}. * @@ -179,13 +162,10 @@ public Builder loadBalanceStrategy(LoadBalanceStrategy strategy) { /** Build the {@link LoadBalanceRemotingClient} instance. */ public LoadBalanceRemotingClient build() { - final ChannelConnector connector = - (this.connector != null ? this.connector : ChannelConnector.create()); - - final LoadBalanceStrategy strategy = - (this.loadbalanceStrategy != null - ? this.loadbalanceStrategy - : new RoundRobinLoadBalanceStrategy()); + final ChannelConnector connector = this.connector != null ? this.connector : ChannelConnector.create(); + final LoadBalanceStrategy strategy = loadbalanceStrategy != null + ? loadbalanceStrategy + : new RoundRobinLoadBalanceStrategy(); if (strategy instanceof ClientLoadBalanceStrategy) { ((ClientLoadBalanceStrategy) strategy).initialize(connector); diff --git a/today-remoting/src/main/java/infra/remoting/lb/LoadBalanceStrategy.java b/today-remoting/src/main/java/infra/remoting/lb/LoadBalanceStrategy.java index 010ece7..16606f6 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/LoadBalanceStrategy.java +++ b/today-remoting/src/main/java/infra/remoting/lb/LoadBalanceStrategy.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; diff --git a/today-remoting/src/main/java/infra/remoting/lb/LoadBalanceTarget.java b/today-remoting/src/main/java/infra/remoting/lb/LoadBalanceTarget.java index 852ef6a..18630fb 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/LoadBalanceTarget.java +++ b/today-remoting/src/main/java/infra/remoting/lb/LoadBalanceTarget.java @@ -1,24 +1,24 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; import org.reactivestreams.Publisher; import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingClient; import infra.remoting.transport.ClientTransport; /** @@ -27,7 +27,8 @@ * #getKey()} is used to identify a target uniquely while the {@link #getTransport() transport} is * used to connect to the target server. * - * @see LoadBalanceRemotingClient#create(ChannelConnector, Publisher) + * @author 海子 Yang + * @see RemotingClient#forLoadBalance(ChannelConnector, Publisher) */ public class LoadBalanceTarget { diff --git a/today-remoting/src/main/java/infra/remoting/lb/Median.java b/today-remoting/src/main/java/infra/remoting/lb/Median.java index 2fa9db8..7920b22 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/Median.java +++ b/today-remoting/src/main/java/infra/remoting/lb/Median.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; diff --git a/today-remoting/src/main/java/infra/remoting/lb/MonoDeferredResolution.java b/today-remoting/src/main/java/infra/remoting/lb/MonoDeferredResolution.java index f6160ad..99d13b9 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/MonoDeferredResolution.java +++ b/today-remoting/src/main/java/infra/remoting/lb/MonoDeferredResolution.java @@ -1,27 +1,26 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Subscription; import java.util.concurrent.atomic.AtomicLongFieldUpdater; import java.util.function.BiConsumer; -import infra.lang.Nullable; import infra.remoting.Payload; import infra.remoting.frame.FrameType; import io.netty.util.ReferenceCountUtil; diff --git a/today-remoting/src/main/java/infra/remoting/lb/PooledChannel.java b/today-remoting/src/main/java/infra/remoting/lb/PooledChannel.java index 9768d3a..c6665f6 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/PooledChannel.java +++ b/today-remoting/src/main/java/infra/remoting/lb/PooledChannel.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; @@ -40,7 +39,7 @@ final class PooledChannel extends ResolvingOperator implements CoreSubs final ChannelPool parent; - final Mono rSocketSource; + final Mono channelSource; final LoadBalanceTarget loadbalanceTarget; @@ -51,9 +50,9 @@ final class PooledChannel extends ResolvingOperator implements CoreSubs static final AtomicReferenceFieldUpdater S = AtomicReferenceFieldUpdater.newUpdater(PooledChannel.class, Subscription.class, "s"); - PooledChannel(ChannelPool parent, Mono rSocketSource, LoadBalanceTarget loadbalanceTarget) { + PooledChannel(ChannelPool parent, Mono channelSource, LoadBalanceTarget loadbalanceTarget) { this.parent = parent; - this.rSocketSource = rSocketSource; + this.channelSource = channelSource; this.loadbalanceTarget = loadbalanceTarget; this.onCloseSink = Sinks.unsafe().empty(); } @@ -114,7 +113,7 @@ public void onNext(Channel value) { @Override protected void doSubscribe() { - this.rSocketSource.subscribe(this); + this.channelSource.subscribe(this); } @Override @@ -131,12 +130,12 @@ void doCleanup(Throwable t) { final ChannelPool parent = this.parent; for (; ; ) { - final PooledChannel[] sockets = parent.activeSockets; - final int activeSocketsCount = sockets.length; + final PooledChannel[] channels = parent.activeChannels; + final int activeChannelsCount = channels.length; int index = -1; - for (int i = 0; i < activeSocketsCount; i++) { - if (sockets[i] == this) { + for (int i = 0; i < activeChannelsCount; i++) { + if (channels[i] == this) { index = i; break; } @@ -146,24 +145,24 @@ void doCleanup(Throwable t) { break; } - final PooledChannel[] newSockets; - if (activeSocketsCount == 1) { - newSockets = ChannelPool.EMPTY; + final PooledChannel[] newChannels; + if (activeChannelsCount == 1) { + newChannels = ChannelPool.EMPTY; } else { - final int lastIndex = activeSocketsCount - 1; + final int lastIndex = activeChannelsCount - 1; - newSockets = new PooledChannel[lastIndex]; + newChannels = new PooledChannel[lastIndex]; if (index != 0) { - System.arraycopy(sockets, 0, newSockets, 0, index); + System.arraycopy(channels, 0, newChannels, 0, index); } if (index != lastIndex) { - System.arraycopy(sockets, index + 1, newSockets, index, lastIndex - index); + System.arraycopy(channels, index + 1, newChannels, index, lastIndex - index); } } - if (ChannelPool.ACTIVE_SOCKETS.compareAndSet(parent, sockets, newSockets)) { + if (ChannelPool.ACTIVE_CHANNELS.compareAndSet(parent, channels, newChannels)) { break; } } @@ -230,8 +229,8 @@ public Mono onClose() { @Override public double availability() { - final Channel socket = valueIfResolved(); - return socket != null ? socket.availability() : 0.0d; + final Channel channel = valueIfResolved(); + return channel != null ? channel.availability() : 0.0d; } static final class MonoInner extends MonoDeferredResolution { diff --git a/today-remoting/src/main/java/infra/remoting/lb/Quantile.java b/today-remoting/src/main/java/infra/remoting/lb/Quantile.java index 85144a0..6097cbc 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/Quantile.java +++ b/today-remoting/src/main/java/infra/remoting/lb/Quantile.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; diff --git a/today-remoting/src/main/java/infra/remoting/lb/ResolvingOperator.java b/today-remoting/src/main/java/infra/remoting/lb/ResolvingOperator.java index 541e61b..b12e6f1 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/ResolvingOperator.java +++ b/today-remoting/src/main/java/infra/remoting/lb/ResolvingOperator.java @@ -1,28 +1,28 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; +import org.jspecify.annotations.Nullable; + import java.time.Duration; import java.util.concurrent.CancellationException; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.BiConsumer; -import infra.lang.Nullable; import reactor.core.Disposable; import reactor.core.Exceptions; import reactor.core.publisher.Operators; diff --git a/today-remoting/src/main/java/infra/remoting/lb/RoundRobinLoadBalanceStrategy.java b/today-remoting/src/main/java/infra/remoting/lb/RoundRobinLoadBalanceStrategy.java index ba39bce..9a67552 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/RoundRobinLoadBalanceStrategy.java +++ b/today-remoting/src/main/java/infra/remoting/lb/RoundRobinLoadBalanceStrategy.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; @@ -22,7 +21,7 @@ import infra.remoting.Channel; /** - * Simple {@link LoadBalanceStrategy} that selects the {@code RSocket} to use in round-robin order. + * Simple {@link LoadBalanceStrategy} that selects the {@code Channel} to use in round-robin order. */ public class RoundRobinLoadBalanceStrategy implements LoadBalanceStrategy { diff --git a/today-remoting/src/main/java/infra/remoting/lb/WeightedLoadBalanceStrategy.java b/today-remoting/src/main/java/infra/remoting/lb/WeightedLoadBalanceStrategy.java index c071b46..27d6ba5 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/WeightedLoadBalanceStrategy.java +++ b/today-remoting/src/main/java/infra/remoting/lb/WeightedLoadBalanceStrategy.java @@ -1,40 +1,40 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; +import org.jspecify.annotations.Nullable; + import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; import java.util.function.Function; -import infra.lang.Nullable; import infra.remoting.Channel; import infra.remoting.core.ChannelConnector; /** - * {@link LoadBalanceStrategy} that assigns a weight to each {@code RSocket} based on {@link + * {@link LoadBalanceStrategy} that assigns a weight to each {@code Channel} based on {@link * Channel#availability() availability} and usage statistics. The weight is used to decide which - * {@code RSocket} to select. + * {@code Channel} to select. * *

    Use {@link #create()} or a {@link #builder() Builder} to create an instance. * * @see Predictive Load-Balancing: Unfair but - * Faster & more Robust + * Faster and more Robust * @see WeightedStatsRequestInterceptor */ public class WeightedLoadBalanceStrategy implements ClientLoadBalanceStrategy { @@ -184,7 +184,7 @@ private Builder() { } /** - * How many times to try to randomly select a pair of RSocket connections with non-zero + * How many times to try to randomly select a pair of Channel connections with non-zero * availability. This is applicable when there are more than two connections in the pool. If the * number of attempts is exceeded, the last selected pair is used. * @@ -199,7 +199,7 @@ public Builder maxPairSelectionAttempts(int numberOfAttempts) { /** * Configure how the created {@link WeightedLoadBalanceStrategy} should find the stats for a - * given RSocket. + * given Channel. * *

    By default this resolver is not set. * @@ -209,7 +209,7 @@ public Builder maxPairSelectionAttempts(int numberOfAttempts) { * ClientLoadBalanceStrategy} callback. If this strategy is used in any other context however, a * resolver here must be provided. * - * @param resolver to find the stats for an RSocket with + * @param resolver to find the stats for an Channel with */ public Builder weightedStatsResolver(Function resolver) { this.weightedStatsResolver = resolver; @@ -233,14 +233,14 @@ public WeightedStats apply(Channel channel) { } void init(ChannelConnector connector) { - connector.interceptors(registry -> registry.forRequestsInRequester(rSocket -> { + connector.interceptors(registry -> registry.forRequestsInRequester(channel -> { final WeightedStatsRequestInterceptor interceptor = new WeightedStatsRequestInterceptor() { @Override public void dispose() { - statsMap.remove(rSocket); + statsMap.remove(channel); } }; - statsMap.put(rSocket, interceptor); + statsMap.put(channel, interceptor); return interceptor; })); } diff --git a/today-remoting/src/main/java/infra/remoting/lb/WeightedStats.java b/today-remoting/src/main/java/infra/remoting/lb/WeightedStats.java index b6730a7..818a297 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/WeightedStats.java +++ b/today-remoting/src/main/java/infra/remoting/lb/WeightedStats.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; diff --git a/today-remoting/src/main/java/infra/remoting/lb/WeightedStatsChannel.java b/today-remoting/src/main/java/infra/remoting/lb/WeightedStatsChannel.java index 82ca2a7..9f9177a 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/WeightedStatsChannel.java +++ b/today-remoting/src/main/java/infra/remoting/lb/WeightedStatsChannel.java @@ -1,30 +1,29 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; import infra.remoting.Channel; -import infra.remoting.util.ChannelDecorator; +import infra.remoting.DecoratingChannel; /** - * Package private {@code ChannelDecorator} used from {@link WeightedStats#wrap(Channel)} to attach a + * Package private {@code ChannelWrapper} used from {@link WeightedStats#wrap(Channel)} to attach a * {@link WeightedStats} instance to an {@code Channel}. */ -final class WeightedStatsChannel extends ChannelDecorator implements WeightedStats { +final class WeightedStatsChannel extends DecoratingChannel implements WeightedStats { private final WeightedStats weightedStats; diff --git a/today-remoting/src/main/java/infra/remoting/lb/WeightedStatsRequestInterceptor.java b/today-remoting/src/main/java/infra/remoting/lb/WeightedStatsRequestInterceptor.java index 0661e67..891a219 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/WeightedStatsRequestInterceptor.java +++ b/today-remoting/src/main/java/infra/remoting/lb/WeightedStatsRequestInterceptor.java @@ -1,22 +1,22 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; + import infra.remoting.frame.FrameType; import infra.remoting.plugins.RequestInterceptor; import io.netty.buffer.ByteBuf; diff --git a/today-remoting/src/main/java/infra/remoting/lb/package-info.java b/today-remoting/src/main/java/infra/remoting/lb/package-info.java index 4d47c73..b4dbb9a 100644 --- a/today-remoting/src/main/java/infra/remoting/lb/package-info.java +++ b/today-remoting/src/main/java/infra/remoting/lb/package-info.java @@ -1,26 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /** * Support client load-balancing in remoting Java. */ -@NonNullApi -@NonNullFields +@NullMarked package infra.remoting.lb; -import infra.lang.NonNullApi; -import infra.lang.NonNullFields; \ No newline at end of file +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting/src/main/java/infra/remoting/lease/Lease.java b/today-remoting/src/main/java/infra/remoting/lease/Lease.java index 761517e..7dc0fb3 100644 --- a/today-remoting/src/main/java/infra/remoting/lease/Lease.java +++ b/today-remoting/src/main/java/infra/remoting/lease/Lease.java @@ -1,29 +1,29 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lease; +import org.jspecify.annotations.Nullable; + import java.time.Duration; -import infra.lang.Nullable; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -/** A contract for RSocket lease, which is sent by a request acceptor and is time bound. */ +/** A contract for lease, which is sent by a request acceptor and is time bound. */ public final class Lease { public static Lease create( diff --git a/today-remoting/src/main/java/infra/remoting/lease/LeaseSender.java b/today-remoting/src/main/java/infra/remoting/lease/LeaseSender.java index 7699534..4555168 100644 --- a/today-remoting/src/main/java/infra/remoting/lease/LeaseSender.java +++ b/today-remoting/src/main/java/infra/remoting/lease/LeaseSender.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lease; diff --git a/today-remoting/src/main/java/infra/remoting/lease/MissingLeaseException.java b/today-remoting/src/main/java/infra/remoting/lease/MissingLeaseException.java index 73a845c..2cb9e4a 100644 --- a/today-remoting/src/main/java/infra/remoting/lease/MissingLeaseException.java +++ b/today-remoting/src/main/java/infra/remoting/lease/MissingLeaseException.java @@ -1,22 +1,21 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lease; -import infra.remoting.exceptions.RejectedException; +import infra.remoting.error.RejectedException; public class MissingLeaseException extends RejectedException { private static final long serialVersionUID = -6169748673403858959L; diff --git a/today-remoting/src/main/java/infra/remoting/lease/TrackingLeaseSender.java b/today-remoting/src/main/java/infra/remoting/lease/TrackingLeaseSender.java index 5218270..e112e94 100644 --- a/today-remoting/src/main/java/infra/remoting/lease/TrackingLeaseSender.java +++ b/today-remoting/src/main/java/infra/remoting/lease/TrackingLeaseSender.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lease; diff --git a/today-remoting/src/main/java/infra/remoting/lease/package-info.java b/today-remoting/src/main/java/infra/remoting/lease/package-info.java index 9d59091..9205044 100644 --- a/today-remoting/src/main/java/infra/remoting/lease/package-info.java +++ b/today-remoting/src/main/java/infra/remoting/lease/package-info.java @@ -1,28 +1,27 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /** - * Contains support classes for the Lease feature of the RSocket protocol. + * Contains support classes for the Lease feature of the protocol. * * @see Resuming * Operation */ -@NonNullApi +@NullMarked package infra.remoting.lease; -import infra.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting/src/main/java/infra/remoting/package-info.java b/today-remoting/src/main/java/infra/remoting/package-info.java index e1c71b3..8935386 100644 --- a/today-remoting/src/main/java/infra/remoting/package-info.java +++ b/today-remoting/src/main/java/infra/remoting/package-info.java @@ -1,23 +1,20 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ -@NonNullApi -@NonNullFields +@NullMarked package infra.remoting; -import infra.lang.NonNullApi; -import infra.lang.NonNullFields; \ No newline at end of file +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting/src/main/java/infra/remoting/plugins/ChannelAcceptorDecorator.java b/today-remoting/src/main/java/infra/remoting/plugins/ChannelAcceptorDecorator.java new file mode 100644 index 0000000..8ad47fe --- /dev/null +++ b/today-remoting/src/main/java/infra/remoting/plugins/ChannelAcceptorDecorator.java @@ -0,0 +1,33 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.plugins; + +import infra.remoting.ChannelAcceptor; + +/** + * Contract to decorate a {@link ChannelAcceptor}, providing access to connection {@code setup} + * information and the ability to also decorate the channels for requesting and responding. + * + *

    This could be used as an alternative to registering an individual "requester" {@code + * ChannelInterceptor} and "responder" {@code ChannelInterceptor}. + */ +@FunctionalInterface +public interface ChannelAcceptorDecorator { + + ChannelAcceptor decorate(ChannelAcceptor delegate); + +} diff --git a/today-remoting/src/main/java/infra/remoting/plugins/ChannelAcceptorInterceptor.java b/today-remoting/src/main/java/infra/remoting/plugins/ChannelAcceptorInterceptor.java deleted file mode 100644 index 6a6ebd6..0000000 --- a/today-remoting/src/main/java/infra/remoting/plugins/ChannelAcceptorInterceptor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.plugins; - -import infra.remoting.ChannelAcceptor; - -/** - * Contract to decorate a {@link ChannelAcceptor}, providing access to connection {@code setup} - * information and the ability to also decorate the sockets for requesting and responding. - * - *

    This could be used as an alternative to registering an individual "requester" {@code - * RSocketInterceptor} and "responder" {@code RSocketInterceptor}. - */ -@FunctionalInterface -public interface ChannelAcceptorInterceptor { - - ChannelAcceptor decorate(ChannelAcceptor channelAcceptor); - -} diff --git a/today-remoting/src/main/java/infra/remoting/plugins/ChannelDecorator.java b/today-remoting/src/main/java/infra/remoting/plugins/ChannelDecorator.java new file mode 100644 index 0000000..d4b3458 --- /dev/null +++ b/today-remoting/src/main/java/infra/remoting/plugins/ChannelDecorator.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.plugins; + +import infra.remoting.Channel; + +/** + * Contract to decorate an {@link Channel}, providing a way to intercept interactions. This can be + * applied to a {@link InterceptorRegistry#forRequester(ChannelDecorator) requester} or {@link + * InterceptorRegistry#forResponder(ChannelDecorator) responder} {@code Channel} of a client or + * server. + */ +@FunctionalInterface +public interface ChannelDecorator { + + /** + * Decorate Channel + */ + Channel decorate(Channel channel); + +} diff --git a/today-remoting/src/main/java/infra/remoting/plugins/ChannelInterceptor.java b/today-remoting/src/main/java/infra/remoting/plugins/ChannelInterceptor.java deleted file mode 100644 index 83de841..0000000 --- a/today-remoting/src/main/java/infra/remoting/plugins/ChannelInterceptor.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.plugins; - -import infra.remoting.Channel; - -/** - * Contract to decorate an {@link Channel}, providing a way to intercept interactions. This can be - * applied to a {@link InterceptorRegistry#forRequester(ChannelInterceptor) requester} or {@link - * InterceptorRegistry#forResponder(ChannelInterceptor) responder} {@code Channel} of a client or - * server. - */ -@FunctionalInterface -public interface ChannelInterceptor { - - Channel decorate(Channel channel); - -} diff --git a/today-remoting/src/main/java/infra/remoting/plugins/CompositeRequestInterceptor.java b/today-remoting/src/main/java/infra/remoting/plugins/CompositeRequestInterceptor.java index 36c7a30..fbb3cb5 100644 --- a/today-remoting/src/main/java/infra/remoting/plugins/CompositeRequestInterceptor.java +++ b/today-remoting/src/main/java/infra/remoting/plugins/CompositeRequestInterceptor.java @@ -1,25 +1,25 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.plugins; +import org.jspecify.annotations.Nullable; + import java.util.List; -import infra.lang.Nullable; import infra.remoting.frame.FrameType; import io.netty.buffer.ByteBuf; import reactor.core.publisher.Operators; diff --git a/today-remoting/src/main/java/infra/remoting/plugins/ConnectionDecorator.java b/today-remoting/src/main/java/infra/remoting/plugins/ConnectionDecorator.java new file mode 100644 index 0000000..80244cc --- /dev/null +++ b/today-remoting/src/main/java/infra/remoting/plugins/ConnectionDecorator.java @@ -0,0 +1,36 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.plugins; + +import infra.remoting.Connection; + +/** + * Contract to decorate a {@link Connection} and intercept the sending and receiving of + * protocol frames at the transport level. + */ +@FunctionalInterface +public interface ConnectionDecorator { + + enum Type { + CLIENT, + SERVER, + SOURCE + } + + Connection decorate(Type type, Connection connection); + +} diff --git a/today-remoting/src/main/java/infra/remoting/plugins/ConnectionInterceptor.java b/today-remoting/src/main/java/infra/remoting/plugins/ConnectionInterceptor.java deleted file mode 100644 index 439d4ac..0000000 --- a/today-remoting/src/main/java/infra/remoting/plugins/ConnectionInterceptor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.plugins; - -import infra.remoting.DuplexConnection; - -/** - * Contract to decorate a {@link DuplexConnection} and intercept the sending and receiving of - * RSocket frames at the transport level. - */ -@FunctionalInterface -public interface ConnectionInterceptor { - - enum Type { - CLIENT, - SERVER, - SOURCE - } - - DuplexConnection intercept(Type type, DuplexConnection duplexConnection); - -} diff --git a/today-remoting/src/main/java/infra/remoting/plugins/InitializingInterceptorRegistry.java b/today-remoting/src/main/java/infra/remoting/plugins/InitializingInterceptorRegistry.java index a500d8d..36dd857 100644 --- a/today-remoting/src/main/java/infra/remoting/plugins/InitializingInterceptorRegistry.java +++ b/today-remoting/src/main/java/infra/remoting/plugins/InitializingInterceptorRegistry.java @@ -1,28 +1,28 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.plugins; +import org.jspecify.annotations.Nullable; + import java.util.stream.Collectors; import java.util.stream.Stream; -import infra.lang.Nullable; import infra.remoting.Channel; import infra.remoting.ChannelAcceptor; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; /** * Extends {@link InterceptorRegistry} with methods for building a chain of registered interceptors. @@ -30,8 +30,7 @@ */ public class InitializingInterceptorRegistry extends InterceptorRegistry { - @Nullable - public RequestInterceptor initRequesterRequestInterceptor(Channel channelRequester) { + public @Nullable RequestInterceptor initRequesterRequestInterceptor(Channel channelRequester) { return CompositeRequestInterceptor.create( requesterRequestInterceptors .stream() @@ -39,37 +38,36 @@ public RequestInterceptor initRequesterRequestInterceptor(Channel channelRequest .collect(Collectors.toList())); } - @Nullable - public RequestInterceptor initResponderRequestInterceptor(Channel channelResponder, RequestInterceptor... perConnectionInterceptors) { + public @Nullable RequestInterceptor initResponderRequestInterceptor(Channel channelResponder, RequestInterceptor... perConnectionInterceptors) { return CompositeRequestInterceptor.create( Stream.concat(Stream.of(perConnectionInterceptors), responderRequestInterceptors.stream() .map(inteptorFactory -> inteptorFactory.apply(channelResponder))) .collect(Collectors.toList())); } - public DuplexConnection initConnection(ConnectionInterceptor.Type type, DuplexConnection connection) { - for (ConnectionInterceptor interceptor : connectionInterceptors) { - connection = interceptor.intercept(type, connection); + public Connection initConnection(ConnectionDecorator.Type type, Connection connection) { + for (ConnectionDecorator interceptor : connectionDecorators) { + connection = interceptor.decorate(type, connection); } return connection; } - public Channel decorateRequester(Channel rsocket) { - for (ChannelInterceptor interceptor : requesterChannelInterceptors) { - rsocket = interceptor.decorate(rsocket); + public Channel decorateRequester(Channel channel) { + for (ChannelDecorator interceptor : requesterChannelDecorators) { + channel = interceptor.decorate(channel); } - return rsocket; + return channel; } - public Channel decorateResponder(Channel rsocket) { - for (ChannelInterceptor interceptor : responderChannelInterceptors) { - rsocket = interceptor.decorate(rsocket); + public Channel decorateResponder(Channel channel) { + for (ChannelDecorator interceptor : responderChannelDecorators) { + channel = interceptor.decorate(channel); } - return rsocket; + return channel; } public ChannelAcceptor decorateAcceptor(ChannelAcceptor acceptor) { - for (ChannelAcceptorInterceptor interceptor : channelAcceptorInterceptors) { + for (ChannelAcceptorDecorator interceptor : channelAcceptorDecorators) { acceptor = interceptor.decorate(acceptor); } return acceptor; diff --git a/today-remoting/src/main/java/infra/remoting/plugins/InterceptorRegistry.java b/today-remoting/src/main/java/infra/remoting/plugins/InterceptorRegistry.java index c02767e..e877765 100644 --- a/today-remoting/src/main/java/infra/remoting/plugins/InterceptorRegistry.java +++ b/today-remoting/src/main/java/infra/remoting/plugins/InterceptorRegistry.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.plugins; @@ -28,10 +27,10 @@ * Provides support for registering interceptors at the following levels: * *

      - *
    • {@link #forConnection(ConnectionInterceptor)} -- transport level - *
    • {@link #forChannelAcceptor(ChannelAcceptorInterceptor)} -- for accepting new connections - *
    • {@link #forRequester(ChannelInterceptor)} -- for performing of requests - *
    • {@link #forResponder(ChannelInterceptor)} -- for responding to requests + *
    • {@link #forConnection(ConnectionDecorator)} -- transport level + *
    • {@link #forChannelAcceptor(ChannelAcceptorDecorator)} -- for accepting new connections + *
    • {@link #forRequester(ChannelDecorator)} -- for performing of requests + *
    • {@link #forResponder(ChannelDecorator)} -- for responding to requests *
    */ public class InterceptorRegistry { @@ -40,13 +39,13 @@ public class InterceptorRegistry { protected final ArrayList> responderRequestInterceptors = new ArrayList<>(); - protected final ArrayList requesterChannelInterceptors = new ArrayList<>(); + protected final ArrayList requesterChannelDecorators = new ArrayList<>(); - protected final ArrayList responderChannelInterceptors = new ArrayList<>(); + protected final ArrayList responderChannelDecorators = new ArrayList<>(); - protected final ArrayList channelAcceptorInterceptors = new ArrayList<>(); + protected final ArrayList channelAcceptorDecorators = new ArrayList<>(); - protected final ArrayList connectionInterceptors = new ArrayList<>(); + protected final ArrayList connectionDecorators = new ArrayList<>(); /** * Add an {@link RequestInterceptor} that will hook into Requester Channel requests' phases. @@ -71,69 +70,69 @@ public InterceptorRegistry forRequestsInResponder(Function> consumer) { - consumer.accept(requesterChannelInterceptors); + public InterceptorRegistry forRequester(Consumer> consumer) { + consumer.accept(requesterChannelDecorators); return this; } /** - * Add an {@link ChannelInterceptor} that will decorate the Channel used for responding to + * Add an {@link ChannelDecorator} that will decorate the Channel used for responding to * requests. */ - public InterceptorRegistry forResponder(ChannelInterceptor interceptor) { - responderChannelInterceptors.add(interceptor); + public InterceptorRegistry forResponder(ChannelDecorator interceptor) { + responderChannelDecorators.add(interceptor); return this; } /** - * Variant of {@link #forResponder(ChannelInterceptor)} with access to the list of existing + * Variant of {@link #forResponder(ChannelDecorator)} with access to the list of existing * registrations. */ - public InterceptorRegistry forResponder(Consumer> consumer) { - consumer.accept(responderChannelInterceptors); + public InterceptorRegistry forResponder(Consumer> consumer) { + consumer.accept(responderChannelDecorators); return this; } /** - * Add a {@link ChannelAcceptorInterceptor} that will intercept the accepting of new connections. + * Add a {@link ChannelAcceptorDecorator} that will intercept the accepting of new connections. */ - public InterceptorRegistry forChannelAcceptor(ChannelAcceptorInterceptor interceptor) { - channelAcceptorInterceptors.add(interceptor); + public InterceptorRegistry forChannelAcceptor(ChannelAcceptorDecorator interceptor) { + channelAcceptorDecorators.add(interceptor); return this; } /** - * Variant of {@link #forChannelAcceptor(ChannelAcceptorInterceptor)} with access to the list of + * Variant of {@link #forChannelAcceptor(ChannelAcceptorDecorator)} with access to the list of * existing registrations. */ - public InterceptorRegistry forChannelAcceptor(Consumer> consumer) { - consumer.accept(channelAcceptorInterceptors); + public InterceptorRegistry forChannelAcceptor(Consumer> consumer) { + consumer.accept(channelAcceptorDecorators); return this; } - /** Add a {@link ConnectionInterceptor}. */ - public InterceptorRegistry forConnection(ConnectionInterceptor interceptor) { - connectionInterceptors.add(interceptor); + /** Add a {@link ConnectionDecorator}. */ + public InterceptorRegistry forConnection(ConnectionDecorator interceptor) { + connectionDecorators.add(interceptor); return this; } /** - * Variant of {@link #forConnection(ConnectionInterceptor)} with access to the list of + * Variant of {@link #forConnection(ConnectionDecorator)} with access to the list of * existing registrations. */ - public InterceptorRegistry forConnection(Consumer> consumer) { - consumer.accept(connectionInterceptors); + public InterceptorRegistry forConnection(Consumer> consumer) { + consumer.accept(connectionDecorators); return this; } diff --git a/today-remoting/src/main/java/infra/remoting/plugins/RateLimitInterceptor.java b/today-remoting/src/main/java/infra/remoting/plugins/RateLimitDecorator.java similarity index 64% rename from today-remoting/src/main/java/infra/remoting/plugins/RateLimitInterceptor.java rename to today-remoting/src/main/java/infra/remoting/plugins/RateLimitDecorator.java index 23e38ea..33a3eca 100644 --- a/today-remoting/src/main/java/infra/remoting/plugins/RateLimitInterceptor.java +++ b/today-remoting/src/main/java/infra/remoting/plugins/RateLimitDecorator.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.plugins; @@ -20,8 +19,8 @@ import org.reactivestreams.Publisher; import infra.remoting.Channel; +import infra.remoting.DecoratingChannel; import infra.remoting.Payload; -import infra.remoting.util.ChannelDecorator; import reactor.core.publisher.Flux; /** @@ -41,7 +40,7 @@ * * @since 1.0 */ -public class RateLimitInterceptor implements ChannelInterceptor { +public class RateLimitDecorator implements ChannelDecorator { private final int highTide; @@ -49,66 +48,66 @@ public class RateLimitInterceptor implements ChannelInterceptor { private final boolean requesterProxy; - private RateLimitInterceptor(int highTide, int lowTide, boolean requesterProxy) { + private RateLimitDecorator(int highTide, int lowTide, boolean requesterProxy) { this.highTide = highTide; this.lowTide = lowTide; this.requesterProxy = requesterProxy; } @Override - public Channel decorate(Channel socket) { - return requesterProxy ? new RequesterChannel(socket) : new ResponderChannel(socket); + public Channel decorate(Channel channel) { + return requesterProxy ? new RequesterChannel(channel) : new ResponderChannel(channel); } /** - * Create an interceptor for an {@code RSocket} that handles request-stream and/or request-channel + * Create an interceptor for an {@code Channel} that handles request-stream and/or request-channel * interactions. * * @param prefetchRate the prefetch rate to pass to {@link Flux#limitRate(int)} * @return the created interceptor */ - public static RateLimitInterceptor forResponder(int prefetchRate) { + public static RateLimitDecorator forResponder(int prefetchRate) { return forResponder(prefetchRate, prefetchRate); } /** - * Create an interceptor for an {@code RSocket} that handles request-stream and/or request-channel + * Create an interceptor for an {@code Channel} that handles request-stream and/or request-channel * interactions with more control over the overall prefetch rate and replenish threshold. * * @param highTide the high tide value to pass to {@link Flux#limitRate(int, int)} * @param lowTide the low tide value to pass to {@link Flux#limitRate(int, int)} * @return the created interceptor */ - public static RateLimitInterceptor forResponder(int highTide, int lowTide) { - return new RateLimitInterceptor(highTide, lowTide, false); + public static RateLimitDecorator forResponder(int highTide, int lowTide) { + return new RateLimitDecorator(highTide, lowTide, false); } /** - * Create an interceptor for an {@code RSocket} that performs request-channel interactions. + * Create an interceptor for an {@code Channel} that performs request-channel interactions. * * @param prefetchRate the prefetch rate to pass to {@link Flux#limitRate(int)} * @return the created interceptor */ - public static RateLimitInterceptor forRequester(int prefetchRate) { + public static RateLimitDecorator forRequester(int prefetchRate) { return forRequester(prefetchRate, prefetchRate); } /** - * Create an interceptor for an {@code RSocket} that performs request-channel interactions with + * Create an interceptor for an {@code Channel} that performs request-channel interactions with * more control over the overall prefetch rate and replenish threshold. * * @param highTide the high tide value to pass to {@link Flux#limitRate(int, int)} * @param lowTide the low tide value to pass to {@link Flux#limitRate(int, int)} * @return the created interceptor */ - public static RateLimitInterceptor forRequester(int highTide, int lowTide) { - return new RateLimitInterceptor(highTide, lowTide, true); + public static RateLimitDecorator forRequester(int highTide, int lowTide) { + return new RateLimitDecorator(highTide, lowTide, true); } /** * Responder side proxy, limits response streams. */ - private class ResponderChannel extends ChannelDecorator { + private class ResponderChannel extends DecoratingChannel { ResponderChannel(Channel source) { super(source); @@ -128,7 +127,7 @@ public Flux requestChannel(Publisher payloads) { /** * Requester side proxy, limits channel request stream. */ - private class RequesterChannel extends ChannelDecorator { + private class RequesterChannel extends DecoratingChannel { RequesterChannel(Channel source) { super(source); diff --git a/today-remoting/src/main/java/infra/remoting/plugins/RequestInterceptor.java b/today-remoting/src/main/java/infra/remoting/plugins/RequestInterceptor.java index fba579f..bfa09e1 100644 --- a/today-remoting/src/main/java/infra/remoting/plugins/RequestInterceptor.java +++ b/today-remoting/src/main/java/infra/remoting/plugins/RequestInterceptor.java @@ -1,23 +1,23 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.plugins; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; + import infra.remoting.Payload; import infra.remoting.frame.FrameType; import infra.remoting.lease.Lease; @@ -26,8 +26,8 @@ import reactor.util.context.Context; /** - * Class used to track the RSocket requests lifecycles. The main difference and advantage of this - * interceptor compares to {@link ChannelInterceptor} is that it allows intercepting the initial and + * Class used to track the protocol requests lifecycles. The main difference and advantage of this + * interceptor compares to {@link ChannelDecorator} is that it allows intercepting the initial and * terminal phases on every individual request. * *

    Note, if any of the invocations will rise a runtime exception, this exception will be diff --git a/today-remoting/src/main/java/infra/remoting/plugins/package-info.java b/today-remoting/src/main/java/infra/remoting/plugins/package-info.java index 061ddb4..a86e4e3 100644 --- a/today-remoting/src/main/java/infra/remoting/plugins/package-info.java +++ b/today-remoting/src/main/java/infra/remoting/plugins/package-info.java @@ -1,22 +1,21 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ -/** Contracts for interception of transports, connections, and requests in in RSocket Java. */ -@NonNullApi +/** Contracts for interception of transports, connections, and requests. */ +@NullMarked package infra.remoting.plugins; -import infra.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting/src/main/java/infra/remoting/protocol/Metadata.java b/today-remoting/src/main/java/infra/remoting/protocol/Metadata.java deleted file mode 100644 index 26f50b8..0000000 --- a/today-remoting/src/main/java/infra/remoting/protocol/Metadata.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.protocol; - -/** - * @author 海子 Yang - * @since 1.0 2025/7/29 11:41 - */ -public class Metadata { - - public static final Metadata EMPTY = new Metadata(); - - public T get(int key) { - return null; - } - -} diff --git a/today-remoting/src/main/java/infra/remoting/protocol/ProtocolFrame.java b/today-remoting/src/main/java/infra/remoting/protocol/ProtocolFrame.java index e525290..2bc3244 100644 --- a/today-remoting/src/main/java/infra/remoting/protocol/ProtocolFrame.java +++ b/today-remoting/src/main/java/infra/remoting/protocol/ProtocolFrame.java @@ -1,26 +1,28 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.protocol; +import org.jspecify.annotations.Nullable; + import java.util.concurrent.Flow; -import infra.lang.Nullable; +import infra.remoting.Payload; import infra.remoting.frame.FrameType; +import infra.remoting.frame.decoder.PayloadDecoder; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufUtil; @@ -58,10 +60,7 @@ public class ProtocolFrame { public final FrameType frameType; - public final Metadata metadata; - - @Nullable - public final ByteBuf data; + public final Payload payload; private final int flags; @@ -70,23 +69,20 @@ public class ProtocolFrame { @Nullable private FrameType syntheticFrameType; - public ProtocolFrame(int streamId, FrameType frameType, int flags, Metadata metadata, ByteBuf data) { + public ProtocolFrame(int streamId, FrameType frameType, int flags, Payload payload) { this.streamId = streamId; this.frameType = frameType; this.flags = flags; - this.metadata = metadata; - this.data = data; + this.payload = payload; this.typeAndFlags = 0; } - public ProtocolFrame(int streamId, FrameType frameType, int flags, - int typeAndFlags, Metadata metadata, ByteBuf data) { + public ProtocolFrame(int streamId, FrameType frameType, int flags, int typeAndFlags, Payload payload) { this.streamId = streamId; this.frameType = frameType; this.flags = flags; this.typeAndFlags = typeAndFlags; - this.metadata = metadata; - this.data = data; + this.payload = payload; } public int getStreamId() { @@ -98,8 +94,8 @@ public FrameType syntheticFrameType() { if (result == null) { result = frameType; if (result == FrameType.PAYLOAD) { - boolean next = (flags & FLAGS_N) == FLAGS_N; - boolean complete = (flags & FLAGS_C) == FLAGS_C; + boolean next = hasFlag(FLAGS_N); + boolean complete = hasFlag(FLAGS_C); if (next && complete) { result = FrameType.NEXT_COMPLETE; } @@ -119,7 +115,7 @@ else if (next) { } public boolean hasMetadata() { - return metadata != Metadata.EMPTY; + return payload.hasMetadata(); } public boolean hasFollows() { @@ -130,25 +126,22 @@ public boolean hasComplete() { return hasFlag(flags, FLAGS_C); } - public Metadata getMetadata() { - return metadata; + public boolean hasFlag(int flag) { + return (flags & flag) == flag; } public int getLength() { - if (data != null) { - return HEADER_SIZE + data.readableBytes(); - } - return HEADER_SIZE; + return HEADER_SIZE + payload.length(); } public void release() { - if (data != null) { - data.release(); + if (payload != null) { + payload.release(); } } public ByteBuf serialize(ByteBufAllocator allocator) { - if (!frameType.canHaveMetadata() && ((flags & FLAGS_M) == FLAGS_M)) { + if (!frameType.canHaveMetadata() && hasFlag(FLAGS_M)) { throw new IllegalStateException("bad value for metadata flag"); } @@ -168,9 +161,14 @@ public String toString() { .append(" Length: ") .append(getLength()); + if (hasMetadata()) { + builder.append("\nMetadata:\n"); + ByteBufUtil.appendPrettyHexDump(builder, payload.metadata()); + } + builder.append("\nData:\n"); - if (data != null) { - ByteBufUtil.appendPrettyHexDump(builder, data); + if (payload != null) { + ByteBufUtil.appendPrettyHexDump(builder, payload.data()); } else { ByteBufUtil.appendPrettyHexDump(builder, Unpooled.EMPTY_BUFFER); @@ -188,24 +186,14 @@ public static boolean hasFlag(int flags, int flag) { * @param frame frame buffer * @throws ProtocolParsingException protocol parsing errors */ - public static ProtocolFrame parse(ByteBuf frame) throws ProtocolParsingException { - int streamId = frame.readUnsignedShort(); + public static ProtocolFrame parse(ByteBuf frame, PayloadDecoder decoder) throws ProtocolParsingException { + int streamId = frame.readInt(); int typeAndFlags = frame.readShort() & 0xFFFF; FrameType nativeFrameType = FrameType.forEncodedType(typeAndFlags >> FRAME_TYPE_SHIFT); final int flags = typeAndFlags & FRAME_FLAGS_MASK; - boolean hasMetadata = hasFlag(flags, FLAGS_M); - if (hasMetadata) { - int length = decodeLength(frame); - ByteBuf metadataBuf = frame.readSlice(length); - - } - ByteBuf data = frame.readableBytes() > 0 ? frame.readSlice(frame.readableBytes()) : Unpooled.EMPTY_BUFFER; - return new ProtocolFrame(streamId, nativeFrameType, flags, typeAndFlags, Metadata.EMPTY, data); - } - - private static int decodeLength(ByteBuf byteBuf) { - return byteBuf.readUnsignedMedium(); + Payload payload = decoder.decode(frame); + return new ProtocolFrame(streamId, nativeFrameType, flags, typeAndFlags, payload); } } diff --git a/today-remoting/src/main/java/infra/remoting/protocol/ProtocolParsingException.java b/today-remoting/src/main/java/infra/remoting/protocol/ProtocolParsingException.java index 16e4348..c3a2fef 100644 --- a/today-remoting/src/main/java/infra/remoting/protocol/ProtocolParsingException.java +++ b/today-remoting/src/main/java/infra/remoting/protocol/ProtocolParsingException.java @@ -1,23 +1,22 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.protocol; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Protocol parsing exception diff --git a/today-remoting/src/main/java/infra/remoting/resume/ChannelSession.java b/today-remoting/src/main/java/infra/remoting/resume/ChannelSession.java index e4628e1..76bb5a5 100644 --- a/today-remoting/src/main/java/infra/remoting/resume/ChannelSession.java +++ b/today-remoting/src/main/java/infra/remoting/resume/ChannelSession.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.resume; diff --git a/today-remoting/src/main/java/infra/remoting/resume/ClientChannelSession.java b/today-remoting/src/main/java/infra/remoting/resume/ClientChannelSession.java index 02d9dbd..1bc1008 100644 --- a/today-remoting/src/main/java/infra/remoting/resume/ClientChannelSession.java +++ b/today-remoting/src/main/java/infra/remoting/resume/ClientChannelSession.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.resume; @@ -25,17 +24,16 @@ import infra.logging.Logger; import infra.logging.LoggerFactory; -import infra.remoting.DuplexConnection; -import infra.remoting.exceptions.ConnectionErrorException; -import infra.remoting.exceptions.Exceptions; -import infra.remoting.exceptions.RejectedResumeException; +import infra.remoting.Connection; +import infra.remoting.error.ConnectionErrorException; +import infra.remoting.error.Exceptions; +import infra.remoting.error.RejectedResumeException; import infra.remoting.frame.FrameHeaderCodec; import infra.remoting.frame.FrameType; import infra.remoting.frame.ResumeFrameCodec; import infra.remoting.frame.ResumeOkFrameCodec; import infra.remoting.keepalive.KeepAliveSupport; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; import io.netty.util.CharsetUtil; import reactor.core.CoreSubscriber; import reactor.core.Disposable; @@ -44,106 +42,80 @@ import reactor.util.function.Tuple2; import reactor.util.retry.Retry; -public class ClientChannelSession implements ChannelSession, ResumeStateHolder, - CoreSubscriber> { +public class ClientChannelSession implements ChannelSession, ResumeStateHolder, CoreSubscriber> { private static final Logger logger = LoggerFactory.getLogger(ClientChannelSession.class); - final ResumableDuplexConnection resumableConnection; - final Mono> connectionFactory; - final ResumableFramesStore resumableFramesStore; + private final ResumableConnection resumableConnection; + private final Mono> connectionFactory; + private final ResumableFramesStore resumableFramesStore; - final ByteBufAllocator allocator; - final Duration resumeSessionDuration; - final Retry retry; - final boolean cleanupStoreOnKeepAlive; - final ByteBuf resumeToken; - final String session; - final Disposable reconnectDisposable; + private final Duration resumeSessionDuration; + private final Retry retry; + private final boolean cleanupStoreOnKeepAlive; + private final ByteBuf resumeToken; + private final String session; + private final Disposable reconnectDisposable; volatile Subscription s; static final AtomicReferenceFieldUpdater S = AtomicReferenceFieldUpdater.newUpdater(ClientChannelSession.class, Subscription.class, "s"); - KeepAliveSupport keepAliveSupport; - - public ClientChannelSession( - ByteBuf resumeToken, - ResumableDuplexConnection resumableDuplexConnection, - Mono connectionFactory, - Function>> connectionTransformer, - ResumableFramesStore resumableFramesStore, - Duration resumeSessionDuration, - Retry retry, - boolean cleanupStoreOnKeepAlive) { - this.resumeToken = resumeToken; + private KeepAliveSupport keepAliveSupport; + + public ClientChannelSession(ByteBuf resumeToken, ResumableConnection resumableConnection, + Mono connectionFactory, Function>> connectionTransformer, + ResumableFramesStore resumableFramesStore, Duration resumeSessionDuration, Retry retry, boolean cleanupStoreOnKeepAlive) { + this.retry = retry; this.session = resumeToken.toString(CharsetUtil.UTF_8); - this.connectionFactory = - connectionFactory - .doOnDiscard( - DuplexConnection.class, - c -> { - final ConnectionErrorException connectionErrorException = - new ConnectionErrorException("resumption_server=[Session Expired]"); - c.sendErrorAndClose(connectionErrorException); - c.receive().subscribe(); - }) - .flatMap( - dc -> { - final long impliedPosition = resumableFramesStore.frameImpliedPosition(); - final long position = resumableFramesStore.framePosition(); - dc.sendFrame( - 0, - ResumeFrameCodec.encode( - dc.alloc(), - resumeToken.retain(), - // server uses this to release its cache - impliedPosition, // observed on the client side - // server uses this to check whether there is no mismatch - position // sent from the client sent - )); - - if (logger.isDebugEnabled()) { - logger.debug( - "Side[client]|Session[{}]. ResumeFrame[impliedPosition[{}], position[{}]] has been sent.", - session, - impliedPosition, - position); - } - - return connectionTransformer.apply(dc); - }) - .doOnDiscard(Tuple2.class, this::tryReestablishSession); + this.resumeToken = resumeToken; + this.resumableConnection = resumableConnection; this.resumableFramesStore = resumableFramesStore; - this.allocator = resumableDuplexConnection.alloc(); this.resumeSessionDuration = resumeSessionDuration; - this.retry = retry; this.cleanupStoreOnKeepAlive = cleanupStoreOnKeepAlive; - this.resumableConnection = resumableDuplexConnection; - - resumableDuplexConnection.onClose().doFinally(__ -> dispose()).subscribe(); - this.reconnectDisposable = - resumableDuplexConnection.onActiveConnectionClosed().subscribe(this::reconnect); + this.connectionFactory = connectionFactory + .doOnDiscard(Connection.class, c -> { + final ConnectionErrorException connectionErrorException = + new ConnectionErrorException("resumption_server=[Session Expired]"); + c.sendErrorAndClose(connectionErrorException); + c.receive().subscribe(); + }) + .flatMap(dc -> { + final long impliedPosition = resumableFramesStore.frameImpliedPosition(); + final long position = resumableFramesStore.framePosition(); + dc.sendFrame(0, ResumeFrameCodec.encode(dc.alloc(), + resumeToken.retain(), + // server uses this to release its cache + impliedPosition, // observed on the client side + // server uses this to check whether there is no mismatch + position // sent from the client sent + )); + + if (logger.isDebugEnabled()) { + logger.debug("Side[client]|Session[{}]. ResumeFrame[impliedPosition[{}], position[{}]] has been sent.", + session, impliedPosition, position); + } + + return connectionTransformer.apply(dc); + }) + .doOnDiscard(Tuple2.class, this::tryReestablishSession); + + resumableConnection.onClose().doFinally(__ -> dispose()).subscribe(); + this.reconnectDisposable = resumableConnection.onActiveConnectionClosed().subscribe(this::reconnect); } - void reconnect(int index) { + private void reconnect(int index) { if (this.s == Operators.cancelledSubscription()) { if (logger.isDebugEnabled()) { - logger.debug( - "Side[client]|Session[{}]. Connection[{}] is lost. Reconnecting rejected since session is closed", - session, - index); + logger.debug("Side[client]|Session[{}]. Connection[{}] is lost. Reconnecting rejected since session is closed", session, index); } return; } keepAliveSupport.stop(); if (logger.isDebugEnabled()) { - logger.debug( - "Side[client]|Session[{}]. Connection[{}] is lost. Reconnecting to resume...", - session, - index); + logger.debug("Side[client]|Session[{}]. Connection[{}] is lost. Reconnecting to resume...", session, index); } connectionFactory .doOnNext(this::tryReestablishSession) @@ -196,25 +168,22 @@ public boolean isDisposed() { return resumableConnection.isDisposed(); } - void tryReestablishSession(Tuple2 tuple2) { + private void tryReestablishSession(Tuple2 tuple2) { if (logger.isDebugEnabled()) { logger.debug("Active subscription is canceled {}", s == Operators.cancelledSubscription()); } ByteBuf shouldBeResumeOKFrame = tuple2.getT1(); - DuplexConnection nextDuplexConnection = tuple2.getT2(); + Connection nextConnection = tuple2.getT2(); final int streamId = FrameHeaderCodec.streamId(shouldBeResumeOKFrame); if (streamId != 0) { if (logger.isDebugEnabled()) { - logger.debug( - "Side[client]|Session[{}]. Illegal first frame received. RESUME_OK frame must be received before any others. Terminating received connection", - session); + logger.debug("Side[client]|Session[{}]. Illegal first frame received. RESUME_OK frame must be received before any others. Terminating received connection", session); } - final ConnectionErrorException connectionErrorException = - new ConnectionErrorException("RESUME_OK frame must be received before any others"); - resumableConnection.dispose(nextDuplexConnection, connectionErrorException); - nextDuplexConnection.sendErrorAndClose(connectionErrorException); - nextDuplexConnection.receive().subscribe(); + final var connectionErrorException = new ConnectionErrorException("RESUME_OK frame must be received before any others"); + resumableConnection.dispose(nextConnection, connectionErrorException); + nextConnection.sendErrorAndClose(connectionErrorException); + nextConnection.receive().subscribe(); throw connectionErrorException; // throw to retry connection again } @@ -229,12 +198,8 @@ void tryReestablishSession(Tuple2 tuple2) { final long position = resumableFramesStore.framePosition(); final long impliedPosition = resumableFramesStore.frameImpliedPosition(); if (logger.isDebugEnabled()) { - logger.debug( - "Side[client]|Session[{}]. ResumeOK FRAME received. ServerResumeState[remoteImpliedPosition[{}]]. ClientResumeState[impliedPosition[{}], position[{}]]", - session, - remoteImpliedPos, - impliedPosition, - position); + logger.debug("Side[client]|Session[{}]. ResumeOK FRAME received. ServerResumeState[remoteImpliedPosition[{}]]. ClientResumeState[impliedPosition[{}], position[{}]]", + session, remoteImpliedPos, impliedPosition, position); } if (position <= remoteImpliedPos) { try { @@ -244,31 +209,25 @@ void tryReestablishSession(Tuple2 tuple2) { } catch (IllegalStateException e) { if (logger.isDebugEnabled()) { - logger.debug( - "Side[client]|Session[{}]. Exception occurred while releasing frames in the frameStore", - session, - e); + logger.debug("Side[client]|Session[{}]. Exception occurred while releasing frames in the frameStore", session, e); } final ConnectionErrorException t = new ConnectionErrorException(e.getMessage(), e); - resumableConnection.dispose(nextDuplexConnection, t); + resumableConnection.dispose(nextConnection, t); - nextDuplexConnection.sendErrorAndClose(t); - nextDuplexConnection.receive().subscribe(); + nextConnection.sendErrorAndClose(t); + nextConnection.receive().subscribe(); return; } if (!tryCancelSessionTimeout()) { if (logger.isDebugEnabled()) { - logger.debug( - "Side[client]|Session[{}]. Session has already been expired. Terminating received connection", - session); + logger.debug("Side[client]|Session[{}]. Session has already been expired. Terminating received connection", session); } - final ConnectionErrorException connectionErrorException = - new ConnectionErrorException("resumption_server=[Session Expired]"); - nextDuplexConnection.sendErrorAndClose(connectionErrorException); - nextDuplexConnection.receive().subscribe(); + final ConnectionErrorException connectionErrorException = new ConnectionErrorException("resumption_server=[Session Expired]"); + nextConnection.sendErrorAndClose(connectionErrorException); + nextConnection.receive().subscribe(); return; } @@ -278,16 +237,14 @@ void tryReestablishSession(Tuple2 tuple2) { logger.debug("Side[client]|Session[{}]. Session has been resumed successfully", session); } - if (!resumableConnection.connect(nextDuplexConnection)) { + if (!resumableConnection.connect(nextConnection)) { if (logger.isDebugEnabled()) { - logger.debug( - "Side[client]|Session[{}]. Session has already been expired. Terminating received connection", - session); + logger.debug("Side[client]|Session[{}]. Session has already been expired. Terminating received connection", session); } final ConnectionErrorException connectionErrorException = new ConnectionErrorException("resumption_server_pos=[Session Expired]"); - nextDuplexConnection.sendErrorAndClose(connectionErrorException); - nextDuplexConnection.receive().subscribe(); + nextConnection.sendErrorAndClose(connectionErrorException); + nextConnection.receive().subscribe(); // no need to do anything since connection resumable connection is liklly to // be disposed } @@ -296,57 +253,48 @@ void tryReestablishSession(Tuple2 tuple2) { if (logger.isDebugEnabled()) { logger.debug( "Side[client]|Session[{}]. Mismatching remote and local state. Expected RemoteImpliedPosition[{}] to be greater or equal to the LocalPosition[{}]. Terminating received connection", - session, - remoteImpliedPos, - position); + session, remoteImpliedPos, position); } final ConnectionErrorException connectionErrorException = new ConnectionErrorException("resumption_server_pos=[" + remoteImpliedPos + "]"); - resumableConnection.dispose(nextDuplexConnection, connectionErrorException); + resumableConnection.dispose(nextConnection, connectionErrorException); - nextDuplexConnection.sendErrorAndClose(connectionErrorException); - nextDuplexConnection.receive().subscribe(); + nextConnection.sendErrorAndClose(connectionErrorException); + nextConnection.receive().subscribe(); } } else if (frameType == FrameType.ERROR) { final RuntimeException exception = Exceptions.from(0, shouldBeResumeOKFrame); if (logger.isDebugEnabled()) { - logger.debug( - "Side[client]|Session[{}]. Received error frame. Terminating received connection", - session, - exception); + logger.debug("Side[client]|Session[{}]. Received error frame. Terminating received connection", session, exception); } if (exception instanceof RejectedResumeException) { - resumableConnection.dispose(nextDuplexConnection, exception); - nextDuplexConnection.dispose(); - nextDuplexConnection.receive().subscribe(); + resumableConnection.dispose(nextConnection, exception); + nextConnection.dispose(); + nextConnection.receive().subscribe(); return; } - nextDuplexConnection.dispose(); - nextDuplexConnection.receive().subscribe(); + nextConnection.dispose(); + nextConnection.receive().subscribe(); throw exception; // assume retryable exception } else { if (logger.isDebugEnabled()) { - logger.debug( - "Side[client]|Session[{}]. Illegal first frame received. RESUME_OK frame must be received before any others. Terminating received connection", - session); + logger.debug("Side[client]|Session[{}]. Illegal first frame received. RESUME_OK frame must be received before any others. Terminating received connection", session); } - final ConnectionErrorException connectionErrorException = - new ConnectionErrorException("RESUME_OK frame must be received before any others"); - - resumableConnection.dispose(nextDuplexConnection, connectionErrorException); + final var connectionErrorException = new ConnectionErrorException("RESUME_OK frame must be received before any others"); + resumableConnection.dispose(nextConnection, connectionErrorException); - nextDuplexConnection.sendErrorAndClose(connectionErrorException); - nextDuplexConnection.receive().subscribe(); + nextConnection.sendErrorAndClose(connectionErrorException); + nextConnection.receive().subscribe(); // no need to do anything since remote server rejected our connection completely } } - boolean tryCancelSessionTimeout() { + private boolean tryCancelSessionTimeout() { for (; ; ) { final Subscription subscription = this.s; @@ -369,7 +317,8 @@ public void onSubscribe(Subscription s) { } @Override - public void onNext(Tuple2 objects) { } + public void onNext(Tuple2 objects) { + } @Override public void onError(Throwable t) { @@ -381,9 +330,12 @@ public void onError(Throwable t) { } @Override - public void onComplete() { } + public void onComplete() { + } + @Override public void setKeepAliveSupport(KeepAliveSupport keepAliveSupport) { this.keepAliveSupport = keepAliveSupport; } + } diff --git a/today-remoting/src/main/java/infra/remoting/resume/ClientResume.java b/today-remoting/src/main/java/infra/remoting/resume/ClientResume.java deleted file mode 100644 index 1b09654..0000000 --- a/today-remoting/src/main/java/infra/remoting/resume/ClientResume.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.resume; - -import java.time.Duration; - -import io.netty.buffer.ByteBuf; - -public class ClientResume { - private final Duration sessionDuration; - private final ByteBuf resumeToken; - - public ClientResume(Duration sessionDuration, ByteBuf resumeToken) { - this.sessionDuration = sessionDuration; - this.resumeToken = resumeToken; - } - - public Duration sessionDuration() { - return sessionDuration; - } - - public ByteBuf resumeToken() { - return resumeToken; - } -} diff --git a/today-remoting/src/main/java/infra/remoting/resume/InMemoryResumableFramesStore.java b/today-remoting/src/main/java/infra/remoting/resume/InMemoryResumableFramesStore.java index 2cef98b..812e382 100644 --- a/today-remoting/src/main/java/infra/remoting/resume/InMemoryResumableFramesStore.java +++ b/today-remoting/src/main/java/infra/remoting/resume/InMemoryResumableFramesStore.java @@ -1,22 +1,22 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.resume; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Subscription; import java.util.ArrayDeque; @@ -24,7 +24,6 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.atomic.AtomicLongFieldUpdater; -import infra.lang.Nullable; import infra.logging.Logger; import infra.logging.LoggerFactory; import infra.remoting.internal.UnboundedProcessor; @@ -37,46 +36,16 @@ import reactor.core.publisher.Operators; import reactor.core.publisher.Sinks; -import static infra.remoting.resume.ResumableDuplexConnection.isResumableFrame; +import static infra.remoting.resume.ResumableConnection.isResumableFrame; /** * writes - n (where n is frequent, primary operation) reads - m (where m == KeepAliveFrequency) * skip - k -> 0 (where k is the rare operation which happens after disconnection */ -public class InMemoryResumableFramesStore extends Flux - implements ResumableFramesStore, Subscription { +public class InMemoryResumableFramesStore extends Flux implements ResumableFramesStore, Subscription { - private FramesSubscriber framesSubscriber; private static final Logger logger = LoggerFactory.getLogger(InMemoryResumableFramesStore.class); - final Sinks.Empty disposed = Sinks.empty(); - final Queue cachedFrames; - final String side; - final String session; - final int cacheLimit; - - volatile long impliedPosition; - static final AtomicLongFieldUpdater IMPLIED_POSITION = - AtomicLongFieldUpdater.newUpdater(InMemoryResumableFramesStore.class, "impliedPosition"); - - volatile long firstAvailableFramePosition; - static final AtomicLongFieldUpdater FIRST_AVAILABLE_FRAME_POSITION = - AtomicLongFieldUpdater.newUpdater( - InMemoryResumableFramesStore.class, "firstAvailableFramePosition"); - - long remoteImpliedPosition; - - int cacheSize; - - Throwable terminal; - - CoreSubscriber actual; - CoreSubscriber pendingActual; - - volatile long state; - static final AtomicLongFieldUpdater STATE = - AtomicLongFieldUpdater.newUpdater(InMemoryResumableFramesStore.class, "state"); - /** * Flag which indicates that {@link InMemoryResumableFramesStore} is finalized and all related * stores are cleaned @@ -124,18 +93,48 @@ public class InMemoryResumableFramesStore extends Flux static final long MAX_WORK_IN_PROGRESS = 0b0000_0000_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111L; + final Sinks.Empty disposed = Sinks.empty(); + final Queue cachedFrames; + final String side; + final String session; + final int cacheLimit; + + volatile long impliedPosition; + static final AtomicLongFieldUpdater IMPLIED_POSITION = + AtomicLongFieldUpdater.newUpdater(InMemoryResumableFramesStore.class, "impliedPosition"); + + volatile long firstAvailableFramePosition; + static final AtomicLongFieldUpdater FIRST_AVAILABLE_FRAME_POSITION = + AtomicLongFieldUpdater.newUpdater( + InMemoryResumableFramesStore.class, "firstAvailableFramePosition"); + + int cacheSize; + + private Throwable terminal; + + private long remoteImpliedPosition; + + private CoreSubscriber actual; + + private CoreSubscriber pendingActual; + + private FramesSubscriber framesSubscriber; + + volatile long state; + static final AtomicLongFieldUpdater STATE = + AtomicLongFieldUpdater.newUpdater(InMemoryResumableFramesStore.class, "state"); + public InMemoryResumableFramesStore(String side, ByteBuf session, int cacheSizeBytes) { this.side = side; - this.session = session.toString(CharsetUtil.UTF_8); this.cacheLimit = cacheSizeBytes; this.cachedFrames = new ArrayDeque<>(); + this.session = session.toString(CharsetUtil.UTF_8); } + @Override public Mono saveFrames(Flux frames) { - return frames - .transform( - Operators.lift( - (__, actual) -> this.framesSubscriber = new FramesSubscriber(actual, this))) + return frames.transform(Operators.lift((__, actual) -> + this.framesSubscriber = new FramesSubscriber(actual, this))) .then(); } @@ -157,7 +156,7 @@ public void releaseFrames(long remoteImpliedPos) { drain((previousState + 1) | REMOTE_IMPLIED_POSITION_CHANGED_FLAG); } - void drain(long expectedState) { + private void drain(long expectedState) { final Fuseable.QueueSubscription qs = this.framesSubscriber.qs; final Queue cachedFrames = this.cachedFrames; @@ -198,7 +197,7 @@ else if (hasFrames(expectedState)) { } } - long handlePendingRemoteImpliedPositionChanges(long expectedState, Queue cachedFrames) { + private long handlePendingRemoteImpliedPositionChanges(long expectedState, Queue cachedFrames) { final long remoteImpliedPosition = this.remoteImpliedPosition; final long firstAvailableFramePosition = this.firstAvailableFramePosition; final long toDropFromCache = Math.max(0, remoteImpliedPosition - firstAvailableFramePosition); @@ -207,33 +206,23 @@ long handlePendingRemoteImpliedPositionChanges(long expectedState, Queue droppedFromCache) { - this.terminal = - new IllegalStateException( - String.format( - "Local and remote state disagreement: " - + "need to remove additional %d bytes, but cache is empty", - toDropFromCache)); + this.terminal = new IllegalStateException( + "Local and remote state disagreement: need to remove additional %d bytes, but cache is empty".formatted(toDropFromCache)); expectedState = markTerminated(this) | TERMINATED_FLAG; } if (toDropFromCache < droppedFromCache) { - this.terminal = - new IllegalStateException( - "Local and remote state disagreement: local and remote frame sizes are not equal"); + this.terminal = new IllegalStateException( + "Local and remote state disagreement: local and remote frame sizes are not equal"); expectedState = markTerminated(this) | TERMINATED_FLAG; } FIRST_AVAILABLE_FRAME_POSITION.lazySet(this, firstAvailableFramePosition + droppedFromCache); if (this.cacheLimit != Integer.MAX_VALUE) { this.cacheSize -= droppedFromCache; - if (logger.isDebugEnabled()) { - logger.debug( - "Side[{}]|Session[{}]. Removed frames from cache to position[{}]. CacheSize[{}]", - this.side, - this.session, - this.remoteImpliedPosition, - this.cacheSize); + logger.debug("Side[{}]|Session[{}]. Removed frames from cache to position[{}]. CacheSize[{}]", + this.side, this.session, this.remoteImpliedPosition, this.cacheSize); } } } @@ -241,7 +230,7 @@ long handlePendingRemoteImpliedPositionChanges(long expectedState, Queue qs) { + private void handlePendingFrames(Fuseable.QueueSubscription qs) { for (; ; ) { final ByteBuf frame = qs.poll(); final boolean empty = frame == null; @@ -258,7 +247,7 @@ void handlePendingFrames(Fuseable.QueueSubscription qs) { } } - long handlePendingConnection(long expectedState, Queue cachedFrames) { + private long handlePendingConnection(long expectedState, Queue cachedFrames) { CoreSubscriber lastActual = null; for (; ; ) { final CoreSubscriber nextActual = this.pendingActual; @@ -272,12 +261,8 @@ long handlePendingConnection(long expectedState, Queue cachedFrames) { expectedState = markConnected(this, expectedState); if (isConnected(expectedState)) { if (logger.isDebugEnabled()) { - logger.debug( - "Side[{}]|Session[{}]. Connected at Position[{}] and ImpliedPosition[{}]", - side, - session, - firstAvailableFramePosition, - impliedPosition); + logger.debug("Side[{}]|Session[{}]. Connected at Position[{}] and ImpliedPosition[{}]", + side, session, firstAvailableFramePosition, impliedPosition); } this.actual = nextActual; @@ -293,9 +278,9 @@ long handlePendingConnection(long expectedState, Queue cachedFrames) { return expectedState; } - static int dropFramesFromCache(long toRemoveBytes, Queue cache) { + private static int dropFramesFromCache(long toRemoveBytes, Queue cache) { int removedBytes = 0; - while (toRemoveBytes > removedBytes && cache.size() > 0) { + while (toRemoveBytes > removedBytes && !cache.isEmpty()) { final ByteBuf cachedFrame = cache.poll(); final int frameSize = cachedFrame.readableBytes(); @@ -338,29 +323,24 @@ public boolean resumableFrameReceived(ByteBuf frame) { } } - void pauseImplied() { + private void pauseImplied() { for (; ; ) { final long impliedPosition = this.impliedPosition; if (IMPLIED_POSITION.compareAndSet(this, impliedPosition, impliedPosition | Long.MIN_VALUE)) { - logger.debug( - "Side[{}]|Session[{}]. Paused at position[{}]", side, session, impliedPosition); + logger.debug("Side[{}]|Session[{}]. Paused at position[{}]", side, session, impliedPosition); return; } } } - void resumeImplied() { + private void resumeImplied() { for (; ; ) { final long impliedPosition = this.impliedPosition; final long restoredImpliedPosition = impliedPosition & Long.MAX_VALUE; if (IMPLIED_POSITION.compareAndSet(this, impliedPosition, restoredImpliedPosition)) { - logger.debug( - "Side[{}]|Session[{}]. Resumed at position[{}]", - side, - session, - restoredImpliedPosition); + logger.debug("Side[{}]|Session[{}]. Resumed at position[{}]", side, session, restoredImpliedPosition); return; } } @@ -383,9 +363,9 @@ public void dispose() { drain((previousState + 1) | DISPOSED_FLAG); } - void clearCache() { - final Queue frames = this.cachedFrames; + private void clearCache() { this.cacheSize = 0; + final Queue frames = this.cachedFrames; ByteBuf frame; while ((frame = frames.poll()) != null) { @@ -482,19 +462,16 @@ void handleResumableFrame(ByteBuf frame) { } @Override - public void request(long n) { } + public void request(long n) { + } @Override public void cancel() { pauseImplied(); markDisconnected(this); if (logger.isDebugEnabled()) { - logger.debug( - "Side[{}]|Session[{}]. Disconnected at Position[{}] and ImpliedPosition[{}]", - side, - session, - firstAvailableFramePosition, - frameImpliedPosition()); + logger.debug("Side[{}]|Session[{}]. Disconnected at Position[{}] and ImpliedPosition[{}]", + side, session, firstAvailableFramePosition, frameImpliedPosition()); } } @@ -522,16 +499,15 @@ public void subscribe(CoreSubscriber actual) { drain((previousState + 1) | PENDING_CONNECTION_FLAG); } - static class FramesSubscriber - implements CoreSubscriber, Fuseable.QueueSubscription { + private static class FramesSubscriber implements CoreSubscriber, Fuseable.QueueSubscription { final CoreSubscriber actual; final InMemoryResumableFramesStore parent; - Fuseable.QueueSubscription qs; - boolean done; + public Fuseable.QueueSubscription qs; + FramesSubscriber(CoreSubscriber actual, InMemoryResumableFramesStore parent) { this.actual = actual; this.parent = parent; @@ -670,7 +646,7 @@ public boolean isEmpty() { public void clear() { } } - static long markFrameAdded(InMemoryResumableFramesStore store) { + private static long markFrameAdded(InMemoryResumableFramesStore store) { for (; ; ) { final long state = store.state; @@ -690,7 +666,7 @@ static long markFrameAdded(InMemoryResumableFramesStore store) { } } - static long markPendingConnection(InMemoryResumableFramesStore store) { + private static long markPendingConnection(InMemoryResumableFramesStore store) { for (; ; ) { final long state = store.state; @@ -710,7 +686,7 @@ static long markPendingConnection(InMemoryResumableFramesStore store) { } } - static long markRemoteImpliedPositionChanged(InMemoryResumableFramesStore store) { + private static long markRemoteImpliedPositionChanged(InMemoryResumableFramesStore store) { for (; ; ) { final long state = store.state; @@ -726,7 +702,7 @@ static long markRemoteImpliedPositionChanged(InMemoryResumableFramesStore store) } } - static long markDisconnected(InMemoryResumableFramesStore store) { + private static long markDisconnected(InMemoryResumableFramesStore store) { for (; ; ) { final long state = store.state; @@ -740,7 +716,7 @@ static long markDisconnected(InMemoryResumableFramesStore store) { } } - static long markWorkDone(InMemoryResumableFramesStore store, long expectedState) { + private static long markWorkDone(InMemoryResumableFramesStore store, long expectedState) { for (; ; ) { final long state = store.state; @@ -759,7 +735,7 @@ static long markWorkDone(InMemoryResumableFramesStore store, long expectedState) } } - static long markConnected(InMemoryResumableFramesStore store, long expectedState) { + private static long markConnected(InMemoryResumableFramesStore store, long expectedState) { for (; ; ) { final long state = store.state; @@ -778,7 +754,7 @@ static long markConnected(InMemoryResumableFramesStore store, long expectedState } } - static long markTerminated(InMemoryResumableFramesStore store) { + private static long markTerminated(InMemoryResumableFramesStore store) { for (; ; ) { final long state = store.state; @@ -794,7 +770,7 @@ static long markTerminated(InMemoryResumableFramesStore store) { } } - static long markDisposed(InMemoryResumableFramesStore store) { + private static long markDisposed(InMemoryResumableFramesStore store) { for (; ; ) { final long state = store.state; @@ -810,7 +786,7 @@ static long markDisposed(InMemoryResumableFramesStore store) { } } - static void clearAndFinalize(InMemoryResumableFramesStore store) { + private static void clearAndFinalize(InMemoryResumableFramesStore store) { final Fuseable.QueueSubscription qs = store.framesSubscriber.qs; for (; ; ) { final long state = store.state; @@ -830,31 +806,31 @@ static void clearAndFinalize(InMemoryResumableFramesStore store) { } } - static boolean isConnected(long state) { + private static boolean isConnected(long state) { return (state & CONNECTED_FLAG) == CONNECTED_FLAG; } - static boolean hasRemoteImpliedPositionChanged(long state) { + private static boolean hasRemoteImpliedPositionChanged(long state) { return (state & REMOTE_IMPLIED_POSITION_CHANGED_FLAG) == REMOTE_IMPLIED_POSITION_CHANGED_FLAG; } - static boolean hasPendingConnection(long state) { + private static boolean hasPendingConnection(long state) { return (state & PENDING_CONNECTION_FLAG) == PENDING_CONNECTION_FLAG; } - static boolean hasFrames(long state) { + private static boolean hasFrames(long state) { return (state & HAS_FRAME_FLAG) == HAS_FRAME_FLAG; } - static boolean isTerminated(long state) { + private static boolean isTerminated(long state) { return (state & TERMINATED_FLAG) == TERMINATED_FLAG; } - static boolean isDisposed(long state) { + private static boolean isDisposed(long state) { return (state & DISPOSED_FLAG) == DISPOSED_FLAG; } - static boolean isFinalized(long state) { + private static boolean isFinalized(long state) { return (state & FINALIZED_FLAG) == FINALIZED_FLAG; } diff --git a/today-remoting/src/main/java/infra/remoting/resume/InMemoryResumableFramesStoreFactory.java b/today-remoting/src/main/java/infra/remoting/resume/InMemoryResumableFramesStoreFactory.java new file mode 100644 index 0000000..5e53e0c --- /dev/null +++ b/today-remoting/src/main/java/infra/remoting/resume/InMemoryResumableFramesStoreFactory.java @@ -0,0 +1,46 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.resume; + +import infra.lang.Assert; +import io.netty.buffer.ByteBuf; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/22 22:45 + */ +public class InMemoryResumableFramesStoreFactory implements ResumableFramesStoreFactory { + + private final String side; + + private final int cacheSizeBytes; + + /** + * @param cacheLimit cache size bytes + */ + public InMemoryResumableFramesStoreFactory(String side, int cacheLimit) { + Assert.notNull(side, "side is required"); + this.side = side; + this.cacheSizeBytes = cacheLimit; + } + + @Override + public ResumableFramesStore create(ByteBuf resumeToken) { + return new InMemoryResumableFramesStore(side, resumeToken, cacheSizeBytes); + } + +} diff --git a/today-remoting/src/main/java/infra/remoting/resume/RandomUUIDResumeTokenGenerator.java b/today-remoting/src/main/java/infra/remoting/resume/RandomUUIDResumeTokenGenerator.java new file mode 100644 index 0000000..1e6a8ee --- /dev/null +++ b/today-remoting/src/main/java/infra/remoting/resume/RandomUUIDResumeTokenGenerator.java @@ -0,0 +1,41 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.resume; + +import java.util.UUID; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; + +/** + * Random UUID ResumeTokenGenerator + * + * @author 海子 Yang + * @since 1.0 2025/8/23 00:26 + */ +public class RandomUUIDResumeTokenGenerator implements ResumeTokenGenerator { + + @Override + public ByteBuf generate() { + UUID uuid = UUID.randomUUID(); + ByteBuf bb = Unpooled.buffer(16); + bb.writeLong(uuid.getMostSignificantBits()); + bb.writeLong(uuid.getLeastSignificantBits()); + return bb; + } + +} diff --git a/today-remoting/src/main/java/infra/remoting/resume/ResumableDuplexConnection.java b/today-remoting/src/main/java/infra/remoting/resume/ResumableConnection.java similarity index 50% rename from today-remoting/src/main/java/infra/remoting/resume/ResumableDuplexConnection.java rename to today-remoting/src/main/java/infra/remoting/resume/ResumableConnection.java index 69cc589..ef3370c 100644 --- a/today-remoting/src/main/java/infra/remoting/resume/ResumableDuplexConnection.java +++ b/today-remoting/src/main/java/infra/remoting/resume/ResumableConnection.java @@ -1,34 +1,33 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.resume; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Subscription; import java.net.SocketAddress; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import infra.lang.Nullable; import infra.logging.Logger; import infra.logging.LoggerFactory; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.ProtocolErrorException; -import infra.remoting.exceptions.ConnectionErrorException; +import infra.remoting.error.ConnectionErrorException; import infra.remoting.frame.ErrorFrameCodec; import infra.remoting.frame.FrameHeaderCodec; import infra.remoting.internal.UnboundedProcessor; @@ -43,57 +42,53 @@ import reactor.core.publisher.Operators; import reactor.core.publisher.Sinks; -public class ResumableDuplexConnection extends Flux - implements DuplexConnection, Subscription { +public class ResumableConnection extends Flux implements Connection, Subscription { - static final Logger logger = LoggerFactory.getLogger(ResumableDuplexConnection.class); + static final Logger logger = LoggerFactory.getLogger(ResumableConnection.class); final String side; final String session; + final SocketAddress remoteAddress; + final ResumableFramesStore resumableFramesStore; final UnboundedProcessor savableFramesSender; final Sinks.Empty onQueueClose; final Sinks.Empty onLastConnectionClose; - final SocketAddress remoteAddress; final Sinks.Many onConnectionClosedSink; CoreSubscriber receiveSubscriber; FrameReceivingSubscriber activeReceivingSubscriber; volatile int state; - static final AtomicIntegerFieldUpdater STATE = - AtomicIntegerFieldUpdater.newUpdater(ResumableDuplexConnection.class, "state"); + static final AtomicIntegerFieldUpdater STATE = + AtomicIntegerFieldUpdater.newUpdater(ResumableConnection.class, "state"); - volatile DuplexConnection activeConnection; - static final AtomicReferenceFieldUpdater + volatile Connection activeConnection; + static final AtomicReferenceFieldUpdater ACTIVE_CONNECTION = AtomicReferenceFieldUpdater.newUpdater( - ResumableDuplexConnection.class, DuplexConnection.class, "activeConnection"); + ResumableConnection.class, Connection.class, "activeConnection"); int connectionIndex = 0; - public ResumableDuplexConnection( - String side, - ByteBuf session, - DuplexConnection initialConnection, - ResumableFramesStore resumableFramesStore) { + public ResumableConnection(String side, ByteBuf session, Connection initialConnection, ResumableFramesStore store) { this.side = side; this.session = session.toString(CharsetUtil.UTF_8); this.onConnectionClosedSink = Sinks.unsafe().many().unicast().onBackpressureBuffer(); - this.resumableFramesStore = resumableFramesStore; + this.resumableFramesStore = store; this.onQueueClose = Sinks.unsafe().empty(); this.onLastConnectionClose = Sinks.unsafe().empty(); this.savableFramesSender = new UnboundedProcessor(onQueueClose::tryEmitEmpty); this.remoteAddress = initialConnection.remoteAddress(); - resumableFramesStore.saveFrames(savableFramesSender).subscribe(); + store.saveFrames(savableFramesSender).subscribe(); ACTIVE_CONNECTION.lazySet(this, initialConnection); } - public boolean connect(DuplexConnection nextConnection) { - final DuplexConnection activeConnection = this.activeConnection; + public boolean connect(Connection nextConnection) { + final Connection activeConnection = this.activeConnection; if (activeConnection != DisposedConnection.INSTANCE && ACTIVE_CONNECTION.compareAndSet(this, activeConnection, nextConnection)) { @@ -111,63 +106,48 @@ public boolean connect(DuplexConnection nextConnection) { } } - void initConnection(DuplexConnection nextConnection) { + private void initConnection(Connection nextConnection) { final int nextConnectionIndex = this.connectionIndex + 1; - final FrameReceivingSubscriber frameReceivingSubscriber = - new FrameReceivingSubscriber(side, resumableFramesStore, receiveSubscriber); + final var frameReceivingSubscriber = new FrameReceivingSubscriber(side, resumableFramesStore, receiveSubscriber); this.connectionIndex = nextConnectionIndex; this.activeReceivingSubscriber = frameReceivingSubscriber; if (logger.isDebugEnabled()) { - logger.debug( - "Side[{}]|Session[{}]|DuplexConnection[{}]. Connecting", side, session, connectionIndex); + logger.debug("Side[{}]|Session[{}]|Connection[{}]. Connecting", side, session, connectionIndex); } - final Disposable resumeStreamSubscription = - resumableFramesStore - .resumeStream() - .subscribe( - f -> nextConnection.sendFrame(FrameHeaderCodec.streamId(f), f), - t -> { - dispose(nextConnection, t); - nextConnection.sendErrorAndClose(new ConnectionErrorException(t.getMessage(), t)); - }, - () -> { - final ConnectionErrorException e = - new ConnectionErrorException("Connection Closed Unexpectedly"); - dispose(nextConnection, e); - nextConnection.sendErrorAndClose(e); - }); + final Disposable resumeStreamSubscription = resumableFramesStore + .resumeStream() + .subscribe(f -> nextConnection.sendFrame(FrameHeaderCodec.streamId(f), f), + t -> { + dispose(nextConnection, t); + nextConnection.sendErrorAndClose(new ConnectionErrorException(t.getMessage(), t)); + }, + () -> { + final ConnectionErrorException e = + new ConnectionErrorException("Connection Closed Unexpectedly"); + dispose(nextConnection, e); + nextConnection.sendErrorAndClose(e); + }); nextConnection.receive().subscribe(frameReceivingSubscriber); - nextConnection - .onClose() - .doFinally( - __ -> { - frameReceivingSubscriber.dispose(); - resumeStreamSubscription.dispose(); - if (logger.isDebugEnabled()) { - logger.debug( - "Side[{}]|Session[{}]|DuplexConnection[{}]. Disconnected", - side, - session, - connectionIndex); - } - Sinks.EmitResult result = onConnectionClosedSink.tryEmitNext(nextConnectionIndex); - if (!result.equals(Sinks.EmitResult.OK)) { - logger.error( - "Side[{}]|Session[{}]|DuplexConnection[{}]. Failed to notify session of closed connection: {}", - side, - session, - connectionIndex, - result); - } - }) + nextConnection.onClose().doFinally(__ -> { + frameReceivingSubscriber.dispose(); + resumeStreamSubscription.dispose(); + if (logger.isDebugEnabled()) { + logger.debug("Side[{}]|Session[{}]|Connection[{}]. Disconnected", side, session, connectionIndex); + } + Sinks.EmitResult result = onConnectionClosedSink.tryEmitNext(nextConnectionIndex); + if (!result.equals(Sinks.EmitResult.OK)) { + logger.error("Side[{}]|Session[{}]|Connection[{}]. Failed to notify session of closed connection: {}", + side, session, connectionIndex, result); + } + }) .subscribe(); } public void disconnect() { - final DuplexConnection activeConnection = this.activeConnection; + final Connection activeConnection = this.activeConnection; if (activeConnection != DisposedConnection.INSTANCE && !activeConnection.isDisposed()) { activeConnection.dispose(); } @@ -194,7 +174,7 @@ Flux onActiveConnectionClosed() { @Override public void sendErrorAndClose(ProtocolErrorException exception) { - final DuplexConnection activeConnection = + final Connection activeConnection = ACTIVE_CONNECTION.getAndSet(this, DisposedConnection.INSTANCE); if (activeConnection == DisposedConnection.INSTANCE) { return; @@ -203,25 +183,22 @@ public void sendErrorAndClose(ProtocolErrorException exception) { savableFramesSender.tryEmitFinal( ErrorFrameCodec.encode(activeConnection.alloc(), 0, exception)); - activeConnection - .onClose() - .subscribe( - null, - t -> { - onConnectionClosedSink.tryEmitComplete(); - onLastConnectionClose.tryEmitEmpty(); - }, - () -> { - onConnectionClosedSink.tryEmitComplete(); - - final Throwable cause = exception.getCause(); - if (cause == null) { - onLastConnectionClose.tryEmitEmpty(); - } - else { - onLastConnectionClose.tryEmitError(cause); - } - }); + activeConnection.onClose().subscribe(null, + t -> { + onConnectionClosedSink.tryEmitComplete(); + onLastConnectionClose.tryEmitEmpty(); + }, + () -> { + onConnectionClosedSink.tryEmitComplete(); + + final Throwable cause = exception.getCause(); + if (cause == null) { + onLastConnectionClose.tryEmitEmpty(); + } + else { + onLastConnectionClose.tryEmitError(cause); + } + }); } @Override @@ -242,55 +219,47 @@ public Mono onClose() { @Override public void dispose() { - final DuplexConnection activeConnection = - ACTIVE_CONNECTION.getAndSet(this, DisposedConnection.INSTANCE); + final Connection activeConnection = ACTIVE_CONNECTION.getAndSet(this, DisposedConnection.INSTANCE); if (activeConnection == DisposedConnection.INSTANCE) { return; } savableFramesSender.onComplete(); - activeConnection - .onClose() - .subscribe( - null, - t -> { - onConnectionClosedSink.tryEmitComplete(); - onLastConnectionClose.tryEmitEmpty(); - }, - () -> { - onConnectionClosedSink.tryEmitComplete(); - onLastConnectionClose.tryEmitEmpty(); - }); + activeConnection.onClose().subscribe(null, + t -> { + onConnectionClosedSink.tryEmitComplete(); + onLastConnectionClose.tryEmitEmpty(); + }, + () -> { + onConnectionClosedSink.tryEmitComplete(); + onLastConnectionClose.tryEmitEmpty(); + }); } - void dispose(DuplexConnection nextConnection, @Nullable Throwable e) { - final DuplexConnection activeConnection = - ACTIVE_CONNECTION.getAndSet(this, DisposedConnection.INSTANCE); + void dispose(Connection nextConnection, @Nullable Throwable e) { + final Connection activeConnection = ACTIVE_CONNECTION.getAndSet(this, DisposedConnection.INSTANCE); if (activeConnection == DisposedConnection.INSTANCE) { return; } savableFramesSender.onComplete(); - nextConnection - .onClose() - .subscribe( - null, - t -> { - if (e != null) { - onLastConnectionClose.tryEmitError(e); - } - else { - onLastConnectionClose.tryEmitEmpty(); - } - onConnectionClosedSink.tryEmitComplete(); - }, - () -> { - if (e != null) { - onLastConnectionClose.tryEmitError(e); - } - else { - onLastConnectionClose.tryEmitEmpty(); - } - onConnectionClosedSink.tryEmitComplete(); - }); + nextConnection.onClose().subscribe(null, + t -> { + if (e != null) { + onLastConnectionClose.tryEmitError(e); + } + else { + onLastConnectionClose.tryEmitEmpty(); + } + onConnectionClosedSink.tryEmitComplete(); + }, + () -> { + if (e != null) { + onLastConnectionClose.tryEmitError(e); + } + else { + onLastConnectionClose.tryEmitEmpty(); + } + onConnectionClosedSink.tryEmitComplete(); + }); } @Override @@ -332,29 +301,16 @@ static boolean isResumableFrame(ByteBuf frame) { @Override public String toString() { - return "ResumableDuplexConnection{" - + "side='" - + side - + '\'' - + ", session='" - + session - + '\'' - + ", remoteAddress=" - + remoteAddress - + ", state=" - + state - + ", activeConnection=" - + activeConnection - + ", connectionIndex=" - + connectionIndex - + '}'; + return "ResumableConnection{side='%s', session='%s', remoteAddress=%s, state=%d, activeConnection=%s, connectionIndex=%d}" + .formatted(side, session, remoteAddress, state, activeConnection, connectionIndex); } - private static final class DisposedConnection implements DuplexConnection { + private static final class DisposedConnection implements Connection { static final DisposedConnection INSTANCE = new DisposedConnection(); - private DisposedConnection() { } + private DisposedConnection() { + } @Override public void dispose() { } @@ -365,7 +321,8 @@ public Mono onClose() { } @Override - public void sendFrame(int streamId, ByteBuf frame) { } + public void sendFrame(int streamId, ByteBuf frame) { + } @Override public Flux receive() { @@ -373,7 +330,8 @@ public Flux receive() { } @Override - public void sendErrorAndClose(ProtocolErrorException e) { } + public void sendErrorAndClose(ProtocolErrorException e) { + } @Override public ByteBufAllocator alloc() { @@ -387,12 +345,11 @@ public SocketAddress remoteAddress() { } } - private static final class FrameReceivingSubscriber - implements CoreSubscriber, Disposable { + private static final class FrameReceivingSubscriber implements CoreSubscriber, Disposable { - final ResumableFramesStore resumableFramesStore; - final CoreSubscriber actual; - final String tag; + private final ResumableFramesStore resumableFramesStore; + private final CoreSubscriber actual; + private final String tag; volatile Subscription s; static final AtomicReferenceFieldUpdater S = @@ -401,8 +358,7 @@ private static final class FrameReceivingSubscriber boolean cancelled; - private FrameReceivingSubscriber( - String tag, ResumableFramesStore store, CoreSubscriber actual) { + private FrameReceivingSubscriber(String tag, ResumableFramesStore store, CoreSubscriber actual) { this.tag = tag; this.resumableFramesStore = store; this.actual = actual; diff --git a/today-remoting/src/main/java/infra/remoting/resume/ResumableFramesStore.java b/today-remoting/src/main/java/infra/remoting/resume/ResumableFramesStore.java index 9bfa9a1..fab3b9e 100644 --- a/today-remoting/src/main/java/infra/remoting/resume/ResumableFramesStore.java +++ b/today-remoting/src/main/java/infra/remoting/resume/ResumableFramesStore.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.resume; @@ -22,7 +21,9 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -/** Store for resumable frames */ +/** + * Store for resumable frames + */ public interface ResumableFramesStore extends Closeable { /** @@ -33,7 +34,9 @@ public interface ResumableFramesStore extends Closeable { */ Mono saveFrames(Flux frames); - /** Release frames from tail of the store up to remote implied position */ + /** + * Release frames from tail of the store up to remote implied position + */ void releaseFrames(long remoteImpliedPos); /** @@ -42,17 +45,22 @@ public interface ResumableFramesStore extends Closeable { */ Flux resumeStream(); - /** @return Local frame position as defined by RSocket protocol */ + /** + * @return Local frame position as defined by protocol + */ long framePosition(); - /** @return Implied frame position as defined by RSocket protocol */ + /** + * @return Implied frame position as defined by protocol + */ long frameImpliedPosition(); /** - * Received resumable frame as defined by RSocket protocol. Implementation must increment frame + * Received resumable frame as defined by protocol. Implementation must increment frame * implied position * * @return {@code true} if information about the frame has been stored */ boolean resumableFrameReceived(ByteBuf frame); + } diff --git a/today-remoting/src/main/java/infra/remoting/resume/ResumableFramesStoreFactory.java b/today-remoting/src/main/java/infra/remoting/resume/ResumableFramesStoreFactory.java new file mode 100644 index 0000000..8c4394b --- /dev/null +++ b/today-remoting/src/main/java/infra/remoting/resume/ResumableFramesStoreFactory.java @@ -0,0 +1,29 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.resume; + +import io.netty.buffer.ByteBuf; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/22 22:37 + */ +public interface ResumableFramesStoreFactory { + + ResumableFramesStore create(ByteBuf resumeToken); + +} diff --git a/today-remoting/src/main/java/infra/remoting/resume/ResumeStateException.java b/today-remoting/src/main/java/infra/remoting/resume/ResumeStateException.java index 311d87b..a1e938c 100644 --- a/today-remoting/src/main/java/infra/remoting/resume/ResumeStateException.java +++ b/today-remoting/src/main/java/infra/remoting/resume/ResumeStateException.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.resume; diff --git a/today-remoting/src/main/java/infra/remoting/resume/ResumeStateHolder.java b/today-remoting/src/main/java/infra/remoting/resume/ResumeStateHolder.java index d40d2b3..c217aeb 100644 --- a/today-remoting/src/main/java/infra/remoting/resume/ResumeStateHolder.java +++ b/today-remoting/src/main/java/infra/remoting/resume/ResumeStateHolder.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.resume; diff --git a/today-remoting/src/main/java/infra/remoting/resume/ResumeTokenGenerator.java b/today-remoting/src/main/java/infra/remoting/resume/ResumeTokenGenerator.java new file mode 100644 index 0000000..99aee1c --- /dev/null +++ b/today-remoting/src/main/java/infra/remoting/resume/ResumeTokenGenerator.java @@ -0,0 +1,38 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.resume; + +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.Resume; +import io.netty.buffer.ByteBuf; + +/** + * Generator for a resume identification token + * + * @author 海子 Yang + * @see ChannelConnector#resume(Resume) + * @since 1.0 2025/8/23 00:21 + */ +public interface ResumeTokenGenerator { + + /** + * Customize the generation of the resume identification token used to resume. + * This setting is for use with {@link ChannelConnector#resume(Resume)} on the client side only. + */ + ByteBuf generate(); + +} diff --git a/today-remoting/src/main/java/infra/remoting/resume/ServerChannelSession.java b/today-remoting/src/main/java/infra/remoting/resume/ServerChannelSession.java index d7b7507..b01abf1 100644 --- a/today-remoting/src/main/java/infra/remoting/resume/ServerChannelSession.java +++ b/today-remoting/src/main/java/infra/remoting/resume/ServerChannelSession.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.resume; @@ -26,9 +25,9 @@ import infra.logging.Logger; import infra.logging.LoggerFactory; -import infra.remoting.DuplexConnection; -import infra.remoting.exceptions.ConnectionErrorException; -import infra.remoting.exceptions.RejectedResumeException; +import infra.remoting.Connection; +import infra.remoting.error.ConnectionErrorException; +import infra.remoting.error.RejectedResumeException; import infra.remoting.frame.ResumeFrameCodec; import infra.remoting.frame.ResumeOkFrameCodec; import infra.remoting.keepalive.KeepAliveSupport; @@ -44,19 +43,24 @@ public class ServerChannelSession implements ChannelSession, ResumeStateHolder, private static final Logger logger = LoggerFactory.getLogger(ServerChannelSession.class); - final ResumableDuplexConnection resumableConnection; - final Duration resumeSessionDuration; - final ResumableFramesStore resumableFramesStore; - final String session; - final ByteBufAllocator allocator; - final boolean cleanupStoreOnKeepAlive; + private final String session; + + private final ByteBufAllocator allocator; + + private final Duration resumeSessionDuration; + + private final boolean cleanupStoreOnKeepAlive; + + private final ResumableFramesStore resumableFramesStore; /** * All incoming connections with the Resume intent are enqueued in this queue. Such an approach * ensure that the new connection will affect the resumption state anyhow until the previous * (active) connection is finally closed */ - final Queue connectionsQueue; + private final Queue connectionsQueue; + + final ResumableConnection resumableConnection; volatile int wip; static final AtomicIntegerFieldUpdater WIP = @@ -66,29 +70,25 @@ public class ServerChannelSession implements ChannelSession, ResumeStateHolder, static final AtomicReferenceFieldUpdater S = AtomicReferenceFieldUpdater.newUpdater(ServerChannelSession.class, Subscription.class, "s"); - KeepAliveSupport keepAliveSupport; + private KeepAliveSupport keepAliveSupport; - public ServerChannelSession(ByteBuf session, - ResumableDuplexConnection resumableDuplexConnection, - DuplexConnection initialDuplexConnection, - ResumableFramesStore resumableFramesStore, - Duration resumeSessionDuration, - boolean cleanupStoreOnKeepAlive) { + public ServerChannelSession(ByteBuf session, ResumableConnection resumableConnection, Connection initialConnection, + ResumableFramesStore resumableFramesStore, Duration resumeSessionDuration, boolean cleanupStoreOnKeepAlive) { this.session = session.toString(CharsetUtil.UTF_8); - this.allocator = initialDuplexConnection.alloc(); - this.resumeSessionDuration = resumeSessionDuration; + this.allocator = initialConnection.alloc(); + this.resumableConnection = resumableConnection; this.resumableFramesStore = resumableFramesStore; + this.resumeSessionDuration = resumeSessionDuration; this.cleanupStoreOnKeepAlive = cleanupStoreOnKeepAlive; - this.resumableConnection = resumableDuplexConnection; this.connectionsQueue = Queues.unboundedMultiproducer().get(); WIP.lazySet(this, 1); - resumableDuplexConnection.onClose().doFinally(__ -> dispose()).subscribe(); - resumableDuplexConnection.onActiveConnectionClosed().subscribe(__ -> tryTimeoutSession()); + resumableConnection.onClose().doFinally(__ -> dispose()).subscribe(); + resumableConnection.onActiveConnectionClosed().subscribe(__ -> tryTimeoutSession()); } - void tryTimeoutSession() { + private void tryTimeoutSession() { keepAliveSupport.stop(); if (logger.isDebugEnabled()) { @@ -109,16 +109,16 @@ void tryTimeoutSession() { } } - public void resumeWith(ByteBuf resumeFrame, DuplexConnection nextDuplexConnection) { + public void resumeWith(ByteBuf resumeFrame, Connection nextConnection) { if (logger.isDebugEnabled()) { - logger.debug("Side[server]|Session[{}]. New DuplexConnection received.", session); + logger.debug("Side[server]|Session[{}]. New Connection received.", session); } long remotePos = ResumeFrameCodec.firstAvailableClientPos(resumeFrame); long remoteImpliedPos = ResumeFrameCodec.lastReceivedServerPos(resumeFrame); - connectionsQueue.offer(() -> doResume(remotePos, remoteImpliedPos, nextDuplexConnection)); + connectionsQueue.offer(() -> doResume(remotePos, remoteImpliedPos, nextConnection)); if (WIP.getAndIncrement(this) != 0) { return; @@ -130,17 +130,14 @@ public void resumeWith(ByteBuf resumeFrame, DuplexConnection nextDuplexConnectio } } - void doResume(long remotePos, long remoteImpliedPos, DuplexConnection nextDuplexConnection) { + private void doResume(long remotePos, long remoteImpliedPos, Connection nextConnection) { if (!tryCancelSessionTimeout()) { if (logger.isDebugEnabled()) { - logger.debug( - "Side[server]|Session[{}]. Session has already been expired. Terminating received connection", - session); + logger.debug("Side[server]|Session[{}]. Session has already been expired. Terminating received connection", session); } - final RejectedResumeException rejectedResumeException = - new RejectedResumeException("resume_internal_error: Session Expired"); - nextDuplexConnection.sendErrorAndClose(rejectedResumeException); - nextDuplexConnection.receive().subscribe(); + final RejectedResumeException rejectedResumeException = new RejectedResumeException("resume_internal_error: Session Expired"); + nextConnection.sendErrorAndClose(rejectedResumeException); + nextConnection.receive().subscribe(); return; } @@ -148,13 +145,8 @@ void doResume(long remotePos, long remoteImpliedPos, DuplexConnection nextDuplex long position = resumableFramesStore.framePosition(); if (logger.isDebugEnabled()) { - logger.debug( - "Side[server]|Session[{}]. Resume FRAME received. ServerResumeState[impliedPosition[{}], position[{}]]. ClientResumeState[remoteImpliedPosition[{}], remotePosition[{}]]", - session, - impliedPosition, - position, - remoteImpliedPos, - remotePos); + logger.debug("Side[server]|Session[{}]. Resume FRAME received. ServerResumeState[impliedPosition[{}], position[{}]]. ClientResumeState[remoteImpliedPosition[{}], remotePosition[{}]]", + session, impliedPosition, position, remoteImpliedPos, remotePos); } if (remotePos <= impliedPosition && position <= remoteImpliedPos) { @@ -162,28 +154,22 @@ void doResume(long remotePos, long remoteImpliedPos, DuplexConnection nextDuplex if (position != remoteImpliedPos) { resumableFramesStore.releaseFrames(remoteImpliedPos); } - nextDuplexConnection.sendFrame(0, ResumeOkFrameCodec.encode(allocator, impliedPosition)); + nextConnection.sendFrame(0, ResumeOkFrameCodec.encode(allocator, impliedPosition)); if (logger.isDebugEnabled()) { - logger.debug( - "Side[server]|Session[{}]. ResumeOKFrame[impliedPosition[{}]] has been sent", - session, - impliedPosition); + logger.debug("Side[server]|Session[{}]. ResumeOKFrame[impliedPosition[{}]] has been sent", session, impliedPosition); } } catch (Throwable t) { if (logger.isDebugEnabled()) { - logger.debug( - "Side[server]|Session[{}]. Exception occurred while releasing frames in the frameStore", - session, - t); + logger.debug("Side[server]|Session[{}]. Exception occurred while releasing frames in the frameStore", + session, t); } dispose(); - final RejectedResumeException rejectedResumeException = - new RejectedResumeException(t.getMessage(), t); - nextDuplexConnection.sendErrorAndClose(rejectedResumeException); - nextDuplexConnection.receive().subscribe(); + final RejectedResumeException rejectedResumeException = new RejectedResumeException(t.getMessage(), t); + nextConnection.sendErrorAndClose(rejectedResumeException); + nextConnection.receive().subscribe(); return; } @@ -194,16 +180,14 @@ void doResume(long remotePos, long remoteImpliedPos, DuplexConnection nextDuplex logger.debug("Side[server]|Session[{}]. Session has been resumed successfully", session); } - if (!resumableConnection.connect(nextDuplexConnection)) { + if (!resumableConnection.connect(nextConnection)) { if (logger.isDebugEnabled()) { - logger.debug( - "Side[server]|Session[{}]. Session has already been expired. Terminating received connection", - session); + logger.debug("Side[server]|Session[{}]. Session has already been expired. Terminating received connection", session); } final RejectedResumeException rejectedResumeException = new RejectedResumeException("resume_internal_error: Session Expired"); - nextDuplexConnection.sendErrorAndClose(rejectedResumeException); - nextDuplexConnection.receive().subscribe(); + nextConnection.sendErrorAndClose(rejectedResumeException); + nextConnection.receive().subscribe(); // resumableConnection is likely to be disposed at this stage. Thus we have // nothing to do @@ -213,26 +197,20 @@ void doResume(long remotePos, long remoteImpliedPos, DuplexConnection nextDuplex if (logger.isDebugEnabled()) { logger.debug( "Side[server]|Session[{}]. Mismatching remote and local state. Expected RemoteImpliedPosition[{}] to be greater or equal to the LocalPosition[{}] and RemotePosition[{}] to be less or equal to LocalImpliedPosition[{}]. Terminating received connection", - session, - remoteImpliedPos, - position, - remotePos, - impliedPosition); + session, remoteImpliedPos, position, remotePos, impliedPosition); } dispose(); final RejectedResumeException rejectedResumeException = - new RejectedResumeException( - String.format( - "resumption_pos=[ remote: { pos: %d, impliedPos: %d }, local: { pos: %d, impliedPos: %d }]", - remotePos, remoteImpliedPos, position, impliedPosition)); - nextDuplexConnection.sendErrorAndClose(rejectedResumeException); - nextDuplexConnection.receive().subscribe(); + new RejectedResumeException(String.format("resumption_pos=[ remote: { pos: %d, impliedPos: %d }, local: { pos: %d, impliedPos: %d }]", + remotePos, remoteImpliedPos, position, impliedPosition)); + nextConnection.sendErrorAndClose(rejectedResumeException); + nextConnection.receive().subscribe(); } } - boolean tryCancelSessionTimeout() { + private boolean tryCancelSessionTimeout() { for (; ; ) { final Subscription subscription = this.s; @@ -281,10 +259,12 @@ public void onNext(Long aLong) { } @Override - public void onComplete() { } + public void onComplete() { + } @Override - public void onError(Throwable t) { } + public void onError(Throwable t) { + } public void setKeepAliveSupport(KeepAliveSupport keepAliveSupport) { this.keepAliveSupport = keepAliveSupport; diff --git a/today-remoting/src/main/java/infra/remoting/resume/SessionManager.java b/today-remoting/src/main/java/infra/remoting/resume/SessionManager.java index c21ecf9..b95402a 100644 --- a/today-remoting/src/main/java/infra/remoting/resume/SessionManager.java +++ b/today-remoting/src/main/java/infra/remoting/resume/SessionManager.java @@ -1,26 +1,26 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.resume; +import org.jspecify.annotations.Nullable; + import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import infra.lang.Nullable; import infra.logging.Logger; import infra.logging.LoggerFactory; import io.netty.buffer.ByteBuf; @@ -38,19 +38,12 @@ public ServerChannelSession save(ServerChannelSession session, ByteBuf resumeTok } else { final String token = resumeToken.toString(CharsetUtil.UTF_8); - session - .resumableConnection - .onClose() - .doFinally( - __ -> { - logger.debug( - "ResumableConnection has been closed. Removing associated session {" - + token - + "}"); - if (isDisposed || sessions.get(token) == session) { - sessions.remove(token); - } - }) + session.resumableConnection.onClose().doFinally(__ -> { + logger.debug("ResumableConnection has been closed. Removing associated session '{}'", token); + if (isDisposed || sessions.get(token) == session) { + sessions.remove(token); + } + }) .subscribe(); ServerChannelSession prevSession = sessions.remove(token); if (prevSession != null) { diff --git a/today-remoting/src/main/java/infra/remoting/resume/package-info.java b/today-remoting/src/main/java/infra/remoting/resume/package-info.java index 31031ed..bd3d0f7 100644 --- a/today-remoting/src/main/java/infra/remoting/resume/package-info.java +++ b/today-remoting/src/main/java/infra/remoting/resume/package-info.java @@ -1,28 +1,27 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /** - * Contains support classes for the RSocket resume capability. + * Contains support classes for the protocol resume capability. * * @see Resuming * Operation */ -@NonNullApi +@NullMarked package infra.remoting.resume; -import infra.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting/src/main/java/infra/remoting/transport/ClientTransport.java b/today-remoting/src/main/java/infra/remoting/transport/ClientTransport.java index 6e3dd7a..fcfcea2 100644 --- a/today-remoting/src/main/java/infra/remoting/transport/ClientTransport.java +++ b/today-remoting/src/main/java/infra/remoting/transport/ClientTransport.java @@ -1,31 +1,30 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import reactor.core.publisher.Mono; -/** A client contract for writing transports of RSocket. */ +/** A client contract for writing transports of protocol. */ public interface ClientTransport extends Transport { /** * Return a {@code Mono} that connects for each subscriber. */ - Mono connect(); + Mono connect(); } diff --git a/today-remoting/src/main/java/infra/remoting/transport/ConnectionAcceptor.java b/today-remoting/src/main/java/infra/remoting/transport/ConnectionAcceptor.java index dec92a8..de1710a 100644 --- a/today-remoting/src/main/java/infra/remoting/transport/ConnectionAcceptor.java +++ b/today-remoting/src/main/java/infra/remoting/transport/ConnectionAcceptor.java @@ -1,27 +1,26 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import reactor.core.publisher.Mono; /** - * A contract to accept a new {@code DuplexConnection}. + * A contract to accept a new {@code Connection}. * * @author 海子 Yang * @since 1.0 2025/8/2 22:06 @@ -29,12 +28,12 @@ public interface ConnectionAcceptor { /** - * Accept a new {@code DuplexConnection} and returns {@code Publisher} signifying the end of + * Accept a new {@code Connection} and returns {@code Publisher} signifying the end of * processing of the connection. * - * @param connection New {@code DuplexConnection} to be processed. + * @param connection New {@code Connection} to be processed. * @return A {@code Publisher} which terminates when the processing of the connection finishes. */ - Mono accept(DuplexConnection connection); + Mono accept(Connection connection); } diff --git a/today-remoting/src/main/java/infra/remoting/transport/ServerTransport.java b/today-remoting/src/main/java/infra/remoting/transport/ServerTransport.java index 0255fba..7f32948 100644 --- a/today-remoting/src/main/java/infra/remoting/transport/ServerTransport.java +++ b/today-remoting/src/main/java/infra/remoting/transport/ServerTransport.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport; @@ -21,7 +20,7 @@ import reactor.core.publisher.Mono; /** - * A server contract for writing transports of RSocket. + * A server contract for writing transports of protocol. */ public interface ServerTransport extends Transport { diff --git a/today-remoting/src/main/java/infra/remoting/transport/Transport.java b/today-remoting/src/main/java/infra/remoting/transport/Transport.java index 6ed90e1..5ceb258 100644 --- a/today-remoting/src/main/java/infra/remoting/transport/Transport.java +++ b/today-remoting/src/main/java/infra/remoting/transport/Transport.java @@ -1,34 +1,31 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; -/** */ public interface Transport { /** - * Configurations that exposes the maximum frame size that a {@link DuplexConnection} can bring up - * to RSocket level. + * Configurations that exposes the maximum frame size that a {@link Connection} can bring up. * - *

    This number should not exist the 16,777,215 (maximum frame size specified by RSocket spec) + *

    This number should not exist the 16,777,215 (maximum frame size specified by protocol spec) * * @return return maximum configured frame size limit */ diff --git a/today-remoting/src/main/java/infra/remoting/transport/package-info.java b/today-remoting/src/main/java/infra/remoting/transport/package-info.java index eed45b0..2268e63 100644 --- a/today-remoting/src/main/java/infra/remoting/transport/package-info.java +++ b/today-remoting/src/main/java/infra/remoting/transport/package-info.java @@ -1,22 +1,21 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /** Client and server transport contracts for pluggable transports. */ -@NonNullApi +@NullMarked package infra.remoting.transport; -import infra.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting/src/main/java/infra/remoting/util/ByteBufPayload.java b/today-remoting/src/main/java/infra/remoting/util/ByteBufPayload.java index d9c14af..dad4284 100644 --- a/today-remoting/src/main/java/infra/remoting/util/ByteBufPayload.java +++ b/today-remoting/src/main/java/infra/remoting/util/ByteBufPayload.java @@ -1,27 +1,27 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.util; +import org.jspecify.annotations.Nullable; + import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; -import infra.lang.Nullable; import infra.remoting.Payload; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; @@ -33,15 +33,18 @@ import io.netty.util.Recycler.Handle; public final class ByteBufPayload extends AbstractReferenceCounted implements Payload { - private static final Recycler RECYCLER = - new Recycler() { - protected ByteBufPayload newObject(Handle handle) { - return new ByteBufPayload(handle); - } - }; + + private static final Recycler RECYCLER = new Recycler<>() { + protected ByteBufPayload newObject(Handle handle) { + return new ByteBufPayload(handle); + } + }; private final Handle handle; + private ByteBuf data; + + @Nullable private ByteBuf metadata; private ByteBufPayload(final Handle handle) { @@ -78,13 +81,9 @@ public static Payload create(CharSequence data, Charset dataCharset) { null); } - public static Payload create( - CharSequence data, - Charset dataCharset, - @Nullable CharSequence metadata, - Charset metadataCharset) { - return create( - ByteBufUtil.encodeString(ByteBufAllocator.DEFAULT, CharBuffer.wrap(data), dataCharset), + public static Payload create(CharSequence data, Charset dataCharset, + @Nullable CharSequence metadata, Charset metadataCharset) { + return create(ByteBufUtil.encodeString(ByteBufAllocator.DEFAULT, CharBuffer.wrap(data), dataCharset), metadata == null ? null : ByteBufUtil.encodeString( @@ -123,11 +122,17 @@ public static Payload create(ByteBuf data, @Nullable ByteBuf metadata) { } public static Payload create(Payload payload) { - return create( - payload.sliceData().retain(), + return create(payload.sliceData().retain(), payload.hasMetadata() ? payload.sliceMetadata().retain() : null); } + @Override + public int length() { + return metadata == null + ? data.readableBytes() + : metadata.readableBytes() + data.readableBytes(); + } + @Override public boolean hasMetadata() { ensureAccessible(); diff --git a/today-remoting/src/main/java/infra/remoting/util/CharByteBufUtil.java b/today-remoting/src/main/java/infra/remoting/util/CharByteBufUtil.java deleted file mode 100644 index 624e92c..0000000 --- a/today-remoting/src/main/java/infra/remoting/util/CharByteBufUtil.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.util; - -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.CharacterCodingException; -import java.nio.charset.CharsetDecoder; -import java.nio.charset.CoderResult; -import java.util.Arrays; - -import io.netty.buffer.ByteBuf; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.MathUtil; - -import static io.netty.util.internal.StringUtil.isSurrogate; - -public class CharByteBufUtil { - - private static final byte WRITE_UTF_UNKNOWN = (byte) '?'; - - private CharByteBufUtil() { } - - /** - * Returns the exact bytes length of UTF8 character sequence. - * - *

    This method is producing the exact length according to {@link #writeUtf8(ByteBuf, char[])}. - */ - public static int utf8Bytes(final char[] seq) { - return utf8ByteCount(seq, 0, seq.length); - } - - /** - * This method is producing the exact length according to {@link #writeUtf8(ByteBuf, char[], int, - * int)}. - */ - public static int utf8Bytes(final char[] seq, int start, int end) { - return utf8ByteCount(checkCharSequenceBounds(seq, start, end), start, end); - } - - private static int utf8ByteCount(final char[] seq, int start, int end) { - int i = start; - // ASCII fast path - while (i < end && seq[i] < 0x80) { - ++i; - } - // !ASCII is packed in a separate method to let the ASCII case be smaller - return i < end ? (i - start) + utf8BytesNonAscii(seq, i, end) : i - start; - } - - private static int utf8BytesNonAscii(final char[] seq, final int start, final int end) { - int encodedLength = 0; - for (int i = start; i < end; i++) { - final char c = seq[i]; - // making it 100% branchless isn't rewarding due to the many bit operations necessary! - if (c < 0x800) { - // branchless version of: (c <= 127 ? 0:1) + 1 - encodedLength += ((0x7f - c) >>> 31) + 1; - } - else if (isSurrogate(c)) { - if (!Character.isHighSurrogate(c)) { - encodedLength++; - // WRITE_UTF_UNKNOWN - continue; - } - final char c2; - try { - // Surrogate Pair consumes 2 characters. Optimistically try to get the next character to - // avoid - // duplicate bounds checking with charAt. - c2 = seq[++i]; - } - catch (IndexOutOfBoundsException ignored) { - encodedLength++; - // WRITE_UTF_UNKNOWN - break; - } - if (!Character.isLowSurrogate(c2)) { - // WRITE_UTF_UNKNOWN + (Character.isHighSurrogate(c2) ? WRITE_UTF_UNKNOWN : c2) - encodedLength += 2; - continue; - } - // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630. - encodedLength += 4; - } - else { - encodedLength += 3; - } - } - return encodedLength; - } - - private static char[] checkCharSequenceBounds(char[] seq, int start, int end) { - if (MathUtil.isOutOfBounds(start, end - start, seq.length)) { - throw new IndexOutOfBoundsException( - "expected: 0 <= start(" - + start - + ") <= end (" - + end - + ") <= seq.length(" - + seq.length - + ')'); - } - return seq; - } - - /** - * Encode a {@code char[]} in UTF-8 and write it - * into {@link ByteBuf}. - * - *

    This method returns the actual number of bytes written. - */ - public static int writeUtf8(ByteBuf buf, char[] seq) { - return writeUtf8(buf, seq, 0, seq.length); - } - - /** - * Equivalent to {@link #writeUtf8(ByteBuf, char[]) writeUtf8(buf, seq.subSequence(start, end), - * reserveBytes)} but avoids subsequence object allocation if possible. - * - * @return actual number of bytes written - */ - public static int writeUtf8(ByteBuf buf, char[] seq, int start, int end) { - return writeUtf8(buf, buf.writerIndex(), checkCharSequenceBounds(seq, start, end), start, end); - } - - // Fast-Path implementation - static int writeUtf8(ByteBuf buffer, int writerIndex, char[] seq, int start, int end) { - int oldWriterIndex = writerIndex; - - // We can use the _set methods as these not need to do any index checks and reference checks. - // This is possible as we called ensureWritable(...) before. - for (int i = start; i < end; i++) { - char c = seq[i]; - if (c < 0x80) { - buffer.setByte(writerIndex++, (byte) c); - } - else if (c < 0x800) { - buffer.setByte(writerIndex++, (byte) (0xc0 | (c >> 6))); - buffer.setByte(writerIndex++, (byte) (0x80 | (c & 0x3f))); - } - else if (isSurrogate(c)) { - if (!Character.isHighSurrogate(c)) { - buffer.setByte(writerIndex++, WRITE_UTF_UNKNOWN); - continue; - } - final char c2; - if (seq.length > ++i) { - // Surrogate Pair consumes 2 characters. Optimistically try to get the next character to - // avoid - // duplicate bounds checking with charAt. If an IndexOutOfBoundsException is thrown we - // will - // re-throw a more informative exception describing the problem. - c2 = seq[i]; - } - else { - buffer.setByte(writerIndex++, WRITE_UTF_UNKNOWN); - break; - } - // Extra method to allow inlining the rest of writeUtf8 which is the most likely code path. - writerIndex = writeUtf8Surrogate(buffer, writerIndex, c, c2); - } - else { - buffer.setByte(writerIndex++, (byte) (0xe0 | (c >> 12))); - buffer.setByte(writerIndex++, (byte) (0x80 | ((c >> 6) & 0x3f))); - buffer.setByte(writerIndex++, (byte) (0x80 | (c & 0x3f))); - } - } - buffer.writerIndex(writerIndex); - return writerIndex - oldWriterIndex; - } - - private static int writeUtf8Surrogate(ByteBuf buffer, int writerIndex, char c, char c2) { - if (!Character.isLowSurrogate(c2)) { - buffer.setByte(writerIndex++, WRITE_UTF_UNKNOWN); - buffer.setByte(writerIndex++, Character.isHighSurrogate(c2) ? WRITE_UTF_UNKNOWN : c2); - return writerIndex; - } - int codePoint = Character.toCodePoint(c, c2); - // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630. - buffer.setByte(writerIndex++, (byte) (0xf0 | (codePoint >> 18))); - buffer.setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 12) & 0x3f))); - buffer.setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 6) & 0x3f))); - buffer.setByte(writerIndex++, (byte) (0x80 | (codePoint & 0x3f))); - return writerIndex; - } - - public static char[] readUtf8(ByteBuf byteBuf, int length) { - CharsetDecoder charsetDecoder = CharsetUtil.UTF_8.newDecoder(); - int en = (int) (length * (double) charsetDecoder.maxCharsPerByte()); - char[] ca = new char[en]; - - CharBuffer charBuffer = CharBuffer.wrap(ca); - ByteBuffer byteBuffer = - byteBuf.nioBufferCount() == 1 - ? byteBuf.internalNioBuffer(byteBuf.readerIndex(), length) - : byteBuf.nioBuffer(byteBuf.readerIndex(), length); - byteBuffer.mark(); - try { - CoderResult cr = charsetDecoder.decode(byteBuffer, charBuffer, true); - if (!cr.isUnderflow()) - cr.throwException(); - cr = charsetDecoder.flush(charBuffer); - if (!cr.isUnderflow()) - cr.throwException(); - - byteBuffer.reset(); - byteBuf.skipBytes(length); - - return safeTrim(charBuffer.array(), charBuffer.position()); - } - catch (CharacterCodingException x) { - // Substitution is always enabled, - // so this shouldn't happen - throw new IllegalStateException("unable to decode char array from the given buffer", x); - } - } - - private static char[] safeTrim(char[] ca, int len) { - if (len == ca.length) - return ca; - else - return Arrays.copyOf(ca, len); - } -} diff --git a/today-remoting/src/main/java/infra/remoting/util/Clock.java b/today-remoting/src/main/java/infra/remoting/util/Clock.java index dd9c1c7..2b65ed8 100644 --- a/today-remoting/src/main/java/infra/remoting/util/Clock.java +++ b/today-remoting/src/main/java/infra/remoting/util/Clock.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.util; diff --git a/today-remoting/src/main/java/infra/remoting/util/DefaultPayload.java b/today-remoting/src/main/java/infra/remoting/util/DefaultPayload.java index b27fa4b..ecc8f82 100644 --- a/today-remoting/src/main/java/infra/remoting/util/DefaultPayload.java +++ b/today-remoting/src/main/java/infra/remoting/util/DefaultPayload.java @@ -1,28 +1,28 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.util; +import org.jspecify.annotations.Nullable; + import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import infra.lang.Nullable; import infra.remoting.Payload; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -125,6 +125,13 @@ private static byte[] toBytes(ByteBuf byteBuf) { return bytes; } + @Override + public int length() { + return metadata == null + ? data.remaining() + : metadata.remaining() + data.remaining(); + } + @Override public boolean hasMetadata() { return metadata != null; diff --git a/today-remoting/src/main/java/infra/remoting/util/EmptyPayload.java b/today-remoting/src/main/java/infra/remoting/util/EmptyPayload.java index 61bd99a..b1aad4e 100644 --- a/today-remoting/src/main/java/infra/remoting/util/EmptyPayload.java +++ b/today-remoting/src/main/java/infra/remoting/util/EmptyPayload.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.util; @@ -24,7 +23,13 @@ public class EmptyPayload implements Payload { public static final EmptyPayload INSTANCE = new EmptyPayload(); - private EmptyPayload() { } + private EmptyPayload() { + } + + @Override + public int length() { + return 0; + } @Override public boolean hasMetadata() { diff --git a/today-remoting/src/main/java/infra/remoting/util/FutureMono.java b/today-remoting/src/main/java/infra/remoting/util/FutureMono.java deleted file mode 100644 index f077a45..0000000 --- a/today-remoting/src/main/java/infra/remoting/util/FutureMono.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.util; - -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -import java.nio.channels.ClosedChannelException; -import java.util.Objects; -import java.util.function.Supplier; - -import infra.remoting.AbortedException; -import infra.util.concurrent.Future; -import infra.util.concurrent.FutureListener; -import reactor.core.CoreSubscriber; -import reactor.core.publisher.Mono; -import reactor.core.publisher.Operators; -import reactor.util.context.Context; - -/** - * @author 海子 Yang - * @since 1.0 2025/8/3 11:34 - */ -public abstract class FutureMono extends Mono { - - /** - * Convert a {@link Future} into {@link Mono}. {@link Mono#subscribe(Subscriber)} - * will bridge to {@link Future#onCompleted(FutureListener)}. - * - * @param future the future to convert from - * @param the future type - * @return A {@link Mono} forwarding {@link Future} success or failure - */ - public static > Mono of(F future) { - Objects.requireNonNull(future, "future"); - if (future.isDone()) { - if (!future.isSuccess()) { - return Mono.error(FutureSubscription.wrapError(future.getCause())); - } - return Mono.empty(); - } - return new ImmediateFutureMono<>(future); - } - - /** - * Convert a supplied {@link Future} for each subscriber into {@link Mono}. - * {@link Mono#subscribe(Subscriber)} - * will bridge to {@link Future#onCompleted(FutureListener)}. - * - * @param deferredFuture the future to evaluate and convert from - * @param the future type - * @return A {@link Mono} forwarding {@link Future} success or failure - */ - public static > Mono deferFuture(Supplier deferredFuture) { - return new DeferredFutureMono<>(deferredFuture); - } - - static final class ImmediateFutureMono> extends FutureMono { - - final F future; - - ImmediateFutureMono(F future) { - this.future = Objects.requireNonNull(future, "future"); - } - - @Override - public void subscribe(final CoreSubscriber s) { - doSubscribe(s, future); - } - } - - static final class DeferredFutureMono> extends FutureMono { - - final Supplier deferredFuture; - - DeferredFutureMono(Supplier deferredFuture) { - this.deferredFuture = Objects.requireNonNull(deferredFuture, "deferredFuture"); - } - - @Override - public void subscribe(CoreSubscriber s) { - F f; - try { - f = deferredFuture.get(); - } - catch (Throwable t) { - Operators.error(s, t); - return; - } - - if (f == null) { - Operators.error(s, - Operators.onOperatorError(new NullPointerException( - "Deferred supplied null"), s.currentContext())); - return; - } - - doSubscribe(s, f); - } - } - - @SuppressWarnings("FutureReturnValueIgnored") - static > void doSubscribe(CoreSubscriber s, F future) { - if (future.isDone()) { - if (future.isSuccess()) { - Operators.complete(s); - } - else { - Operators.error(s, FutureSubscription.wrapError(future.getCause())); - } - return; - } - - FutureSubscription fs = new FutureSubscription<>(future, s); - // propagate subscription before adding listener to avoid any race between finishing future and onSubscribe - // is called - s.onSubscribe(fs); - - // check if subscription was not cancelled immediately. - if (fs.cancelled) { - // if so do nothing anymore - return; - } - - // add listener to the future to propagate on complete when future is done - // onCompleted likely to be thread safe method - future.onCompleted(fs); - - // check once again if is cancelled to see if we need to removeListener in case onCompleted racing with - // subscription.cancel (which should remove listener) - if (fs.cancelled) { - // Returned value is deliberately ignored - future.cancel(); - } - } - - static final class FutureSubscription> - implements FutureListener, Subscription, Supplier { - - final CoreSubscriber s; - - final F future; - - boolean cancelled; - - FutureSubscription(F future, CoreSubscriber s) { - this.s = s; - this.future = future; - } - - @Override - public void request(long n) { - //noop - } - - @Override - public Context get() { - return s.currentContext(); - } - - @Override - @SuppressWarnings("FutureReturnValueIgnored") - public void cancel() { - // cancel is not thread safe since we assume that removeListener is thread-safe. That said if we have - // concurrent onCompleted and removeListener and if onCompleted is after removeListener, the other Thread - // after execution onCompleted should see changes happened before removeListener. Thus, it should see - // cancelled flag set to true and should cleanup added handler - this.cancelled = true; - future.cancel(); - } - - @Override - public void operationComplete(F future) { - if (cancelled) { - // Returned value is deliberately ignored - return; - } - if (future.isSuccess()) { - T now = future.getNow(); - if (now != null) { - s.onNext(now); - } - s.onComplete(); - } - else { - s.onError(wrapError(future.getCause())); - } - } - - private static Throwable wrapError(Throwable error) { - if (error instanceof ClosedChannelException) { - return new AbortedException(error); - } - else { - return error; - } - } - } -} diff --git a/today-remoting/src/main/java/infra/remoting/util/NumberUtils.java b/today-remoting/src/main/java/infra/remoting/util/NumberUtils.java deleted file mode 100644 index a43cc89..0000000 --- a/today-remoting/src/main/java/infra/remoting/util/NumberUtils.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.util; - -import java.util.Objects; - -import io.netty.buffer.ByteBuf; - -public final class NumberUtils { - - /** The size of a medium in {@code byte}s. */ - public static final int MEDIUM_BYTES = 3; - - private static final int UNSIGNED_BYTE_SIZE = 8; - - private static final int UNSIGNED_BYTE_MAX_VALUE = (1 << UNSIGNED_BYTE_SIZE) - 1; - - private static final int UNSIGNED_MEDIUM_SIZE = 24; - - private static final int UNSIGNED_MEDIUM_MAX_VALUE = (1 << UNSIGNED_MEDIUM_SIZE) - 1; - - private static final int UNSIGNED_SHORT_SIZE = 16; - - private static final int UNSIGNED_SHORT_MAX_VALUE = (1 << UNSIGNED_SHORT_SIZE) - 1; - - private NumberUtils() { } - - /** - * Requires that an {@code int} is greater than or equal to zero. - * - * @param i the {@code int} to test - * @param message detail message to be used in the event that a {@link IllegalArgumentException} - * is thrown - * @return the {@code int} if greater than or equal to zero - * @throws IllegalArgumentException if {@code i} is less than zero - */ - public static int requireNonNegative(int i, String message) { - Objects.requireNonNull(message, "message is required"); - - if (i < 0) { - throw new IllegalArgumentException(message); - } - - return i; - } - - /** - * Requires that a {@code long} is greater than zero. - * - * @param l the {@code long} to test - * @param message detail message to be used in the event that a {@link IllegalArgumentException} - * is thrown - * @return the {@code long} if greater than zero - * @throws IllegalArgumentException if {@code l} is less than or equal to zero - */ - public static long requirePositive(long l, String message) { - Objects.requireNonNull(message, "message is required"); - - if (l <= 0) { - throw new IllegalArgumentException(message); - } - - return l; - } - - /** - * Requires that an {@code int} is greater than zero. - * - * @param i the {@code int} to test - * @param message detail message to be used in the event that a {@link IllegalArgumentException} - * is thrown - * @return the {@code int} if greater than zero - * @throws IllegalArgumentException if {@code i} is less than or equal to zero - */ - public static int requirePositive(int i, String message) { - Objects.requireNonNull(message, "message is required"); - - if (i <= 0) { - throw new IllegalArgumentException(message); - } - - return i; - } - - /** - * Requires that an {@code int} can be represented as an unsigned {@code byte}. - * - * @param i the {@code int} to test - * @return the {@code int} if it can be represented as an unsigned {@code byte} - * @throws IllegalArgumentException if {@code i} cannot be represented as an unsigned {@code byte} - */ - public static int requireUnsignedByte(int i) { - if (i > UNSIGNED_BYTE_MAX_VALUE) { - throw new IllegalArgumentException( - String.format("%d is larger than %d bits", i, UNSIGNED_BYTE_SIZE)); - } - - return i; - } - - /** - * Requires that an {@code int} can be represented as an unsigned {@code medium}. - * - * @param i the {@code int} to test - * @return the {@code int} if it can be represented as an unsigned {@code medium} - * @throws IllegalArgumentException if {@code i} cannot be represented as an unsigned {@code - * medium} - */ - public static int requireUnsignedMedium(int i) { - if (i > UNSIGNED_MEDIUM_MAX_VALUE) { - throw new IllegalArgumentException( - String.format("%d is larger than %d bits", i, UNSIGNED_MEDIUM_SIZE)); - } - - return i; - } - - /** - * Requires that an {@code int} can be represented as an unsigned {@code short}. - * - * @param i the {@code int} to test - * @return the {@code int} if it can be represented as an unsigned {@code short} - * @throws IllegalArgumentException if {@code i} cannot be represented as an unsigned {@code - * short} - */ - public static int requireUnsignedShort(int i) { - if (i > UNSIGNED_SHORT_MAX_VALUE) { - throw new IllegalArgumentException( - String.format("%d is larger than %d bits", i, UNSIGNED_SHORT_SIZE)); - } - - return i; - } - - /** - * Encode an unsigned medium integer on 3 bytes / 24 bits. This can be decoded directly by the - * {@link ByteBuf#readUnsignedMedium()} method. - * - * @param byteBuf the {@link ByteBuf} into which to write the bits - * @param i the medium integer to encode - * @see #requireUnsignedMedium(int) - */ - public static void encodeUnsignedMedium(ByteBuf byteBuf, int i) { - requireUnsignedMedium(i); - // Write each byte separately in reverse order, this mean we can write 1 << 23 without - // overflowing. - byteBuf.writeByte(i >> 16); - byteBuf.writeByte(i >> 8); - byteBuf.writeByte(i); - } -} diff --git a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/tcp/PromiseAdapter.java b/today-remoting/src/main/java/infra/remoting/util/PromiseAdapter.java similarity index 58% rename from today-remoting-transport-tcp/src/main/java/infra/remoting/transport/tcp/PromiseAdapter.java rename to today-remoting/src/main/java/infra/remoting/util/PromiseAdapter.java index 5aa5a93..a4a0bdb 100644 --- a/today-remoting-transport-tcp/src/main/java/infra/remoting/transport/tcp/PromiseAdapter.java +++ b/today-remoting/src/main/java/infra/remoting/util/PromiseAdapter.java @@ -1,21 +1,20 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.transport.tcp; +package infra.remoting.util; import infra.util.concurrent.Future; import infra.util.concurrent.Promise; diff --git a/today-remoting/src/main/java/infra/remoting/util/package-info.java b/today-remoting/src/main/java/infra/remoting/util/package-info.java index a19a694..6757960 100644 --- a/today-remoting/src/main/java/infra/remoting/util/package-info.java +++ b/today-remoting/src/main/java/infra/remoting/util/package-info.java @@ -1,22 +1,21 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ /** Shared utility classes and {@link infra.remoting.Payload} implementations. */ -@NonNullApi +@NullMarked package infra.remoting.util; -import infra.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting/src/test/java/infra/remoting/FrameAssert.java b/today-remoting/src/test/java/infra/remoting/FrameAssert.java index 9a3cbf4..19137d5 100644 --- a/today-remoting/src/test/java/infra/remoting/FrameAssert.java +++ b/today-remoting/src/test/java/infra/remoting/FrameAssert.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting; @@ -22,16 +21,12 @@ import org.assertj.core.error.BasicErrorMessageFactory; import org.assertj.core.internal.Failures; import org.assertj.core.internal.Objects; +import org.jspecify.annotations.Nullable; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; -import infra.lang.Nullable; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; import infra.remoting.frame.ByteBufRepresentation; import infra.remoting.frame.ErrorFrameCodec; import infra.remoting.frame.FrameHeaderCodec; @@ -40,6 +35,10 @@ import infra.remoting.frame.PayloadFrameCodec; import infra.remoting.frame.RequestNFrameCodec; import infra.remoting.frame.RequestStreamFrameCodec; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; +import io.netty.util.CharsetUtil; import static org.assertj.core.error.ShouldBe.shouldBe; import static org.assertj.core.error.ShouldBeEqual.shouldBeEqual; diff --git a/today-remoting/src/test/java/infra/remoting/FrameTest.java b/today-remoting/src/test/java/infra/remoting/FrameTest.java deleted file mode 100644 index 7448c44..0000000 --- a/today-remoting/src/test/java/infra/remoting/FrameTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting; - -public class FrameTest { - /*@Test - public void testFrameToString() { - final io.rsocket.Frame requestFrame = - io.rsocket.Frame.Request.from( - 1, FrameType.REQUEST_RESPONSE, DefaultPayload.create("streaming in -> 0"), 1); - assertEquals( - "Frame => Stream ID: 1 Type: REQUEST_RESPONSE Payload: data: \"streaming in -> 0\" ", - requestFrame.toString()); - } - - @Test - public void testFrameWithMetadataToString() { - final io.rsocket.Frame requestFrame = - io.rsocket.Frame.Request.from( - 1, - FrameType.REQUEST_RESPONSE, - DefaultPayload.create("streaming in -> 0", "metadata"), - 1); - assertEquals( - "Frame => Stream ID: 1 Type: REQUEST_RESPONSE Payload: metadata: \"metadata\" data: \"streaming in -> 0\" ", - requestFrame.toString()); - } - - @Test - public void testPayload() { - io.rsocket.Frame frame = - io.rsocket.Frame.PayloadFrame.from( - 1, - FrameType.NEXT_COMPLETE, - DefaultPayload.create("Hello"), - FrameHeaderFlyweight.FLAGS_C); - frame.toString(); - }*/ -} diff --git a/today-remoting/src/test/java/infra/remoting/PayloadAssert.java b/today-remoting/src/test/java/infra/remoting/PayloadAssert.java index fe32c9f..be0c3e3 100755 --- a/today-remoting/src/test/java/infra/remoting/PayloadAssert.java +++ b/today-remoting/src/test/java/infra/remoting/PayloadAssert.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting; @@ -22,18 +21,18 @@ import org.assertj.core.error.BasicErrorMessageFactory; import org.assertj.core.internal.Failures; import org.assertj.core.internal.Objects; +import org.jspecify.annotations.Nullable; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; -import infra.lang.Nullable; +import infra.remoting.frame.ByteBufRepresentation; +import infra.remoting.util.DefaultPayload; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.util.CharsetUtil; -import infra.remoting.frame.ByteBufRepresentation; -import infra.remoting.util.DefaultPayload; import static org.assertj.core.error.ShouldBeEqual.shouldBeEqual; import static org.assertj.core.error.ShouldHave.shouldHave; diff --git a/today-remoting/src/test/java/infra/remoting/RaceTestConstants.java b/today-remoting/src/test/java/infra/remoting/RaceTestConstants.java index 65f282d..e234e24 100644 --- a/today-remoting/src/test/java/infra/remoting/RaceTestConstants.java +++ b/today-remoting/src/test/java/infra/remoting/RaceTestConstants.java @@ -1,23 +1,22 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting; public class RaceTestConstants { public static final int REPEATS = - Integer.parseInt(System.getProperty("rsocket.test.race.repeats", "1000")); + Integer.parseInt(System.getProperty("infra.remoting.test.race.repeats", "1000")); } diff --git a/today-remoting/src/test/java/infra/remoting/TestScheduler.java b/today-remoting/src/test/java/infra/remoting/TestScheduler.java index 7e8236e..c38c7ab 100644 --- a/today-remoting/src/test/java/infra/remoting/TestScheduler.java +++ b/today-remoting/src/test/java/infra/remoting/TestScheduler.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting; diff --git a/today-remoting/src/test/java/infra/remoting/buffer/LeaksTrackingByteBufAllocator.java b/today-remoting/src/test/java/infra/remoting/buffer/LeaksTrackingByteBufAllocator.java index f12047e..a4fd883 100644 --- a/today-remoting/src/test/java/infra/remoting/buffer/LeaksTrackingByteBufAllocator.java +++ b/today-remoting/src/test/java/infra/remoting/buffer/LeaksTrackingByteBufAllocator.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.buffer; diff --git a/today-remoting/src/test/java/infra/remoting/core/AbstractSocketRule.java b/today-remoting/src/test/java/infra/remoting/core/AbstractChannelRule.java similarity index 55% rename from today-remoting/src/test/java/infra/remoting/core/AbstractSocketRule.java rename to today-remoting/src/test/java/infra/remoting/core/AbstractChannelRule.java index 0a1298e..2473508 100644 --- a/today-remoting/src/test/java/infra/remoting/core/AbstractSocketRule.java +++ b/today-remoting/src/test/java/infra/remoting/core/AbstractChannelRule.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -21,19 +20,19 @@ import java.time.Duration; -import io.netty.buffer.ByteBufAllocator; import infra.remoting.Channel; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; import infra.remoting.test.util.TestSubscriber; +import io.netty.buffer.ByteBufAllocator; import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; -public abstract class AbstractSocketRule { +public abstract class AbstractChannelRule { - protected TestDuplexConnection connection; + protected TestConnection connection; protected Subscriber connectSub; - protected T socket; + protected T channel; protected LeaksTrackingByteBufAllocator allocator; protected int maxFrameLength = FRAME_LENGTH_MASK; protected int maxInboundPayloadSize = Integer.MAX_VALUE; @@ -50,11 +49,11 @@ protected void doInit() { if (connection != null) { connection.dispose(); } - if (socket != null) { - socket.dispose(); + if (channel != null) { + channel.dispose(); } - connection = new TestDuplexConnection(allocator); - socket = newRSocket(); + connection = new TestConnection(allocator); + channel = newChannel(); } public void setMaxInboundPayloadSize(int maxInboundPayloadSize) { @@ -67,7 +66,7 @@ public void setMaxFrameLength(int maxFrameLength) { doInit(); } - protected abstract T newRSocket(); + protected abstract T newChannel(); public LeaksTrackingByteBufAllocator alloc() { return allocator; diff --git a/today-remoting/src/test/java/infra/remoting/core/ChannelConnectorTests.java b/today-remoting/src/test/java/infra/remoting/core/ChannelConnectorTests.java index 33e3cb9..aa85c60 100644 --- a/today-remoting/src/test/java/infra/remoting/core/ChannelConnectorTests.java +++ b/today-remoting/src/test/java/infra/remoting/core/ChannelConnectorTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -26,20 +25,20 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCounted; +import infra.remoting.Channel; import infra.remoting.ConnectionSetupPayload; import infra.remoting.FrameAssert; import infra.remoting.Payload; -import infra.remoting.Channel; import infra.remoting.frame.FrameType; import infra.remoting.frame.KeepAliveFrameCodec; import infra.remoting.frame.RequestResponseFrameCodec; import infra.remoting.test.util.TestClientTransport; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; import infra.remoting.util.ByteBufPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.util.CharsetUtil; +import io.netty.util.ReferenceCounted; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import reactor.util.retry.Retry; @@ -58,7 +57,7 @@ public void unexpectedFramesBeforeResumeOKFrame(String frameType) { .connect(transport) .block(); - final TestDuplexConnection duplexConnection = transport.testConnection(); + final TestConnection duplexConnection = transport.testConnection(); duplexConnection.addToReceivedBuffer( KeepAliveFrameCodec.encode(duplexConnection.alloc(), false, 1, Unpooled.EMPTY_BUFFER)); @@ -71,7 +70,7 @@ public void unexpectedFramesBeforeResumeOKFrame(String frameType) { duplexConnection.dispose(); - final TestDuplexConnection duplexConnection2 = transport.testConnection(); + final TestConnection duplexConnection2 = transport.testConnection(); final ByteBuf frame; switch (frameType) { @@ -118,7 +117,7 @@ public void ensuresThatSetupPayloadCanBeRetained() { ChannelConnector.create() .setupPayload(ByteBufPayload.create(data)) - .acceptor((setup, sendingSocket) -> { + .acceptor((setup, channel) -> { retainedSetupPayload.set(setup.retain()); return Mono.just(new Channel() { }); }) @@ -148,7 +147,7 @@ public void ensuresThatSetupPayloadCanBeRetained() { } @Test - public void ensuresThatMonoFromRSocketConnectorCanBeUsedForMultipleSubscriptions() { + public void ensuresThatMonoFromChannelConnectorCanBeUsedForMultipleSubscriptions() { Payload setupPayload = ByteBufPayload.create("TestData", "TestMetadata"); assertThat(setupPayload.refCnt()).isOne(); diff --git a/today-remoting/src/test/java/infra/remoting/core/ChannelLeaseTests.java b/today-remoting/src/test/java/infra/remoting/core/ChannelLeaseTests.java index fc0d0cb..e3f0c0f 100644 --- a/today-remoting/src/test/java/infra/remoting/core/ChannelLeaseTests.java +++ b/today-remoting/src/test/java/infra/remoting/core/ChannelLeaseTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -35,16 +34,11 @@ import java.util.function.BiFunction; import java.util.stream.Stream; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCounted; -import infra.remoting.Payload; import infra.remoting.Channel; +import infra.remoting.Payload; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; -import infra.remoting.exceptions.Exceptions; -import infra.remoting.exceptions.RejectedException; +import infra.remoting.error.Exceptions; +import infra.remoting.error.RejectedException; import infra.remoting.frame.FrameHeaderCodec; import infra.remoting.frame.FrameType; import infra.remoting.frame.LeaseFrameCodec; @@ -60,10 +54,15 @@ import infra.remoting.lease.MissingLeaseException; import infra.remoting.plugins.InitializingInterceptorRegistry; import infra.remoting.test.util.TestClientTransport; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; import infra.remoting.test.util.TestServerTransport; import infra.remoting.util.ByteBufPayload; import infra.remoting.util.DefaultPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.Unpooled; +import io.netty.util.CharsetUtil; +import io.netty.util.ReferenceCounted; import reactor.core.publisher.BaseSubscriber; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -89,8 +88,8 @@ class ChannelLeaseTests { private Channel channelRequester; private ResponderLeaseTracker responderLeaseTracker; private LeaksTrackingByteBufAllocator byteBufAllocator; - private TestDuplexConnection connection; - private ChannelResponder rSocketResponder; + private TestConnection connection; + private ResponderChannel responderChannel; private Channel mockChannelHandler; private Sinks.Many leaseSender = Sinks.many().multicast().onBackpressureBuffer(); @@ -103,7 +102,7 @@ void setUp() { PayloadDecoder payloadDecoder = PayloadDecoder.DEFAULT; byteBufAllocator = LeaksTrackingByteBufAllocator.instrument(ByteBufAllocator.DEFAULT); - connection = new TestDuplexConnection(byteBufAllocator); + connection = new TestConnection(byteBufAllocator); requesterLeaseTracker = new RequesterLeaseTracker(TAG, 0); responderLeaseTracker = new ResponderLeaseTracker(TAG, connection, () -> leaseSender.asFlux()); this.thisClosedSink = Sinks.empty(); @@ -112,7 +111,7 @@ void setUp() { ClientServerInputMultiplexer multiplexer = new ClientServerInputMultiplexer(connection, new InitializingInterceptorRegistry(), true); channelRequester = - new ChannelRequester( + new RequesterChannel( multiplexer.asClientConnection(), payloadDecoder, StreamIdProvider.forClient(), @@ -183,8 +182,8 @@ protected void hookOnError(Throwable throwable) { })); }); - rSocketResponder = - new ChannelResponder( + responderChannel = + new ResponderChannel( multiplexer.asServerConnection(), mockChannelHandler, payloadDecoder, @@ -202,7 +201,7 @@ void tearDownAndCheckForLeaks() { } @Test - public void serverRSocketFactoryRejectsUnsupportedLease() { + public void serverChannelFactoryRejectsUnsupportedLease() { Payload payload = DefaultPayload.create(DefaultPayload.EMPTY_BUFFER); ByteBuf setupFrame = SetupFrameCodec.encode( @@ -217,7 +216,7 @@ public void serverRSocketFactoryRejectsUnsupportedLease() { TestServerTransport transport = new TestServerTransport(); RemotingServer.create().bind(transport).block(); - TestDuplexConnection connection = transport.connect(); + TestConnection connection = transport.connect(); connection.addToReceivedBuffer(setupFrame); Collection sent = connection.getSent(); @@ -232,7 +231,7 @@ public void serverRSocketFactoryRejectsUnsupportedLease() { } @Test - public void clientRSocketFactorySetsLeaseFlag() { + public void clientChannelFactorySetsLeaseFlag() { TestClientTransport clientTransport = new TestClientTransport(); try { ChannelConnector.create().lease().connect(clientTransport).block(); @@ -426,25 +425,25 @@ void responderMissingLeaseRequestsAreRejected(FrameType frameType) { case REQUEST_FNF: final ByteBuf fnfFrame = RequestFireAndForgetFrameCodec.encodeReleasingPayload(byteBufAllocator, 1, payload1); - rSocketResponder.handleFrame(fnfFrame); + responderChannel.handleFrame(fnfFrame); fnfFrame.release(); break; case REQUEST_RESPONSE: final ByteBuf requestResponseFrame = RequestResponseFrameCodec.encodeReleasingPayload(byteBufAllocator, 1, payload1); - rSocketResponder.handleFrame(requestResponseFrame); + responderChannel.handleFrame(requestResponseFrame); requestResponseFrame.release(); break; case REQUEST_STREAM: final ByteBuf requestStreamFrame = RequestStreamFrameCodec.encodeReleasingPayload(byteBufAllocator, 1, 1, payload1); - rSocketResponder.handleFrame(requestStreamFrame); + responderChannel.handleFrame(requestStreamFrame); requestStreamFrame.release(); break; case REQUEST_CHANNEL: final ByteBuf requestChannelFrame = RequestChannelFrameCodec.encodeReleasingPayload(byteBufAllocator, 1, true, 1, payload1); - rSocketResponder.handleFrame(requestChannelFrame); + responderChannel.handleFrame(requestChannelFrame); requestChannelFrame.release(); break; } @@ -474,25 +473,25 @@ void responderPresentLeaseRequestsAreAccepted(FrameType frameType) { case REQUEST_FNF: final ByteBuf fnfFrame = RequestFireAndForgetFrameCodec.encodeReleasingPayload(byteBufAllocator, 1, payload1); - rSocketResponder.handleFireAndForget(1, fnfFrame); + responderChannel.handleFireAndForget(1, fnfFrame); fnfFrame.release(); break; case REQUEST_RESPONSE: final ByteBuf requestResponseFrame = RequestResponseFrameCodec.encodeReleasingPayload(byteBufAllocator, 1, payload1); - rSocketResponder.handleFrame(requestResponseFrame); + responderChannel.handleFrame(requestResponseFrame); requestResponseFrame.release(); break; case REQUEST_STREAM: final ByteBuf requestStreamFrame = RequestStreamFrameCodec.encodeReleasingPayload(byteBufAllocator, 1, 1, payload1); - rSocketResponder.handleFrame(requestStreamFrame); + responderChannel.handleFrame(requestStreamFrame); requestStreamFrame.release(); break; case REQUEST_CHANNEL: final ByteBuf requestChannelFrame = RequestChannelFrameCodec.encodeReleasingPayload(byteBufAllocator, 1, true, 1, payload1); - rSocketResponder.handleFrame(requestChannelFrame); + responderChannel.handleFrame(requestChannelFrame); requestChannelFrame.release(); break; } @@ -547,8 +546,8 @@ void responderDepletedAllowedLeaseRequestsAreRejected(FrameType frameType) { RequestFireAndForgetFrameCodec.encodeReleasingPayload(byteBufAllocator, 1, payload1); final ByteBuf fnfFrame2 = RequestFireAndForgetFrameCodec.encodeReleasingPayload(byteBufAllocator, 3, payload2); - rSocketResponder.handleFrame(fnfFrame); - rSocketResponder.handleFrame(fnfFrame2); + responderChannel.handleFrame(fnfFrame); + responderChannel.handleFrame(fnfFrame2); fnfFrame.release(); fnfFrame2.release(); break; @@ -557,8 +556,8 @@ void responderDepletedAllowedLeaseRequestsAreRejected(FrameType frameType) { RequestResponseFrameCodec.encodeReleasingPayload(byteBufAllocator, 1, payload1); final ByteBuf requestResponseFrame2 = RequestResponseFrameCodec.encodeReleasingPayload(byteBufAllocator, 3, payload2); - rSocketResponder.handleFrame(requestResponseFrame); - rSocketResponder.handleFrame(requestResponseFrame2); + responderChannel.handleFrame(requestResponseFrame); + responderChannel.handleFrame(requestResponseFrame2); requestResponseFrame.release(); requestResponseFrame2.release(); break; @@ -567,8 +566,8 @@ void responderDepletedAllowedLeaseRequestsAreRejected(FrameType frameType) { RequestStreamFrameCodec.encodeReleasingPayload(byteBufAllocator, 1, 1, payload1); final ByteBuf requestStreamFrame2 = RequestStreamFrameCodec.encodeReleasingPayload(byteBufAllocator, 3, 1, payload2); - rSocketResponder.handleFrame(requestStreamFrame); - rSocketResponder.handleFrame(requestStreamFrame2); + responderChannel.handleFrame(requestStreamFrame); + responderChannel.handleFrame(requestStreamFrame2); requestStreamFrame.release(); requestStreamFrame2.release(); break; @@ -577,8 +576,8 @@ void responderDepletedAllowedLeaseRequestsAreRejected(FrameType frameType) { RequestChannelFrameCodec.encodeReleasingPayload(byteBufAllocator, 1, true, 1, payload1); final ByteBuf requestChannelFrame2 = RequestChannelFrameCodec.encodeReleasingPayload(byteBufAllocator, 3, true, 1, payload2); - rSocketResponder.handleFrame(requestChannelFrame); - rSocketResponder.handleFrame(requestChannelFrame2); + responderChannel.handleFrame(requestChannelFrame); + responderChannel.handleFrame(requestChannelFrame2); requestChannelFrame.release(); requestChannelFrame2.release(); break; @@ -718,7 +717,7 @@ static Stream interactions() { FrameType.REQUEST_STREAM), Arguments.of( (BiFunction>) - (rSocket, payload) -> rSocket.requestChannel(Mono.just(payload)), + (channel, payload) -> channel.requestChannel(Mono.just(payload)), FrameType.REQUEST_CHANNEL)); } diff --git a/today-remoting/src/test/java/infra/remoting/core/ChannelReconnectTests.java b/today-remoting/src/test/java/infra/remoting/core/ChannelReconnectTests.java index b857b5e..85cd1ce 100644 --- a/today-remoting/src/test/java/infra/remoting/core/ChannelReconnectTests.java +++ b/today-remoting/src/test/java/infra/remoting/core/ChannelReconnectTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -27,11 +26,11 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.Consumer; -import infra.remoting.FrameAssert; import infra.remoting.Channel; +import infra.remoting.FrameAssert; import infra.remoting.frame.FrameType; import infra.remoting.test.util.TestClientTransport; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; import infra.remoting.transport.ClientTransport; import reactor.core.Exceptions; import reactor.core.publisher.Mono; @@ -44,16 +43,16 @@ public class ChannelReconnectTests { private Queue retries = new ConcurrentLinkedQueue<>(); @Test - public void shouldBeASharedReconnectableInstanceOfRSocketMono() throws InterruptedException { + public void shouldBeASharedReconnectableInstanceOfChannelMono() throws InterruptedException { TestClientTransport[] testClientTransport = new TestClientTransport[] { new TestClientTransport() }; - Mono rSocketMono = + Mono channelMono = ChannelConnector.create() .reconnect(Retry.indefinitely()) .connect(() -> testClientTransport[0]); - Channel channel1 = rSocketMono.block(); - Channel channel2 = rSocketMono.block(); + Channel channel1 = channelMono.block(); + Channel channel2 = channelMono.block(); FrameAssert.assertThat(testClientTransport[0].testConnection().awaitFrame()) .typeOf(FrameType.SETUP) @@ -67,8 +66,8 @@ public void shouldBeASharedReconnectableInstanceOfRSocketMono() throws Interrupt testClientTransport[0].alloc().assertHasNoLeaks(); testClientTransport[0] = new TestClientTransport(); - Channel channel3 = rSocketMono.block(); - Channel channel4 = rSocketMono.block(); + Channel channel3 = channelMono.block(); + Channel channel4 = channelMono.block(); FrameAssert.assertThat(testClientTransport[0].testConnection().awaitFrame()) .typeOf(FrameType.SETUP) @@ -84,7 +83,7 @@ public void shouldBeASharedReconnectableInstanceOfRSocketMono() throws Interrupt @Test @SuppressWarnings({ "rawtype" }) - public void shouldBeRetrieableConnectionSharedReconnectableInstanceOfRSocketMono() { + public void shouldBeRetrieableConnectionSharedReconnectableInstanceOfChannelMono() { ClientTransport transport = Mockito.mock(ClientTransport.class); TestClientTransport transport1 = new TestClientTransport(); Mockito.when(transport.connect()) @@ -93,7 +92,7 @@ public void shouldBeRetrieableConnectionSharedReconnectableInstanceOfRSocketMono .thenThrow(UncheckedIOException.class) .thenThrow(UncheckedIOException.class) .thenReturn(transport1.connect()); - Mono rSocketMono = + Mono channelMono = ChannelConnector.create() .reconnect( Retry.backoff(4, Duration.ofMillis(100)) @@ -101,8 +100,8 @@ public void shouldBeRetrieableConnectionSharedReconnectableInstanceOfRSocketMono .doAfterRetry(onRetry())) .connect(transport); - Channel channel1 = rSocketMono.block(); - Channel channel2 = rSocketMono.block(); + Channel channel1 = channelMono.block(); + Channel channel2 = channelMono.block(); assertThat(channel1).isEqualTo(channel2); assertRetries( @@ -123,7 +122,7 @@ public void shouldBeRetrieableConnectionSharedReconnectableInstanceOfRSocketMono @Test @SuppressWarnings({ "rawtype" }) - public void shouldBeExaustedRetrieableConnectionSharedReconnectableInstanceOfRSocketMono() { + public void shouldBeExaustedRetrieableConnectionSharedReconnectableInstanceOfChannelMono() { ClientTransport transport = Mockito.mock(ClientTransport.class); TestClientTransport transport1 = new TestClientTransport(); Mockito.when(transport.connect()) @@ -133,7 +132,7 @@ public void shouldBeExaustedRetrieableConnectionSharedReconnectableInstanceOfRSo .thenThrow(UncheckedIOException.class) .thenThrow(UncheckedIOException.class) .thenReturn(transport1.connect()); - Mono rSocketMono = + Mono channelMono = ChannelConnector.create() .reconnect( Retry.backoff(4, Duration.ofMillis(100)) @@ -141,11 +140,11 @@ public void shouldBeExaustedRetrieableConnectionSharedReconnectableInstanceOfRSo .doAfterRetry(onRetry())) .connect(transport); - Assertions.assertThatThrownBy(rSocketMono::block) + Assertions.assertThatThrownBy(channelMono::block) .matches(Exceptions::isRetryExhausted) .hasCauseInstanceOf(UncheckedIOException.class); - Assertions.assertThatThrownBy(rSocketMono::block) + Assertions.assertThatThrownBy(channelMono::block) .matches(Exceptions::isRetryExhausted) .hasCauseInstanceOf(UncheckedIOException.class); @@ -159,20 +158,20 @@ public void shouldBeExaustedRetrieableConnectionSharedReconnectableInstanceOfRSo } @Test - public void shouldBeNotBeASharedReconnectableInstanceOfRSocketMono() { + public void shouldBeNotBeASharedReconnectableInstanceOfChannelMono() { TestClientTransport transport = new TestClientTransport(); - Mono rSocketMono = ChannelConnector.connectWith(transport); + Mono channelMono = ChannelConnector.connectWith(transport); - Channel channel1 = rSocketMono.block(); - TestDuplexConnection connection1 = transport.testConnection(); + Channel channel1 = channelMono.block(); + TestConnection connection1 = transport.testConnection(); FrameAssert.assertThat(connection1.awaitFrame()) .typeOf(FrameType.SETUP) .hasStreamIdZero() .hasNoLeaks(); - Channel channel2 = rSocketMono.block(); - TestDuplexConnection connection2 = transport.testConnection(); + Channel channel2 = channelMono.block(); + TestConnection connection2 = transport.testConnection(); assertThat(channel1).isNotEqualTo(channel2); diff --git a/today-remoting/src/test/java/infra/remoting/core/ChannelTests.java b/today-remoting/src/test/java/infra/remoting/core/ChannelTests.java index cc5e604..020c2b9 100644 --- a/today-remoting/src/test/java/infra/remoting/core/ChannelTests.java +++ b/today-remoting/src/test/java/infra/remoting/core/ChannelTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -29,18 +28,18 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.atomic.AtomicReference; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import infra.remoting.Payload; import infra.remoting.Channel; +import infra.remoting.Payload; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; -import infra.remoting.exceptions.ApplicationErrorException; -import infra.remoting.exceptions.CustomProtocolException; +import infra.remoting.error.ApplicationErrorException; +import infra.remoting.error.CustomProtocolException; import infra.remoting.frame.decoder.PayloadDecoder; import infra.remoting.internal.subscriber.AssertSubscriber; -import infra.remoting.test.util.LocalDuplexConnection; +import infra.remoting.test.util.LocalConnection; import infra.remoting.util.DefaultPayload; import infra.remoting.util.EmptyPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; import reactor.core.Disposable; import reactor.core.Disposables; import reactor.core.publisher.Flux; @@ -53,7 +52,7 @@ public class ChannelTests { - public final SocketRule rule = new SocketRule(); + public final ChannelRule rule = new ChannelRule(); @BeforeEach public void setup() { @@ -66,7 +65,7 @@ public void tearDownAndCheckOnLeaks() { } @Test - public void rsocketDisposalShouldEndupWithNoErrorsOnClose() { + public void channelDisposalShouldEndupWithNoErrorsOnClose() { Channel requestHandlingChannel = new Channel() { final Disposable disposable = Disposables.single(); @@ -506,14 +505,14 @@ void errorFromRequesterPublisher( responderPublisher.assertNoSubscribers(); } - public static class SocketRule { + public static class ChannelRule { Sinks.Many serverProcessor; Sinks.Many clientProcessor; - private ChannelRequester crs; + private RequesterChannel crs; @SuppressWarnings("unused") - private ChannelResponder srs; + private ResponderChannel srs; private Channel requestAcceptor; @@ -533,10 +532,10 @@ public void init() { this.thisClosedSink = Sinks.empty(); this.otherClosedSink = Sinks.empty(); - LocalDuplexConnection serverConnection = - new LocalDuplexConnection("server", allocator, clientProcessor, serverProcessor); - LocalDuplexConnection clientConnection = - new LocalDuplexConnection("client", allocator, serverProcessor, clientProcessor); + LocalConnection serverConnection = + new LocalConnection("server", allocator, clientProcessor, serverProcessor); + LocalConnection clientConnection = + new LocalConnection("client", allocator, serverProcessor, clientProcessor); clientConnection.onClose().doFinally(__ -> serverConnection.dispose()).subscribe(); serverConnection.onClose().doFinally(__ -> clientConnection.dispose()).subscribe(); @@ -572,7 +571,7 @@ public Flux requestChannel(Publisher payloads) { }; srs = - new ChannelResponder( + new ResponderChannel( serverConnection, requestAcceptor, PayloadDecoder.DEFAULT, @@ -584,7 +583,7 @@ public Flux requestChannel(Publisher payloads) { otherClosedSink); crs = - new ChannelRequester( + new RequesterChannel( clientConnection, PayloadDecoder.DEFAULT, StreamIdProvider.forClient(), diff --git a/today-remoting/src/test/java/infra/remoting/core/ClientServerInputMultiplexerTest.java b/today-remoting/src/test/java/infra/remoting/core/ClientServerInputMultiplexerTest.java index e46b68e..816ea11 100644 --- a/today-remoting/src/test/java/infra/remoting/core/ClientServerInputMultiplexerTest.java +++ b/today-remoting/src/test/java/infra/remoting/core/ClientServerInputMultiplexerTest.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -22,21 +21,21 @@ import java.util.concurrent.atomic.AtomicInteger; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; import infra.remoting.frame.ErrorFrameCodec; import infra.remoting.frame.KeepAliveFrameCodec; import infra.remoting.frame.LeaseFrameCodec; import infra.remoting.frame.MetadataPushFrameCodec; import infra.remoting.plugins.InitializingInterceptorRegistry; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.Unpooled; import static org.assertj.core.api.Assertions.assertThat; public class ClientServerInputMultiplexerTest { - private TestDuplexConnection source; + private TestConnection source; private ClientServerInputMultiplexer clientMultiplexer; private LeaksTrackingByteBufAllocator allocator = LeaksTrackingByteBufAllocator.instrument(ByteBufAllocator.DEFAULT); @@ -44,7 +43,7 @@ public class ClientServerInputMultiplexerTest { @BeforeEach public void setup() { - source = new TestDuplexConnection(allocator); + source = new TestConnection(allocator); clientMultiplexer = new ClientServerInputMultiplexer(source, new InitializingInterceptorRegistry(), true); serverMultiplexer = diff --git a/today-remoting/src/test/java/infra/remoting/core/ConnectionSetupPayloadTest.java b/today-remoting/src/test/java/infra/remoting/core/ConnectionSetupPayloadTest.java index edcea5b..1f3ee27 100644 --- a/today-remoting/src/test/java/infra/remoting/core/ConnectionSetupPayloadTest.java +++ b/today-remoting/src/test/java/infra/remoting/core/ConnectionSetupPayloadTest.java @@ -1,31 +1,30 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; import org.junit.jupiter.api.Test; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; import infra.remoting.ConnectionSetupPayload; import infra.remoting.Payload; import infra.remoting.frame.SetupFrameCodec; import infra.remoting.util.DefaultPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.Unpooled; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/today-remoting/src/test/java/infra/remoting/core/DefaultChannelClientTests.java b/today-remoting/src/test/java/infra/remoting/core/DefaultChannelClientTests.java index 39a680f..1fee968 100644 --- a/today-remoting/src/test/java/infra/remoting/core/DefaultChannelClientTests.java +++ b/today-remoting/src/test/java/infra/remoting/core/DefaultChannelClientTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -39,13 +38,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import io.netty.buffer.ByteBuf; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.ReferenceCounted; +import infra.remoting.Channel; +import infra.remoting.DecoratingChannel; import infra.remoting.FrameAssert; import infra.remoting.Payload; -import infra.remoting.Channel; import infra.remoting.RaceTestConstants; import infra.remoting.frame.ErrorFrameCodec; import infra.remoting.frame.FrameHeaderCodec; @@ -53,9 +49,12 @@ import infra.remoting.frame.PayloadFrameCodec; import infra.remoting.frame.decoder.PayloadDecoder; import infra.remoting.internal.subscriber.AssertSubscriber; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; import infra.remoting.util.ByteBufPayload; -import infra.remoting.util.ChannelDecorator; +import io.netty.buffer.ByteBuf; +import io.netty.util.CharsetUtil; +import io.netty.util.ReferenceCountUtil; +import io.netty.util.ReferenceCounted; import reactor.core.Disposable; import reactor.core.publisher.Flux; import reactor.core.publisher.Hooks; @@ -71,13 +70,13 @@ public class DefaultChannelClientTests { - ClientSocketRule rule; + ClientChannelRule rule; @BeforeEach public void setUp() throws Throwable { Hooks.onNextDropped(ReferenceCountUtil::safeRelease); Hooks.onErrorDropped((t) -> { }); - rule = new ClientSocketRule(); + rule = new ClientChannelRule(); rule.init(); } @@ -201,7 +200,7 @@ public void shouldHaveNoLeaksOnPayloadInCaseOfRacingOfOnNextAndCancel( Assumptions.assumeThat(requestType).isNotEqualTo(FrameType.REQUEST_CHANNEL); for (int i = 0; i < RaceTestConstants.REPEATS; i++) { - ClientSocketRule rule = new ClientSocketRule(); + ClientChannelRule rule = new ClientChannelRule(); rule.init(); Payload payload = ByteBufPayload.create("test", "testMetadata"); TestPublisher testPublisher = @@ -258,7 +257,7 @@ public void shouldHaveNoLeaksOnPayloadInCaseOfRacingOfRequestAndCancel( Assumptions.assumeThat(requestType).isNotEqualTo(FrameType.REQUEST_CHANNEL); for (int i = 0; i < RaceTestConstants.REPEATS; i++) { - ClientSocketRule rule = new ClientSocketRule(); + ClientChannelRule rule = new ClientChannelRule(); rule.init(); ByteBuf dataBuffer = rule.allocator.buffer(); dataBuffer.writeCharSequence("test", CharsetUtil.UTF_8); @@ -496,7 +495,7 @@ public void shouldDisposeOriginalSource() { .assertError(CancellationException.class) .assertErrorMessage("Disposed"); - Assertions.assertThat(rule.socket.isDisposed()).isTrue(); + Assertions.assertThat(rule.channel.isDisposed()).isTrue(); FrameAssert.assertThat(rule.connection.awaitFrame()) .hasStreamIdZero() @@ -509,11 +508,11 @@ public void shouldDisposeOriginalSource() { @Test public void shouldReceiveOnCloseNotificationOnDisposeOriginalSource() { Sinks.Empty onCloseDelayer = Sinks.empty(); - ClientSocketRule rule = - new ClientSocketRule() { + ClientChannelRule rule = + new ClientChannelRule() { @Override - protected Channel newRSocket() { - return new ChannelDecorator(super.newRSocket()) { + protected Channel newChannel() { + return new DecoratingChannel(super.newChannel()) { @Override public Mono onClose() { return super.onClose().and(onCloseDelayer.asMono()); @@ -540,7 +539,7 @@ public Mono onClose() { onCloseSubscriber.assertTerminated().assertComplete(); - Assertions.assertThat(rule.socket.isDisposed()).isTrue(); + Assertions.assertThat(rule.channel.isDisposed()).isTrue(); FrameAssert.assertThat(rule.connection.awaitFrame()) .hasStreamIdZero() @@ -568,7 +567,7 @@ public void shouldResolveOnStartSource() { assertSubscriber1.assertTerminated().assertComplete(); - Assertions.assertThat(rule.socket.isDisposed()).isTrue(); + Assertions.assertThat(rule.channel.isDisposed()).isTrue(); FrameAssert.assertThat(rule.connection.awaitFrame()) .hasStreamIdZero() @@ -596,7 +595,7 @@ public void shouldNotStartIfAlreadyDisposed() { assertSubscriber1.assertTerminated().assertComplete(); - Assertions.assertThat(rule.socket.isDisposed()).isTrue(); + Assertions.assertThat(rule.channel.isDisposed()).isTrue(); FrameAssert.assertThat(rule.connection.awaitFrame()) .hasStreamIdZero() @@ -619,7 +618,7 @@ public void shouldBeRestartedIfSourceWasClosed() { assertSubscriber.assertTerminated().assertValueCount(1); - rule.socket.dispose(); + rule.channel.dispose(); FrameAssert.assertThat(rule.connection.awaitFrame()) .hasStreamIdZero() @@ -629,8 +628,8 @@ public void shouldBeRestartedIfSourceWasClosed() { terminateSubscriber.assertNotTerminated(); Assertions.assertThat(rule.client.isDisposed()).isFalse(); - rule.connection = new TestDuplexConnection(rule.allocator); - rule.socket = rule.newRSocket(); + rule.connection = new TestConnection(rule.allocator); + rule.channel = rule.newChannel(); rule.producer = Sinks.one(); AssertSubscriber assertSubscriber2 = AssertSubscriber.create(); @@ -648,7 +647,7 @@ public void shouldBeRestartedIfSourceWasClosed() { Assertions.assertThat(rule.client.connect()).isFalse(); - Assertions.assertThat(rule.socket.isDisposed()).isTrue(); + Assertions.assertThat(rule.channel.isDisposed()).isTrue(); FrameAssert.assertThat(rule.connection.awaitFrame()) .hasStreamIdZero() @@ -661,7 +660,7 @@ public void shouldBeRestartedIfSourceWasClosed() { @Test public void shouldDisposeOriginalSourceIfRacing() { for (int i = 0; i < RaceTestConstants.REPEATS; i++) { - ClientSocketRule rule = new ClientSocketRule(); + ClientChannelRule rule = new ClientChannelRule(); rule.init(); @@ -673,7 +672,7 @@ public void shouldDisposeOriginalSourceIfRacing() { assertSubscriber.assertTerminated(); Assertions.assertThat(rule.client.isDisposed()).isTrue(); - Assertions.assertThat(rule.socket.isDisposed()).isTrue(); + Assertions.assertThat(rule.channel.isDisposed()).isTrue(); AssertSubscriber assertSubscriber1 = AssertSubscriber.create(); @@ -696,7 +695,7 @@ public void shouldDisposeOriginalSourceIfRacing() { @Test public void shouldStartOriginalSourceOnceIfRacing() { for (int i = 0; i < RaceTestConstants.REPEATS; i++) { - ClientSocketRule rule = new ClientSocketRule(); + ClientChannelRule rule = new ClientChannelRule(); rule.init(); @@ -714,7 +713,7 @@ public void shouldStartOriginalSourceOnceIfRacing() { rule.client.dispose(); Assertions.assertThat(rule.client.isDisposed()).isTrue(); - Assertions.assertThat(rule.socket.isDisposed()).isTrue(); + Assertions.assertThat(rule.channel.isDisposed()).isTrue(); AssertSubscriber assertSubscriber1 = AssertSubscriber.create(); @@ -730,7 +729,7 @@ public void shouldStartOriginalSourceOnceIfRacing() { } } - public static class ClientSocketRule extends AbstractSocketRule { + public static class ClientChannelRule extends AbstractChannelRule { protected RemotingClient client; protected Runnable delayer; @@ -741,7 +740,7 @@ public static class ClientSocketRule extends AbstractSocketRule { @Override protected void doInit() { super.doInit(); - delayer = () -> producer.tryEmitValue(socket); + delayer = () -> producer.tryEmitValue(channel); producer = Sinks.one(); client = new DefaultRemotingClient( @@ -749,14 +748,14 @@ protected void doInit() { () -> producer .asMono() - .doOnCancel(() -> socket.dispose()) + .doOnCancel(() -> channel.dispose()) .doOnDiscard(Disposable.class, Disposable::dispose))); } @Override - protected Channel newRSocket() { + protected Channel newChannel() { this.thisClosedSink = Sinks.empty(); - return new ChannelRequester( + return new RequesterChannel( connection, PayloadDecoder.ZERO_COPY, StreamIdProvider.forClient(), diff --git a/today-remoting/src/test/java/infra/remoting/core/FireAndForgetRequesterMonoTest.java b/today-remoting/src/test/java/infra/remoting/core/FireAndForgetRequesterMonoTests.java similarity index 66% rename from today-remoting/src/test/java/infra/remoting/core/FireAndForgetRequesterMonoTest.java rename to today-remoting/src/test/java/infra/remoting/core/FireAndForgetRequesterMonoTests.java index 833f553..aa73ad1 100644 --- a/today-remoting/src/test/java/infra/remoting/core/FireAndForgetRequesterMonoTest.java +++ b/today-remoting/src/test/java/infra/remoting/core/FireAndForgetRequesterMonoTests.java @@ -1,23 +1,21 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -30,17 +28,17 @@ import java.util.function.Consumer; import java.util.stream.Stream; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import io.netty.util.IllegalReferenceCountException; import infra.remoting.FrameAssert; import infra.remoting.Payload; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; import infra.remoting.frame.FrameType; import infra.remoting.plugins.TestRequestInterceptor; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; import infra.remoting.util.ByteBufPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.util.CharsetUtil; +import io.netty.util.IllegalReferenceCountException; import reactor.core.Exceptions; import reactor.core.Scannable; import reactor.test.StepVerifier; @@ -49,10 +47,12 @@ import static infra.remoting.core.FragmentationUtils.FRAME_OFFSET; import static infra.remoting.core.FragmentationUtils.FRAME_OFFSET_WITH_METADATA; import static infra.remoting.core.PayloadValidationUtils.INVALID_PAYLOAD_ERROR_MESSAGE; -import static infra.remoting.core.TestRequesterResponderSupport.genericPayload; +import static infra.remoting.core.TestChannelSupport.genericPayload; import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -public class FireAndForgetRequesterMonoTest { +class FireAndForgetRequesterMonoTests { @BeforeAll public static void setUp() { @@ -67,8 +67,8 @@ public static void setUp() { @MethodSource("frameSent") public void frameShouldBeSentOnSubscription(Consumer monoConsumer) { final TestRequestInterceptor testRequestInterceptor = new TestRequestInterceptor(); - final TestRequesterResponderSupport activeStreams = - TestRequesterResponderSupport.client(testRequestInterceptor); + final TestChannelSupport activeStreams = + TestChannelSupport.client(testRequestInterceptor); final Payload payload = genericPayload(activeStreams.getAllocator()); final FireAndForgetRequesterMono fireAndForgetRequesterMono = new FireAndForgetRequesterMono(payload, activeStreams); @@ -80,11 +80,11 @@ public void frameShouldBeSentOnSubscription(Consumer monoConsumer.accept(fireAndForgetRequesterMono); - Assertions.assertThat(payload.refCnt()).isZero(); + assertThat(payload.refCnt()).isZero(); // should not add anything to map stateAssert.isTerminated(); activeStreams.assertNoActiveStreams(); - final ByteBuf frame = activeStreams.getDuplexConnection().awaitFrame(); + final ByteBuf frame = activeStreams.getConnection().awaitFrame(); FrameAssert.assertThat(frame) .isNotNull() .hasPayloadSize( @@ -98,7 +98,7 @@ public void frameShouldBeSentOnSubscription(Consumer .hasStreamId(1) .hasNoLeaks(); - Assertions.assertThat(activeStreams.getDuplexConnection().isEmpty()).isTrue(); + assertThat(activeStreams.getConnection().isEmpty()).isTrue(); activeStreams.getAllocator().assertHasNoLeaks(); testRequestInterceptor .expectOnStart(1, FrameType.REQUEST_FNF) @@ -116,9 +116,9 @@ public void frameShouldBeSentOnSubscription(Consumer public void frameFragmentsShouldBeSentOnSubscription( Consumer monoConsumer) { final int mtu = 64; - final TestRequesterResponderSupport streamManager = TestRequesterResponderSupport.client(mtu); + final TestChannelSupport streamManager = TestChannelSupport.client(mtu); final LeaksTrackingByteBufAllocator allocator = streamManager.getAllocator(); - final TestDuplexConnection sender = streamManager.getDuplexConnection(); + final TestConnection sender = streamManager.getConnection(); final byte[] metadata = new byte[65]; final byte[] data = new byte[129]; @@ -141,7 +141,7 @@ public void frameFragmentsShouldBeSentOnSubscription( streamManager.assertNoActiveStreams(); stateAssert.isTerminated(); - Assertions.assertThat(payload.refCnt()).isZero(); + assertThat(payload.refCnt()).isZero(); final ByteBuf frameFragment1 = sender.awaitFrame(); FrameAssert.assertThat(frameFragment1) @@ -196,7 +196,7 @@ public void frameFragmentsShouldBeSentOnSubscription( .hasStreamId(1) .hasNoLeaks(); - Assertions.assertThat(sender.isEmpty()).isTrue(); + assertThat(sender.isEmpty()).isTrue(); allocator.assertHasNoLeaks(); } @@ -215,10 +215,10 @@ static Stream> frameSent() { public void shouldErrorOnIncorrectRefCntInGivenPayload( Consumer monoConsumer) { final TestRequestInterceptor testRequestInterceptor = new TestRequestInterceptor(); - final TestRequesterResponderSupport streamManager = - TestRequesterResponderSupport.client(testRequestInterceptor); + final TestChannelSupport streamManager = + TestChannelSupport.client(testRequestInterceptor); final LeaksTrackingByteBufAllocator allocator = streamManager.getAllocator(); - final TestDuplexConnection sender = streamManager.getDuplexConnection(); + final TestConnection sender = streamManager.getConnection(); final Payload payload = ByteBufPayload.create(""); payload.release(); @@ -235,7 +235,7 @@ public void shouldErrorOnIncorrectRefCntInGivenPayload( stateAssert.isTerminated(); streamManager.assertNoActiveStreams(); - Assertions.assertThat(sender.isEmpty()).isTrue(); + assertThat(sender.isEmpty()).isTrue(); allocator.assertHasNoLeaks(); testRequestInterceptor .expectOnReject(FrameType.REQUEST_FNF, new IllegalReferenceCountException("refCnt: 0")) @@ -251,7 +251,7 @@ public void shouldErrorOnIncorrectRefCntInGivenPayload( .expectError(IllegalReferenceCountException.class) .verify(), fireAndForgetRequesterMono -> - Assertions.assertThatThrownBy(fireAndForgetRequesterMono::block) + assertThatThrownBy(fireAndForgetRequesterMono::block) .isInstanceOf(IllegalReferenceCountException.class)); } @@ -264,10 +264,10 @@ public void shouldErrorOnIncorrectRefCntInGivenPayload( public void shouldErrorIfFragmentExitsAllowanceIfFragmentationDisabled( Consumer monoConsumer) { final TestRequestInterceptor testRequestInterceptor = new TestRequestInterceptor(); - final TestRequesterResponderSupport streamManager = - TestRequesterResponderSupport.client(testRequestInterceptor); + final TestChannelSupport streamManager = + TestChannelSupport.client(testRequestInterceptor); final LeaksTrackingByteBufAllocator allocator = streamManager.getAllocator(); - final TestDuplexConnection sender = streamManager.getDuplexConnection(); + final TestConnection sender = streamManager.getConnection(); final byte[] metadata = new byte[FRAME_LENGTH_MASK]; final byte[] data = new byte[FRAME_LENGTH_MASK]; @@ -286,11 +286,11 @@ public void shouldErrorIfFragmentExitsAllowanceIfFragmentationDisabled( monoConsumer.accept(fireAndForgetRequesterMono); - Assertions.assertThat(payload.refCnt()).isZero(); + assertThat(payload.refCnt()).isZero(); stateAssert.isTerminated(); streamManager.assertNoActiveStreams(); - Assertions.assertThat(sender.isEmpty()).isTrue(); + assertThat(sender.isEmpty()).isTrue(); allocator.assertHasNoLeaks(); testRequestInterceptor .expectOnReject( @@ -302,37 +302,33 @@ public void shouldErrorIfFragmentExitsAllowanceIfFragmentationDisabled( static Stream> shouldErrorIfFragmentExitsAllowanceIfFragmentationDisabledSource() { - return Stream.of( - (s) -> - StepVerifier.create(s) - .expectSubscription() - .consumeErrorWith( - t -> - Assertions.assertThat(t) - .hasMessage( - String.format(INVALID_PAYLOAD_ERROR_MESSAGE, FRAME_LENGTH_MASK)) - .isInstanceOf(IllegalArgumentException.class)) - .verify(), + return Stream.of((s) -> StepVerifier.create(s) + .expectSubscription() + .consumeErrorWith(t -> + assertThat(t) + .hasMessage(String.format(INVALID_PAYLOAD_ERROR_MESSAGE, FRAME_LENGTH_MASK)) + .isInstanceOf(IllegalArgumentException.class)) + .verify(), fireAndForgetRequesterMono -> - Assertions.assertThatThrownBy(fireAndForgetRequesterMono::block) + assertThatThrownBy(fireAndForgetRequesterMono::block) .hasMessage(String.format(INVALID_PAYLOAD_ERROR_MESSAGE, FRAME_LENGTH_MASK)) .isInstanceOf(IllegalArgumentException.class)); } /** - * Ensures that frame will not be sent if we dont have availability for that. Options: 1. RSocket + * Ensures that frame will not be sent if we dont have availability for that. Options: 1. Channel * disposed / Connection Error, so all racing on existing interactions should be terminated as - * well 2. RSocket tries to use lease and end-ups with no available leases + * well 2. Channel tries to use lease and end-ups with no available leases */ @ParameterizedTest @MethodSource("shouldErrorIfNoAvailabilitySource") public void shouldErrorIfNoAvailability(Consumer monoConsumer) { final TestRequestInterceptor testRequestInterceptor = new TestRequestInterceptor(); final RuntimeException exception = new RuntimeException("test"); - final TestRequesterResponderSupport streamManager = - TestRequesterResponderSupport.client(exception, testRequestInterceptor); + final TestChannelSupport streamManager = + TestChannelSupport.client(exception, testRequestInterceptor); final LeaksTrackingByteBufAllocator allocator = streamManager.getAllocator(); - final TestDuplexConnection sender = streamManager.getDuplexConnection(); + final TestConnection sender = streamManager.getConnection(); final Payload payload = genericPayload(allocator); final FireAndForgetRequesterMono fireAndForgetRequesterMono = @@ -345,28 +341,25 @@ public void shouldErrorIfNoAvailability(Consumer mon monoConsumer.accept(fireAndForgetRequesterMono); - Assertions.assertThat(payload.refCnt()).isZero(); + assertThat(payload.refCnt()).isZero(); stateAssert.isTerminated(); streamManager.assertNoActiveStreams(); - Assertions.assertThat(sender.isEmpty()).isTrue(); + assertThat(sender.isEmpty()).isTrue(); allocator.assertHasNoLeaks(); testRequestInterceptor.expectOnReject(FrameType.REQUEST_FNF, exception).expectNothing(); } static Stream> shouldErrorIfNoAvailabilitySource() { - return Stream.of( - (s) -> - StepVerifier.create(s) - .expectSubscription() - .consumeErrorWith( - t -> - Assertions.assertThat(t) - .hasMessage("test") - .isInstanceOf(RuntimeException.class)) - .verify(), + return Stream.of((s) -> StepVerifier.create(s) + .expectSubscription() + .consumeErrorWith(t -> + assertThat(t) + .hasMessage("test") + .isInstanceOf(RuntimeException.class)) + .verify(), fireAndForgetRequesterMono -> - Assertions.assertThatThrownBy(fireAndForgetRequesterMono::block) + assertThatThrownBy(fireAndForgetRequesterMono::block) .hasMessage("test") .isInstanceOf(RuntimeException.class)); } @@ -375,44 +368,36 @@ static Stream> shouldErrorIfNoAvailabilityS @Test public void shouldSubscribeExactlyOnce1() { final TestRequestInterceptor testRequestInterceptor = new TestRequestInterceptor(); - final TestRequesterResponderSupport streamManager = - TestRequesterResponderSupport.client(testRequestInterceptor); + final TestChannelSupport streamManager = TestChannelSupport.client(testRequestInterceptor); final LeaksTrackingByteBufAllocator allocator = streamManager.getAllocator(); - final TestDuplexConnection sender = streamManager.getDuplexConnection(); + final TestConnection sender = streamManager.getConnection(); for (int i = 1; i < 50000; i += 2) { final Payload payload = ByteBufPayload.create("testData", "testMetadata"); - final FireAndForgetRequesterMono fireAndForgetRequesterMono = - new FireAndForgetRequesterMono(payload, streamManager); + final var fireAndForgetRequesterMono = new FireAndForgetRequesterMono(payload, streamManager); final StateAssert stateAssert = StateAssert.assertThat(FireAndForgetRequesterMono.STATE, fireAndForgetRequesterMono); - Assertions.assertThatThrownBy( - () -> - RaceTestUtils.race( - () -> { - AtomicReference atomicReference = new AtomicReference<>(); - fireAndForgetRequesterMono.subscribe(null, atomicReference::set); - Throwable throwable = atomicReference.get(); - if (throwable != null) { - throw Exceptions.propagate(throwable); - } - }, - fireAndForgetRequesterMono::block)) - .matches( - t -> { - Assertions.assertThat(t) - .hasMessageContaining("FireAndForgetMono allows only a single Subscriber"); - return true; - }); + assertThatThrownBy(() -> RaceTestUtils.race(() -> { + AtomicReference atomicReference = new AtomicReference<>(); + fireAndForgetRequesterMono.subscribe(null, atomicReference::set); + Throwable throwable = atomicReference.get(); + if (throwable != null) { + throw Exceptions.propagate(throwable); + } + }, fireAndForgetRequesterMono::block)) + + .matches(t -> { + assertThat(t).hasMessageContaining("FireAndForgetMono allows only a single Subscriber"); + return true; + }); final ByteBuf frame = sender.awaitFrame(); FrameAssert.assertThat(frame) .isNotNull() - .hasPayloadSize( - "testData".getBytes(CharsetUtil.UTF_8).length - + "testMetadata".getBytes(CharsetUtil.UTF_8).length) + .hasPayloadSize("testData".getBytes(CharsetUtil.UTF_8).length + + "testMetadata".getBytes(CharsetUtil.UTF_8).length) .hasMetadata("testMetadata") .hasData("testData") .hasNoFragmentsFollow() @@ -424,43 +409,34 @@ public void shouldSubscribeExactlyOnce1() { stateAssert.isTerminated(); streamManager.assertNoActiveStreams(); testRequestInterceptor - .assertNext( - event -> - Assertions.assertThat(event.eventType) - .isIn( - TestRequestInterceptor.EventType.ON_START, - TestRequestInterceptor.EventType.ON_REJECT)) - .assertNext( - event -> - Assertions.assertThat(event.eventType) - .isIn( - TestRequestInterceptor.EventType.ON_START, - TestRequestInterceptor.EventType.ON_COMPLETE, - TestRequestInterceptor.EventType.ON_REJECT)) - .assertNext( - event -> - Assertions.assertThat(event.eventType) - .isIn( - TestRequestInterceptor.EventType.ON_COMPLETE, - TestRequestInterceptor.EventType.ON_REJECT)) + .assertNext(event -> assertThat(event.eventType) + .isIn(TestRequestInterceptor.EventType.ON_START, + TestRequestInterceptor.EventType.ON_REJECT)) + .assertNext(event -> assertThat(event.eventType) + .isIn(TestRequestInterceptor.EventType.ON_START, + TestRequestInterceptor.EventType.ON_COMPLETE, + TestRequestInterceptor.EventType.ON_REJECT)) + .assertNext(event -> assertThat(event.eventType) + .isIn(TestRequestInterceptor.EventType.ON_COMPLETE, + TestRequestInterceptor.EventType.ON_REJECT)) .expectNothing(); } - Assertions.assertThat(sender.isEmpty()).isTrue(); + assertThat(sender.isEmpty()).isTrue(); allocator.assertHasNoLeaks(); } @Test public void checkName() { - final TestRequesterResponderSupport testRequesterResponderSupport = - TestRequesterResponderSupport.client(); + final TestChannelSupport testRequesterResponderSupport = + TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = testRequesterResponderSupport.getAllocator(); final Payload payload = ByteBufPayload.create("testData", "testMetadata"); final FireAndForgetRequesterMono fireAndForgetRequesterMono = new FireAndForgetRequesterMono(payload, testRequesterResponderSupport); - Assertions.assertThat(Scannable.from(fireAndForgetRequesterMono).name()) + assertThat(Scannable.from(fireAndForgetRequesterMono).name()) .isEqualTo("source(FireAndForgetMono)"); allocator.assertHasNoLeaks(); } diff --git a/today-remoting/src/test/java/infra/remoting/core/KeepAliveTest.java b/today-remoting/src/test/java/infra/remoting/core/KeepAliveTests.java similarity index 67% rename from today-remoting/src/test/java/infra/remoting/core/KeepAliveTest.java rename to today-remoting/src/test/java/infra/remoting/core/KeepAliveTests.java index 1b41f30..cd63412 100644 --- a/today-remoting/src/test/java/infra/remoting/core/KeepAliveTest.java +++ b/today-remoting/src/test/java/infra/remoting/core/KeepAliveTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -25,22 +24,22 @@ import java.time.Duration; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; -import infra.remoting.FrameAssert; import infra.remoting.Channel; +import infra.remoting.FrameAssert; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; -import infra.remoting.exceptions.ConnectionErrorException; +import infra.remoting.error.ConnectionErrorException; import infra.remoting.frame.FrameHeaderCodec; import infra.remoting.frame.FrameType; import infra.remoting.frame.KeepAliveFrameCodec; import infra.remoting.frame.decoder.PayloadDecoder; -import infra.remoting.resume.InMemoryResumableFramesStore; import infra.remoting.resume.ChannelSession; -import infra.remoting.resume.ResumableDuplexConnection; +import infra.remoting.resume.InMemoryResumableFramesStore; +import infra.remoting.resume.ResumableConnection; import infra.remoting.resume.ResumeStateHolder; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.Unpooled; import reactor.core.Disposable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -52,7 +51,7 @@ import static infra.remoting.keepalive.KeepAliveHandler.DefaultKeepAliveHandler; import static infra.remoting.keepalive.KeepAliveHandler.ResumableKeepAliveHandler; -public class KeepAliveTest { +public class KeepAliveTests { private static final int KEEP_ALIVE_INTERVAL = 100; private static final int KEEP_ALIVE_TIMEOUT = 1000; private static final int RESUMABLE_KEEP_ALIVE_TIMEOUT = 200; @@ -69,13 +68,13 @@ public void tearDown() { VirtualTimeScheduler.reset(); } - static RSocketState requester(int tickPeriod, int timeout) { + static ChannelState requester(int tickPeriod, int timeout) { LeaksTrackingByteBufAllocator allocator = LeaksTrackingByteBufAllocator.instrument(ByteBufAllocator.DEFAULT); - TestDuplexConnection connection = new TestDuplexConnection(allocator); + TestConnection connection = new TestConnection(allocator); Sinks.Empty empty = Sinks.empty(); - ChannelRequester rSocket = - new ChannelRequester( + RequesterChannel channel = + new RequesterChannel( connection, PayloadDecoder.ZERO_COPY, StreamIdProvider.forClient(), @@ -89,23 +88,23 @@ static RSocketState requester(int tickPeriod, int timeout) { null, empty, empty.asMono()); - return new RSocketState(rSocket, allocator, connection, empty); + return new ChannelState(channel, allocator, connection, empty); } - static ResumableRSocketState resumableRequester(int tickPeriod, int timeout) { + static ResumableChannelState resumableRequester(int tickPeriod, int timeout) { LeaksTrackingByteBufAllocator allocator = LeaksTrackingByteBufAllocator.instrument(ByteBufAllocator.DEFAULT); - TestDuplexConnection connection = new TestDuplexConnection(allocator); - ResumableDuplexConnection resumableConnection = - new ResumableDuplexConnection( + TestConnection connection = new TestConnection(allocator); + ResumableConnection resumableConnection = + new ResumableConnection( "test", Unpooled.EMPTY_BUFFER, connection, new InMemoryResumableFramesStore("test", Unpooled.EMPTY_BUFFER, 10_000)); Sinks.Empty onClose = Sinks.empty(); - ChannelRequester rSocket = - new ChannelRequester( + RequesterChannel channel = + new RequesterChannel( resumableConnection, PayloadDecoder.ZERO_COPY, StreamIdProvider.forClient(), @@ -122,14 +121,14 @@ static ResumableRSocketState resumableRequester(int tickPeriod, int timeout) { null, onClose, onClose.asMono()); - return new ResumableRSocketState(rSocket, connection, resumableConnection, onClose, allocator); + return new ResumableChannelState(channel, connection, resumableConnection, onClose, allocator); } @Test - void rSocketNotDisposedOnPresentKeepAlives() { - RSocketState requesterState = requester(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIMEOUT); + void channelNotDisposedOnPresentKeepAlives() { + ChannelState requesterState = requester(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIMEOUT); - TestDuplexConnection connection = requesterState.connection(); + TestConnection connection = requesterState.connection(); Disposable disposable = Flux.interval(Duration.ofMillis(KEEP_ALIVE_INTERVAL)) @@ -141,7 +140,7 @@ void rSocketNotDisposedOnPresentKeepAlives() { virtualTimeScheduler.advanceTimeBy(Duration.ofMillis(KEEP_ALIVE_TIMEOUT * 2)); - Channel channel = requesterState.rSocket(); + Channel channel = requesterState.channel(); Assertions.assertThat(channel.isDisposed()).isFalse(); @@ -156,10 +155,10 @@ void rSocketNotDisposedOnPresentKeepAlives() { } @Test - void noKeepAlivesSentAfterRSocketDispose() { - RSocketState requesterState = requester(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIMEOUT); + void noKeepAlivesSentAfterChannelDispose() { + ChannelState requesterState = requester(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIMEOUT); - requesterState.rSocket().dispose(); + requesterState.channel().dispose(); Duration duration = Duration.ofMillis(500); @@ -174,10 +173,10 @@ void noKeepAlivesSentAfterRSocketDispose() { } @Test - void rSocketDisposedOnMissingKeepAlives() { - RSocketState requesterState = requester(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIMEOUT); + void channelDisposedOnMissingKeepAlives() { + ChannelState requesterState = requester(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIMEOUT); - Channel channel = requesterState.rSocket(); + Channel channel = requesterState.channel(); virtualTimeScheduler.advanceTimeBy(Duration.ofMillis(KEEP_ALIVE_TIMEOUT * 2)); @@ -195,8 +194,8 @@ void rSocketDisposedOnMissingKeepAlives() { @Test void clientRequesterSendsKeepAlives() { - RSocketState RSocketState = requester(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIMEOUT); - TestDuplexConnection connection = RSocketState.connection(); + ChannelState channelState = requester(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIMEOUT); + TestConnection connection = channelState.connection(); virtualTimeScheduler.advanceTimeBy(Duration.ofMillis(KEEP_ALIVE_INTERVAL)); this.keepAliveFrameWithRespondFlag(connection.pollFrame()); @@ -205,72 +204,72 @@ void clientRequesterSendsKeepAlives() { virtualTimeScheduler.advanceTimeBy(Duration.ofMillis(KEEP_ALIVE_INTERVAL)); this.keepAliveFrameWithRespondFlag(connection.pollFrame()); - RSocketState.channel.dispose(); + channelState.channel.dispose(); FrameAssert.assertThat(connection.pollFrame()) .typeOf(FrameType.ERROR) .hasData("Disposed") .hasNoLeaks(); - RSocketState.connection.dispose(); + channelState.connection.dispose(); - RSocketState.allocator.assertHasNoLeaks(); + channelState.allocator.assertHasNoLeaks(); } @Test void requesterRespondsToKeepAlives() { - RSocketState rSocketState = requester(100_000, 100_000); - TestDuplexConnection connection = rSocketState.connection(); + ChannelState channelState = requester(100_000, 100_000); + TestConnection connection = channelState.connection(); Duration duration = Duration.ofMillis(100); Mono.delay(duration) .subscribe( l -> connection.addToReceivedBuffer( KeepAliveFrameCodec.encode( - rSocketState.allocator, true, 0, Unpooled.EMPTY_BUFFER))); + channelState.allocator, true, 0, Unpooled.EMPTY_BUFFER))); virtualTimeScheduler.advanceTimeBy(duration); FrameAssert.assertThat(connection.awaitFrame()) .typeOf(FrameType.KEEPALIVE) .matches(this::keepAliveFrameWithoutRespondFlag); - rSocketState.channel.dispose(); - FrameAssert.assertThat(rSocketState.connection.pollFrame()) + channelState.channel.dispose(); + FrameAssert.assertThat(channelState.connection.pollFrame()) .typeOf(FrameType.ERROR) .hasStreamIdZero() .hasData("Disposed") .hasNoLeaks(); - rSocketState.connection.dispose(); + channelState.connection.dispose(); - rSocketState.allocator.assertHasNoLeaks(); + channelState.allocator.assertHasNoLeaks(); } @Test void resumableRequesterNoKeepAlivesAfterDisconnect() { - ResumableRSocketState rSocketState = + ResumableChannelState channelState = resumableRequester(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIMEOUT); - TestDuplexConnection testConnection = rSocketState.connection(); - ResumableDuplexConnection resumableDuplexConnection = rSocketState.resumableDuplexConnection(); + TestConnection testConnection = channelState.connection(); + ResumableConnection resumableConnection = channelState.resumableConnection(); - resumableDuplexConnection.disconnect(); + resumableConnection.disconnect(); Duration duration = Duration.ofMillis(KEEP_ALIVE_INTERVAL * 5); virtualTimeScheduler.advanceTimeBy(duration); Assertions.assertThat(testConnection.pollFrame()).isNull(); - rSocketState.channel.dispose(); - rSocketState.connection.dispose(); + channelState.channel.dispose(); + channelState.connection.dispose(); - rSocketState.allocator.assertHasNoLeaks(); + channelState.allocator.assertHasNoLeaks(); } @Test void resumableRequesterKeepAlivesAfterReconnect() { - ResumableRSocketState rSocketState = + ResumableChannelState channelState = resumableRequester(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIMEOUT); - ResumableDuplexConnection resumableDuplexConnection = rSocketState.resumableDuplexConnection(); - resumableDuplexConnection.disconnect(); - TestDuplexConnection newTestConnection = new TestDuplexConnection(rSocketState.alloc()); - resumableDuplexConnection.connect(newTestConnection); - // resumableDuplexConnection.(0, 0, ignored -> Mono.empty()); + ResumableConnection resumableConnection = channelState.resumableConnection(); + resumableConnection.disconnect(); + TestConnection newTestConnection = new TestConnection(channelState.alloc()); + resumableConnection.connect(newTestConnection); + // resumableConnection.(0, 0, ignored -> Mono.empty()); virtualTimeScheduler.advanceTimeBy(Duration.ofMillis(KEEP_ALIVE_INTERVAL)); @@ -279,7 +278,7 @@ void resumableRequesterKeepAlivesAfterReconnect() { .hasStreamIdZero() .hasNoLeaks(); - rSocketState.channel.dispose(); + channelState.channel.dispose(); FrameAssert.assertThat(newTestConnection.pollFrame()) .typeOf(FrameType.ERROR) .hasStreamIdZero() @@ -292,42 +291,42 @@ void resumableRequesterKeepAlivesAfterReconnect() { .hasNoLeaks(); newTestConnection.dispose(); - rSocketState.allocator.assertHasNoLeaks(); + channelState.allocator.assertHasNoLeaks(); } @Test void resumableRequesterNoKeepAlivesAfterDispose() { - ResumableRSocketState rSocketState = + ResumableChannelState channelState = resumableRequester(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIMEOUT); - rSocketState.rSocket().dispose(); + channelState.channel().dispose(); Duration duration = Duration.ofMillis(500); - StepVerifier.create(Flux.from(rSocketState.connection().getSentAsPublisher()).take(duration)) + StepVerifier.create(Flux.from(channelState.connection().getSentAsPublisher()).take(duration)) .then(() -> virtualTimeScheduler.advanceTimeBy(duration)) .expectComplete() .verify(Duration.ofSeconds(5)); - rSocketState.channel.dispose(); - FrameAssert.assertThat(rSocketState.connection.pollFrame()) + channelState.channel.dispose(); + FrameAssert.assertThat(channelState.connection.pollFrame()) .typeOf(FrameType.ERROR) .hasStreamIdZero() .hasData("Disposed") .hasNoLeaks(); - rSocketState.connection.dispose(); - FrameAssert.assertThat(rSocketState.connection.pollFrame()) + channelState.connection.dispose(); + FrameAssert.assertThat(channelState.connection.pollFrame()) .typeOf(FrameType.ERROR) .hasStreamIdZero() .hasData("Connection Closed Unexpectedly") .hasNoLeaks(); - rSocketState.allocator.assertHasNoLeaks(); + channelState.allocator.assertHasNoLeaks(); } @Test - void resumableRSocketsNotDisposedOnMissingKeepAlives() throws InterruptedException { - ResumableRSocketState resumableRequesterState = + void resumableChannelsNotDisposedOnMissingKeepAlives() throws InterruptedException { + ResumableChannelState resumableRequesterState = resumableRequester(KEEP_ALIVE_INTERVAL, RESUMABLE_KEEP_ALIVE_TIMEOUT); - Channel channel = resumableRequesterState.rSocket(); - TestDuplexConnection connection = resumableRequesterState.connection(); + Channel channel = resumableRequesterState.channel(); + TestConnection connection = resumableRequesterState.connection(); virtualTimeScheduler.advanceTimeBy(Duration.ofMillis(500)); @@ -354,16 +353,15 @@ private boolean keepAliveFrameWithoutRespondFlag(ByteBuf frame) { return keepAliveFrame(frame) && !KeepAliveFrameCodec.respondFlag(frame) && frame.release(); } - static class RSocketState { + static class ChannelState { private final Channel channel; - private final TestDuplexConnection connection; + private final TestConnection connection; private final LeaksTrackingByteBufAllocator allocator; private final Sinks.Empty onClose; - public RSocketState( - Channel channel, + public ChannelState(Channel channel, LeaksTrackingByteBufAllocator allocator, - TestDuplexConnection connection, + TestConnection connection, Sinks.Empty onClose) { this.channel = channel; this.connection = connection; @@ -371,11 +369,11 @@ public RSocketState( this.onClose = onClose; } - public TestDuplexConnection connection() { + public TestConnection connection() { return connection; } - public Channel rSocket() { + public Channel channel() { return channel; } @@ -384,35 +382,35 @@ public LeaksTrackingByteBufAllocator alloc() { } } - static class ResumableRSocketState { + static class ResumableChannelState { private final Channel channel; - private final TestDuplexConnection connection; - private final ResumableDuplexConnection resumableDuplexConnection; + private final TestConnection connection; + private final ResumableConnection resumableConnection; private final LeaksTrackingByteBufAllocator allocator; private final Sinks.Empty onClose; - public ResumableRSocketState( + public ResumableChannelState( Channel channel, - TestDuplexConnection connection, - ResumableDuplexConnection resumableDuplexConnection, + TestConnection connection, + ResumableConnection resumableConnection, Sinks.Empty onClose, LeaksTrackingByteBufAllocator allocator) { this.channel = channel; this.connection = connection; - this.resumableDuplexConnection = resumableDuplexConnection; + this.resumableConnection = resumableConnection; this.onClose = onClose; this.allocator = allocator; } - public TestDuplexConnection connection() { + public TestConnection connection() { return connection; } - public ResumableDuplexConnection resumableDuplexConnection() { - return resumableDuplexConnection; + public ResumableConnection resumableConnection() { + return resumableConnection; } - public Channel rSocket() { + public Channel channel() { return channel; } diff --git a/today-remoting/src/test/java/infra/remoting/core/PayloadValidationUtilsTest.java b/today-remoting/src/test/java/infra/remoting/core/PayloadValidationUtilsTests.java similarity index 88% rename from today-remoting/src/test/java/infra/remoting/core/PayloadValidationUtilsTest.java rename to today-remoting/src/test/java/infra/remoting/core/PayloadValidationUtilsTests.java index 9199810..71538b3 100644 --- a/today-remoting/src/test/java/infra/remoting/core/PayloadValidationUtilsTest.java +++ b/today-remoting/src/test/java/infra/remoting/core/PayloadValidationUtilsTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -29,7 +28,7 @@ import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_SIZE; -class PayloadValidationUtilsTest { +class PayloadValidationUtilsTests { @Test void shouldBeValidFrameWithNoFragmentation() { diff --git a/today-remoting/src/test/java/infra/remoting/core/ReconnectMonoTests.java b/today-remoting/src/test/java/infra/remoting/core/ReconnectMonoTests.java index d3387ac..ec7d608 100644 --- a/today-remoting/src/test/java/infra/remoting/core/ReconnectMonoTests.java +++ b/today-remoting/src/test/java/infra/remoting/core/ReconnectMonoTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; diff --git a/today-remoting/src/test/java/infra/remoting/core/RemotingServerFragmentationTest.java b/today-remoting/src/test/java/infra/remoting/core/RemotingServerFragmentationTests.java similarity index 75% rename from today-remoting/src/test/java/infra/remoting/core/RemotingServerFragmentationTest.java rename to today-remoting/src/test/java/infra/remoting/core/RemotingServerFragmentationTests.java index a8eae95..5435e4a 100644 --- a/today-remoting/src/test/java/infra/remoting/core/RemotingServerFragmentationTest.java +++ b/today-remoting/src/test/java/infra/remoting/core/RemotingServerFragmentationTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -26,7 +25,7 @@ import infra.remoting.test.util.TestClientTransport; import infra.remoting.test.util.TestServerTransport; -public class RemotingServerFragmentationTest { +public class RemotingServerFragmentationTests { @Test public void serverErrorsWithEnabledFragmentationOnInsufficientMtu() { diff --git a/today-remoting/src/test/java/infra/remoting/core/RemotingServerTest.java b/today-remoting/src/test/java/infra/remoting/core/RemotingServerTests.java similarity index 82% rename from today-remoting/src/test/java/infra/remoting/core/RemotingServerTest.java rename to today-remoting/src/test/java/infra/remoting/core/RemotingServerTests.java index 42e8db4..5b85a6c 100644 --- a/today-remoting/src/test/java/infra/remoting/core/RemotingServerTest.java +++ b/today-remoting/src/test/java/infra/remoting/core/RemotingServerTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -22,19 +21,19 @@ import java.time.Duration; import java.util.Random; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; +import infra.remoting.Channel; import infra.remoting.Closeable; import infra.remoting.FrameAssert; -import infra.remoting.Channel; -import infra.remoting.exceptions.RejectedSetupException; +import infra.remoting.error.RejectedSetupException; import infra.remoting.frame.FrameType; import infra.remoting.frame.KeepAliveFrameCodec; import infra.remoting.frame.RequestResponseFrameCodec; import infra.remoting.frame.SetupFrameCodec; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; import infra.remoting.test.util.TestServerTransport; import infra.remoting.util.EmptyPayload; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.Unpooled; import reactor.core.Scannable; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; @@ -44,14 +43,14 @@ import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; import static org.assertj.core.api.Assertions.assertThat; -public class RemotingServerTest { +public class RemotingServerTests { @Test public void unexpectedFramesBeforeSetupFrame() { TestServerTransport transport = new TestServerTransport(); RemotingServer.create().bind(transport).block(); - final TestDuplexConnection duplexConnection = transport.connect(); + final TestConnection duplexConnection = transport.connect(); duplexConnection.addToReceivedBuffer( KeepAliveFrameCodec.encode(duplexConnection.alloc(), false, 1, Unpooled.EMPTY_BUFFER)); @@ -77,7 +76,7 @@ public void timeoutOnNoFirstFrame() { try { RemotingServer.create().maxTimeToFirstFrame(Duration.ofMinutes(2)).bind(transport).block(); - final TestDuplexConnection duplexConnection = transport.connect(); + final TestConnection duplexConnection = transport.connect(); scheduler.advanceTimeBy(Duration.ofMinutes(1)); @@ -141,7 +140,7 @@ public void unexpectedFramesBeforeSetup() { Closeable server = RemotingServer.create() .acceptor( - (setup, sendingSocket) -> { + (setup, channel) -> { connectedSink.tryEmitEmpty(); return Mono.just(new Channel() { }); }) @@ -151,7 +150,7 @@ public void unexpectedFramesBeforeSetup() { byte[] bytes = new byte[16_000_000]; new Random().nextBytes(bytes); - TestDuplexConnection connection = transport.connect(); + TestConnection connection = transport.connect(); connection.addToReceivedBuffer( RequestResponseFrameCodec.encode( ByteBufAllocator.DEFAULT, @@ -176,11 +175,11 @@ public void unexpectedFramesBeforeSetup() { public void ensuresErrorFrameDeliveredPriorConnectionDisposal() { TestServerTransport transport = new TestServerTransport(); Closeable server = RemotingServer.create() - .acceptor((setup, sendingSocket) -> Mono.error(new RejectedSetupException("ACCESS_DENIED"))) + .acceptor((setup, channel) -> Mono.error(new RejectedSetupException("ACCESS_DENIED"))) .bind(transport) .block(); - TestDuplexConnection connection = transport.connect(); + TestConnection connection = transport.connect(); connection.addToReceivedBuffer( SetupFrameCodec.encode( ByteBufAllocator.DEFAULT, diff --git a/today-remoting/src/test/java/infra/remoting/core/RequestChannelRequesterFluxTest.java b/today-remoting/src/test/java/infra/remoting/core/RequestRequesterChannelFluxTests.java similarity index 89% rename from today-remoting/src/test/java/infra/remoting/core/RequestChannelRequesterFluxTest.java rename to today-remoting/src/test/java/infra/remoting/core/RequestRequesterChannelFluxTests.java index dc214ad..0d39b87 100644 --- a/today-remoting/src/test/java/infra/remoting/core/RequestChannelRequesterFluxTest.java +++ b/today-remoting/src/test/java/infra/remoting/core/RequestRequesterChannelFluxTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -31,21 +30,21 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Stream; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import io.netty.util.IllegalReferenceCountException; import infra.remoting.FrameAssert; import infra.remoting.Payload; import infra.remoting.PayloadAssert; import infra.remoting.RaceTestConstants; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; -import infra.remoting.exceptions.ApplicationErrorException; +import infra.remoting.error.ApplicationErrorException; import infra.remoting.frame.FrameType; import infra.remoting.internal.subscriber.AssertSubscriber; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; import infra.remoting.util.ByteBufPayload; import infra.remoting.util.DefaultPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.util.CharsetUtil; +import io.netty.util.IllegalReferenceCountException; import reactor.core.publisher.Hooks; import reactor.core.publisher.Signal; import reactor.test.StepVerifier; @@ -55,7 +54,7 @@ import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; import static infra.remoting.frame.FrameType.CANCEL; -public class RequestChannelRequesterFluxTest { +public class RequestRequesterChannelFluxTests { @BeforeAll public static void setUp() { @@ -70,10 +69,10 @@ public static void setUp() { @ParameterizedTest @ValueSource(strings = { "inbound", "outbound" }) public void requestNFrameShouldBeSentOnSubscriptionAndThenSeparately(String completionCase) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); - final Payload payload = TestRequesterResponderSupport.genericPayload(allocator); + final TestConnection sender = activeStreams.getConnection(); + final Payload payload = TestChannelSupport.genericPayload(allocator); final TestPublisher publisher = TestPublisher.create(); final RequestChannelRequesterFlux requestChannelRequesterFlux = @@ -163,13 +162,13 @@ public void requestNFrameShouldBeSentOnSubscriptionAndThenSeparately(String comp // state machine check stateAssert.hasSubscribedFlag().hasRequestN(Integer.MAX_VALUE).hasFirstFrameSentFlag(); - Payload nextPayload = TestRequesterResponderSupport.genericPayload(allocator); + Payload nextPayload = TestChannelSupport.genericPayload(allocator); requestChannelRequesterFlux.handlePayload(nextPayload); int mtu = ThreadLocalRandom.current().nextInt(64, 256); - Payload randomPayload = TestRequesterResponderSupport.randomPayload(allocator); + Payload randomPayload = TestChannelSupport.randomPayload(allocator); ArrayList fragments = - TestRequesterResponderSupport.prepareFragments(allocator, mtu, randomPayload); + TestChannelSupport.prepareFragments(allocator, mtu, randomPayload); ByteBuf firstFragment = fragments.remove(0); requestChannelRequesterFlux.handleNext(firstFragment, true, false); @@ -252,9 +251,9 @@ else if (completionCase.equals("outbound")) { @ParameterizedTest @ValueSource(booleans = { true, false }) public void streamShouldErrorWithoutInitializingRemoteStreamIfSourceIsEmpty(boolean doRequest) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); final TestPublisher publisher = TestPublisher.create(); final RequestChannelRequesterFlux requestChannelRequesterFlux = @@ -297,9 +296,9 @@ public void streamShouldErrorWithoutInitializingRemoteStreamIfSourceIsEmpty(bool @ValueSource(booleans = { true, false }) public void streamShouldPropagateErrorWithoutInitializingRemoteStreamIfTheFirstSignalIsError( boolean doRequest) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); final TestPublisher publisher = TestPublisher.create(); final RequestChannelRequesterFlux requestChannelRequesterFlux = @@ -341,9 +340,9 @@ public void streamShouldPropagateErrorWithoutInitializingRemoteStreamIfTheFirstS @ParameterizedTest @ValueSource(strings = { "inbound", "outbound" }) public void streamShouldBeInHalfClosedStateOnTheInboundCancellation(String terminationMode) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); final TestPublisher publisher = TestPublisher.create(); final RequestChannelRequesterFlux requestChannelRequesterFlux = @@ -367,9 +366,9 @@ public void streamShouldBeInHalfClosedStateOnTheInboundCancellation(String termi stateAssert.hasSubscribedFlag().hasRequestN(Integer.MAX_VALUE).hasNoFirstFrameSentFlag(); activeStreams.assertNoActiveStreams(); - Payload payload1 = TestRequesterResponderSupport.randomPayload(allocator); - Payload payload2 = TestRequesterResponderSupport.randomPayload(allocator); - Payload payload3 = TestRequesterResponderSupport.randomPayload(allocator); + Payload payload1 = TestChannelSupport.randomPayload(allocator); + Payload payload2 = TestChannelSupport.randomPayload(allocator); + Payload payload3 = TestChannelSupport.randomPayload(allocator); publisher.next(payload1.retain()); @@ -440,9 +439,9 @@ else if (terminationMode.equals("inbound")) { @ParameterizedTest @ValueSource(strings = { "inbound", "outbound" }) public void errorShouldTerminateExecution(String terminationMode) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); final TestPublisher publisher = TestPublisher.create(); final RequestChannelRequesterFlux requestChannelRequesterFlux = @@ -466,9 +465,9 @@ public void errorShouldTerminateExecution(String terminationMode) { stateAssert.hasSubscribedFlag().hasRequestN(Integer.MAX_VALUE).hasNoFirstFrameSentFlag(); activeStreams.assertNoActiveStreams(); - Payload payload1 = TestRequesterResponderSupport.randomPayload(allocator); - Payload payload2 = TestRequesterResponderSupport.randomPayload(allocator); - Payload payload3 = TestRequesterResponderSupport.randomPayload(allocator); + Payload payload1 = TestChannelSupport.randomPayload(allocator); + Payload payload2 = TestChannelSupport.randomPayload(allocator); + Payload payload3 = TestChannelSupport.randomPayload(allocator); publisher.next(payload1.retain()); @@ -523,9 +522,9 @@ else if (terminationMode.equals("inbound")) { @Test public void failOnOverflow() { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); final TestPublisher publisher = TestPublisher.create(); final RequestChannelRequesterFlux requestChannelRequesterFlux = @@ -549,7 +548,7 @@ public void failOnOverflow() { stateAssert.hasSubscribedFlag().hasRequestN(1).hasNoFirstFrameSentFlag(); activeStreams.assertNoActiveStreams(); - Payload payload1 = TestRequesterResponderSupport.randomPayload(allocator); + Payload payload1 = TestChannelSupport.randomPayload(allocator); publisher.next(payload1.retain()); @@ -565,10 +564,10 @@ public void failOnOverflow() { publisher.assertMaxRequested(1); - Payload nextPayload = TestRequesterResponderSupport.genericPayload(allocator); + Payload nextPayload = TestChannelSupport.genericPayload(allocator); requestChannelRequesterFlux.handlePayload(nextPayload); - Payload unrequestedPayload = TestRequesterResponderSupport.genericPayload(allocator); + Payload unrequestedPayload = TestChannelSupport.genericPayload(allocator); requestChannelRequesterFlux.handlePayload(unrequestedPayload); final ByteBuf cancelFrame = sender.awaitFrame(); @@ -626,9 +625,9 @@ public void shouldHaveEventsDeliveredSeriallyWhenOutboundErrorRacingWithInboundS Hooks.onErrorDropped(droppedErrors::add); try { for (int i = 0; i < RaceTestConstants.REPEATS; i++) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); final TestPublisher publisher = TestPublisher.createNoncompliant(TestPublisher.Violation.DEFER_CANCELLATION); @@ -651,7 +650,7 @@ public void shouldHaveEventsDeliveredSeriallyWhenOutboundErrorRacingWithInboundS stateAssert.hasSubscribedFlag().hasRequestN(Integer.MAX_VALUE).hasNoFirstFrameSentFlag(); activeStreams.assertNoActiveStreams(); - Payload requestPayload = TestRequesterResponderSupport.randomPayload(allocator); + Payload requestPayload = TestChannelSupport.randomPayload(allocator); publisher.next(requestPayload); stateAssert.hasSubscribedFlag().hasRequestN(Integer.MAX_VALUE).hasFirstFrameSentFlag(); @@ -663,9 +662,9 @@ public void shouldHaveEventsDeliveredSeriallyWhenOutboundErrorRacingWithInboundS requestChannelRequesterFlux.handleRequestN(Long.MAX_VALUE); - Payload responsePayload1 = TestRequesterResponderSupport.randomPayload(allocator); - Payload responsePayload2 = TestRequesterResponderSupport.randomPayload(allocator); - Payload responsePayload3 = TestRequesterResponderSupport.randomPayload(allocator); + Payload responsePayload1 = TestChannelSupport.randomPayload(allocator); + Payload responsePayload2 = TestChannelSupport.randomPayload(allocator); + Payload responsePayload3 = TestChannelSupport.randomPayload(allocator); Payload releasedPayload = ByteBufPayload.create(Unpooled.EMPTY_BUFFER); releasedPayload.release(); @@ -798,9 +797,9 @@ else if (inboundTerminationMode.equals("complete")) { public void shouldRemoveItselfFromActiveStreamsWhenInboundAndOutboundAreTerminated( String outboundTerminationMode) { for (int i = 0; i < RaceTestConstants.REPEATS; i++) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); final TestPublisher publisher = TestPublisher.createNoncompliant(TestPublisher.Violation.DEFER_CANCELLATION); @@ -823,7 +822,7 @@ public void shouldRemoveItselfFromActiveStreamsWhenInboundAndOutboundAreTerminat stateAssert.hasSubscribedFlag().hasRequestN(Integer.MAX_VALUE).hasNoFirstFrameSentFlag(); activeStreams.assertNoActiveStreams(); - Payload requestPayload = TestRequesterResponderSupport.randomPayload(allocator); + Payload requestPayload = TestChannelSupport.randomPayload(allocator); publisher.next(requestPayload); stateAssert.hasSubscribedFlag().hasRequestN(Integer.MAX_VALUE).hasFirstFrameSentFlag(); diff --git a/today-remoting/src/test/java/infra/remoting/core/RequestChannelResponderSubscriberTest.java b/today-remoting/src/test/java/infra/remoting/core/RequestResponderChannelSubscriberTests.java similarity index 89% rename from today-remoting/src/test/java/infra/remoting/core/RequestChannelResponderSubscriberTest.java rename to today-remoting/src/test/java/infra/remoting/core/RequestResponderChannelSubscriberTests.java index c168981..6659f3f 100644 --- a/today-remoting/src/test/java/infra/remoting/core/RequestChannelResponderSubscriberTest.java +++ b/today-remoting/src/test/java/infra/remoting/core/RequestResponderChannelSubscriberTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -32,21 +31,21 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Stream; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import io.netty.util.IllegalReferenceCountException; import infra.remoting.FrameAssert; import infra.remoting.Payload; import infra.remoting.PayloadAssert; import infra.remoting.RaceTestConstants; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; -import infra.remoting.exceptions.ApplicationErrorException; +import infra.remoting.error.ApplicationErrorException; import infra.remoting.frame.FrameType; import infra.remoting.internal.subscriber.AssertSubscriber; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; import infra.remoting.util.ByteBufPayload; import infra.remoting.util.DefaultPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.util.CharsetUtil; +import io.netty.util.IllegalReferenceCountException; import reactor.core.Exceptions; import reactor.core.publisher.Hooks; import reactor.core.publisher.Signal; @@ -64,7 +63,7 @@ import static reactor.test.publisher.TestPublisher.Violation.CLEANUP_ON_TERMINATE; import static reactor.test.publisher.TestPublisher.Violation.DEFER_CANCELLATION; -public class RequestChannelResponderSubscriberTest { +public class RequestResponderChannelSubscriberTests { @BeforeAll public static void setUp() { @@ -79,10 +78,10 @@ public static void setUp() { @ParameterizedTest @ValueSource(strings = { "inbound", "outbound", "inboundCancel" }) public void requestNFrameShouldBeSentOnSubscriptionAndThenSeparately(String completionCase) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); - final Payload firstPayload = TestRequesterResponderSupport.genericPayload(allocator); + final TestConnection sender = activeStreams.getConnection(); + final Payload firstPayload = TestChannelSupport.genericPayload(allocator); final TestPublisher publisher = TestPublisher.create(); final RequestChannelResponderSubscriber requestChannelResponderSubscriber = @@ -126,7 +125,7 @@ public void requestNFrameShouldBeSentOnSubscriptionAndThenSeparately(String comp .hasRequestN(1) .hasNoLeaks(); - publisher.next(TestRequesterResponderSupport.genericPayload(allocator)); + publisher.next(TestChannelSupport.genericPayload(allocator)); final ByteBuf frame = sender.awaitFrame(); FrameAssert.assertThat(frame) @@ -157,13 +156,13 @@ public void requestNFrameShouldBeSentOnSubscriptionAndThenSeparately(String comp // state machine check stateAssert.hasSubscribedFlag().hasRequestN(Integer.MAX_VALUE).hasFirstFrameSentFlag(); - Payload nextPayload = TestRequesterResponderSupport.genericPayload(allocator); + Payload nextPayload = TestChannelSupport.genericPayload(allocator); requestChannelResponderSubscriber.handlePayload(nextPayload); int mtu = ThreadLocalRandom.current().nextInt(64, 256); - Payload randomPayload = TestRequesterResponderSupport.randomPayload(allocator); + Payload randomPayload = TestChannelSupport.randomPayload(allocator); ArrayList fragments = - TestRequesterResponderSupport.prepareFragments(allocator, mtu, randomPayload); + TestChannelSupport.prepareFragments(allocator, mtu, randomPayload); ByteBuf firstFragment = fragments.remove(0); requestChannelResponderSubscriber.handleNext(firstFragment, true, false); @@ -274,10 +273,10 @@ else if (completionCase.equals("outbound")) { @Test public void failOnOverflow() { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); - final Payload firstPayload = TestRequesterResponderSupport.genericPayload(allocator); + final TestConnection sender = activeStreams.getConnection(); + final Payload firstPayload = TestChannelSupport.genericPayload(allocator); final TestPublisher publisher = TestPublisher.create(); final RequestChannelResponderSubscriber requestChannelResponderSubscriber = @@ -321,10 +320,10 @@ public void failOnOverflow() { .hasRequestN(1) .hasNoLeaks(); - Payload nextPayload = TestRequesterResponderSupport.genericPayload(allocator); + Payload nextPayload = TestChannelSupport.genericPayload(allocator); requestChannelResponderSubscriber.handlePayload(nextPayload); - Payload unrequestedPayload = TestRequesterResponderSupport.genericPayload(allocator); + Payload unrequestedPayload = TestChannelSupport.genericPayload(allocator); requestChannelResponderSubscriber.handlePayload(unrequestedPayload); final ByteBuf cancelErrorFrame = sender.awaitFrame(); @@ -354,10 +353,10 @@ public void failOnOverflow() { @Test public void failOnOverflowBeforeFirstPayloadIsSent() { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); - final Payload firstPayload = TestRequesterResponderSupport.genericPayload(allocator); + final TestConnection sender = activeStreams.getConnection(); + final Payload firstPayload = TestChannelSupport.genericPayload(allocator); final TestPublisher publisher = TestPublisher.create(); final RequestChannelResponderSubscriber requestChannelResponderSubscriber = @@ -382,7 +381,7 @@ public void failOnOverflowBeforeFirstPayloadIsSent() { // state machine check stateAssert.hasSubscribedFlagOnly().hasRequestN(0); - Payload unrequestedPayload = TestRequesterResponderSupport.genericPayload(allocator); + Payload unrequestedPayload = TestChannelSupport.genericPayload(allocator); requestChannelResponderSubscriber.handlePayload(unrequestedPayload); final ByteBuf cancelErrorFrame = sender.awaitFrame(); @@ -418,11 +417,11 @@ public void failOnOverflowBeforeFirstPayloadIsSent() { @Test public void streamShouldWorkCorrectlyWhenRacingHandleCompleteWithSubscription() { for (int i = 0; i < RaceTestConstants.REPEATS; i++) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); ; - final Payload firstPayload = TestRequesterResponderSupport.randomPayload(allocator); + final Payload firstPayload = TestChannelSupport.randomPayload(allocator); final TestPublisher publisher = TestPublisher.create(); final RequestChannelResponderSubscriber requestChannelResponderSubscriber = @@ -479,9 +478,9 @@ public void streamShouldWorkCorrectlyWhenRacingHandleErrorWithSubscription() { ApplicationErrorException applicationErrorException = new ApplicationErrorException("test"); for (int i = 0; i < RaceTestConstants.REPEATS; i++) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final Payload firstPayload = TestRequesterResponderSupport.randomPayload(allocator); + final Payload firstPayload = TestChannelSupport.randomPayload(allocator); final TestPublisher publisher = TestPublisher.create(); final RequestChannelResponderSubscriber requestChannelResponderSubscriber = @@ -525,9 +524,9 @@ public void streamShouldWorkCorrectlyWhenRacingOutboundErrorWithSubscription() { RuntimeException exception = new RuntimeException("test"); for (int i = 0; i < RaceTestConstants.REPEATS; i++) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final Payload firstPayload = TestRequesterResponderSupport.randomPayload(allocator); + final Payload firstPayload = TestChannelSupport.randomPayload(allocator); final TestPublisher publisher = TestPublisher.create(); final RequestChannelResponderSubscriber requestChannelResponderSubscriber = @@ -550,7 +549,7 @@ public void streamShouldWorkCorrectlyWhenRacingOutboundErrorWithSubscription() { stateAssert.isTerminated(); - FrameAssert.assertThat(activeStreams.getDuplexConnection().awaitFrame()) + FrameAssert.assertThat(activeStreams.getConnection().awaitFrame()) .typeOf(ERROR) .hasData("test") .hasStreamId(1) @@ -573,9 +572,9 @@ public void streamShouldWorkCorrectlyWhenRacingOutboundErrorWithSubscription() { @Test public void streamShouldWorkCorrectlyWhenRacingHandleCancelWithSubscription() { for (int i = 0; i < RaceTestConstants.REPEATS; i++) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final Payload firstPayload = TestRequesterResponderSupport.randomPayload(allocator); + final Payload firstPayload = TestChannelSupport.randomPayload(allocator); final TestPublisher publisher = TestPublisher.create(); final RequestChannelResponderSubscriber requestChannelResponderSubscriber = @@ -641,13 +640,13 @@ public void shouldHaveEventsDeliveredSeriallyWhenOutboundErrorRacingWithInboundS Hooks.onErrorDropped(droppedErrors::add); try { for (int i = 0; i < RaceTestConstants.REPEATS; i++) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); final TestPublisher publisher = TestPublisher.createNoncompliant(DEFER_CANCELLATION, CLEANUP_ON_TERMINATE); - Payload requestPayload = TestRequesterResponderSupport.randomPayload(allocator); + Payload requestPayload = TestChannelSupport.randomPayload(allocator); final RequestChannelResponderSubscriber requestChannelResponderSubscriber = new RequestChannelResponderSubscriber(1, 1, requestPayload, activeStreams); @@ -668,9 +667,9 @@ public void shouldHaveEventsDeliveredSeriallyWhenOutboundErrorRacingWithInboundS requestChannelResponderSubscriber.handleRequestN(Long.MAX_VALUE); - Payload responsePayload1 = TestRequesterResponderSupport.randomPayload(allocator); - Payload responsePayload2 = TestRequesterResponderSupport.randomPayload(allocator); - Payload responsePayload3 = TestRequesterResponderSupport.randomPayload(allocator); + Payload responsePayload1 = TestChannelSupport.randomPayload(allocator); + Payload responsePayload2 = TestChannelSupport.randomPayload(allocator); + Payload responsePayload3 = TestChannelSupport.randomPayload(allocator); Payload releasedPayload = ByteBufPayload.create(Unpooled.EMPTY_BUFFER); releasedPayload.release(); @@ -817,15 +816,15 @@ public void shouldHaveNoLeaksOnReassemblyAndCancelRacing(String terminationMode) DefaultPayload.create(new byte[FRAME_LENGTH_MASK], new byte[FRAME_LENGTH_MASK]); for (int i = 0; i < RaceTestConstants.REPEATS; i++) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); ; final TestPublisher publisher = TestPublisher.createNoncompliant(DEFER_CANCELLATION, CLEANUP_ON_TERMINATE); final AssertSubscriber assertSubscriber = new AssertSubscriber<>(2); - Payload firstPayload = TestRequesterResponderSupport.genericPayload(allocator); + Payload firstPayload = TestChannelSupport.genericPayload(allocator); final RequestChannelResponderSubscriber requestOperator = new RequestChannelResponderSubscriber(1, Long.MAX_VALUE, firstPayload, activeStreams); @@ -833,9 +832,9 @@ public void shouldHaveNoLeaksOnReassemblyAndCancelRacing(String terminationMode) requestOperator.subscribe(assertSubscriber); int mtu = ThreadLocalRandom.current().nextInt(64, 256); - Payload responsePayload = TestRequesterResponderSupport.randomPayload(allocator); + Payload responsePayload = TestChannelSupport.randomPayload(allocator); ArrayList fragments = - TestRequesterResponderSupport.prepareFragments(allocator, mtu, responsePayload); + TestChannelSupport.prepareFragments(allocator, mtu, responsePayload); Payload releasedPayload1 = ByteBufPayload.create(new byte[0]); Payload releasedPayload2 = ByteBufPayload.create(new byte[0]); diff --git a/today-remoting/src/test/java/infra/remoting/core/RequestResponseRequesterMonoTest.java b/today-remoting/src/test/java/infra/remoting/core/RequestResponseRequesterMonoTests.java similarity index 90% rename from today-remoting/src/test/java/infra/remoting/core/RequestResponseRequesterMonoTest.java rename to today-remoting/src/test/java/infra/remoting/core/RequestResponseRequesterMonoTests.java index 3d3aa0b..f7d6490 100644 --- a/today-remoting/src/test/java/infra/remoting/core/RequestResponseRequesterMonoTest.java +++ b/today-remoting/src/test/java/infra/remoting/core/RequestResponseRequesterMonoTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -29,29 +28,29 @@ import java.util.function.Consumer; import java.util.stream.Stream; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import io.netty.util.IllegalReferenceCountException; import infra.remoting.FrameAssert; import infra.remoting.Payload; import infra.remoting.PayloadAssert; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; -import infra.remoting.exceptions.ApplicationErrorException; +import infra.remoting.error.ApplicationErrorException; import infra.remoting.frame.FrameType; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; import infra.remoting.util.ByteBufPayload; import infra.remoting.util.EmptyPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.util.CharsetUtil; +import io.netty.util.IllegalReferenceCountException; import reactor.core.Scannable; import reactor.test.StepVerifier; import static infra.remoting.core.FragmentationUtils.FRAME_OFFSET; import static infra.remoting.core.FragmentationUtils.FRAME_OFFSET_WITH_METADATA; import static infra.remoting.core.PayloadValidationUtils.INVALID_PAYLOAD_ERROR_MESSAGE; -import static infra.remoting.core.TestRequesterResponderSupport.genericPayload; +import static infra.remoting.core.TestChannelSupport.genericPayload; import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; -public class RequestResponseRequesterMonoTest { +public class RequestResponseRequesterMonoTests { @BeforeAll public static void setUp() { @@ -76,9 +75,9 @@ public static void setUp() { public void frameShouldBeSentOnSubscription( BiFunction, StepVerifier> transformer) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); final Payload payload = genericPayload(allocator); final RequestResponseRequesterMono requestResponseRequesterMono = @@ -169,7 +168,7 @@ public void frameShouldBeSentOnSubscription( () -> { final ByteBuf followingFrame = FragmentationUtils.encodeFirstFragment( - rrm.allocator, + rrm.channel.allocator, 64, FrameType.REQUEST_RESPONSE, 1, @@ -190,7 +189,7 @@ public void frameShouldBeSentOnSubscription( () -> { final ByteBuf followingFrame = FragmentationUtils.encodeFollowsFragment( - rrm.allocator, 64, 1, false, payload.metadata(), payload.data()); + rrm.channel.allocator, 64, 1, false, payload.metadata(), payload.data()); rrm.handleNext(followingFrame, true, false); followingFrame.release(); }) @@ -205,7 +204,7 @@ public void frameShouldBeSentOnSubscription( () -> { final ByteBuf followingFrame = FragmentationUtils.encodeFollowsFragment( - rrm.allocator, 64, 1, false, payload.metadata(), payload.data()); + rrm.channel.allocator, 64, 1, false, payload.metadata(), payload.data()); rrm.handleNext(followingFrame, true, false); followingFrame.release(); }) @@ -220,7 +219,7 @@ public void frameShouldBeSentOnSubscription( () -> { final ByteBuf followingFrame = FragmentationUtils.encodeFollowsFragment( - rrm.allocator, 64, 1, false, payload.metadata(), payload.data()); + rrm.channel.allocator, 64, 1, false, payload.metadata(), payload.data()); rrm.handleNext(followingFrame, false, false); followingFrame.release(); }) @@ -247,7 +246,7 @@ public void frameShouldBeSentOnSubscription( ByteBuf[] fragments = new ByteBuf[] { FragmentationUtils.encodeFirstFragment( - rrm.allocator, + rrm.channel.allocator, 64, FrameType.REQUEST_RESPONSE, 1, @@ -255,9 +254,9 @@ public void frameShouldBeSentOnSubscription( payload.metadata(), payload.data()), FragmentationUtils.encodeFollowsFragment( - rrm.allocator, 64, 1, false, payload.metadata(), payload.data()), + rrm.channel.allocator, 64, 1, false, payload.metadata(), payload.data()), FragmentationUtils.encodeFollowsFragment( - rrm.allocator, 64, 1, false, payload.metadata(), payload.data()) + rrm.channel.allocator, 64, 1, false, payload.metadata(), payload.data()) }; final StepVerifier stepVerifier = @@ -321,9 +320,9 @@ public void frameFragmentsShouldBeSentOnSubscription( BiFunction, StepVerifier> transformer) { final int mtu = 64; - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(mtu); + final TestChannelSupport activeStreams = TestChannelSupport.client(mtu); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); final byte[] metadata = new byte[65]; final byte[] data = new byte[129]; @@ -431,9 +430,9 @@ public void frameFragmentsShouldBeSentOnSubscription( */ @Test public void shouldBeNoOpsOnCancel() { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); final Payload payload = ByteBufPayload.create("testData", "testMetadata"); final RequestResponseRequesterMono requestResponseRequesterMono = @@ -467,9 +466,9 @@ public void shouldBeNoOpsOnCancel() { @MethodSource("shouldErrorOnIncorrectRefCntInGivenPayloadSource") public void shouldErrorOnIncorrectRefCntInGivenPayload( Consumer monoConsumer) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); ; final Payload payload = ByteBufPayload.create(""); payload.release(); @@ -511,9 +510,9 @@ public void shouldErrorOnIncorrectRefCntInGivenPayload( */ @Test public void shouldErrorOnIncorrectRefCntInGivenPayloadLatePhase() { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); ; final Payload payload = ByteBufPayload.create(""); @@ -546,9 +545,9 @@ public void shouldErrorOnIncorrectRefCntInGivenPayloadLatePhase() { @Test public void shouldErrorOnIncorrectRefCntInGivenPayloadLatePhaseWithFragmentation() { final int mtu = 64; - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(mtu); + final TestChannelSupport activeStreams = TestChannelSupport.client(mtu); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); ; final byte[] metadata = new byte[65]; final byte[] data = new byte[129]; @@ -586,9 +585,9 @@ public void shouldErrorOnIncorrectRefCntInGivenPayloadLatePhaseWithFragmentation @MethodSource("shouldErrorIfFragmentExitsAllowanceIfFragmentationDisabledSource") public void shouldErrorIfFragmentExitsAllowanceIfFragmentationDisabled( Consumer monoConsumer) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); ; final byte[] metadata = new byte[FRAME_LENGTH_MASK]; @@ -643,8 +642,8 @@ public void shouldErrorIfFragmentExitsAllowanceIfFragmentationDisabled( @ParameterizedTest @MethodSource("shouldErrorIfNoAvailabilitySource") public void shouldErrorIfNoAvailability(Consumer monoConsumer) { - final TestRequesterResponderSupport activeStreams = - TestRequesterResponderSupport.client(new RuntimeException("test")); + final TestChannelSupport activeStreams = + TestChannelSupport.client(new RuntimeException("test")); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); final Payload payload = genericPayload(allocator); @@ -686,7 +685,7 @@ static Stream> shouldErrorIfNoAvailabilit @Test public void checkName() { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); final Payload payload = genericPayload(allocator); diff --git a/today-remoting/src/test/java/infra/remoting/core/RequestStreamRequesterFluxTest.java b/today-remoting/src/test/java/infra/remoting/core/RequestStreamRequesterFluxTests.java similarity index 92% rename from today-remoting/src/test/java/infra/remoting/core/RequestStreamRequesterFluxTest.java rename to today-remoting/src/test/java/infra/remoting/core/RequestStreamRequesterFluxTests.java index 137e79a..77a0586 100644 --- a/today-remoting/src/test/java/infra/remoting/core/RequestStreamRequesterFluxTest.java +++ b/today-remoting/src/test/java/infra/remoting/core/RequestStreamRequesterFluxTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -30,20 +29,20 @@ import java.util.function.Consumer; import java.util.stream.Stream; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import io.netty.util.IllegalReferenceCountException; import infra.remoting.FrameAssert; import infra.remoting.Payload; import infra.remoting.PayloadAssert; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; -import infra.remoting.exceptions.ApplicationErrorException; +import infra.remoting.error.ApplicationErrorException; import infra.remoting.frame.FrameType; import infra.remoting.internal.subscriber.AssertSubscriber; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; import infra.remoting.util.ByteBufPayload; import infra.remoting.util.EmptyPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.util.CharsetUtil; +import io.netty.util.IllegalReferenceCountException; import reactor.core.Scannable; import reactor.test.StepVerifier; @@ -53,7 +52,7 @@ import static infra.remoting.core.PayloadValidationUtils.INVALID_PAYLOAD_ERROR_MESSAGE; import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; -public class RequestStreamRequesterFluxTest { +public class RequestStreamRequesterFluxTests { @BeforeAll public static void setUp() { @@ -81,10 +80,10 @@ public static void setUp() { */ @Test public void requestNFrameShouldBeSentOnSubscriptionAndThenSeparately() { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); - final Payload payload = TestRequesterResponderSupport.genericPayload(allocator); + final TestConnection sender = activeStreams.getConnection(); + final Payload payload = TestChannelSupport.genericPayload(allocator); final RequestStreamRequesterFlux requestStreamRequesterFlux = new RequestStreamRequesterFlux(payload, activeStreams); @@ -166,9 +165,9 @@ public void requestNFrameShouldBeSentOnSubscriptionAndThenSeparately() { stateAssert.hasSubscribedFlag().hasRequestN(Integer.MAX_VALUE).hasFirstFrameSentFlag(); int mtu = ThreadLocalRandom.current().nextInt(64, 256); - Payload randomPayload = TestRequesterResponderSupport.randomPayload(allocator); + Payload randomPayload = TestChannelSupport.randomPayload(allocator); ArrayList fragments = - TestRequesterResponderSupport.prepareFragments(allocator, mtu, randomPayload); + TestChannelSupport.prepareFragments(allocator, mtu, randomPayload); ByteBuf firstFragment = fragments.remove(0); requestStreamRequesterFlux.handleNext(firstFragment, true, false); firstFragment.release(); @@ -195,7 +194,7 @@ public void requestNFrameShouldBeSentOnSubscriptionAndThenSeparately() { .hasFirstFrameSentFlag() .hasNoReassemblingFlag(); - Payload finalRandomPayload = TestRequesterResponderSupport.randomPayload(allocator); + Payload finalRandomPayload = TestChannelSupport.randomPayload(allocator); requestStreamRequesterFlux.handlePayload(finalRandomPayload); requestStreamRequesterFlux.handleComplete(); @@ -228,10 +227,10 @@ public void requestNFrameShouldBeSentOnSubscriptionAndThenSeparately() { */ @Test public void requestNFrameShouldBeSentExactlyOnceIfItIsMaxAllowed() { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); - final Payload payload = TestRequesterResponderSupport.genericPayload(allocator); + final TestConnection sender = activeStreams.getConnection(); + final Payload payload = TestChannelSupport.genericPayload(allocator); final RequestStreamRequesterFlux requestStreamRequesterFlux = new RequestStreamRequesterFlux(payload, activeStreams); @@ -333,10 +332,10 @@ public void requestNFrameShouldBeSentExactlyOnceIfItIsMaxAllowed() { public void frameShouldBeSentOnFirstRequest( BiFunction, StepVerifier> transformer) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); - final Payload payload = TestRequesterResponderSupport.genericPayload(allocator); + final TestConnection sender = activeStreams.getConnection(); + final Payload payload = TestChannelSupport.genericPayload(allocator); final RequestStreamRequesterFlux requestStreamRequesterFlux = new RequestStreamRequesterFlux(payload, activeStreams); @@ -504,7 +503,7 @@ public void frameShouldBeSentOnFirstRequest( () -> { final ByteBuf followingFrame = FragmentationUtils.encodeFirstFragment( - rsf.allocator, + rsf.channel.allocator, 64, FrameType.NEXT, 1, @@ -526,7 +525,7 @@ public void frameShouldBeSentOnFirstRequest( () -> { final ByteBuf followingFrame = FragmentationUtils.encodeFollowsFragment( - rsf.allocator, 64, 1, false, payload.metadata(), payload.data()); + rsf.channel.allocator, 64, 1, false, payload.metadata(), payload.data()); rsf.handleNext(followingFrame, true, false); followingFrame.release(); }) @@ -542,7 +541,7 @@ public void frameShouldBeSentOnFirstRequest( () -> { final ByteBuf followingFrame = FragmentationUtils.encodeFollowsFragment( - rsf.allocator, 64, 1, false, payload.metadata(), payload.data()); + rsf.channel.allocator, 64, 1, false, payload.metadata(), payload.data()); rsf.handleNext(followingFrame, true, false); followingFrame.release(); }) @@ -567,7 +566,7 @@ public void frameShouldBeSentOnFirstRequest( () -> { final ByteBuf followingFrame = FragmentationUtils.encodeFollowsFragment( - rsf.allocator, 64, 1, false, payload.metadata(), payload.data()); + rsf.channel.allocator, 64, 1, false, payload.metadata(), payload.data()); rsf.handleNext(followingFrame, false, false); followingFrame.release(); }) @@ -632,7 +631,7 @@ public void frameShouldBeSentOnFirstRequest( ByteBuf[] fragments = new ByteBuf[] { FragmentationUtils.encodeFirstFragment( - rsf.allocator, + rsf.channel.allocator, 64, FrameType.NEXT, 1, @@ -640,9 +639,9 @@ public void frameShouldBeSentOnFirstRequest( payload.metadata(), payload.data()), FragmentationUtils.encodeFollowsFragment( - rsf.allocator, 64, 1, false, payload.metadata(), payload.data()), + rsf.channel.allocator, 64, 1, false, payload.metadata(), payload.data()), FragmentationUtils.encodeFollowsFragment( - rsf.allocator, 64, 1, false, payload.metadata(), payload.data()) + rsf.channel.allocator, 64, 1, false, payload.metadata(), payload.data()) }; final StepVerifier stepVerifier = @@ -765,9 +764,9 @@ public void frameFragmentsShouldBeSentOnFirstRequest( BiFunction, StepVerifier> transformer) { final int mtu = 64; - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(mtu); + final TestChannelSupport activeStreams = TestChannelSupport.client(mtu); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); final byte[] metadata = new byte[65]; final byte[] data = new byte[129]; @@ -883,9 +882,9 @@ public void frameFragmentsShouldBeSentOnFirstRequest( @MethodSource("shouldErrorOnIncorrectRefCntInGivenPayloadSource") public void shouldErrorOnIncorrectRefCntInGivenPayload( Consumer monoConsumer) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); final Payload payload = ByteBufPayload.create(""); payload.release(); @@ -926,9 +925,9 @@ public void shouldErrorOnIncorrectRefCntInGivenPayload( */ @Test public void shouldErrorOnIncorrectRefCntInGivenPayloadLatePhase() { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); final Payload payload = ByteBufPayload.create(""); @@ -971,9 +970,9 @@ public void shouldErrorOnIncorrectRefCntInGivenPayloadLatePhase() { @Test public void shouldErrorOnIncorrectRefCntInGivenPayloadLatePhaseWithFragmentation() { final int mtu = 64; - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(mtu); + final TestChannelSupport activeStreams = TestChannelSupport.client(mtu); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); final byte[] metadata = new byte[65]; final byte[] data = new byte[129]; @@ -1021,9 +1020,9 @@ public void shouldErrorOnIncorrectRefCntInGivenPayloadLatePhaseWithFragmentation @MethodSource("shouldErrorIfFragmentExitsAllowanceIfFragmentationDisabledSource") public void shouldErrorIfFragmentExitsAllowanceIfFragmentationDisabled( Consumer monoConsumer) { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); + final TestConnection sender = activeStreams.getConnection(); final byte[] metadata = new byte[FRAME_LENGTH_MASK]; final byte[] data = new byte[FRAME_LENGTH_MASK]; @@ -1077,17 +1076,17 @@ public void shouldErrorIfFragmentExitsAllowanceIfFragmentationDisabled( } /** - * Ensures that the interactions check and respect rsocket availability (such as leasing) and + * Ensures that the interactions check and respect channel availability (such as leasing) and * propagate an error to the final subscriber. No frame should be sent. Check should happens * exactly on the first request. */ @ParameterizedTest @MethodSource("shouldErrorIfNoAvailabilitySource") public void shouldErrorIfNoAvailability(Consumer monoConsumer) { - final TestRequesterResponderSupport activeStreams = - TestRequesterResponderSupport.client(new RuntimeException("test")); + final TestChannelSupport activeStreams = + TestChannelSupport.client(new RuntimeException("test")); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final Payload payload = TestRequesterResponderSupport.genericPayload(allocator); + final Payload payload = TestChannelSupport.genericPayload(allocator); final RequestStreamRequesterFlux requestStreamRequesterFlux = new RequestStreamRequesterFlux(payload, activeStreams); @@ -1134,10 +1133,10 @@ static Stream> shouldErrorIfNoAvailabilityS @Test public void failOnOverflow() { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final TestDuplexConnection sender = activeStreams.getDuplexConnection(); - final Payload payload = TestRequesterResponderSupport.genericPayload(allocator); + final TestConnection sender = activeStreams.getConnection(); + final Payload payload = TestChannelSupport.genericPayload(allocator); final RequestStreamRequesterFlux requestStreamRequesterFlux = new RequestStreamRequesterFlux(payload, activeStreams); @@ -1181,10 +1180,10 @@ public void failOnOverflow() { Assertions.assertThat(sender.isEmpty()).isTrue(); - Payload requestedPayload = TestRequesterResponderSupport.randomPayload(allocator); + Payload requestedPayload = TestChannelSupport.randomPayload(allocator); requestStreamRequesterFlux.handlePayload(requestedPayload); - Payload unrequestedPayload = TestRequesterResponderSupport.randomPayload(allocator); + Payload unrequestedPayload = TestChannelSupport.randomPayload(allocator); requestStreamRequesterFlux.handlePayload(unrequestedPayload); final ByteBuf cancelFrame = sender.awaitFrame(); @@ -1215,9 +1214,9 @@ public void failOnOverflow() { @Test public void checkName() { - final TestRequesterResponderSupport activeStreams = TestRequesterResponderSupport.client(); + final TestChannelSupport activeStreams = TestChannelSupport.client(); final LeaksTrackingByteBufAllocator allocator = activeStreams.getAllocator(); - final Payload payload = TestRequesterResponderSupport.genericPayload(allocator); + final Payload payload = TestChannelSupport.genericPayload(allocator); final RequestStreamRequesterFlux requestStreamRequesterFlux = new RequestStreamRequesterFlux(payload, activeStreams); diff --git a/today-remoting/src/test/java/infra/remoting/core/ChannelRequesterSubscribersTests.java b/today-remoting/src/test/java/infra/remoting/core/RequesterChannelSubscribersTests.java similarity index 84% rename from today-remoting/src/test/java/infra/remoting/core/ChannelRequesterSubscribersTests.java rename to today-remoting/src/test/java/infra/remoting/core/RequesterChannelSubscribersTests.java index 6355208..d89e122 100644 --- a/today-remoting/src/test/java/infra/remoting/core/ChannelRequesterSubscribersTests.java +++ b/today-remoting/src/test/java/infra/remoting/core/RequesterChannelSubscribersTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -32,19 +31,19 @@ import java.util.function.Function; import java.util.stream.Stream; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.util.CharsetUtil; -import infra.remoting.FrameAssert; import infra.remoting.Channel; +import infra.remoting.FrameAssert; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; import infra.remoting.frame.FrameHeaderCodec; import infra.remoting.frame.FrameType; import infra.remoting.frame.PayloadFrameCodec; import infra.remoting.frame.decoder.PayloadDecoder; import infra.remoting.internal.subscriber.AssertSubscriber; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; import infra.remoting.util.DefaultPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.util.CharsetUtil; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; @@ -52,7 +51,7 @@ import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; -class ChannelRequesterSubscribersTests { +class RequesterChannelSubscribersTests { private static final Set REQUEST_TYPES = new HashSet<>( @@ -65,7 +64,7 @@ class ChannelRequesterSubscribersTests { private LeaksTrackingByteBufAllocator allocator; private Channel channelRequester; - private TestDuplexConnection connection; + private TestConnection connection; protected Sinks.Empty thisClosedSink; protected Sinks.Empty otherClosedSink; @@ -77,11 +76,11 @@ void tearDownAndCheckNoLeaks() { @BeforeEach void setUp() { allocator = LeaksTrackingByteBufAllocator.instrument(ByteBufAllocator.DEFAULT); - connection = new TestDuplexConnection(allocator); + connection = new TestConnection(allocator); this.thisClosedSink = Sinks.empty(); this.otherClosedSink = Sinks.empty(); channelRequester = - new ChannelRequester( + new RequesterChannel( connection, PayloadDecoder.DEFAULT, StreamIdProvider.forClient(), @@ -186,24 +185,24 @@ static Stream allInteractions() { return Stream.of( Arguments.of( (Function>) - rSocket -> rSocket.fireAndForget(DefaultPayload.create("test")), + channel -> channel.fireAndForget(DefaultPayload.create("test")), FrameType.REQUEST_FNF), Arguments.of( (Function>) - rSocket -> rSocket.requestResponse(DefaultPayload.create("test")), + channel -> channel.requestResponse(DefaultPayload.create("test")), FrameType.REQUEST_RESPONSE), Arguments.of( (Function>) - rSocket -> rSocket.requestStream(DefaultPayload.create("test")), + channel -> channel.requestStream(DefaultPayload.create("test")), FrameType.REQUEST_STREAM), Arguments.of( (Function>) - rSocket -> rSocket.requestChannel(Mono.just(DefaultPayload.create("test"))), + channel -> channel.requestChannel(Mono.just(DefaultPayload.create("test"))), FrameType.REQUEST_CHANNEL), Arguments.of( (Function>) - rSocket -> - rSocket.metadataPush( + channel -> + channel.metadataPush( DefaultPayload.create(new byte[0], "test".getBytes(CharsetUtil.UTF_8))), FrameType.METADATA_PUSH)); } diff --git a/today-remoting/src/test/java/infra/remoting/core/ChannelRequesterTerminationTests.java b/today-remoting/src/test/java/infra/remoting/core/RequesterChannelTerminationTests.java similarity index 64% rename from today-remoting/src/test/java/infra/remoting/core/ChannelRequesterTerminationTests.java rename to today-remoting/src/test/java/infra/remoting/core/RequesterChannelTerminationTests.java index 2ed4edc..0172b7f 100644 --- a/today-remoting/src/test/java/infra/remoting/core/ChannelRequesterTerminationTests.java +++ b/today-remoting/src/test/java/infra/remoting/core/RequesterChannelTerminationTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -29,19 +28,19 @@ import java.util.Arrays; import java.util.function.Function; +import infra.remoting.Channel; import infra.remoting.FrameAssert; import infra.remoting.Payload; -import infra.remoting.Channel; -import infra.remoting.core.ChannelRequesterTests.ClientSocketRule; +import infra.remoting.core.RequesterChannelTests.ClientChannelRule; import infra.remoting.frame.FrameType; import infra.remoting.util.EmptyPayload; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; -public class ChannelRequesterTerminationTests { +public class RequesterChannelTerminationTests { - public final ClientSocketRule rule = new ClientSocketRule(); + public final ClientChannelRule rule = new ClientChannelRule(); @BeforeEach public void setup() { @@ -54,12 +53,12 @@ public void tearDownAndCheckNoLeaks() { } @ParameterizedTest - @MethodSource("rsocketInteractions") + @MethodSource("interactions") public void testCurrentStreamIsTerminatedOnConnectionClose( FrameType requestType, Function> interaction) { - ChannelRequester rSocket = rule.socket; + RequesterChannel channel = rule.channel; - StepVerifier.create(interaction.apply(rSocket)) + StepVerifier.create(interaction.apply(channel)) .then( () -> { FrameAssert.assertThat(rule.connection.pollFrame()).typeOf(requestType).hasNoLeaks(); @@ -70,18 +69,18 @@ public void testCurrentStreamIsTerminatedOnConnectionClose( } @ParameterizedTest - @MethodSource("rsocketInteractions") + @MethodSource("interactions") public void testSubsequentStreamIsTerminatedAfterConnectionClose( FrameType requestType, Function> interaction) { - ChannelRequester rSocket = rule.socket; + RequesterChannel channel = rule.channel; rule.connection.dispose(); - StepVerifier.create(interaction.apply(rSocket)) + StepVerifier.create(interaction.apply(channel)) .expectError(ClosedChannelException.class) .verify(Duration.ofSeconds(5)); } - public static Iterable rsocketInteractions() { + public static Iterable interactions() { EmptyPayload payload = EmptyPayload.INSTANCE; Arguments resp = @@ -89,8 +88,8 @@ public static Iterable rsocketInteractions() { FrameType.REQUEST_RESPONSE, new Function>() { @Override - public Mono apply(Channel rSocket) { - return rSocket.requestResponse(payload); + public Mono apply(Channel channel) { + return channel.requestResponse(payload); } @Override @@ -103,8 +102,8 @@ public String toString() { FrameType.REQUEST_STREAM, new Function>() { @Override - public Flux apply(Channel rSocket) { - return rSocket.requestStream(payload); + public Flux apply(Channel channel) { + return channel.requestStream(payload); } @Override @@ -113,12 +112,11 @@ public String toString() { } }); Arguments channel = - Arguments.of( - FrameType.REQUEST_CHANNEL, + Arguments.of(FrameType.REQUEST_CHANNEL, new Function>() { @Override - public Flux apply(Channel rSocket) { - return rSocket.requestChannel(Flux.never().startWith(payload)); + public Flux apply(Channel channel) { + return channel.requestChannel(Flux.never().startWith(payload)); } @Override diff --git a/today-remoting/src/test/java/infra/remoting/core/ChannelRequesterTests.java b/today-remoting/src/test/java/infra/remoting/core/RequesterChannelTests.java similarity index 87% rename from today-remoting/src/test/java/infra/remoting/core/ChannelRequesterTests.java rename to today-remoting/src/test/java/infra/remoting/core/RequesterChannelTests.java index dc61ab4..9694d11 100644 --- a/today-remoting/src/test/java/infra/remoting/core/ChannelRequesterTests.java +++ b/today-remoting/src/test/java/infra/remoting/core/RequesterChannelTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -44,23 +43,15 @@ import java.util.function.Function; import java.util.stream.Stream; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import io.netty.util.IllegalReferenceCountException; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.ReferenceCounted; +import infra.remoting.Channel; import infra.remoting.FrameAssert; import infra.remoting.Payload; import infra.remoting.PayloadAssert; -import infra.remoting.Channel; import infra.remoting.RaceTestConstants; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; -import infra.remoting.exceptions.ApplicationErrorException; -import infra.remoting.exceptions.CustomProtocolException; -import infra.remoting.exceptions.RejectedSetupException; +import infra.remoting.error.ApplicationErrorException; +import infra.remoting.error.CustomProtocolException; +import infra.remoting.error.RejectedSetupException; import infra.remoting.frame.CancelFrameCodec; import infra.remoting.frame.ErrorFrameCodec; import infra.remoting.frame.FrameHeaderCodec; @@ -78,6 +69,14 @@ import infra.remoting.util.ByteBufPayload; import infra.remoting.util.DefaultPayload; import infra.remoting.util.EmptyPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; +import io.netty.util.CharsetUtil; +import io.netty.util.IllegalReferenceCountException; +import io.netty.util.ReferenceCountUtil; +import io.netty.util.ReferenceCounted; import reactor.core.Scannable; import reactor.core.publisher.BaseSubscriber; import reactor.core.publisher.Flux; @@ -90,11 +89,11 @@ import static infra.remoting.core.PayloadValidationUtils.INVALID_PAYLOAD_ERROR_MESSAGE; import static infra.remoting.core.ReassemblyUtils.ILLEGAL_REASSEMBLED_PAYLOAD_SIZE; -import static infra.remoting.core.TestRequesterResponderSupport.fixedSizePayload; -import static infra.remoting.core.TestRequesterResponderSupport.genericPayload; -import static infra.remoting.core.TestRequesterResponderSupport.prepareFragments; -import static infra.remoting.core.TestRequesterResponderSupport.randomMetadataOnlyPayload; -import static infra.remoting.core.TestRequesterResponderSupport.randomPayload; +import static infra.remoting.core.TestChannelSupport.fixedSizePayload; +import static infra.remoting.core.TestChannelSupport.genericPayload; +import static infra.remoting.core.TestChannelSupport.prepareFragments; +import static infra.remoting.core.TestChannelSupport.randomMetadataOnlyPayload; +import static infra.remoting.core.TestChannelSupport.randomPayload; import static infra.remoting.frame.FrameHeaderCodec.frameType; import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; import static infra.remoting.frame.FrameType.CANCEL; @@ -109,15 +108,15 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; -public class ChannelRequesterTests { +public class RequesterChannelTests { - ClientSocketRule rule; + ClientChannelRule rule; @BeforeEach public void setUp() throws Throwable { Hooks.onNextDropped(ReferenceCountUtil::safeRelease); Hooks.onErrorDropped((t) -> { }); - rule = new ClientSocketRule(); + rule = new ClientChannelRule(); rule.init(); } @@ -130,16 +129,16 @@ public void tearDown() { @Test @Timeout(2_000) - public void testInvalidFrameOnStream0ShouldNotTerminateRSocket() { + public void testInvalidFrameOnStream0ShouldNotTerminateChannel() { rule.connection.addToReceivedBuffer(RequestNFrameCodec.encode(rule.alloc(), 0, 10)); - assertThat(rule.socket.isDisposed()).isFalse(); + assertThat(rule.channel.isDisposed()).isFalse(); rule.assertHasNoLeaks(); } @Test @Timeout(2_000) public void testStreamInitialN() { - Flux stream = rule.socket.requestStream(EmptyPayload.INSTANCE); + Flux stream = rule.channel.requestStream(EmptyPayload.INSTANCE); BaseSubscriber subscriber = new BaseSubscriber() { @@ -173,7 +172,7 @@ protected void hookOnSubscribe(Subscription subscription) { public void testHandleSetupException() { rule.connection.addToReceivedBuffer( ErrorFrameCodec.encode(rule.alloc(), 0, new RejectedSetupException("boom"))); - assertThatThrownBy(() -> rule.socket.onClose().block()) + assertThatThrownBy(() -> rule.channel.onClose().block()) .isInstanceOf(RejectedSetupException.class); rule.assertHasNoLeaks(); } @@ -182,7 +181,7 @@ public void testHandleSetupException() { @Timeout(2_000) public void testHandleApplicationException() { rule.connection.clearSendReceiveBuffers(); - Publisher response = rule.socket.requestResponse(EmptyPayload.INSTANCE); + Publisher response = rule.channel.requestResponse(EmptyPayload.INSTANCE); Subscriber responseSub = TestSubscriber.create(); response.subscribe(responseSub); @@ -203,7 +202,7 @@ public void testHandleApplicationException() { @Test @Timeout(2_000) public void testHandleValidFrame() { - Publisher response = rule.socket.requestResponse(EmptyPayload.INSTANCE); + Publisher response = rule.channel.requestResponse(EmptyPayload.INSTANCE); Subscriber sub = TestSubscriber.create(); response.subscribe(sub); @@ -220,7 +219,7 @@ public void testHandleValidFrame() { @Test @Timeout(2_000) public void testRequestReplyWithCancel() { - Mono response = rule.socket.requestResponse(EmptyPayload.INSTANCE); + Mono response = rule.channel.requestResponse(EmptyPayload.INSTANCE); try { response.block(Duration.ofMillis(100)); @@ -245,12 +244,12 @@ public void testRequestReplyWithCancel() { @Timeout(2_000) public void testRequestReplyErrorOnSend() { rule.connection.setAvailability(0); // Fails send - Mono response = rule.socket.requestResponse(EmptyPayload.INSTANCE); + Mono response = rule.channel.requestResponse(EmptyPayload.INSTANCE); Subscriber responseSub = TestSubscriber.create(10); response.subscribe(responseSub); this.rule - .socket + .channel .onClose() .as(StepVerifier::create) .expectComplete() @@ -268,7 +267,7 @@ public void testRequestReplyErrorOnSend() { public void testChannelRequestCancellation() { Sinks.Empty cancelled = Sinks.empty(); Flux request = Flux.never().doOnCancel(cancelled::tryEmitEmpty); - rule.socket.requestChannel(request).subscribe().dispose(); + rule.channel.requestChannel(request).subscribe().dispose(); Flux.firstWithSignal( cancelled.asMono(), Flux.error(new IllegalStateException("Channel request not cancelled")) @@ -283,7 +282,7 @@ public void testChannelRequestCancellation2() { Sinks.Empty cancelled = Sinks.empty(); Flux request = Flux.just(EmptyPayload.INSTANCE).repeat(259).doOnCancel(cancelled::tryEmitEmpty); - rule.socket.requestChannel(request).subscribe().dispose(); + rule.channel.requestChannel(request).subscribe().dispose(); Flux.firstWithSignal( cancelled.asMono(), Flux.error(new IllegalStateException("Channel request not cancelled")) @@ -298,7 +297,7 @@ public void testChannelRequestServerSideCancellation() { Sinks.One cancelled = Sinks.one(); Sinks.Many request = Sinks.many().unicast().onBackpressureBuffer(); request.tryEmitNext(EmptyPayload.INSTANCE); - rule.socket + rule.channel .requestChannel(request.asFlux()) .subscribe(cancelled::tryEmitValue, cancelled::tryEmitError, cancelled::tryEmitEmpty); int streamId = rule.getStreamIdForRequestType(REQUEST_CHANNEL); @@ -328,7 +327,7 @@ public void testCorrectFrameOrder() { @Override protected void hookOnSubscribe(Subscription subscription) { } }; - rule.socket + rule.channel .requestChannel( Flux.concat(Flux.just(0).delayUntil(i -> delayer.asMono()), Flux.range(1, 999)) .map(i -> DefaultPayload.create(i + ""))) @@ -365,7 +364,7 @@ public void shouldThrownExceptionIfGivenPayloadIsExitsSizeAllowanceWithNoFragmen ThreadLocalRandom.current().nextBytes(metadata); ThreadLocalRandom.current().nextBytes(data); StepVerifier.create( - generator.apply(rule.socket, DefaultPayload.create(data, metadata))) + generator.apply(rule.channel, DefaultPayload.create(data, metadata))) .expectSubscription() .expectErrorSatisfies( t -> @@ -394,7 +393,7 @@ public void shouldThrownExceptionIfGivenPayloadIsExitsSizeAllowanceWithNoFragmen assertThatThrownBy( () -> { final Publisher source = - generator.apply(rule.socket, DefaultPayload.create(data, metadata)); + generator.apply(rule.channel, DefaultPayload.create(data, metadata)); if (source instanceof Mono) { ((Mono) source).block(); @@ -414,7 +413,7 @@ public void shouldThrownExceptionIfGivenPayloadIsExitsSizeAllowanceWithNoFragmen public void shouldRejectCallOfNoMetadataPayload() { final ByteBuf data = rule.allocator.buffer(10); final Payload payload = ByteBufPayload.create(data); - StepVerifier.create(rule.socket.metadataPush(payload)) + StepVerifier.create(rule.channel.metadataPush(payload)) .expectSubscription() .expectErrorSatisfies( t -> @@ -431,7 +430,7 @@ public void shouldRejectCallOfNoMetadataPayloadBlocking() { final ByteBuf data = rule.allocator.buffer(10); final Payload payload = ByteBufPayload.create(data); - assertThatThrownBy(() -> rule.socket.metadataPush(payload).block()) + assertThatThrownBy(() -> rule.channel.metadataPush(payload).block()) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Metadata push should have metadata field present"); PayloadAssert.assertThat(payload).isReleased(); @@ -443,7 +442,7 @@ static Stream>> prepareCalls() { Channel::fireAndForget, Channel::requestResponse, Channel::requestStream, - (rSocket, payload) -> rSocket.requestChannel(Flux.just(payload)), + (channel, payload) -> channel.requestChannel(Flux.just(payload)), Channel::metadataPush); } @@ -458,7 +457,7 @@ static Stream>> prepareCalls() { ThreadLocalRandom.current().nextBytes(metadata); ThreadLocalRandom.current().nextBytes(data); StepVerifier.create( - rule.socket.requestChannel( + rule.channel.requestChannel( Flux.just(EmptyPayload.INSTANCE, DefaultPayload.create(data, metadata))), 0) .expectSubscription() @@ -486,10 +485,10 @@ static Stream>> prepareCalls() { @ParameterizedTest @MethodSource("racingCases") public void checkNoLeaksOnRacing( - Function> initiator, - BiConsumer, ClientSocketRule> runner) { + Function> initiator, + BiConsumer, ClientChannelRule> runner) { for (int i = 0; i < RaceTestConstants.REPEATS; i++) { - ClientSocketRule clientSocketRule = new ClientSocketRule(); + ClientChannelRule clientSocketRule = new ClientChannelRule(); clientSocketRule.init(); @@ -514,9 +513,9 @@ public void checkNoLeaksOnRacing( private static Stream racingCases() { return Stream.of( Arguments.of( - (Function>) - (rule) -> rule.socket.requestStream(EmptyPayload.INSTANCE), - (BiConsumer, ClientSocketRule>) + (Function>) + (rule) -> rule.channel.requestStream(EmptyPayload.INSTANCE), + (BiConsumer, ClientChannelRule>) (as, rule) -> { ByteBufAllocator allocator = rule.alloc(); ByteBuf metadata = allocator.buffer(); @@ -532,9 +531,9 @@ private static Stream racingCases() { RaceTestUtils.race(as::cancel, () -> rule.connection.addToReceivedBuffer(frame)); }), Arguments.of( - (Function>) - (rule) -> rule.socket.requestChannel(Flux.just(EmptyPayload.INSTANCE)), - (BiConsumer, ClientSocketRule>) + (Function>) + (rule) -> rule.channel.requestChannel(Flux.just(EmptyPayload.INSTANCE)), + (BiConsumer, ClientChannelRule>) (as, rule) -> { ByteBufAllocator allocator = rule.alloc(); ByteBuf metadata = allocator.buffer(); @@ -550,7 +549,7 @@ private static Stream racingCases() { RaceTestUtils.race(as::cancel, () -> rule.connection.addToReceivedBuffer(frame)); }), Arguments.of( - (Function>) + (Function>) (rule) -> { ByteBufAllocator allocator = rule.alloc(); ByteBuf metadata = allocator.buffer(); @@ -559,9 +558,9 @@ private static Stream racingCases() { data.writeCharSequence("data", CharsetUtil.UTF_8); final Payload payload = ByteBufPayload.create(data, metadata); - return rule.socket.requestStream(payload); + return rule.channel.requestStream(payload); }, - (BiConsumer, ClientSocketRule>) + (BiConsumer, ClientChannelRule>) (as, rule) -> { RaceTestUtils.race(() -> as.request(1), as::cancel); // ensures proper frames order @@ -589,10 +588,10 @@ private static Stream racingCases() { } }), Arguments.of( - (Function>) + (Function>) (rule) -> { ByteBufAllocator allocator = rule.alloc(); - return rule.socket.requestChannel( + return rule.channel.requestChannel( Flux.generate( () -> 1L, (index, sink) -> { @@ -606,7 +605,7 @@ private static Stream racingCases() { return ++index; })); }, - (BiConsumer, ClientSocketRule>) + (BiConsumer, ClientChannelRule>) (as, rule) -> { RaceTestUtils.race(() -> as.request(1), as::cancel); // ensures proper frames order @@ -664,9 +663,9 @@ private static Stream racingCases() { } }), Arguments.of( - (Function>) + (Function>) (rule) -> - rule.socket.requestChannel( + rule.channel.requestChannel( Flux.generate( () -> 1L, (index, sink) -> { @@ -678,7 +677,7 @@ private static Stream racingCases() { sink.next(payload); return ++index; })), - (BiConsumer, ClientSocketRule>) + (BiConsumer, ClientChannelRule>) (as, rule) -> { ByteBufAllocator allocator = rule.alloc(); as.request(1); @@ -690,9 +689,9 @@ private static Stream racingCases() { () -> rule.connection.addToReceivedBuffer(frame)); }), Arguments.of( - (Function>) + (Function>) (rule) -> - rule.socket.requestChannel( + rule.channel.requestChannel( Flux.generate( () -> 1L, (index, sink) -> { @@ -704,7 +703,7 @@ private static Stream racingCases() { sink.next(payload); return ++index; })), - (BiConsumer, ClientSocketRule>) + (BiConsumer, ClientChannelRule>) (as, rule) -> { ByteBufAllocator allocator = rule.alloc(); as.request(1); @@ -717,7 +716,7 @@ private static Stream racingCases() { () -> rule.connection.addToReceivedBuffer(frame)); }), Arguments.of( - (Function>) + (Function>) (rule) -> { ByteBuf data = rule.allocator.buffer(); data.writeCharSequence("testData", CharsetUtil.UTF_8); @@ -725,9 +724,9 @@ private static Stream racingCases() { ByteBuf metadata = rule.allocator.buffer(); metadata.writeCharSequence("testMetadata", CharsetUtil.UTF_8); Payload requestPayload = ByteBufPayload.create(data, metadata); - return rule.socket.requestResponse(requestPayload); + return rule.channel.requestResponse(requestPayload); }, - (BiConsumer, ClientSocketRule>) + (BiConsumer, ClientChannelRule>) (as, rule) -> { ByteBufAllocator allocator = rule.alloc(); ByteBuf metadata = allocator.buffer(); @@ -743,7 +742,7 @@ private static Stream racingCases() { RaceTestUtils.race(as::cancel, () -> rule.connection.addToReceivedBuffer(frame)); }), Arguments.of( - (Function>) + (Function>) (rule) -> { ByteBuf data = rule.allocator.buffer(); data.writeCharSequence("testData", CharsetUtil.UTF_8); @@ -751,9 +750,9 @@ private static Stream racingCases() { ByteBuf metadata = rule.allocator.buffer(); metadata.writeCharSequence("testMetadata", CharsetUtil.UTF_8); Payload requestPayload = ByteBufPayload.create(data, metadata); - return rule.socket.requestStream(requestPayload); + return rule.channel.requestStream(requestPayload); }, - (BiConsumer, ClientSocketRule>) + (BiConsumer, ClientChannelRule>) (as, rule) -> { ByteBufAllocator allocator = rule.alloc(); ByteBuf metadata = allocator.buffer(); @@ -775,7 +774,7 @@ public void simpleOnDiscardRequestChannelTest() { AssertSubscriber assertSubscriber = AssertSubscriber.create(1); Sinks.Many testPublisher = Sinks.many().unicast().onBackpressureBuffer(); - Flux payloadFlux = rule.socket.requestChannel(testPublisher.asFlux()); + Flux payloadFlux = rule.channel.requestChannel(testPublisher.asFlux()); payloadFlux.subscribe(assertSubscriber); @@ -802,7 +801,7 @@ public void simpleOnDiscardRequestChannelTest2() { AssertSubscriber assertSubscriber = AssertSubscriber.create(1); Sinks.Many testPublisher = Sinks.many().unicast().onBackpressureBuffer(); - Flux payloadFlux = rule.socket.requestChannel(testPublisher.asFlux()); + Flux payloadFlux = rule.channel.requestChannel(testPublisher.asFlux()); payloadFlux.subscribe(assertSubscriber); @@ -840,16 +839,16 @@ public void verifiesThatFrameWithNoMetadataHasDecodedCorrectlyIntoPayload( switch (frameType) { case REQUEST_FNF: response = - testPublisher.mono().flatMap(p -> rule.socket.fireAndForget(p)).then(Mono.empty()); + testPublisher.mono().flatMap(p -> rule.channel.fireAndForget(p)).then(Mono.empty()); break; case REQUEST_RESPONSE: - response = testPublisher.mono().flatMap(p -> rule.socket.requestResponse(p)); + response = testPublisher.mono().flatMap(p -> rule.channel.requestResponse(p)); break; case REQUEST_STREAM: - response = testPublisher.mono().flatMapMany(p -> rule.socket.requestStream(p)); + response = testPublisher.mono().flatMapMany(p -> rule.channel.requestStream(p)); break; case REQUEST_CHANNEL: - response = rule.socket.requestChannel(testPublisher.flux()); + response = rule.channel.requestChannel(testPublisher.flux()); break; default: throw new UnsupportedOperationException("illegal case"); @@ -927,7 +926,7 @@ static Stream encodeDecodePayloadCases() { @ParameterizedTest @MethodSource("refCntCases") public void ensureSendsErrorOnIllegalRefCntPayload( - BiFunction> sourceProducer) { + BiFunction> sourceProducer) { Payload invalidPayload = ByteBufPayload.create( ByteBufUtil.writeUtf8(rule.alloc(), "test"), @@ -941,12 +940,12 @@ public void ensureSendsErrorOnIllegalRefCntPayload( .verify(Duration.ofMillis(1000)); } - private static Stream>> refCntCases() { + private static Stream>> refCntCases() { return Stream.of( - (p, clientSocketRule) -> clientSocketRule.socket.fireAndForget(p), - (p, clientSocketRule) -> clientSocketRule.socket.requestResponse(p), - (p, clientSocketRule) -> clientSocketRule.socket.requestStream(p), - (p, clientSocketRule) -> clientSocketRule.socket.requestChannel(Mono.just(p)), + (p, clientSocketRule) -> clientSocketRule.channel.fireAndForget(p), + (p, clientSocketRule) -> clientSocketRule.channel.requestResponse(p), + (p, clientSocketRule) -> clientSocketRule.channel.requestStream(p), + (p, clientSocketRule) -> clientSocketRule.channel.requestChannel(Mono.just(p)), (p, clientSocketRule) -> { Flux.from(clientSocketRule.connection.getSentAsPublisher()) .filter(bb -> frameType(bb) == REQUEST_CHANNEL) @@ -959,17 +958,17 @@ private static Stream>> refCn bb.release(); }); - return clientSocketRule.socket.requestChannel(Flux.just(EmptyPayload.INSTANCE, p)); + return clientSocketRule.channel.requestChannel(Flux.just(EmptyPayload.INSTANCE, p)); }); } @Test public void ensuresThatNoOpsMustHappenUntilSubscriptionInCaseOfFnfCall() { Payload payload1 = ByteBufPayload.create("abc1"); - Mono fnf1 = rule.socket.fireAndForget(payload1); + Mono fnf1 = rule.channel.fireAndForget(payload1); Payload payload2 = ByteBufPayload.create("abc2"); - Mono fnf2 = rule.socket.fireAndForget(payload2); + Mono fnf2 = rule.channel.fireAndForget(payload2); assertThat(rule.connection.getSent()).isEmpty(); @@ -1011,7 +1010,7 @@ public void ensuresThatNoOpsMustHappenUntilSubscriptionInCaseOfFnfCall() { @ParameterizedTest @MethodSource("requestNInteractions") public void ensuresThatNoOpsMustHappenUntilFirstRequestN( - FrameType frameType, BiFunction> interaction) { + FrameType frameType, BiFunction> interaction) { Payload payload1 = ByteBufPayload.create("abc1"); Publisher interaction1 = interaction.apply(rule, payload1); @@ -1120,24 +1119,24 @@ private static Stream requestNInteractions() { return Stream.of( Arguments.of( REQUEST_RESPONSE, - (BiFunction>) - (rule, payload) -> rule.socket.requestResponse(payload)), + (BiFunction>) + (rule, payload) -> rule.channel.requestResponse(payload)), Arguments.of( REQUEST_STREAM, - (BiFunction>) - (rule, payload) -> rule.socket.requestStream(payload)), + (BiFunction>) + (rule, payload) -> rule.channel.requestStream(payload)), Arguments.of( REQUEST_CHANNEL, - (BiFunction>) - (rule, payload) -> rule.socket.requestChannel(Flux.just(payload)))); + (BiFunction>) + (rule, payload) -> rule.channel.requestChannel(Flux.just(payload)))); } @ParameterizedTest @MethodSource("streamRacingCases") @Disabled("Connection should take care of ordering if such is necessary") public void ensuresCorrectOrderOfStreamIdIssuingInCaseOfRacing( - BiFunction> interaction1, - BiFunction> interaction2, + BiFunction> interaction1, + BiFunction> interaction2, FrameType interactionType1, FrameType interactionType2) { Assumptions.assumeThat(interactionType1).isNotEqualTo(METADATA_PUSH); @@ -1161,27 +1160,27 @@ public void ensuresCorrectOrderOfStreamIdIssuingInCaseOfRacing( public static Stream streamRacingCases() { return Stream.of( Arguments.of( - (BiFunction>) - (r, p) -> r.socket.fireAndForget(p), - (BiFunction>) - (r, p) -> r.socket.requestResponse(p), + (BiFunction>) + (r, p) -> r.channel.fireAndForget(p), + (BiFunction>) + (r, p) -> r.channel.requestResponse(p), REQUEST_FNF, REQUEST_RESPONSE), Arguments.of( - (BiFunction>) - (r, p) -> r.socket.requestResponse(p), - (BiFunction>) - (r, p) -> r.socket.requestStream(p), + (BiFunction>) + (r, p) -> r.channel.requestResponse(p), + (BiFunction>) + (r, p) -> r.channel.requestStream(p), REQUEST_RESPONSE, REQUEST_STREAM), Arguments.of( - (BiFunction>) - (r, p) -> r.socket.requestStream(p), - (BiFunction>) + (BiFunction>) + (r, p) -> r.channel.requestStream(p), + (BiFunction>) (r, p) -> { AtomicBoolean subscribed = new AtomicBoolean(); Flux just = Flux.just(p).doOnSubscribe((__) -> subscribed.set(true)); - return r.socket + return r.channel .requestChannel(just) .doFinally( __ -> { @@ -1193,11 +1192,11 @@ public static Stream streamRacingCases() { REQUEST_STREAM, REQUEST_CHANNEL), Arguments.of( - (BiFunction>) + (BiFunction>) (r, p) -> { AtomicBoolean subscribed = new AtomicBoolean(); Flux just = Flux.just(p).doOnSubscribe((__) -> subscribed.set(true)); - return r.socket + return r.channel .requestChannel(just) .doFinally( __ -> { @@ -1206,15 +1205,15 @@ public static Stream streamRacingCases() { } }); }, - (BiFunction>) - (r, p) -> r.socket.fireAndForget(p), + (BiFunction>) + (r, p) -> r.channel.fireAndForget(p), REQUEST_CHANNEL, REQUEST_FNF), Arguments.of( - (BiFunction>) - (r, p) -> r.socket.metadataPush(p), - (BiFunction>) - (r, p) -> r.socket.fireAndForget(p), + (BiFunction>) + (r, p) -> r.channel.metadataPush(p), + (BiFunction>) + (r, p) -> r.channel.fireAndForget(p), METADATA_PUSH, REQUEST_FNF)); } @@ -1223,8 +1222,8 @@ public static Stream streamRacingCases() { @MethodSource("streamRacingCases") @SuppressWarnings({ "rawtypes", "unchecked" }) public void shouldTerminateAllStreamsIfThereRacingBetweenDisposeAndRequests( - BiFunction> interaction1, - BiFunction> interaction2, + BiFunction> interaction1, + BiFunction> interaction2, FrameType interactionType1, FrameType interactionType2) { for (int i = 1; i < RaceTestConstants.REPEATS; i++) { @@ -1235,7 +1234,7 @@ public void shouldTerminateAllStreamsIfThereRacingBetweenDisposeAndRequests( Publisher publisher1 = interaction1.apply(rule, payload1); Publisher publisher2 = interaction2.apply(rule, payload2); RaceTestUtils.race( - () -> rule.socket.dispose(), + () -> rule.channel.dispose(), () -> publisher1.subscribe(assertSubscriber1), () -> publisher2.subscribe(assertSubscriber2)); @@ -1279,7 +1278,7 @@ public void testWorkaround858() { ByteBuf buffer = rule.alloc().buffer(); buffer.writeCharSequence("test", CharsetUtil.UTF_8); - rule.socket.requestResponse(ByteBufPayload.create(buffer)).subscribe(); + rule.channel.requestResponse(ByteBufPayload.create(buffer)).subscribe(); rule.connection.addToReceivedBuffer( ErrorFrameCodec.encode(rule.alloc(), 1, new RuntimeException("test"))); @@ -1290,7 +1289,7 @@ public void testWorkaround858() { .matches(bb -> FrameHeaderCodec.frameType(bb) == REQUEST_RESPONSE) .matches(ByteBuf::release); - assertThat(rule.socket.isDisposed()).isFalse(); + assertThat(rule.channel.isDisposed()).isFalse(); rule.assertHasNoLeaks(); } @@ -1300,7 +1299,7 @@ public void testWorkaround858() { @MethodSource("requestNInteractions") void reassembleData( FrameType frameType, - BiFunction> requestFunction) { + BiFunction> requestFunction) { final int mtu = ThreadLocalRandom.current().nextInt(64, 256); final LeaksTrackingByteBufAllocator leaksTrackingByteBufAllocator = LeaksTrackingByteBufAllocator.instrument(ByteBufAllocator.DEFAULT); @@ -1337,7 +1336,7 @@ void reassembleData( @MethodSource("requestNInteractions") void reassembleMetadata( FrameType frameType, - BiFunction> requestFunction) { + BiFunction> requestFunction) { final int mtu = ThreadLocalRandom.current().nextInt(64, 256); final LeaksTrackingByteBufAllocator leaksTrackingByteBufAllocator = LeaksTrackingByteBufAllocator.instrument(ByteBufAllocator.DEFAULT); @@ -1374,7 +1373,7 @@ void reassembleMetadata( @MethodSource("requestNInteractions") public void errorTooBigPayload( FrameType frameType, - BiFunction> requestFunction) { + BiFunction> requestFunction) { final int mtu = ThreadLocalRandom.current().nextInt(64, 256); final int maxInboundPayloadSize = ThreadLocalRandom.current().nextInt(mtu + 1, 4096); final LeaksTrackingByteBufAllocator leaksTrackingByteBufAllocator = @@ -1408,7 +1407,7 @@ public void errorTooBigPayload( @MethodSource("requestNInteractions") public void errorFragmentTooSmall( FrameType frameType, - BiFunction> requestFunction) { + BiFunction> requestFunction) { final int mtu = 32; final LeaksTrackingByteBufAllocator leaksTrackingByteBufAllocator = LeaksTrackingByteBufAllocator.instrument(ByteBufAllocator.DEFAULT); @@ -1443,10 +1442,10 @@ public void testWorkaround959(String type) { final AssertSubscriber assertSubscriber = new AssertSubscriber<>(3); if (type.equals("stream")) { - rule.socket.requestStream(ByteBufPayload.create(buffer)).subscribe(assertSubscriber); + rule.channel.requestStream(ByteBufPayload.create(buffer)).subscribe(assertSubscriber); } else if (type.equals("channel")) { - rule.socket + rule.channel .requestChannel(Flux.just(ByteBufPayload.create(buffer))) .subscribe(assertSubscriber); } @@ -1469,7 +1468,7 @@ else if (type.equals("channel")) { assertThat(rule.connection.getSent()).allMatch(ByteBuf::release); - assertThat(rule.socket.isDisposed()).isFalse(); + assertThat(rule.channel.isDisposed()).isFalse(); assertSubscriber.values().forEach(ReferenceCountUtil::safeRelease); assertSubscriber.assertNoError(); @@ -1479,16 +1478,16 @@ else if (type.equals("channel")) { } } - public static class ClientSocketRule extends AbstractSocketRule { + public static class ClientChannelRule extends AbstractChannelRule { protected Sinks.Empty thisClosedSink; protected Sinks.Empty otherClosedSink; @Override - protected ChannelRequester newRSocket() { + protected RequesterChannel newChannel() { this.thisClosedSink = Sinks.empty(); this.otherClosedSink = Sinks.empty(); - return new ChannelRequester( + return new RequesterChannel( connection, PayloadDecoder.ZERO_COPY, StreamIdProvider.forClient(), diff --git a/today-remoting/src/test/java/infra/remoting/core/RequesterOperatorsRacingTest.java b/today-remoting/src/test/java/infra/remoting/core/RequesterOperatorsRacingTests.java similarity index 78% rename from today-remoting/src/test/java/infra/remoting/core/RequesterOperatorsRacingTest.java rename to today-remoting/src/test/java/infra/remoting/core/RequesterOperatorsRacingTests.java index 1ec5b7d..0203de5 100644 --- a/today-remoting/src/test/java/infra/remoting/core/RequesterOperatorsRacingTest.java +++ b/today-remoting/src/test/java/infra/remoting/core/RequesterOperatorsRacingTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -30,9 +29,6 @@ import java.util.function.Supplier; import java.util.stream.Stream; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.util.CharsetUtil; import infra.remoting.FrameAssert; import infra.remoting.Payload; import infra.remoting.RaceTestConstants; @@ -41,6 +37,9 @@ import infra.remoting.internal.subscriber.AssertSubscriber; import infra.remoting.plugins.TestRequestInterceptor; import infra.remoting.util.ByteBufPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.util.CharsetUtil; import reactor.core.CoreSubscriber; import reactor.core.publisher.Flux; import reactor.core.publisher.Hooks; @@ -55,13 +54,13 @@ import static infra.remoting.frame.FrameType.REQUEST_STREAM; @SuppressWarnings("ALL") -public class RequesterOperatorsRacingTest { +public class RequesterOperatorsRacingTests { interface Scenario { FrameType requestType(); Publisher requestOperator( - Supplier payloadsSupplier, RequesterResponderSupport requesterResponderSupport); + Supplier payloadsSupplier, ChannelSupport channelSupport); } static Stream scenarios() { @@ -75,8 +74,8 @@ public FrameType requestType() { @Override public Publisher requestOperator( Supplier payloadsSupplier, - RequesterResponderSupport requesterResponderSupport) { - return new MetadataPushRequesterMono(payloadsSupplier.get(), requesterResponderSupport); + ChannelSupport channelSupport) { + return new MetadataPushRequesterMono(payloadsSupplier.get(), channelSupport); } @Override @@ -93,9 +92,9 @@ public FrameType requestType() { @Override public Publisher requestOperator( Supplier payloadsSupplier, - RequesterResponderSupport requesterResponderSupport) { + ChannelSupport channelSupport) { return new FireAndForgetRequesterMono( - payloadsSupplier.get(), requesterResponderSupport); + payloadsSupplier.get(), channelSupport); } @Override @@ -112,9 +111,9 @@ public FrameType requestType() { @Override public Publisher requestOperator( Supplier payloadsSupplier, - RequesterResponderSupport requesterResponderSupport) { + ChannelSupport channelSupport) { return new RequestResponseRequesterMono( - payloadsSupplier.get(), requesterResponderSupport); + payloadsSupplier.get(), channelSupport); } @Override @@ -131,9 +130,9 @@ public FrameType requestType() { @Override public Publisher requestOperator( Supplier payloadsSupplier, - RequesterResponderSupport requesterResponderSupport) { + ChannelSupport channelSupport) { return new RequestStreamRequesterFlux( - payloadsSupplier.get(), requesterResponderSupport); + payloadsSupplier.get(), channelSupport); } @Override @@ -150,9 +149,9 @@ public FrameType requestType() { @Override public Publisher requestOperator( Supplier payloadsSupplier, - RequesterResponderSupport requesterResponderSupport) { + ChannelSupport channelSupport) { return new RequestChannelRequesterFlux( - Flux.generate(s -> s.next(payloadsSupplier.get())), requesterResponderSupport); + Flux.generate(s -> s.next(payloadsSupplier.get())), channelSupport); } @Override @@ -174,18 +173,18 @@ public String toString() { public void shouldSubscribeExactlyOnce(Scenario scenario) { for (int i = 0; i < RaceTestConstants.REPEATS; i++) { final TestRequestInterceptor testRequestInterceptor = new TestRequestInterceptor(); - final TestRequesterResponderSupport requesterResponderSupport = - TestRequesterResponderSupport.client(testRequestInterceptor); + final TestChannelSupport requesterResponderSupport = + TestChannelSupport.client(testRequestInterceptor); final Supplier payloadSupplier = () -> - TestRequesterResponderSupport.genericPayload( + TestChannelSupport.genericPayload( requesterResponderSupport.getAllocator()); final Publisher requestOperator = scenario.requestOperator(payloadSupplier, requesterResponderSupport); StepVerifier stepVerifier = - StepVerifier.create(requesterResponderSupport.getDuplexConnection().getSentAsPublisher()) + StepVerifier.create(requesterResponderSupport.getConnection().getSentAsPublisher()) .assertNext( frame -> { FrameAssert frameAssert = @@ -197,24 +196,24 @@ public void shouldSubscribeExactlyOnce(Scenario scenario) { frameAssert .hasStreamIdZero() .hasPayloadSize( - TestRequesterResponderSupport.METADATA_CONTENT.getBytes( + TestChannelSupport.METADATA_CONTENT.getBytes( CharsetUtil.UTF_8) .length) - .hasMetadata(TestRequesterResponderSupport.METADATA_CONTENT); + .hasMetadata(TestChannelSupport.METADATA_CONTENT); } else { frameAssert .hasClientSideStreamId() .hasStreamId(1) .hasPayloadSize( - TestRequesterResponderSupport.METADATA_CONTENT.getBytes( + TestChannelSupport.METADATA_CONTENT.getBytes( CharsetUtil.UTF_8) .length - + TestRequesterResponderSupport.DATA_CONTENT.getBytes( + + TestChannelSupport.DATA_CONTENT.getBytes( CharsetUtil.UTF_8) .length) - .hasMetadata(TestRequesterResponderSupport.METADATA_CONTENT) - .hasData(TestRequesterResponderSupport.DATA_CONTENT); + .hasMetadata(TestChannelSupport.METADATA_CONTENT) + .hasData(TestChannelSupport.DATA_CONTENT); } frameAssert.hasNoLeaks(); @@ -284,10 +283,10 @@ public void shouldSentRequestFrameOnceInCaseOfRequestRacing(Scenario scenario) { for (int i = 0; i < RaceTestConstants.REPEATS; i++) { final TestRequestInterceptor testRequestInterceptor = new TestRequestInterceptor(); - final TestRequesterResponderSupport activeStreams = - TestRequesterResponderSupport.client(testRequestInterceptor); + final TestChannelSupport activeStreams = + TestChannelSupport.client(testRequestInterceptor); final Supplier payloadSupplier = - () -> TestRequesterResponderSupport.genericPayload(activeStreams.getAllocator()); + () -> TestChannelSupport.genericPayload(activeStreams.getAllocator()); final Publisher requestOperator = (Publisher) scenario.requestOperator(payloadSupplier, activeStreams); @@ -299,11 +298,11 @@ public void shouldSentRequestFrameOnceInCaseOfRequestRacing(Scenario scenario) { RaceTestUtils.race(() -> assertSubscriber.request(1), () -> assertSubscriber.request(1)); - final ByteBuf sentFrame = activeStreams.getDuplexConnection().awaitFrame(); + final ByteBuf sentFrame = activeStreams.getConnection().awaitFrame(); if (scenario.requestType().hasInitialRequestN()) { if (RequestStreamFrameCodec.initialRequestN(sentFrame) == 1) { - FrameAssert.assertThat(activeStreams.getDuplexConnection().awaitFrame()) + FrameAssert.assertThat(activeStreams.getConnection().awaitFrame()) .isNotNull() .hasStreamId(1) .hasRequestN(1) @@ -318,11 +317,11 @@ public void shouldSentRequestFrameOnceInCaseOfRequestRacing(Scenario scenario) { FrameAssert.assertThat(sentFrame) .isNotNull() .hasPayloadSize( - TestRequesterResponderSupport.DATA_CONTENT.getBytes(CharsetUtil.UTF_8).length - + TestRequesterResponderSupport.METADATA_CONTENT.getBytes(CharsetUtil.UTF_8) + TestChannelSupport.DATA_CONTENT.getBytes(CharsetUtil.UTF_8).length + + TestChannelSupport.METADATA_CONTENT.getBytes(CharsetUtil.UTF_8) .length) - .hasMetadata(TestRequesterResponderSupport.METADATA_CONTENT) - .hasData(TestRequesterResponderSupport.DATA_CONTENT) + .hasMetadata(TestChannelSupport.METADATA_CONTENT) + .hasData(TestChannelSupport.DATA_CONTENT) .hasNoFragmentsFollow() .typeOf(scenario.requestType()) .hasClientSideStreamId() @@ -334,7 +333,7 @@ public void shouldSentRequestFrameOnceInCaseOfRequestRacing(Scenario scenario) { if (scenario.requestType() == REQUEST_CHANNEL) { ((CoreSubscriber) requestOperator).onComplete(); - FrameAssert.assertThat(activeStreams.getDuplexConnection().awaitFrame()) + FrameAssert.assertThat(activeStreams.getConnection().awaitFrame()) .typeOf(COMPLETE) .hasStreamId(1) .hasNoLeaks(); @@ -349,7 +348,7 @@ public void shouldSentRequestFrameOnceInCaseOfRequestRacing(Scenario scenario) { }); activeStreams.assertNoActiveStreams(); - Assertions.assertThat(activeStreams.getDuplexConnection().isEmpty()).isTrue(); + Assertions.assertThat(activeStreams.getConnection().isEmpty()).isTrue(); activeStreams.getAllocator().assertHasNoLeaks(); if (scenario.requestType() != METADATA_PUSH) { testRequestInterceptor @@ -372,10 +371,10 @@ public void shouldHaveNoLeaksOnReassemblyAndCancelRacing(Scenario scenario) { for (int i = 0; i < RaceTestConstants.REPEATS; i++) { final TestRequestInterceptor testRequestInterceptor = new TestRequestInterceptor(); - final TestRequesterResponderSupport activeStreams = - TestRequesterResponderSupport.client(testRequestInterceptor); + final TestChannelSupport activeStreams = + TestChannelSupport.client(testRequestInterceptor); final Supplier payloadSupplier = - () -> TestRequesterResponderSupport.genericPayload(activeStreams.getAllocator()); + () -> TestChannelSupport.genericPayload(activeStreams.getAllocator()); final Publisher requestOperator = (Publisher) scenario.requestOperator(payloadSupplier, activeStreams); @@ -384,15 +383,15 @@ public void shouldHaveNoLeaksOnReassemblyAndCancelRacing(Scenario scenario) { requestOperator.subscribe(assertSubscriber); - final ByteBuf sentFrame = activeStreams.getDuplexConnection().awaitFrame(); + final ByteBuf sentFrame = activeStreams.getConnection().awaitFrame(); FrameAssert.assertThat(sentFrame) .isNotNull() .hasPayloadSize( - TestRequesterResponderSupport.DATA_CONTENT.getBytes(CharsetUtil.UTF_8).length - + TestRequesterResponderSupport.METADATA_CONTENT.getBytes(CharsetUtil.UTF_8) + TestChannelSupport.DATA_CONTENT.getBytes(CharsetUtil.UTF_8).length + + TestChannelSupport.METADATA_CONTENT.getBytes(CharsetUtil.UTF_8) .length) - .hasMetadata(TestRequesterResponderSupport.METADATA_CONTENT) - .hasData(TestRequesterResponderSupport.DATA_CONTENT) + .hasMetadata(TestChannelSupport.METADATA_CONTENT) + .hasData(TestChannelSupport.DATA_CONTENT) .hasNoFragmentsFollow() .typeOf(scenario.requestType()) .hasClientSideStreamId() @@ -401,9 +400,9 @@ public void shouldHaveNoLeaksOnReassemblyAndCancelRacing(Scenario scenario) { int mtu = ThreadLocalRandom.current().nextInt(64, 256); Payload responsePayload = - TestRequesterResponderSupport.randomPayload(activeStreams.getAllocator()); + TestChannelSupport.randomPayload(activeStreams.getAllocator()); ArrayList fragments = - TestRequesterResponderSupport.prepareFragments( + TestChannelSupport.prepareFragments( activeStreams.getAllocator(), mtu, responsePayload); RaceTestUtils.race( assertSubscriber::cancel, @@ -435,12 +434,12 @@ public void shouldHaveNoLeaksOnReassemblyAndCancelRacing(Scenario scenario) { }); } - if (!activeStreams.getDuplexConnection().isEmpty()) { + if (!activeStreams.getConnection().isEmpty()) { if (scenario.requestType() != REQUEST_CHANNEL) { assertSubscriber.assertNotTerminated(); } - final ByteBuf cancellationFrame = activeStreams.getDuplexConnection().awaitFrame(); + final ByteBuf cancellationFrame = activeStreams.getConnection().awaitFrame(); FrameAssert.assertThat(cancellationFrame) .isNotNull() .typeOf(FrameType.CANCEL) @@ -464,7 +463,7 @@ public void shouldHaveNoLeaksOnReassemblyAndCancelRacing(Scenario scenario) { Assertions.assertThat(responsePayload.refCnt()).isZero(); activeStreams.assertNoActiveStreams(); - Assertions.assertThat(activeStreams.getDuplexConnection().isEmpty()).isTrue(); + Assertions.assertThat(activeStreams.getConnection().isEmpty()).isTrue(); activeStreams.getAllocator().assertHasNoLeaks(); } } @@ -481,10 +480,10 @@ public void shouldHaveNoLeaksOnNextAndCancelRacing(Scenario scenario) { for (int i = 0; i < RaceTestConstants.REPEATS; i++) { final TestRequestInterceptor testRequestInterceptor = new TestRequestInterceptor(); - final TestRequesterResponderSupport activeStreams = - TestRequesterResponderSupport.client(testRequestInterceptor); + final TestChannelSupport activeStreams = + TestChannelSupport.client(testRequestInterceptor); final Supplier payloadSupplier = - () -> TestRequesterResponderSupport.genericPayload(activeStreams.getAllocator()); + () -> TestChannelSupport.genericPayload(activeStreams.getAllocator()); final Publisher requestOperator = scenario.requestOperator(payloadSupplier, activeStreams); @@ -492,15 +491,15 @@ public void shouldHaveNoLeaksOnNextAndCancelRacing(Scenario scenario) { AssertSubscriber assertSubscriber = AssertSubscriber.create(); requestOperator.subscribe((AssertSubscriber) assertSubscriber); - final ByteBuf sentFrame = activeStreams.getDuplexConnection().awaitFrame(); + final ByteBuf sentFrame = activeStreams.getConnection().awaitFrame(); FrameAssert.assertThat(sentFrame) .isNotNull() .hasPayloadSize( - TestRequesterResponderSupport.DATA_CONTENT.getBytes(CharsetUtil.UTF_8).length - + TestRequesterResponderSupport.METADATA_CONTENT.getBytes(CharsetUtil.UTF_8) + TestChannelSupport.DATA_CONTENT.getBytes(CharsetUtil.UTF_8).length + + TestChannelSupport.METADATA_CONTENT.getBytes(CharsetUtil.UTF_8) .length) - .hasMetadata(TestRequesterResponderSupport.METADATA_CONTENT) - .hasData(TestRequesterResponderSupport.DATA_CONTENT) + .hasMetadata(TestChannelSupport.METADATA_CONTENT) + .hasData(TestChannelSupport.DATA_CONTENT) .hasNoFragmentsFollow() .typeOf(scenario.requestType()) .hasClientSideStreamId() @@ -515,9 +514,9 @@ public void shouldHaveNoLeaksOnNextAndCancelRacing(Scenario scenario) { Assertions.assertThat(response.refCnt()).isZero(); activeStreams.assertNoActiveStreams(); - final boolean isEmpty = activeStreams.getDuplexConnection().isEmpty(); + final boolean isEmpty = activeStreams.getConnection().isEmpty(); if (!isEmpty) { - final ByteBuf cancellationFrame = activeStreams.getDuplexConnection().awaitFrame(); + final ByteBuf cancellationFrame = activeStreams.getConnection().awaitFrame(); FrameAssert.assertThat(cancellationFrame) .isNotNull() .typeOf(FrameType.CANCEL) @@ -537,7 +536,7 @@ public void shouldHaveNoLeaksOnNextAndCancelRacing(Scenario scenario) { .expectOnComplete(1) .expectNothing(); } - Assertions.assertThat(activeStreams.getDuplexConnection().isEmpty()).isTrue(); + Assertions.assertThat(activeStreams.getConnection().isEmpty()).isTrue(); activeStreams.getAllocator().assertHasNoLeaks(); } } @@ -560,10 +559,10 @@ public void shouldHaveNoUnexpectedErrorDuringOnErrorAndCancelRacing(Scenario sce for (boolean withReassembly : withReassemblyOptions) { for (int i = 0; i < RaceTestConstants.REPEATS; i++) { final TestRequestInterceptor testRequestInterceptor = new TestRequestInterceptor(); - final TestRequesterResponderSupport activeStreams = - TestRequesterResponderSupport.client(testRequestInterceptor); + final TestChannelSupport activeStreams = + TestChannelSupport.client(testRequestInterceptor); final Supplier payloadSupplier = - () -> TestRequesterResponderSupport.genericPayload(activeStreams.getAllocator()); + () -> TestChannelSupport.genericPayload(activeStreams.getAllocator()); final Publisher requestOperator = scenario.requestOperator(payloadSupplier, activeStreams); @@ -590,15 +589,15 @@ else if (requestOperator instanceof RequestStreamRequesterFlux) { stateAssert.hasSubscribedFlag().hasRequestN(1).hasFirstFrameSentFlag(); - final ByteBuf sentFrame = activeStreams.getDuplexConnection().awaitFrame(); + final ByteBuf sentFrame = activeStreams.getConnection().awaitFrame(); FrameAssert.assertThat(sentFrame) .isNotNull() .hasPayloadSize( - TestRequesterResponderSupport.DATA_CONTENT.getBytes(CharsetUtil.UTF_8).length - + TestRequesterResponderSupport.METADATA_CONTENT.getBytes(CharsetUtil.UTF_8) + TestChannelSupport.DATA_CONTENT.getBytes(CharsetUtil.UTF_8).length + + TestChannelSupport.METADATA_CONTENT.getBytes(CharsetUtil.UTF_8) .length) - .hasMetadata(TestRequesterResponderSupport.METADATA_CONTENT) - .hasData(TestRequesterResponderSupport.DATA_CONTENT) + .hasMetadata(TestChannelSupport.METADATA_CONTENT) + .hasData(TestChannelSupport.DATA_CONTENT) .hasNoFragmentsFollow() .typeOf(scenario.requestType()) .hasClientSideStreamId() @@ -621,9 +620,9 @@ else if (requestOperator instanceof RequestStreamRequesterFlux) { activeStreams.assertNoActiveStreams(); stateAssert.isTerminated(); - final boolean isEmpty = activeStreams.getDuplexConnection().isEmpty(); + final boolean isEmpty = activeStreams.getConnection().isEmpty(); if (!isEmpty) { - final ByteBuf cancellationFrame = activeStreams.getDuplexConnection().awaitFrame(); + final ByteBuf cancellationFrame = activeStreams.getConnection().awaitFrame(); FrameAssert.assertThat(cancellationFrame) .isNotNull() .typeOf(FrameType.CANCEL) @@ -645,7 +644,7 @@ else if (requestOperator instanceof RequestStreamRequesterFlux) { assertSubscriber.assertTerminated().assertErrorMessage("test"); } - Assertions.assertThat(activeStreams.getDuplexConnection().isEmpty()).isTrue(); + Assertions.assertThat(activeStreams.getConnection().isEmpty()).isTrue(); stateAssert.isTerminated(); droppedErrors.clear(); @@ -682,10 +681,10 @@ public void shouldBeConsistentInCaseOfRacingOfCancellationAndRequest(Scenario sc .isIn(REQUEST_RESPONSE, REQUEST_STREAM, REQUEST_CHANNEL); for (int i = 0; i < RaceTestConstants.REPEATS; i++) { final TestRequestInterceptor testRequestInterceptor = new TestRequestInterceptor(); - final TestRequesterResponderSupport activeStreams = - TestRequesterResponderSupport.client(testRequestInterceptor); + final TestChannelSupport activeStreams = + TestChannelSupport.client(testRequestInterceptor); final Supplier payloadSupplier = - () -> TestRequesterResponderSupport.genericPayload(activeStreams.getAllocator()); + () -> TestChannelSupport.genericPayload(activeStreams.getAllocator()); final Publisher requestOperator = scenario.requestOperator(payloadSupplier, activeStreams); @@ -697,19 +696,19 @@ public void shouldBeConsistentInCaseOfRacingOfCancellationAndRequest(Scenario sc RaceTestUtils.race(() -> assertSubscriber.cancel(), () -> assertSubscriber.request(1)); - if (!activeStreams.getDuplexConnection().isEmpty()) { - final ByteBuf sentFrame = activeStreams.getDuplexConnection().awaitFrame(); + if (!activeStreams.getConnection().isEmpty()) { + final ByteBuf sentFrame = activeStreams.getConnection().awaitFrame(); FrameAssert.assertThat(sentFrame) .isNotNull() .typeOf(scenario.requestType()) - .hasMetadata(TestRequesterResponderSupport.METADATA_CONTENT) - .hasData(TestRequesterResponderSupport.DATA_CONTENT) + .hasMetadata(TestChannelSupport.METADATA_CONTENT) + .hasData(TestChannelSupport.DATA_CONTENT) .hasNoFragmentsFollow() .hasClientSideStreamId() .hasStreamId(1) .hasNoLeaks(); - final ByteBuf cancelFrame = activeStreams.getDuplexConnection().awaitFrame(); + final ByteBuf cancelFrame = activeStreams.getConnection().awaitFrame(); FrameAssert.assertThat(cancelFrame) .isNotNull() .typeOf(FrameType.CANCEL) @@ -728,7 +727,7 @@ public void shouldBeConsistentInCaseOfRacingOfCancellationAndRequest(Scenario sc Assertions.assertThat(response.refCnt()).isZero(); activeStreams.assertNoActiveStreams(); - Assertions.assertThat(activeStreams.getDuplexConnection().isEmpty()).isTrue(); + Assertions.assertThat(activeStreams.getConnection().isEmpty()).isTrue(); activeStreams.getAllocator().assertHasNoLeaks(); } } @@ -741,10 +740,10 @@ public void shouldSentCancelFrameExactlyOnce(Scenario scenario) { .isIn(REQUEST_RESPONSE, REQUEST_STREAM, REQUEST_CHANNEL); for (int i = 0; i < RaceTestConstants.REPEATS; i++) { final TestRequestInterceptor testRequestInterceptor = new TestRequestInterceptor(); - final TestRequesterResponderSupport activeStreams = - TestRequesterResponderSupport.client(testRequestInterceptor); + final TestChannelSupport activeStreams = + TestChannelSupport.client(testRequestInterceptor); final Supplier payloadSupplier = - () -> TestRequesterResponderSupport.genericPayload(activeStreams.getAllocator()); + () -> TestChannelSupport.genericPayload(activeStreams.getAllocator()); final Publisher requesterOperator = scenario.requestOperator(payloadSupplier, activeStreams); @@ -757,21 +756,21 @@ public void shouldSentCancelFrameExactlyOnce(Scenario scenario) { assertSubscriber.request(1); - final ByteBuf sentFrame = activeStreams.getDuplexConnection().awaitFrame(); + final ByteBuf sentFrame = activeStreams.getConnection().awaitFrame(); FrameAssert.assertThat(sentFrame) .isNotNull() .hasNoFragmentsFollow() .typeOf(scenario.requestType()) .hasClientSideStreamId() - .hasMetadata(TestRequesterResponderSupport.METADATA_CONTENT) - .hasData(TestRequesterResponderSupport.DATA_CONTENT) + .hasMetadata(TestChannelSupport.METADATA_CONTENT) + .hasData(TestChannelSupport.DATA_CONTENT) .hasStreamId(1) .hasNoLeaks(); RaceTestUtils.race( ((Subscription) requesterOperator)::cancel, ((Subscription) requesterOperator)::cancel); - final ByteBuf cancelFrame = activeStreams.getDuplexConnection().awaitFrame(); + final ByteBuf cancelFrame = activeStreams.getConnection().awaitFrame(); FrameAssert.assertThat(cancelFrame) .isNotNull() .typeOf(FrameType.CANCEL) @@ -794,7 +793,7 @@ public void shouldSentCancelFrameExactlyOnce(Scenario scenario) { assertSubscriber.assertNotTerminated(); activeStreams.assertNoActiveStreams(); - Assertions.assertThat(activeStreams.getDuplexConnection().isEmpty()).isTrue(); + Assertions.assertThat(activeStreams.getConnection().isEmpty()).isTrue(); activeStreams.getAllocator().assertHasNoLeaks(); } } diff --git a/today-remoting/src/test/java/infra/remoting/core/ResolvingOperatorTests.java b/today-remoting/src/test/java/infra/remoting/core/ResolvingOperatorTests.java index 59d6c70..a177c9c 100644 --- a/today-remoting/src/test/java/infra/remoting/core/ResolvingOperatorTests.java +++ b/today-remoting/src/test/java/infra/remoting/core/ResolvingOperatorTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; diff --git a/today-remoting/src/test/java/infra/remoting/core/ChannelResponderTests.java b/today-remoting/src/test/java/infra/remoting/core/ResponderChannelTests.java similarity index 94% rename from today-remoting/src/test/java/infra/remoting/core/ChannelResponderTests.java rename to today-remoting/src/test/java/infra/remoting/core/ResponderChannelTests.java index 6cc540a..27349dd 100644 --- a/today-remoting/src/test/java/infra/remoting/core/ChannelResponderTests.java +++ b/today-remoting/src/test/java/infra/remoting/core/ResponderChannelTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -38,16 +37,10 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.ReferenceCounted; +import infra.remoting.Channel; import infra.remoting.FrameAssert; import infra.remoting.Payload; import infra.remoting.PayloadAssert; -import infra.remoting.Channel; import infra.remoting.RaceTestConstants; import infra.remoting.frame.CancelFrameCodec; import infra.remoting.frame.ErrorFrameCodec; @@ -64,11 +57,17 @@ import infra.remoting.internal.subscriber.AssertSubscriber; import infra.remoting.plugins.RequestInterceptor; import infra.remoting.plugins.TestRequestInterceptor; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; import infra.remoting.test.util.TestSubscriber; import infra.remoting.util.ByteBufPayload; import infra.remoting.util.DefaultPayload; import infra.remoting.util.EmptyPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.Unpooled; +import io.netty.util.CharsetUtil; +import io.netty.util.ReferenceCountUtil; +import io.netty.util.ReferenceCounted; import reactor.core.CoreSubscriber; import reactor.core.publisher.BaseSubscriber; import reactor.core.publisher.Flux; @@ -82,11 +81,11 @@ import static infra.remoting.core.PayloadValidationUtils.INVALID_PAYLOAD_ERROR_MESSAGE; import static infra.remoting.core.ReassemblyUtils.ILLEGAL_REASSEMBLED_PAYLOAD_SIZE; -import static infra.remoting.core.TestRequesterResponderSupport.fixedSizePayload; -import static infra.remoting.core.TestRequesterResponderSupport.genericPayload; -import static infra.remoting.core.TestRequesterResponderSupport.prepareFragments; -import static infra.remoting.core.TestRequesterResponderSupport.randomMetadataOnlyPayload; -import static infra.remoting.core.TestRequesterResponderSupport.randomPayload; +import static infra.remoting.core.TestChannelSupport.fixedSizePayload; +import static infra.remoting.core.TestChannelSupport.genericPayload; +import static infra.remoting.core.TestChannelSupport.prepareFragments; +import static infra.remoting.core.TestChannelSupport.randomMetadataOnlyPayload; +import static infra.remoting.core.TestChannelSupport.randomPayload; import static infra.remoting.frame.FrameHeaderCodec.frameType; import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; import static infra.remoting.frame.FrameType.COMPLETE; @@ -100,15 +99,15 @@ import static infra.remoting.frame.FrameType.REQUEST_STREAM; import static org.assertj.core.api.Assertions.assertThat; -public class ChannelResponderTests { +public class ResponderChannelTests { - ServerSocketRule rule; + ServerChannelRule rule; @BeforeEach public void setUp() { Hooks.onNextDropped(ReferenceCountUtil::safeRelease); Hooks.onErrorDropped(t -> { }); - rule = new ServerSocketRule(); + rule = new ServerChannelRule(); rule.init(); } @@ -141,7 +140,7 @@ public void testHandleResponseFrameNoError() { final int streamId = 4; rule.connection.clearSendReceiveBuffers(); final TestPublisher testPublisher = TestPublisher.create(); - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Mono requestResponse(Payload payload) { @@ -172,7 +171,7 @@ public void testCancel() { ByteBufAllocator allocator = rule.alloc(); final int streamId = 4; final AtomicBoolean cancelled = new AtomicBoolean(); - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Mono requestResponse(Payload payload) { @@ -232,7 +231,7 @@ protected void hookOnSubscribe(Subscription subscription) { return Flux.just(payload).doOnCancel(() -> cancelled.set(true)); } }; - rule.setAcceptingSocket(acceptingSocket); + rule.setAcceptingChannel(acceptingSocket); final Runnable[] runnables = { () -> rule.sendRequest(streamId, FrameType.REQUEST_RESPONSE), @@ -268,7 +267,7 @@ public void checkNoLeaksOnRacingCancelFromRequestChannelAndNextFromUpstream() { AssertSubscriber assertSubscriber = AssertSubscriber.create(); final Sinks.One sink = Sinks.one(); - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Flux requestChannel(Publisher payloads) { @@ -328,7 +327,7 @@ public void checkNoLeaksOnRacingBetweenDownstreamCancelAndOnNextFromRequestChann FluxSink[] sinks = new FluxSink[1]; - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Flux requestChannel(Publisher payloads) { @@ -371,7 +370,7 @@ public void checkNoLeaksOnRacingBetweenDownstreamCancelAndOnNextFromRequestChann FluxSink[] sinks = new FluxSink[1]; - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Flux requestChannel(Publisher payloads) { @@ -414,7 +413,7 @@ public Flux requestChannel(Publisher payloads) { for (int i = 0; i < RaceTestConstants.REPEATS; i++) { FluxSink[] sinks = new FluxSink[1]; AssertSubscriber assertSubscriber = AssertSubscriber.create(); - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Flux requestChannel(Publisher payloads) { @@ -509,7 +508,7 @@ public void checkNoLeaksOnRacingBetweenDownstreamCancelAndOnNextFromRequestStrea for (int i = 0; i < RaceTestConstants.REPEATS; i++) { FluxSink[] sinks = new FluxSink[1]; - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Flux requestStream(Payload payload) { @@ -548,7 +547,7 @@ public void checkNoLeaksOnRacingBetweenDownstreamCancelAndOnNextFromRequestRespo for (int i = 0; i < RaceTestConstants.REPEATS; i++) { Operators.MonoSubscriber[] sources = new Operators.MonoSubscriber[1]; - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Mono requestResponse(Payload payload) { @@ -594,7 +593,7 @@ public void simpleDiscardRequestStreamTest() { ByteBufAllocator allocator = rule.alloc(); FluxSink[] sinks = new FluxSink[1]; - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Flux requestStream(Payload payload) { @@ -623,7 +622,7 @@ public Flux requestStream(Payload payload) { public void simpleDiscardRequestChannelTest() { ByteBufAllocator allocator = rule.alloc(); - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Flux requestChannel(Publisher payloads) { @@ -673,7 +672,7 @@ public void verifiesThatFrameWithNoMetadataHasDecodedCorrectlyIntoPayload( AssertSubscriber assertSubscriber = AssertSubscriber.create(framesCnt); TestPublisher testPublisher = TestPublisher.create(); - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Mono fireAndForget(Payload payload) { @@ -768,7 +767,7 @@ static Stream encodeDecodePayloadCases() { @ParameterizedTest @MethodSource("refCntCases") public void ensureSendsErrorOnIllegalRefCntPayload(FrameType frameType) { - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Mono requestResponse(Payload payload) { @@ -818,7 +817,7 @@ public void testWorkaround858() { TestPublisher testPublisher = TestPublisher.create(); - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Flux requestChannel(Publisher payloads) { @@ -840,7 +839,7 @@ public Flux requestChannel(Publisher payloads) { .matches(bb -> FrameHeaderCodec.frameType(bb) == REQUEST_N) .matches(ReferenceCounted::release); - assertThat(rule.socket.isDisposed()).isFalse(); + assertThat(rule.channel.isDisposed()).isFalse(); testPublisher.assertWasCancelled(); rule.assertHasNoLeaks(); @@ -855,7 +854,7 @@ static Stream requestCases() { @MethodSource("requestCases") void reassemblePayload(FrameType frameType) { AtomicReference receivedPayload = new AtomicReference<>(); - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Mono fireAndForget(Payload payload) { @@ -894,8 +893,8 @@ public Flux requestChannel(Publisher payloads) { if (frameType != REQUEST_FNF) { FrameAssert.assertThat(rule.connection.getSent().poll()) .typeOf(frameType == REQUEST_RESPONSE ? NEXT_COMPLETE : NEXT) - .hasData(TestRequesterResponderSupport.DATA_CONTENT) - .hasMetadata(TestRequesterResponderSupport.METADATA_CONTENT) + .hasData(TestChannelSupport.DATA_CONTENT) + .hasMetadata(TestChannelSupport.METADATA_CONTENT) .hasNoLeaks(); if (frameType != REQUEST_RESPONSE) { FrameAssert.assertThat(rule.connection.getSent().poll()).typeOf(COMPLETE).hasNoLeaks(); @@ -910,7 +909,7 @@ public Flux requestChannel(Publisher payloads) { @MethodSource("requestCases") void reassembleMetadataOnly(FrameType frameType) { AtomicReference receivedPayload = new AtomicReference<>(); - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Mono fireAndForget(Payload payload) { @@ -952,8 +951,8 @@ public Flux requestChannel(Publisher payloads) { if (frameType != REQUEST_FNF) { FrameAssert.assertThat(rule.connection.getSent().poll()) .typeOf(frameType == REQUEST_RESPONSE ? NEXT_COMPLETE : NEXT) - .hasData(TestRequesterResponderSupport.DATA_CONTENT) - .hasMetadata(TestRequesterResponderSupport.METADATA_CONTENT) + .hasData(TestChannelSupport.DATA_CONTENT) + .hasMetadata(TestChannelSupport.METADATA_CONTENT) .hasNoLeaks(); if (frameType != REQUEST_RESPONSE) { FrameAssert.assertThat(rule.connection.getSent().poll()).typeOf(COMPLETE).hasNoLeaks(); @@ -970,7 +969,7 @@ public void errorTooBigPayload(FrameType frameType) { final int maxInboundPayloadSize = ThreadLocalRandom.current().nextInt(mtu + 1, 4096); AtomicReference receivedPayload = new AtomicReference<>(); rule.setMaxInboundPayloadSize(maxInboundPayloadSize); - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Mono fireAndForget(Payload payload) { @@ -1021,7 +1020,7 @@ public Flux requestChannel(Publisher payloads) { public void errorFragmentTooSmall(FrameType frameType) { final int mtu = 32; AtomicReference receivedPayload = new AtomicReference<>(); - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Mono fireAndForget(Payload payload) { @@ -1071,7 +1070,7 @@ void receivingRequestOnStreamIdThaIsAlreadyInUseMUSTBeIgnored_ReassemblyCase( FrameType requestType) { AtomicReference receivedPayload = new AtomicReference<>(); final Sinks.Empty delayer = Sinks.empty(); - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override @@ -1141,7 +1140,7 @@ void receivingRequestOnStreamIdThaIsAlreadyInUseMUSTBeIgnored(FrameType requestT Assumptions.assumeThat(requestType).isNotEqualTo(REQUEST_FNF); AtomicReference receivedPayload = new AtomicReference<>(); final Sinks.One delayer = Sinks.one(); - rule.setAcceptingSocket( + rule.setAcceptingChannel( new Channel() { @Override public Mono requestResponse(Payload payload) { @@ -1182,16 +1181,16 @@ public Flux requestChannel(Publisher payloads) { rule.assertHasNoLeaks(); } - public static class ServerSocketRule extends AbstractSocketRule { + public static class ServerChannelRule extends AbstractChannelRule { - private Channel acceptingSocket; + private Channel acceptingChannel; private volatile int prefetch; private RequestInterceptor requestInterceptor; protected Sinks.Empty onCloseSink; @Override protected void doInit() { - acceptingSocket = + acceptingChannel = new Channel() { @Override public Mono requestResponse(Payload payload) { @@ -1201,9 +1200,9 @@ public Mono requestResponse(Payload payload) { super.doInit(); } - public void setAcceptingSocket(Channel acceptingSocket) { - this.acceptingSocket = acceptingSocket; - connection = new TestDuplexConnection(alloc()); + public void setAcceptingChannel(Channel acceptingSocket) { + this.acceptingChannel = acceptingSocket; + connection = new TestConnection(alloc()); connectSub = TestSubscriber.create(); this.prefetch = Integer.MAX_VALUE; super.doInit(); @@ -1214,20 +1213,20 @@ public void setRequestInterceptor(RequestInterceptor requestInterceptor) { super.doInit(); } - public void setAcceptingSocket(Channel acceptingSocket, int prefetch) { - this.acceptingSocket = acceptingSocket; - connection = new TestDuplexConnection(alloc()); + public void setAcceptingChannel(Channel channel, int prefetch) { + this.acceptingChannel = channel; + connection = new TestConnection(alloc()); connectSub = TestSubscriber.create(); this.prefetch = prefetch; super.doInit(); } @Override - protected ChannelResponder newRSocket() { + protected ResponderChannel newChannel() { onCloseSink = Sinks.empty(); - return new ChannelResponder( + return new ResponderChannel( connection, - acceptingSocket, + acceptingChannel, PayloadDecoder.ZERO_COPY, null, 0, diff --git a/today-remoting/src/test/java/infra/remoting/core/ResponderOperatorsCommonTest.java b/today-remoting/src/test/java/infra/remoting/core/ResponderOperatorsCommonTests.java similarity index 86% rename from today-remoting/src/test/java/infra/remoting/core/ResponderOperatorsCommonTest.java rename to today-remoting/src/test/java/infra/remoting/core/ResponderOperatorsCommonTests.java index 900f180..dd8cb21 100755 --- a/today-remoting/src/test/java/infra/remoting/core/ResponderOperatorsCommonTest.java +++ b/today-remoting/src/test/java/infra/remoting/core/ResponderOperatorsCommonTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -25,17 +24,17 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Stream; -import io.netty.buffer.ByteBuf; +import infra.remoting.Channel; import infra.remoting.FrameAssert; import infra.remoting.Payload; import infra.remoting.PayloadAssert; -import infra.remoting.Channel; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; import infra.remoting.frame.FrameType; import infra.remoting.internal.subscriber.AssertSubscriber; import infra.remoting.plugins.RequestInterceptor; import infra.remoting.plugins.TestRequestInterceptor; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; +import io.netty.buffer.ByteBuf; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.publisher.Operators; @@ -48,7 +47,7 @@ import static infra.remoting.frame.FrameType.REQUEST_RESPONSE; import static infra.remoting.frame.FrameType.REQUEST_STREAM; -public class ResponderOperatorsCommonTest { +public class ResponderOperatorsCommonTests { interface Scenario { FrameType requestType(); @@ -58,13 +57,13 @@ interface Scenario { ResponderFrameHandler responseOperator( long initialRequestN, Payload firstPayload, - TestRequesterResponderSupport streamManager, + TestChannelSupport streamManager, Channel handler); ResponderFrameHandler responseOperator( long initialRequestN, ByteBuf firstFragment, - TestRequesterResponderSupport streamManager, + TestChannelSupport streamManager, Channel handler); } @@ -85,7 +84,7 @@ public int maxElements() { public ResponderFrameHandler responseOperator( long initialRequestN, ByteBuf firstFragment, - TestRequesterResponderSupport streamManager, + TestChannelSupport streamManager, Channel handler) { int streamId = streamManager.getNextStreamId(); RequestResponseResponderSubscriber subscriber = @@ -105,7 +104,7 @@ public ResponderFrameHandler responseOperator( public ResponderFrameHandler responseOperator( long initialRequestN, Payload firstPayload, - TestRequesterResponderSupport streamManager, + TestChannelSupport streamManager, Channel handler) { int streamId = streamManager.getNextStreamId(); RequestResponseResponderSubscriber subscriber = @@ -140,7 +139,7 @@ public int maxElements() { public ResponderFrameHandler responseOperator( long initialRequestN, ByteBuf firstFragment, - TestRequesterResponderSupport streamManager, + TestChannelSupport streamManager, Channel handler) { int streamId = streamManager.getNextStreamId(); RequestStreamResponderSubscriber subscriber = @@ -160,7 +159,7 @@ public ResponderFrameHandler responseOperator( public ResponderFrameHandler responseOperator( long initialRequestN, Payload firstPayload, - TestRequesterResponderSupport streamManager, + TestChannelSupport streamManager, Channel handler) { int streamId = streamManager.getNextStreamId(); RequestStreamResponderSubscriber subscriber = @@ -195,7 +194,7 @@ public int maxElements() { public ResponderFrameHandler responseOperator( long initialRequestN, ByteBuf firstFragment, - TestRequesterResponderSupport streamManager, + TestChannelSupport streamManager, Channel handler) { int streamId = streamManager.getNextStreamId(); RequestChannelResponderSubscriber subscriber = @@ -215,7 +214,7 @@ public ResponderFrameHandler responseOperator( public ResponderFrameHandler responseOperator( long initialRequestN, Payload firstPayload, - TestRequesterResponderSupport streamManager, + TestChannelSupport streamManager, Channel handler) { int streamId = streamManager.getNextStreamId(); RequestChannelResponderSubscriber responderSubscriber = @@ -285,21 +284,21 @@ void shouldHandleRequest(Scenario scenario) { Assumptions.assumeThat(scenario.requestType()).isNotIn(REQUEST_FNF, METADATA_PUSH); TestRequestInterceptor testRequestInterceptor = new TestRequestInterceptor(); - TestRequesterResponderSupport testRequesterResponderSupport = - TestRequesterResponderSupport.client(testRequestInterceptor); + TestChannelSupport testRequesterResponderSupport = + TestChannelSupport.client(testRequestInterceptor); final LeaksTrackingByteBufAllocator allocator = testRequesterResponderSupport.getAllocator(); - final TestDuplexConnection sender = testRequesterResponderSupport.getDuplexConnection(); + final TestConnection sender = testRequesterResponderSupport.getConnection(); TestPublisher testPublisher = TestPublisher.create(); TestHandler testHandler = new TestHandler(testPublisher, new AssertSubscriber<>(0)); ResponderFrameHandler responderFrameHandler = scenario.responseOperator( Long.MAX_VALUE, - TestRequesterResponderSupport.genericPayload(allocator), + TestChannelSupport.genericPayload(allocator), testRequesterResponderSupport, testHandler); - Payload randomPayload = TestRequesterResponderSupport.randomPayload(allocator); + Payload randomPayload = TestChannelSupport.randomPayload(allocator); testPublisher.assertWasSubscribed(); testPublisher.next(randomPayload.retain()); testPublisher.complete(); @@ -353,17 +352,17 @@ void shouldHandleFragmentedRequest(Scenario scenario) { Assumptions.assumeThat(scenario.requestType()).isNotIn(REQUEST_FNF, METADATA_PUSH); TestRequestInterceptor testRequestInterceptor = new TestRequestInterceptor(); - TestRequesterResponderSupport testRequesterResponderSupport = - TestRequesterResponderSupport.client(testRequestInterceptor); + TestChannelSupport testRequesterResponderSupport = + TestChannelSupport.client(testRequestInterceptor); final LeaksTrackingByteBufAllocator allocator = testRequesterResponderSupport.getAllocator(); - final TestDuplexConnection sender = testRequesterResponderSupport.getDuplexConnection(); + final TestConnection sender = testRequesterResponderSupport.getConnection(); TestPublisher testPublisher = TestPublisher.create(); TestHandler testHandler = new TestHandler(testPublisher, new AssertSubscriber<>(0)); int mtu = ThreadLocalRandom.current().nextInt(64, 256); - Payload firstPayload = TestRequesterResponderSupport.randomPayload(allocator); + Payload firstPayload = TestChannelSupport.randomPayload(allocator); ArrayList fragments = - TestRequesterResponderSupport.prepareFragments(allocator, mtu, firstPayload); + TestChannelSupport.prepareFragments(allocator, mtu, firstPayload); ByteBuf firstFragment = fragments.remove(0); ResponderFrameHandler responderFrameHandler = @@ -381,7 +380,7 @@ void shouldHandleFragmentedRequest(Scenario scenario) { fragment.release(); } - Payload randomPayload = TestRequesterResponderSupport.randomPayload(allocator); + Payload randomPayload = TestChannelSupport.randomPayload(allocator); testPublisher.assertWasSubscribed(); testPublisher.next(randomPayload.retain()); testPublisher.complete(); @@ -435,16 +434,16 @@ void shouldHandleInterruptedFragmentation(Scenario scenario) { Assumptions.assumeThat(scenario.requestType()).isNotIn(REQUEST_FNF, METADATA_PUSH); final TestRequestInterceptor testRequestInterceptor = new TestRequestInterceptor(); - TestRequesterResponderSupport testRequesterResponderSupport = - TestRequesterResponderSupport.client(testRequestInterceptor); + TestChannelSupport testRequesterResponderSupport = + TestChannelSupport.client(testRequestInterceptor); final LeaksTrackingByteBufAllocator allocator = testRequesterResponderSupport.getAllocator(); TestPublisher testPublisher = TestPublisher.create(); TestHandler testHandler = new TestHandler(testPublisher, new AssertSubscriber<>(0)); int mtu = ThreadLocalRandom.current().nextInt(64, 256); - Payload firstPayload = TestRequesterResponderSupport.randomPayload(allocator); + Payload firstPayload = TestChannelSupport.randomPayload(allocator); ArrayList fragments = - TestRequesterResponderSupport.prepareFragments(allocator, mtu, firstPayload); + TestChannelSupport.prepareFragments(allocator, mtu, firstPayload); firstPayload.release(); ByteBuf firstFragment = fragments.remove(0); diff --git a/today-remoting/src/test/java/infra/remoting/core/SendUtilsTest.java b/today-remoting/src/test/java/infra/remoting/core/SendUtilsTests.java similarity index 56% rename from today-remoting/src/test/java/infra/remoting/core/SendUtilsTest.java rename to today-remoting/src/test/java/infra/remoting/core/SendUtilsTests.java index bb6d981..78bf185 100644 --- a/today-remoting/src/test/java/infra/remoting/core/SendUtilsTest.java +++ b/today-remoting/src/test/java/infra/remoting/core/SendUtilsTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -27,7 +26,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -public class SendUtilsTest { +public class SendUtilsTests { @Test void droppedElementsConsumerShouldAcceptOtherTypesThanReferenceCounted() { diff --git a/today-remoting/src/test/java/infra/remoting/core/SetupRejectionTest.java b/today-remoting/src/test/java/infra/remoting/core/SetupRejectionTests.java similarity index 77% rename from today-remoting/src/test/java/infra/remoting/core/SetupRejectionTest.java rename to today-remoting/src/test/java/infra/remoting/core/SetupRejectionTests.java index 74c73db..5837216 100644 --- a/today-remoting/src/test/java/infra/remoting/core/SetupRejectionTest.java +++ b/today-remoting/src/test/java/infra/remoting/core/SetupRejectionTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -20,25 +19,25 @@ import java.time.Duration; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; +import infra.remoting.Channel; +import infra.remoting.ChannelAcceptor; import infra.remoting.Closeable; +import infra.remoting.Connection; import infra.remoting.ConnectionSetupPayload; -import infra.remoting.DuplexConnection; import infra.remoting.Payload; -import infra.remoting.Channel; -import infra.remoting.ChannelAcceptor; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; -import infra.remoting.exceptions.Exceptions; -import infra.remoting.exceptions.RejectedSetupException; +import infra.remoting.error.Exceptions; +import infra.remoting.error.RejectedSetupException; import infra.remoting.frame.ErrorFrameCodec; import infra.remoting.frame.FrameHeaderCodec; import infra.remoting.frame.FrameType; import infra.remoting.frame.SetupFrameCodec; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; import infra.remoting.transport.ConnectionAcceptor; import infra.remoting.transport.ServerTransport; import infra.remoting.util.DefaultPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; import reactor.test.StepVerifier; @@ -46,7 +45,7 @@ import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; import static org.assertj.core.api.Assertions.assertThat; -public class SetupRejectionTest { +public class SetupRejectionTests { @Test void responderRejectSetup() { @@ -64,7 +63,7 @@ void responderRejectSetup() { sentFrame.release(); assertThat(errorMsg).isEqualTo(error.getMessage()); assertThat(error).isInstanceOf(RejectedSetupException.class); - Channel acceptorSender = acceptor.senderRSocket().block(); + Channel acceptorSender = acceptor.senderChannel().block(); assertThat(acceptorSender.isDisposed()).isTrue(); transport.allocator.assertHasNoLeaks(); } @@ -73,11 +72,11 @@ void responderRejectSetup() { void requesterStreamsTerminatedOnZeroErrorFrame() { LeaksTrackingByteBufAllocator allocator = LeaksTrackingByteBufAllocator.instrument(ByteBufAllocator.DEFAULT); - TestDuplexConnection conn = new TestDuplexConnection(allocator); + TestConnection conn = new TestConnection(allocator); Sinks.Empty onThisSideClosedSink = Sinks.empty(); - ChannelRequester rSocket = - new ChannelRequester( + RequesterChannel channel = + new RequesterChannel( conn, DefaultPayload::create, StreamIdProvider.forClient(), @@ -95,7 +94,7 @@ void requesterStreamsTerminatedOnZeroErrorFrame() { String errorMsg = "error"; StepVerifier.create( - rSocket + channel .requestResponse(DefaultPayload.create("test")) .doOnRequest( ignored -> @@ -108,7 +107,7 @@ void requesterStreamsTerminatedOnZeroErrorFrame() { err -> err instanceof RejectedSetupException && errorMsg.equals(err.getMessage())) .verify(Duration.ofSeconds(5)); - assertThat(rSocket.isDisposed()).isTrue(); + assertThat(channel.isDisposed()).isTrue(); allocator.assertHasNoLeaks(); } @@ -116,10 +115,10 @@ void requesterStreamsTerminatedOnZeroErrorFrame() { void requesterNewStreamsTerminatedAfterZeroErrorFrame() { LeaksTrackingByteBufAllocator allocator = LeaksTrackingByteBufAllocator.instrument(ByteBufAllocator.DEFAULT); - TestDuplexConnection conn = new TestDuplexConnection(allocator); + TestConnection conn = new TestConnection(allocator); Sinks.Empty onThisSideClosedSink = Sinks.empty(); - ChannelRequester rSocket = - new ChannelRequester( + RequesterChannel channel = + new RequesterChannel( conn, DefaultPayload::create, StreamIdProvider.forClient(), @@ -138,7 +137,7 @@ void requesterNewStreamsTerminatedAfterZeroErrorFrame() { ErrorFrameCodec.encode(ByteBufAllocator.DEFAULT, 0, new RejectedSetupException("error"))); StepVerifier.create( - rSocket + channel .requestResponse(DefaultPayload.create("test")) .delaySubscription(Duration.ofMillis(100))) .expectErrorMatches( @@ -149,7 +148,7 @@ void requesterNewStreamsTerminatedAfterZeroErrorFrame() { private static class RejectingAcceptor implements ChannelAcceptor { private final String errorMessage; - private final Sinks.Many senderRSockets = + private final Sinks.Many senderChannels = Sinks.many().unicast().onBackpressureBuffer(); public RejectingAcceptor(String errorMessage) { @@ -157,13 +156,13 @@ public RejectingAcceptor(String errorMessage) { } @Override - public Mono accept(ConnectionSetupPayload setup, Channel sendingSocket) { - senderRSockets.tryEmitNext(sendingSocket); + public Mono accept(ConnectionSetupPayload setup, Channel channel) { + senderChannels.tryEmitNext(channel); return Mono.error(new RuntimeException(errorMessage)); } - public Mono senderRSocket() { - return senderRSockets.asFlux().next(); + public Mono senderChannel() { + return senderChannels.asFlux().next(); } } @@ -171,7 +170,7 @@ private static class SingleConnectionTransport implements ServerTransport start(ConnectionAcceptor acceptor) { @@ -192,9 +191,9 @@ public void connect() { private static class TestCloseable implements Closeable { - private final DuplexConnection conn; + private final Connection conn; - TestCloseable(ConnectionAcceptor acceptor, DuplexConnection conn) { + TestCloseable(ConnectionAcceptor acceptor, Connection conn) { this.conn = conn; Mono.from(acceptor.accept(conn)).subscribe(notUsed -> { }, err -> conn.dispose()); } diff --git a/today-remoting/src/test/java/infra/remoting/core/ShouldHaveFlag.java b/today-remoting/src/test/java/infra/remoting/core/ShouldHaveFlag.java index 78ac868..3097f2f 100644 --- a/today-remoting/src/test/java/infra/remoting/core/ShouldHaveFlag.java +++ b/today-remoting/src/test/java/infra/remoting/core/ShouldHaveFlag.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; diff --git a/today-remoting/src/test/java/infra/remoting/core/ShouldNotHaveFlag.java b/today-remoting/src/test/java/infra/remoting/core/ShouldNotHaveFlag.java index 2dafae9..db11b63 100644 --- a/today-remoting/src/test/java/infra/remoting/core/ShouldNotHaveFlag.java +++ b/today-remoting/src/test/java/infra/remoting/core/ShouldNotHaveFlag.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; @@ -29,19 +28,18 @@ class ShouldNotHaveFlag extends BasicErrorMessageFactory { - static final Map FLAGS_NAMES = - new HashMap() { - { - put(StateUtils.UNSUBSCRIBED_STATE, "UNSUBSCRIBED"); - put(StateUtils.TERMINATED_STATE, "TERMINATED"); - put(SUBSCRIBED_FLAG, "SUBSCRIBED"); - put(StateUtils.REQUEST_MASK, "REQUESTED(%n)"); - put(StateUtils.FIRST_FRAME_SENT_FLAG, "FIRST_FRAME_SENT"); - put(StateUtils.REASSEMBLING_FLAG, "REASSEMBLING"); - put(StateUtils.INBOUND_TERMINATED_FLAG, "INBOUND_TERMINATED"); - put(StateUtils.OUTBOUND_TERMINATED_FLAG, "OUTBOUND_TERMINATED"); - } - }; + static final Map FLAGS_NAMES = new HashMap<>() { + { + put(StateUtils.UNSUBSCRIBED_STATE, "UNSUBSCRIBED"); + put(StateUtils.TERMINATED_STATE, "TERMINATED"); + put(SUBSCRIBED_FLAG, "SUBSCRIBED"); + put(StateUtils.REQUEST_MASK, "REQUESTED(%n)"); + put(StateUtils.FIRST_FRAME_SENT_FLAG, "FIRST_FRAME_SENT"); + put(StateUtils.REASSEMBLING_FLAG, "REASSEMBLING"); + put(StateUtils.INBOUND_TERMINATED_FLAG, "INBOUND_TERMINATED"); + put(StateUtils.OUTBOUND_TERMINATED_FLAG, "OUTBOUND_TERMINATED"); + } + }; static final String SHOULD_NOT_HAVE_FLAG = "Expected state\n\t%s\nto not have\n\t%s\nbut had\n\t[%s]"; @@ -59,7 +57,7 @@ static ErrorMessageFactory shouldNotHaveFlag(long currentState, long expectedFla long flag = 1L << 31; for (int i = 0; i < 33; i++, flag <<= 1) { if ((currentState & flag) == flag) { - if (stringBuilder.length() > 0) { + if (!stringBuilder.isEmpty()) { stringBuilder.append(", "); } stringBuilder.append(FLAGS_NAMES.get(flag)); @@ -67,7 +65,7 @@ static ErrorMessageFactory shouldNotHaveFlag(long currentState, long expectedFla } long requestN = extractRequestN(currentState); if (requestN > 0) { - if (stringBuilder.length() > 0) { + if (!stringBuilder.isEmpty()) { stringBuilder.append(", "); } stringBuilder.append(String.format(FLAGS_NAMES.get(REQUEST_MASK), requestN)); diff --git a/today-remoting/src/test/java/infra/remoting/core/StateAssert.java b/today-remoting/src/test/java/infra/remoting/core/StateAssert.java index 60e7b8d..1fabd5b 100644 --- a/today-remoting/src/test/java/infra/remoting/core/StateAssert.java +++ b/today-remoting/src/test/java/infra/remoting/core/StateAssert.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; diff --git a/today-remoting/src/test/java/infra/remoting/core/StreamIdProviderTest.java b/today-remoting/src/test/java/infra/remoting/core/StreamIdProviderTest.java index 8599227..e3c45b1 100644 --- a/today-remoting/src/test/java/infra/remoting/core/StreamIdProviderTest.java +++ b/today-remoting/src/test/java/infra/remoting/core/StreamIdProviderTest.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; diff --git a/today-remoting/src/test/java/infra/remoting/core/TestRequesterResponderSupport.java b/today-remoting/src/test/java/infra/remoting/core/TestChannelSupport.java similarity index 76% rename from today-remoting/src/test/java/infra/remoting/core/TestRequesterResponderSupport.java rename to today-remoting/src/test/java/infra/remoting/core/TestChannelSupport.java index 76d83ea..f1bb0b9 100644 --- a/today-remoting/src/test/java/infra/remoting/core/TestRequesterResponderSupport.java +++ b/today-remoting/src/test/java/infra/remoting/core/TestChannelSupport.java @@ -1,60 +1,59 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.core; import org.assertj.core.api.Assertions; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.concurrent.ThreadLocalRandom; -import infra.lang.Nullable; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import infra.remoting.DuplexConnection; -import infra.remoting.Payload; import infra.remoting.Channel; +import infra.remoting.Connection; +import infra.remoting.Payload; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; import infra.remoting.frame.FrameType; import infra.remoting.frame.decoder.PayloadDecoder; import infra.remoting.plugins.RequestInterceptor; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; import infra.remoting.util.ByteBufPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.Unpooled; +import io.netty.util.CharsetUtil; import reactor.core.Exceptions; import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; -final class TestRequesterResponderSupport extends RequesterResponderSupport implements Channel { +final class TestChannelSupport extends ChannelSupport implements Channel { static final String DATA_CONTENT = "testData"; static final String METADATA_CONTENT = "testMetadata"; final Throwable error; - TestRequesterResponderSupport(@Nullable Throwable error, StreamIdProvider streamIdProvider, DuplexConnection connection, + TestChannelSupport(@Nullable Throwable error, StreamIdProvider streamIdProvider, Connection connection, int mtu, int maxFrameLength, int maxInboundPayloadSize, @Nullable RequestInterceptor requestInterceptor) { super(mtu, maxFrameLength, maxInboundPayloadSize, PayloadDecoder.ZERO_COPY, connection, streamIdProvider, (__) -> requestInterceptor); this.error = error; } @Override - public TestDuplexConnection getDuplexConnection() { - return (TestDuplexConnection) super.getDuplexConnection(); + public TestConnection getConnection() { + return (TestConnection) super.getConnection(); } static Payload genericPayload(LeaksTrackingByteBufAllocator allocator) { @@ -166,10 +165,10 @@ public synchronized int addAndGetNextStreamId(FrameHandler frameHandler) { return nextStreamId; } - public static TestRequesterResponderSupport client( + public static TestChannelSupport client( @Nullable Throwable e, @Nullable RequestInterceptor requestInterceptor) { return client( - new TestDuplexConnection( + new TestConnection( LeaksTrackingByteBufAllocator.instrument(ByteBufAllocator.DEFAULT)), 0, FRAME_LENGTH_MASK, @@ -178,14 +177,14 @@ public static TestRequesterResponderSupport client( e); } - public static TestRequesterResponderSupport client(@Nullable Throwable e) { + public static TestChannelSupport client(@Nullable Throwable e) { return client(0, FRAME_LENGTH_MASK, Integer.MAX_VALUE, e); } - public static TestRequesterResponderSupport client( + public static TestChannelSupport client( int mtu, int maxFrameLength, int maxInboundPayloadSize, @Nullable Throwable e) { return client( - new TestDuplexConnection( + new TestConnection( LeaksTrackingByteBufAllocator.instrument(ByteBufAllocator.DEFAULT)), mtu, maxFrameLength, @@ -194,16 +193,16 @@ public static TestRequesterResponderSupport client( e); } - public static TestRequesterResponderSupport client( - TestDuplexConnection duplexConnection, + public static TestChannelSupport client( + TestConnection duplexConnection, int mtu, int maxFrameLength, int maxInboundPayloadSize) { return client(duplexConnection, mtu, maxFrameLength, maxInboundPayloadSize, null); } - public static TestRequesterResponderSupport client( - TestDuplexConnection duplexConnection, + public static TestChannelSupport client( + TestConnection duplexConnection, int mtu, int maxFrameLength, int maxInboundPayloadSize, @@ -212,14 +211,14 @@ public static TestRequesterResponderSupport client( duplexConnection, mtu, maxFrameLength, maxInboundPayloadSize, requestInterceptor, null); } - public static TestRequesterResponderSupport client( - TestDuplexConnection duplexConnection, + public static TestChannelSupport client( + TestConnection duplexConnection, int mtu, int maxFrameLength, int maxInboundPayloadSize, @Nullable RequestInterceptor requestInterceptor, @Nullable Throwable e) { - return new TestRequesterResponderSupport( + return new TestChannelSupport( e, StreamIdProvider.forClient(), duplexConnection, @@ -229,26 +228,26 @@ public static TestRequesterResponderSupport client( requestInterceptor); } - public static TestRequesterResponderSupport client( + public static TestChannelSupport client( int mtu, int maxFrameLength, int maxInboundPayloadSize) { return client(mtu, maxFrameLength, maxInboundPayloadSize, null); } - public static TestRequesterResponderSupport client(int mtu, int maxFrameLength) { + public static TestChannelSupport client(int mtu, int maxFrameLength) { return client(mtu, maxFrameLength, Integer.MAX_VALUE); } - public static TestRequesterResponderSupport client(int mtu) { + public static TestChannelSupport client(int mtu) { return client(mtu, FRAME_LENGTH_MASK); } - public static TestRequesterResponderSupport client() { + public static TestChannelSupport client() { return client(0); } - public static TestRequesterResponderSupport client(RequestInterceptor requestInterceptor) { + public static TestChannelSupport client(RequestInterceptor requestInterceptor) { return client( - new TestDuplexConnection( + new TestConnection( LeaksTrackingByteBufAllocator.instrument(ByteBufAllocator.DEFAULT)), 0, FRAME_LENGTH_MASK, @@ -256,12 +255,12 @@ public static TestRequesterResponderSupport client(RequestInterceptor requestInt requestInterceptor); } - public TestRequesterResponderSupport assertNoActiveStreams() { + public TestChannelSupport assertNoActiveStreams() { Assertions.assertThat(activeStreams).isEmpty(); return this; } - public TestRequesterResponderSupport assertHasStream(int i, FrameHandler stream) { + public TestChannelSupport assertHasStream(int i, FrameHandler stream) { Assertions.assertThat(activeStreams).containsEntry(i, stream); return this; } diff --git a/today-remoting/src/test/java/infra/remoting/error/ApplicationErrorExceptionTests.java b/today-remoting/src/test/java/infra/remoting/error/ApplicationErrorExceptionTests.java new file mode 100644 index 0000000..27f9f5a --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/error/ApplicationErrorExceptionTests.java @@ -0,0 +1,36 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.error; + +final class ApplicationErrorExceptionTests + implements ChannelExceptionTests { + + @Override + public ApplicationErrorException getException(String message) { + return new ApplicationErrorException(message); + } + + @Override + public ApplicationErrorException getException(String message, Throwable cause) { + return new ApplicationErrorException(message, cause); + } + + @Override + public int getSpecifiedErrorCode() { + return 0x00000201; + } +} diff --git a/today-remoting/src/test/java/infra/remoting/error/CanceledExceptionTests.java b/today-remoting/src/test/java/infra/remoting/error/CanceledExceptionTests.java new file mode 100644 index 0000000..0683954 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/error/CanceledExceptionTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.error; + +final class CanceledExceptionTests implements ChannelExceptionTests { + + @Override + public CanceledException getException(String message) { + return new CanceledException(message); + } + + @Override + public CanceledException getException(String message, Throwable cause) { + return new CanceledException(message, cause); + } + + @Override + public int getSpecifiedErrorCode() { + return 0x00000203; + } +} diff --git a/today-remoting/src/test/java/infra/remoting/exceptions/ChannelExceptionTests.java b/today-remoting/src/test/java/infra/remoting/error/ChannelExceptionTests.java similarity index 57% rename from today-remoting/src/test/java/infra/remoting/exceptions/ChannelExceptionTests.java rename to today-remoting/src/test/java/infra/remoting/error/ChannelExceptionTests.java index 1beb7cb..7f927cc 100644 --- a/today-remoting/src/test/java/infra/remoting/exceptions/ChannelExceptionTests.java +++ b/today-remoting/src/test/java/infra/remoting/error/ChannelExceptionTests.java @@ -1,21 +1,20 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.exceptions; +package infra.remoting.error; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/today-remoting/src/test/java/infra/remoting/error/ConnectionCloseExceptionTests.java b/today-remoting/src/test/java/infra/remoting/error/ConnectionCloseExceptionTests.java new file mode 100644 index 0000000..bb2de45 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/error/ConnectionCloseExceptionTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.error; + +final class ConnectionCloseExceptionTests implements ChannelExceptionTests { + + @Override + public ConnectionCloseException getException(String message) { + return new ConnectionCloseException(message); + } + + @Override + public ConnectionCloseException getException(String message, Throwable cause) { + return new ConnectionCloseException(message, cause); + } + + @Override + public int getSpecifiedErrorCode() { + return 0x00000102; + } +} diff --git a/today-remoting/src/test/java/infra/remoting/error/ConnectionErrorExceptionTests.java b/today-remoting/src/test/java/infra/remoting/error/ConnectionErrorExceptionTests.java new file mode 100644 index 0000000..2ec7e49 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/error/ConnectionErrorExceptionTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.error; + +final class ConnectionErrorExceptionTests implements ChannelExceptionTests { + + @Override + public ConnectionErrorException getException(String message) { + return new ConnectionErrorException(message); + } + + @Override + public ConnectionErrorException getException(String message, Throwable cause) { + return new ConnectionErrorException(message, cause); + } + + @Override + public int getSpecifiedErrorCode() { + return 0x00000101; + } +} diff --git a/today-remoting/src/test/java/infra/remoting/exceptions/ExceptionsTests.java b/today-remoting/src/test/java/infra/remoting/error/ExceptionsTests.java similarity index 92% rename from today-remoting/src/test/java/infra/remoting/exceptions/ExceptionsTests.java rename to today-remoting/src/test/java/infra/remoting/error/ExceptionsTests.java index 1a62608..5117009 100644 --- a/today-remoting/src/test/java/infra/remoting/exceptions/ExceptionsTests.java +++ b/today-remoting/src/test/java/infra/remoting/error/ExceptionsTests.java @@ -1,31 +1,30 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.exceptions; +package infra.remoting.error; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.concurrent.ThreadLocalRandom; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.UnpooledByteBufAllocator; import infra.remoting.RaceTestConstants; import infra.remoting.frame.ErrorFrameCodec; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.UnpooledByteBufAllocator; import static infra.remoting.frame.ErrorFrameCodec.APPLICATION_ERROR; import static infra.remoting.frame.ErrorFrameCodec.CANCELED; @@ -256,9 +255,8 @@ void fromUnsupportedSetupException() { } } - @DisplayName("from returns CustomRSocketException") @Test - void fromCustomRSocketException() { + void fromCustomChannelException() { for (int i = 0; i < RaceTestConstants.REPEATS; i++) { int randomCode = ThreadLocalRandom.current().nextBoolean() diff --git a/today-remoting/src/test/java/infra/remoting/error/InvalidExceptionTests.java b/today-remoting/src/test/java/infra/remoting/error/InvalidExceptionTests.java new file mode 100644 index 0000000..80478d7 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/error/InvalidExceptionTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.error; + +final class InvalidExceptionTests implements ChannelExceptionTests { + + @Override + public InvalidException getException(String message) { + return new InvalidException(message); + } + + @Override + public InvalidException getException(String message, Throwable cause) { + return new InvalidException(message, cause); + } + + @Override + public int getSpecifiedErrorCode() { + return 0x00000204; + } +} diff --git a/today-remoting/src/test/java/infra/remoting/error/InvalidSetupExceptionTests.java b/today-remoting/src/test/java/infra/remoting/error/InvalidSetupExceptionTests.java new file mode 100644 index 0000000..99db7f5 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/error/InvalidSetupExceptionTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.error; + +final class InvalidSetupExceptionTests implements ChannelExceptionTests { + + @Override + public InvalidSetupException getException(String message) { + return new InvalidSetupException(message); + } + + @Override + public InvalidSetupException getException(String message, Throwable cause) { + return new InvalidSetupException(message, cause); + } + + @Override + public int getSpecifiedErrorCode() { + return 0x00000001; + } +} diff --git a/today-remoting/src/test/java/infra/remoting/error/RejectedExceptionTests.java b/today-remoting/src/test/java/infra/remoting/error/RejectedExceptionTests.java new file mode 100644 index 0000000..652313a --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/error/RejectedExceptionTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.error; + +final class RejectedExceptionTests implements ChannelExceptionTests { + + @Override + public RejectedException getException(String message) { + return new RejectedException(message); + } + + @Override + public RejectedException getException(String message, Throwable cause) { + return new RejectedException(message, cause); + } + + @Override + public int getSpecifiedErrorCode() { + return 0x00000202; + } +} diff --git a/today-remoting/src/test/java/infra/remoting/error/RejectedResumeExceptionTests.java b/today-remoting/src/test/java/infra/remoting/error/RejectedResumeExceptionTests.java new file mode 100644 index 0000000..62053e5 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/error/RejectedResumeExceptionTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.error; + +final class RejectedResumeExceptionTests implements ChannelExceptionTests { + + @Override + public RejectedResumeException getException(String message) { + return new RejectedResumeException(message); + } + + @Override + public RejectedResumeException getException(String message, Throwable cause) { + return new RejectedResumeException(message, cause); + } + + @Override + public int getSpecifiedErrorCode() { + return 0x00000004; + } +} diff --git a/today-remoting/src/test/java/infra/remoting/error/RejectedSetupExceptionTests.java b/today-remoting/src/test/java/infra/remoting/error/RejectedSetupExceptionTests.java new file mode 100644 index 0000000..f99ebcf --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/error/RejectedSetupExceptionTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.error; + +final class RejectedSetupExceptionTests implements ChannelExceptionTests { + + @Override + public RejectedSetupException getException(String message) { + return new RejectedSetupException(message); + } + + @Override + public RejectedSetupException getException(String message, Throwable cause) { + return new RejectedSetupException(message, cause); + } + + @Override + public int getSpecifiedErrorCode() { + return 0x00000003; + } +} diff --git a/today-remoting/src/test/java/infra/remoting/exceptions/TestProtocolException.java b/today-remoting/src/test/java/infra/remoting/error/TestProtocolException.java similarity index 65% rename from today-remoting/src/test/java/infra/remoting/exceptions/TestProtocolException.java rename to today-remoting/src/test/java/infra/remoting/error/TestProtocolException.java index 6cf0c10..6e2442a 100644 --- a/today-remoting/src/test/java/infra/remoting/exceptions/TestProtocolException.java +++ b/today-remoting/src/test/java/infra/remoting/error/TestProtocolException.java @@ -1,21 +1,20 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.exceptions; +package infra.remoting.error; import infra.remoting.ProtocolErrorException; import infra.remoting.frame.ErrorFrameCodec; diff --git a/today-remoting/src/test/java/infra/remoting/error/UnsupportedSetupExceptionTests.java b/today-remoting/src/test/java/infra/remoting/error/UnsupportedSetupExceptionTests.java new file mode 100644 index 0000000..5a0cb08 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/error/UnsupportedSetupExceptionTests.java @@ -0,0 +1,36 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.error; + +final class UnsupportedSetupExceptionTests + implements ChannelExceptionTests { + + @Override + public UnsupportedSetupException getException(String message) { + return new UnsupportedSetupException(message); + } + + @Override + public UnsupportedSetupException getException(String message, Throwable cause) { + return new UnsupportedSetupException(message, cause); + } + + @Override + public int getSpecifiedErrorCode() { + return 0x00000002; + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/channel/ChannelEchoClient.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/channel/ChannelEchoClient.java new file mode 100644 index 0000000..042db28 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/channel/ChannelEchoClient.java @@ -0,0 +1,62 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.channel; + +import java.time.Duration; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.Channel; +import infra.remoting.ChannelAcceptor; +import infra.remoting.Payload; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.transport.netty.client.TcpClientTransport; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.remoting.util.DefaultPayload; +import reactor.core.publisher.Flux; + +public final class ChannelEchoClient { + + private static final Logger logger = LoggerFactory.getLogger(ChannelEchoClient.class); + + public static void main(String[] args) { + + ChannelAcceptor echoAcceptor = + ChannelAcceptor.forRequestChannel( + payloads -> + Flux.from(payloads) + .map(Payload::getDataUtf8) + .map(s -> "Echo: " + s) + .map(DefaultPayload::create)); + + RemotingServer.create(echoAcceptor).bindNow(TcpServerTransport.create("localhost", 7000)); + + Channel channel = + ChannelConnector.connectWith(TcpClientTransport.create("localhost", 7000)).block(); + + channel + .requestChannel( + Flux.interval(Duration.ofMillis(1000)).map(i -> DefaultPayload.create("Hello"))) + .map(Payload::getDataUtf8) + .doOnNext(logger::debug) + .take(10) + .doFinally(signalType -> channel.dispose()) + .then() + .block(); + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/client/ClientExample.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/client/ClientExample.java new file mode 100644 index 0000000..3f32884 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/client/ClientExample.java @@ -0,0 +1,69 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.client; + +import java.time.Duration; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.Channel; +import infra.remoting.ChannelAcceptor; +import infra.remoting.Payload; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingClient; +import infra.remoting.core.RemotingServer; +import infra.remoting.transport.netty.client.TcpClientTransport; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.remoting.util.DefaultPayload; +import reactor.core.publisher.Mono; +import reactor.util.retry.Retry; + +public class ClientExample { + static final Logger logger = LoggerFactory.getLogger(ClientExample.class); + + public static void main(String[] args) { + RemotingServer.create(ChannelAcceptor.forRequestResponse(p -> { + String data = p.getDataUtf8(); + logger.info("Received request data {}", data); + + Payload responsePayload = DefaultPayload.create("Echo: " + data); + p.release(); + + return Mono.just(responsePayload); + })) + .bind(TcpServerTransport.create("localhost", 7000)) + .delaySubscription(Duration.ofSeconds(5)) + .doOnNext(cc -> logger.info("Server started on the address : {}", cc.address())) + .block(); + + Mono source = + ChannelConnector.create() + .reconnect(Retry.backoff(50, Duration.ofMillis(500))) + .connect(TcpClientTransport.create("localhost", 7000)); + + RemotingClient.from(source) + .requestResponse(Mono.just(DefaultPayload.create("Test Request"))) + .doOnSubscribe(s -> logger.info("Executing Request")) + .doOnNext( + d -> { + logger.info("Received response data {}", d.getDataUtf8()); + d.release(); + }) + .repeat(10) + .blockLast(); + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/fnf/TaskProcessingWithServerSideNotificationsExample.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/fnf/TaskProcessingWithServerSideNotificationsExample.java new file mode 100644 index 0000000..442026e --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/fnf/TaskProcessingWithServerSideNotificationsExample.java @@ -0,0 +1,238 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.fnf; + +import java.time.Duration; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadLocalRandom; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.Channel; +import infra.remoting.ChannelAcceptor; +import infra.remoting.ConnectionSetupPayload; +import infra.remoting.Payload; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.transport.netty.client.TcpClientTransport; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.remoting.util.DefaultPayload; +import reactor.core.publisher.BaseSubscriber; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.Sinks; +import reactor.util.concurrent.Queues; + +/** + * An example of long-running tasks processing (a.k.a Kafka style) where a client submits tasks over + * request `FireAndForget` and then receives results over the same method but on it is own side. + * + *

    This example shows a case when the client may disappear, however, another a client can connect + * again and receive undelivered completed tasks remaining for the previous one. + */ +public class TaskProcessingWithServerSideNotificationsExample { + + public static void main(String[] args) throws InterruptedException { + Sinks.Many tasksProcessor = + Sinks.many().unicast().onBackpressureBuffer(Queues.unboundedMultiproducer().get()); + ConcurrentMap> idToCompletedTasksMap = new ConcurrentHashMap<>(); + ConcurrentMap idToChannelMap = new ConcurrentHashMap<>(); + BackgroundWorker backgroundWorker = + new BackgroundWorker(tasksProcessor.asFlux(), idToCompletedTasksMap, idToChannelMap); + + RemotingServer.create(new TasksAcceptor(tasksProcessor, idToCompletedTasksMap, idToChannelMap)) + .bindNow(TcpServerTransport.create(9991)); + + Logger logger = LoggerFactory.getLogger("Channel.Client.ID[Test]"); + + Mono channelMono = + ChannelConnector.create() + .setupPayload(DefaultPayload.create("Test")) + .acceptor( + ChannelAcceptor.forFireAndForget( + p -> { + logger.info("Received Processed Task[{}]", p.getDataUtf8()); + p.release(); + return Mono.empty(); + })) + .connect(TcpClientTransport.create(9991)); + + Channel channelRequester1 = channelMono.block(); + + for (int i = 0; i < 10; i++) { + channelRequester1.fireAndForget(DefaultPayload.create("task" + i)).block(); + } + + Thread.sleep(4000); + + channelRequester1.dispose(); + logger.info("Disposed"); + + Thread.sleep(4000); + + Channel requester2 = channelMono.block(); + + logger.info("Reconnected"); + + Thread.sleep(10000); + } + + static class BackgroundWorker extends BaseSubscriber { + final ConcurrentMap> idToCompletedTasksMap; + final ConcurrentMap idToChannelMap; + + BackgroundWorker( + Flux taskProducer, + ConcurrentMap> idToCompletedTasksMap, + ConcurrentMap idToChannelMap) { + + this.idToCompletedTasksMap = idToCompletedTasksMap; + this.idToChannelMap = idToChannelMap; + + // mimic a long running task processing + taskProducer + .concatMap( + t -> + Mono.delay(Duration.ofMillis(ThreadLocalRandom.current().nextInt(200, 2000))) + .thenReturn(t)) + .subscribe(this); + } + + @Override + protected void hookOnNext(Task task) { + BlockingQueue completedTasksQueue = + idToCompletedTasksMap.computeIfAbsent(task.id, __ -> new LinkedBlockingQueue<>()); + + completedTasksQueue.offer(task); + Channel channel = idToChannelMap.get(task.id); + if (channel != null) { + channel + .fireAndForget(DefaultPayload.create(task.content)) + .subscribe(null, e -> { }, () -> completedTasksQueue.remove(task)); + } + } + } + + static class TasksAcceptor implements ChannelAcceptor { + + static final Logger logger = LoggerFactory.getLogger(TasksAcceptor.class); + + final Sinks.Many tasksToProcess; + final ConcurrentMap> idToCompletedTasksMap; + final ConcurrentMap idToChannelMap; + + TasksAcceptor( + Sinks.Many tasksToProcess, + ConcurrentMap> idToCompletedTasksMap, + ConcurrentMap idToChannelMap) { + this.tasksToProcess = tasksToProcess; + this.idToCompletedTasksMap = idToCompletedTasksMap; + this.idToChannelMap = idToChannelMap; + } + + @Override + public Mono accept(ConnectionSetupPayload setup, Channel channel) { + String id = setup.getDataUtf8(); + logger.info("Accepting a new client connection with ID {}", id); + // sendingChannel represents here an Channel requester to a remote peer + + if (this.idToChannelMap.compute( + id, (__, old) -> old == null || old.isDisposed() ? channel : old) + == channel) { + return Mono.just( + new ChannelTaskHandler(idToChannelMap, tasksToProcess, id, channel)) + .doOnSuccess(__ -> checkTasksToDeliver(channel, id)); + } + + return Mono.error( + new IllegalStateException("There is already a client connected with the same ID")); + } + + private void checkTasksToDeliver(Channel channel, String id) { + logger.info("Accepted a new client connection with ID {}. Checking for remaining tasks", id); + BlockingQueue tasksToDeliver = this.idToCompletedTasksMap.get(id); + + if (tasksToDeliver == null || tasksToDeliver.isEmpty()) { + // means nothing yet to send + return; + } + + logger.info("Found remaining tasks to deliver for client {}", id); + + for (; ; ) { + Task task = tasksToDeliver.poll(); + + if (task == null) { + return; + } + + channel + .fireAndForget(DefaultPayload.create(task.content)) + .subscribe( + null, + e -> { + // offers back a task if it has not been delivered + tasksToDeliver.offer(task); + }); + } + } + + private static class ChannelTaskHandler implements Channel { + + private final String id; + private final Channel channel; + private ConcurrentMap idToChannelMap; + private Sinks.Many tasksToProcess; + + public ChannelTaskHandler( + ConcurrentMap idToChannelMap, + Sinks.Many tasksToProcess, + String id, + Channel channel) { + this.id = id; + this.channel = channel; + this.idToChannelMap = idToChannelMap; + this.tasksToProcess = tasksToProcess; + } + + @Override + public Mono fireAndForget(Payload payload) { + logger.info("Received a Task[{}] from Client.ID[{}]", payload.getDataUtf8(), id); + Sinks.EmitResult result = tasksToProcess.tryEmitNext(new Task(id, payload.getDataUtf8())); + payload.release(); + return result.isFailure() ? Mono.error(new Sinks.EmissionException(result)) : Mono.empty(); + } + + @Override + public void dispose() { + idToChannelMap.remove(id, channel); + } + } + } + + static class Task { + final String id; + final String content; + + Task(String id, String content) { + this.id = id; + this.content = content; + } + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/common/LeaseManager.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/common/LeaseManager.java new file mode 100644 index 0000000..f81b8d1 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/common/LeaseManager.java @@ -0,0 +1,163 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.lease.advanced.common; + +import java.util.concurrent.BlockingDeque; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import reactor.core.scheduler.Scheduler; +import reactor.core.scheduler.Schedulers; + +public class LeaseManager implements Runnable { + + static final Logger logger = LoggerFactory.getLogger(LeaseManager.class); + + volatile int activeConnectionsCount; + static final AtomicIntegerFieldUpdater ACTIVE_CONNECTIONS_COUNT = + AtomicIntegerFieldUpdater.newUpdater(LeaseManager.class, "activeConnectionsCount"); + + volatile int stateAndInFlight; + static final AtomicIntegerFieldUpdater STATE_AND_IN_FLIGHT = + AtomicIntegerFieldUpdater.newUpdater(LeaseManager.class, "stateAndInFlight"); + + static final int MASK_PAUSED = 0b1_000_0000_0000_0000_0000_0000_0000_0000; + static final int MASK_IN_FLIGHT = 0b0_111_1111_1111_1111_1111_1111_1111_1111; + + final BlockingDeque sendersQueue = new LinkedBlockingDeque<>(); + final Scheduler worker = Schedulers.newSingle(LeaseManager.class.getName()); + + final int capacity; + final int ttl; + + public LeaseManager(int capacity, int ttl) { + this.capacity = capacity; + this.ttl = ttl; + } + + @Override + public void run() { + try { + LimitBasedLeaseSender leaseSender = sendersQueue.poll(); + + if (leaseSender == null) { + return; + } + + if (leaseSender.isDisposed()) { + logger.debug("Connection[" + leaseSender.connectionId + "]: LeaseSender is Disposed"); + worker.schedule(this); + return; + } + + int limit = leaseSender.limitAlgorithm.getLimit(); + + if (limit == 0) { + throw new IllegalStateException("Limit is 0"); + } + + if (pauseIfNoCapacity()) { + sendersQueue.addFirst(leaseSender); + logger.debug("Pause execution. Not enough capacity"); + return; + } + + leaseSender.sendLease(ttl, limit); + sendersQueue.offer(leaseSender); + + int activeConnections = activeConnectionsCount; + int nextDelay = activeConnections == 0 ? ttl : (ttl / activeConnections); + + logger.debug("Next check happens in " + nextDelay + "ms"); + + worker.schedule(this, nextDelay, TimeUnit.MILLISECONDS); + } + catch (Throwable e) { + logger.error("LeaseSender failed to send lease", e); + } + } + + int incrementInFlightAndGet() { + for (; ; ) { + int state = stateAndInFlight; + int paused = state & MASK_PAUSED; + int inFlight = stateAndInFlight & MASK_IN_FLIGHT; + + // assume overflow is impossible due to max concurrency in channel it self + int nextInFlight = inFlight + 1; + + if (STATE_AND_IN_FLIGHT.compareAndSet(this, state, nextInFlight | paused)) { + return nextInFlight; + } + } + } + + void decrementInFlight() { + for (; ; ) { + int state = stateAndInFlight; + int paused = state & MASK_PAUSED; + int inFlight = stateAndInFlight & MASK_IN_FLIGHT; + + // assume overflow is impossible due to max concurrency in channel it self + int nextInFlight = inFlight - 1; + + if (inFlight == capacity && paused == MASK_PAUSED) { + if (STATE_AND_IN_FLIGHT.compareAndSet(this, state, nextInFlight)) { + logger.debug("Resume execution"); + worker.schedule(this); + return; + } + } + else { + if (STATE_AND_IN_FLIGHT.compareAndSet(this, state, nextInFlight | paused)) { + return; + } + } + } + } + + boolean pauseIfNoCapacity() { + int capacity = this.capacity; + for (; ; ) { + int inFlight = stateAndInFlight; + + if (inFlight < capacity) { + return false; + } + + if (STATE_AND_IN_FLIGHT.compareAndSet(this, inFlight, inFlight | MASK_PAUSED)) { + return true; + } + } + } + + void unregister() { + ACTIVE_CONNECTIONS_COUNT.decrementAndGet(this); + } + + void register(LimitBasedLeaseSender sender) { + sendersQueue.offer(sender); + final int activeCount = ACTIVE_CONNECTIONS_COUNT.getAndIncrement(this); + + if (activeCount == 0) { + worker.schedule(this); + } + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/common/LimitBasedLeaseSender.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/common/LimitBasedLeaseSender.java new file mode 100644 index 0000000..3f1f51a --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/common/LimitBasedLeaseSender.java @@ -0,0 +1,73 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.lease.advanced.common; + +import com.netflix.concurrency.limits.Limit; + +import java.time.Duration; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.lease.Lease; +import infra.remoting.lease.TrackingLeaseSender; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Sinks; +import reactor.util.concurrent.Queues; + +public class LimitBasedLeaseSender extends LimitBasedStatsCollector implements TrackingLeaseSender { + + static final Logger logger = LoggerFactory.getLogger(LimitBasedLeaseSender.class); + + final String connectionId; + final Sinks.Many sink = + Sinks.many().unicast().onBackpressureBuffer(Queues.one().get()); + + public LimitBasedLeaseSender( + String connectionId, LeaseManager leaseManager, Limit limitAlgorithm) { + super(leaseManager, limitAlgorithm); + this.connectionId = connectionId; + } + + @Override + public Flux send() { + logger.info("Received new leased Connection[{}]", connectionId); + + leaseManager.register(this); + + return sink.asFlux(); + } + + public void sendLease(int ttl, int amount) { + final Lease nextLease = Lease.create(Duration.ofMillis(ttl), amount); + final Sinks.EmitResult result = sink.tryEmitNext(nextLease); + + if (result.isFailure()) { + logger.warn( + "Connection[" + + connectionId + + "]. Issued Lease: [" + + nextLease + + "] was not sent due to " + + result); + } + else { + if (logger.isDebugEnabled()) { + logger.debug("To Connection[" + connectionId + "]: Issued Lease: [" + nextLease + "]"); + } + } + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/common/LimitBasedStatsCollector.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/common/LimitBasedStatsCollector.java new file mode 100644 index 0000000..1fba2b9 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/common/LimitBasedStatsCollector.java @@ -0,0 +1,91 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.lease.advanced.common; + +import com.netflix.concurrency.limits.Limit; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.LongSupplier; + +import infra.remoting.frame.FrameType; +import infra.remoting.plugins.RequestInterceptor; +import io.netty.buffer.ByteBuf; +import reactor.util.annotation.Nullable; + +public class LimitBasedStatsCollector extends AtomicBoolean implements RequestInterceptor { + + final LeaseManager leaseManager; + final Limit limitAlgorithm; + + final ConcurrentMap inFlightMap = new ConcurrentHashMap<>(); + final ConcurrentMap timeMap = new ConcurrentHashMap<>(); + + final LongSupplier clock = System::nanoTime; + + public LimitBasedStatsCollector(LeaseManager leaseManager, Limit limitAlgorithm) { + this.leaseManager = leaseManager; + this.limitAlgorithm = limitAlgorithm; + } + + @Override + public void onStart(int streamId, FrameType requestType, @Nullable ByteBuf metadata) { + long startTime = clock.getAsLong(); + + int currentInFlight = leaseManager.incrementInFlightAndGet(); + + inFlightMap.put(streamId, currentInFlight); + timeMap.put(streamId, startTime); + } + + @Override + public void onReject( + Throwable rejectionReason, FrameType requestType, @Nullable ByteBuf metadata) { } + + @Override + public void onTerminate(int streamId, FrameType requestType, @Nullable Throwable t) { + leaseManager.decrementInFlight(); + + Long startTime = timeMap.remove(streamId); + Integer currentInflight = inFlightMap.remove(streamId); + + limitAlgorithm.onSample(startTime, clock.getAsLong() - startTime, currentInflight, t != null); + } + + @Override + public void onCancel(int streamId, FrameType requestType) { + leaseManager.decrementInFlight(); + + Long startTime = timeMap.remove(streamId); + Integer currentInflight = inFlightMap.remove(streamId); + + limitAlgorithm.onSample(startTime, clock.getAsLong() - startTime, currentInflight, true); + } + + @Override + public boolean isDisposed() { + return get(); + } + + @Override + public void dispose() { + if (!getAndSet(true)) { + leaseManager.unregister(); + } + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/controller/Task.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/controller/Task.java new file mode 100644 index 0000000..5a05569 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/controller/Task.java @@ -0,0 +1,44 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.lease.advanced.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +// emulating a worker that process data from the queue +public class Task implements Runnable { + private static final Logger logger = LoggerFactory.getLogger(Task.class); + + final String message; + final int processingTime; + + Task(String message, int processingTime) { + this.message = message; + this.processingTime = processingTime; + } + + @Override + public void run() { + logger.info("Processing Task[{}]", message); + try { + Thread.sleep(processingTime); // emulating processing + } + catch (InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/controller/TasksHandlingChannel.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/controller/TasksHandlingChannel.java new file mode 100644 index 0000000..3c07ffe --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/controller/TasksHandlingChannel.java @@ -0,0 +1,61 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.lease.advanced.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import infra.remoting.Channel; +import infra.remoting.Payload; +import reactor.core.Disposable; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Scheduler; + +public class TasksHandlingChannel implements Channel { + + private static final Logger logger = LoggerFactory.getLogger(TasksHandlingChannel.class); + + final Disposable terminatable; + final Scheduler workScheduler; + final int processingTime; + + public TasksHandlingChannel(Disposable terminatable, Scheduler scheduler, int processingTime) { + this.terminatable = terminatable; + this.workScheduler = scheduler; + this.processingTime = processingTime; + } + + @Override + public Mono fireAndForget(Payload payload) { + + // specifically to show that lease can limit rate of fnf requests in + // that example + String message = payload.getDataUtf8(); + payload.release(); + + return Mono.fromRunnable(new Task(message, processingTime)) + // schedule task on specific, limited in size scheduler + .subscribeOn(workScheduler) + // if errors - terminates server + .doOnError( + t -> { + logger.error("Queue has been overflowed. Terminating server"); + terminatable.dispose(); + System.exit(9); + }); + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/invertmulticlient/RequestingServer.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/invertmulticlient/RequestingServer.java new file mode 100644 index 0000000..a537b3b --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/invertmulticlient/RequestingServer.java @@ -0,0 +1,79 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.lease.advanced.invertmulticlient; + +import java.util.Comparator; +import java.util.concurrent.PriorityBlockingQueue; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.Channel; +import infra.remoting.core.RemotingServer; +import infra.remoting.transport.netty.server.CloseableChannel; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.remoting.util.ByteBufPayload; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class RequestingServer { + + private static final Logger logger = LoggerFactory.getLogger(RequestingServer.class); + + public static void main(String[] args) { + PriorityBlockingQueue channels = + new PriorityBlockingQueue<>( + 16, Comparator.comparingDouble(Channel::availability).reversed()); + + CloseableChannel server = + RemotingServer.create( + (setup, channel) -> { + logger.info("Received new connection"); + return Mono.just(new Channel() { }) + .doAfterTerminate(() -> channels.put(channel)); + }) + .lease(spec -> spec.maxPendingRequests(Integer.MAX_VALUE)) + .bindNow(TcpServerTransport.create("localhost", 7000)); + + logger.info("Server started on port {}", server.address().getPort()); + + // generate stream of fnfs + Flux.generate( + () -> 0L, + (state, sink) -> { + sink.next(state); + return state + 1; + }) + .flatMap( + tick -> { + logger.info("Requesting FireAndForget({})", tick); + + return Mono.fromCallable( + () -> { + Channel channel = channels.take(); + channels.offer(channel); + return channel; + }) + .flatMap( + clientChannel -> + clientChannel.fireAndForget(ByteBufPayload.create("" + tick))) + .retry(); + }) + .blockLast(); + + server.onClose().block(); + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/invertmulticlient/RespondingClient.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/invertmulticlient/RespondingClient.java new file mode 100644 index 0000000..2826179 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/invertmulticlient/RespondingClient.java @@ -0,0 +1,86 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.lease.advanced.invertmulticlient; + +import com.netflix.concurrency.limits.limit.VegasLimit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import infra.remoting.Channel; +import infra.remoting.ChannelAcceptor; +import infra.remoting.core.ChannelConnector; +import infra.remoting.examples.tcp.lease.advanced.common.LeaseManager; +import infra.remoting.examples.tcp.lease.advanced.common.LimitBasedLeaseSender; +import infra.remoting.examples.tcp.lease.advanced.controller.TasksHandlingChannel; +import infra.remoting.transport.netty.client.TcpClientTransport; +import reactor.core.Disposable; +import reactor.core.Disposables; +import reactor.core.scheduler.Scheduler; +import reactor.core.scheduler.Schedulers; + +public class RespondingClient { + private static final Logger logger = LoggerFactory.getLogger(RespondingClient.class); + + public static final int PROCESSING_TASK_TIME = 500; + public static final int CONCURRENT_WORKERS_COUNT = 1; + public static final int QUEUE_CAPACITY = 50; + + public static void main(String[] args) { + // Queue for incoming messages represented as Flux + // Imagine that every fireAndForget that is pushed is processed by a worker + BlockingQueue tasksQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY); + + ThreadPoolExecutor threadPoolExecutor = + new ThreadPoolExecutor(1, CONCURRENT_WORKERS_COUNT, 1, TimeUnit.MINUTES, tasksQueue); + + Scheduler workScheduler = Schedulers.fromExecutorService(threadPoolExecutor); + + LeaseManager periodicLeaseSender = + new LeaseManager(CONCURRENT_WORKERS_COUNT, PROCESSING_TASK_TIME); + + Disposable.Composite disposable = Disposables.composite(); + Channel clientChannel = + ChannelConnector.create() + .acceptor( + ChannelAcceptor.with( + new TasksHandlingChannel(disposable, workScheduler, PROCESSING_TASK_TIME))) + .lease( + (config) -> + config.sender( + new LimitBasedLeaseSender( + UUID.randomUUID().toString(), + periodicLeaseSender, + VegasLimit.newBuilder() + .initialLimit(CONCURRENT_WORKERS_COUNT) + .maxConcurrency(QUEUE_CAPACITY) + .build()))) + .connect(TcpClientTransport.create("localhost", 7000)) + .block(); + + Objects.requireNonNull(clientChannel); + disposable.add(clientChannel); + clientChannel.onClose().block(); + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/multiclient/RequestingClient.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/multiclient/RequestingClient.java new file mode 100644 index 0000000..42a15b6 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/multiclient/RequestingClient.java @@ -0,0 +1,58 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.lease.advanced.multiclient; + +import java.util.Objects; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.Channel; +import infra.remoting.core.ChannelConnector; +import infra.remoting.transport.netty.client.TcpClientTransport; +import infra.remoting.util.ByteBufPayload; +import reactor.core.publisher.Flux; + +public class RequestingClient { + private static final Logger logger = LoggerFactory.getLogger(RequestingClient.class); + + public static void main(String[] args) { + + Channel clientChannel = + ChannelConnector.create() + .lease() + .connect(TcpClientTransport.create("localhost", 7000)) + .block(); + + Objects.requireNonNull(clientChannel); + + // generate stream of fnfs + Flux.generate( + () -> 0L, + (state, sink) -> { + sink.next(state); + return state + 1; + }) + .concatMap( + tick -> { + logger.info("Requesting FireAndForget({})", tick); + return clientChannel.fireAndForget(ByteBufPayload.create("" + tick)); + }) + .blockLast(); + + clientChannel.onClose().block(); + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/multiclient/RespondingServer.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/multiclient/RespondingServer.java new file mode 100644 index 0000000..4258ba8 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/advanced/multiclient/RespondingServer.java @@ -0,0 +1,83 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.lease.advanced.multiclient; + +import com.netflix.concurrency.limits.limit.VegasLimit; + +import java.util.UUID; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.ChannelAcceptor; +import infra.remoting.core.RemotingServer; +import infra.remoting.examples.tcp.lease.advanced.common.LeaseManager; +import infra.remoting.examples.tcp.lease.advanced.common.LimitBasedLeaseSender; +import infra.remoting.examples.tcp.lease.advanced.controller.TasksHandlingChannel; +import infra.remoting.transport.netty.server.CloseableChannel; +import infra.remoting.transport.netty.server.TcpServerTransport; +import reactor.core.Disposable; +import reactor.core.Disposables; +import reactor.core.scheduler.Scheduler; +import reactor.core.scheduler.Schedulers; + +public class RespondingServer { + + private static final Logger logger = LoggerFactory.getLogger(RespondingServer.class); + + public static final int TASK_PROCESSING_TIME = 500; + public static final int CONCURRENT_WORKERS_COUNT = 1; + public static final int QUEUE_CAPACITY = 50; + + public static void main(String[] args) { + // Queue for incoming messages represented as Flux + // Imagine that every fireAndForget that is pushed is processed by a worker + BlockingQueue tasksQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY); + + ThreadPoolExecutor threadPoolExecutor = + new ThreadPoolExecutor(1, CONCURRENT_WORKERS_COUNT, 1, TimeUnit.MINUTES, tasksQueue); + + Scheduler workScheduler = Schedulers.fromExecutorService(threadPoolExecutor); + + LeaseManager leaseManager = new LeaseManager(CONCURRENT_WORKERS_COUNT, TASK_PROCESSING_TIME); + + Disposable.Composite disposable = Disposables.composite(); + CloseableChannel server = + RemotingServer.create( + ChannelAcceptor.with( + new TasksHandlingChannel(disposable, workScheduler, TASK_PROCESSING_TIME))) + .lease( + (config) -> + config.sender( + new LimitBasedLeaseSender( + UUID.randomUUID().toString(), + leaseManager, + VegasLimit.newBuilder() + .initialLimit(CONCURRENT_WORKERS_COUNT) + .maxConcurrency(QUEUE_CAPACITY) + .build()))) + .bindNow(TcpServerTransport.create("localhost", 7000)); + + disposable.add(server); + + logger.info("Server started on port {}", server.address().getPort()); + server.onClose().block(); + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/simple/LeaseExample.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/simple/LeaseExample.java new file mode 100644 index 0000000..607ac57 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/lease/simple/LeaseExample.java @@ -0,0 +1,160 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.lease.simple; + +import java.time.Duration; +import java.util.Objects; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.Channel; +import infra.remoting.Payload; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.lease.Lease; +import infra.remoting.lease.LeaseSender; +import infra.remoting.transport.netty.client.TcpClientTransport; +import infra.remoting.transport.netty.server.CloseableChannel; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.remoting.util.ByteBufPayload; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class LeaseExample { + + private static final Logger logger = LoggerFactory.getLogger(LeaseExample.class); + + private static final String SERVER_TAG = "server"; + private static final String CLIENT_TAG = "client"; + + public static void main(String[] args) { + // Queue for incoming messages represented as Flux + // Imagine that every fireAndForget that is pushed is processed by a worker + + int queueCapacity = 50; + BlockingQueue messagesQueue = new ArrayBlockingQueue<>(queueCapacity); + + // emulating a worker that process data from the queue + Thread workerThread = + new Thread( + () -> { + try { + while (!Thread.currentThread().isInterrupted()) { + String message = messagesQueue.take(); + logger.info("Process message {}", message); + Thread.sleep(500); // emulating processing + } + } + catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + + workerThread.start(); + + CloseableChannel server = RemotingServer.create((setup, channel) -> + Mono.just(new Channel() { + @Override + public Mono fireAndForget(Payload payload) { + // add element. if overflows errors and terminates execution + // specifically to show that lease can limit rate of fnf requests in + // that example + try { + if (!messagesQueue.offer(payload.getDataUtf8())) { + logger.error("Queue has been overflowed. Terminating execution"); + channel.dispose(); + workerThread.interrupt(); + } + } + finally { + payload.release(); + } + return Mono.empty(); + } + })) + .lease(leases -> leases.sender(new LeaseCalculator(SERVER_TAG, messagesQueue))) + .bindNow(TcpServerTransport.create("localhost", 7000)); + + Channel clientChannel = + ChannelConnector.create() + .lease((config) -> config.maxPendingRequests(1)) + .connect(TcpClientTransport.create(server.address())) + .block(); + + Objects.requireNonNull(clientChannel); + + // generate stream of fnfs + Flux.generate( + () -> 0L, + (state, sink) -> { + sink.next(state); + return state + 1; + }) + // here we wait for the first lease for the responder side and start execution + // on if there is allowance + .concatMap( + tick -> { + logger.info("Requesting FireAndForget({})", tick); + return clientChannel.fireAndForget(ByteBufPayload.create("" + tick)); + }) + .blockLast(); + + clientChannel.onClose().block(); + server.dispose(); + } + + /** + * This is a class responsible for making decision on whether Responder is ready to receive new + * FireAndForget or not base in the number of messages enqueued.
    + * In the nutshell this is responder-side rate-limiter logic which is created for every new + * connection.
    + * In real-world projects this class has to issue leases based on real metrics + */ + private static class LeaseCalculator implements LeaseSender { + final String tag; + final BlockingQueue queue; + + public LeaseCalculator(String tag, BlockingQueue queue) { + this.tag = tag; + this.queue = queue; + } + + @Override + public Flux send() { + Duration ttlDuration = Duration.ofSeconds(5); + // The interval function is used only for the demo purpose and should not be + // considered as the way to issue leases. + // For advanced RateLimiting with Leasing + // consider adopting https://github.com/Netflix/concurrency-limits#server-limiter + return Flux.interval(Duration.ZERO, ttlDuration.dividedBy(2)) + .handle( + (__, sink) -> { + // put queue.remainingCapacity() + 1 here if you want to observe that app is + // terminated because of the queue overflowing + int requests = queue.remainingCapacity(); + + // reissue new lease only if queue has remaining capacity to + // accept more requests + if (requests > 0) { + sink.next(Lease.create(ttlDuration, requests)); + } + }); + } + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/loadbalancer/RoundRobinLoadbalancerExample.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/loadbalancer/RoundRobinLoadbalancerExample.java new file mode 100644 index 0000000..a5e4602 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/loadbalancer/RoundRobinLoadbalancerExample.java @@ -0,0 +1,91 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.loadbalancer; + +import java.time.Duration; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import infra.remoting.ChannelAcceptor; +import infra.remoting.Payload; +import infra.remoting.core.RemotingClient; +import infra.remoting.core.RemotingServer; +import infra.remoting.lb.LoadBalanceTarget; +import infra.remoting.transport.netty.client.TcpClientTransport; +import infra.remoting.transport.netty.server.CloseableChannel; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.remoting.util.DefaultPayload; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class RoundRobinLoadbalancerExample { + + public static void main(String[] args) { + CloseableChannel server1 = RemotingServer.create(ChannelAcceptor.forRequestResponse(p -> { + System.out.println("Server 1 got fnf " + p.getDataUtf8()); + return Mono.just(DefaultPayload.create("Server 1 response")) + .delayElement(Duration.ofMillis(1)); + })) + .bindNow(TcpServerTransport.create(8080)); + + CloseableChannel server2 = RemotingServer.create(ChannelAcceptor.forRequestResponse(p -> { + System.out.println("Server 2 got fnf " + p.getDataUtf8()); + return Mono.just(DefaultPayload.create("Server 2 response")); + })) + .bindNow(TcpServerTransport.create(8081)); + + CloseableChannel server3 = RemotingServer.create(ChannelAcceptor.forRequestResponse(p -> { + System.out.println("Server 3 got fnf " + p.getDataUtf8()); + return Mono.just(DefaultPayload.create("Server 3 response")); + })) + .bindNow(TcpServerTransport.create(8082)); + + LoadBalanceTarget target8080 = LoadBalanceTarget.of("8080", TcpClientTransport.create(8080)); + LoadBalanceTarget target8081 = LoadBalanceTarget.of("8081", TcpClientTransport.create(8081)); + LoadBalanceTarget target8082 = LoadBalanceTarget.of("8082", TcpClientTransport.create(8082)); + + Flux> producer = Flux.interval(Duration.ofSeconds(5)).log().map(i -> { + int val = i.intValue(); + return switch (val) { + case 0, 6, 7 -> Collections.emptyList(); + case 1 -> Collections.singletonList(target8080); + case 2 -> Arrays.asList(target8080, target8081); + case 3 -> Arrays.asList(target8080, target8082); + case 4 -> Arrays.asList(target8081, target8082); + default -> Arrays.asList(target8080, target8081, target8082); + }; + }); + + RemotingClient client = RemotingClient.forLoadBalance(producer) + .roundRobinLoadBalanceStrategy() + .build(); + + for (int i = 0; i < 10000; i++) { + try { + Payload block = client.requestResponse(Mono.just(DefaultPayload.create("test-" + i))).block(); + System.out.println(block.getDataUtf8()); + } + catch (Throwable t) { + // no ops + } + } + + server1.dispose(); + server2.dispose(); + server3.dispose(); + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/plugins/LimitRateInterceptorExample.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/plugins/LimitRateInterceptorExample.java new file mode 100644 index 0000000..d0e4f2a --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/plugins/LimitRateInterceptorExample.java @@ -0,0 +1,101 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.plugins; + +import org.reactivestreams.Publisher; + +import java.time.Duration; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.Channel; +import infra.remoting.ChannelAcceptor; +import infra.remoting.Payload; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.plugins.RateLimitDecorator; +import infra.remoting.transport.netty.client.TcpClientTransport; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.remoting.util.DefaultPayload; +import reactor.core.publisher.Flux; + +public class LimitRateInterceptorExample { + + private static final Logger logger = LoggerFactory.getLogger(LimitRateInterceptorExample.class); + + public static void main(String[] args) { + RemotingServer.create( + ChannelAcceptor.with( + new Channel() { + @Override + public Flux requestStream(Payload payload) { + return Flux.interval(Duration.ofMillis(100)) + .doOnRequest( + e -> logger.debug("Server publisher receives request for " + e)) + .map(aLong -> DefaultPayload.create("Interval: " + aLong)); + } + + @Override + public Flux requestChannel(Publisher payloads) { + return Flux.from(payloads) + .doOnRequest( + e -> logger.debug("Server publisher receives request for " + e)); + } + })) + .interceptors(registry -> registry.forResponder(RateLimitDecorator.forResponder(64))) + .bindNow(TcpServerTransport.create("localhost", 7000)); + + Channel channel = + ChannelConnector.create() + .interceptors(registry -> registry.forRequester(RateLimitDecorator.forRequester(64))) + .connect(TcpClientTransport.create("localhost", 7000)) + .block(); + + logger.debug( + "\n\nStart of requestStream interaction\n" + "----------------------------------\n"); + + channel + .requestStream(DefaultPayload.create("Hello")) + .doOnRequest(e -> logger.debug("Client sends requestN(" + e + ")")) + .map(Payload::getDataUtf8) + .doOnNext(logger::debug) + .take(10) + .then() + .block(); + + logger.debug( + "\n\nStart of requestChannel interaction\n" + "-----------------------------------\n"); + + channel + .requestChannel( + Flux.generate( + () -> 1L, + (s, sink) -> { + sink.next(DefaultPayload.create("Next " + s)); + return ++s; + }) + .doOnRequest(e -> logger.debug("Client publisher receives request for " + e))) + .doOnRequest(e -> logger.debug("Client sends requestN(" + e + ")")) + .map(Payload::getDataUtf8) + .doOnNext(logger::debug) + .take(10) + .then() + .doFinally(signalType -> channel.dispose()) + .then() + .block(); + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/requestresponse/HelloWorldClient.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/requestresponse/HelloWorldClient.java new file mode 100644 index 0000000..975594e --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/requestresponse/HelloWorldClient.java @@ -0,0 +1,69 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.requestresponse; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.Channel; +import infra.remoting.ChannelAcceptor; +import infra.remoting.Payload; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.transport.netty.client.TcpClientTransport; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.remoting.util.DefaultPayload; +import reactor.core.publisher.Mono; + +public final class HelloWorldClient { + + private static final Logger logger = LoggerFactory.getLogger(HelloWorldClient.class); + + public static void main(String[] args) { + + Channel channel = new Channel() { + boolean fail = true; + + @Override + public Mono requestResponse(Payload p) { + if (fail) { + fail = false; + return Mono.error(new Throwable("Simulated error")); + } + else { + return Mono.just(p); + } + } + }; + + RemotingServer.create(ChannelAcceptor.with(channel)) + .bindNow(TcpServerTransport.create("localhost", 7000)); + + Channel channel1 = + ChannelConnector.connectWith(TcpClientTransport.create("localhost", 7000)).block(); + + for (int i = 0; i < 3; i++) { + channel1 + .requestResponse(DefaultPayload.create("Hello")) + .map(Payload::getDataUtf8) + .onErrorReturn("error") + .doOnNext(logger::debug) + .block(); + } + + channel1.dispose(); + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/resume/Files.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/resume/Files.java new file mode 100644 index 0000000..325f688 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/resume/Files.java @@ -0,0 +1,165 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.resume; + +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import infra.remoting.Payload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import reactor.core.publisher.Flux; +import reactor.core.publisher.SynchronousSink; + +class Files { + private static final Logger logger = LoggerFactory.getLogger(Files.class); + + public static Flux fileSource(String fileName, int chunkSizeBytes) { + return Flux.generate( + () -> new FileState(fileName, chunkSizeBytes), FileState::consumeNext, FileState::dispose); + } + + public static Subscriber fileSink(String fileName, int windowSize) { + return new Subscriber() { + Subscription s; + int requests = windowSize; + OutputStream outputStream; + int receivedBytes; + int receivedCount; + + @Override + public void onSubscribe(Subscription s) { + this.s = s; + this.s.request(requests); + } + + @Override + public void onNext(Payload payload) { + ByteBuf data = payload.data(); + receivedBytes += data.readableBytes(); + receivedCount += 1; + logger.debug("Received file chunk: " + receivedCount + ". Total size: " + receivedBytes); + if (outputStream == null) { + outputStream = open(fileName); + } + write(outputStream, data); + payload.release(); + + requests--; + if (requests == windowSize / 2) { + requests += windowSize; + s.request(windowSize); + } + } + + private void write(OutputStream outputStream, ByteBuf byteBuf) { + try { + byteBuf.readBytes(outputStream, byteBuf.readableBytes()); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void onError(Throwable t) { + close(outputStream); + } + + @Override + public void onComplete() { + close(outputStream); + } + + private OutputStream open(String filename) { + try { + /*do not buffer for demo purposes*/ + return new FileOutputStream(filename); + } + catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + } + + private void close(OutputStream stream) { + if (stream != null) { + try { + stream.close(); + } + catch (IOException e) { + } + } + } + }; + } + + private static class FileState { + private final String fileName; + private final int chunkSizeBytes; + private BufferedInputStream inputStream; + private byte[] chunkBytes; + + public FileState(String fileName, int chunkSizeBytes) { + this.fileName = fileName; + this.chunkSizeBytes = chunkSizeBytes; + } + + public FileState consumeNext(SynchronousSink sink) { + if (inputStream == null) { + InputStream in = getClass().getClassLoader().getResourceAsStream(fileName); + if (in == null) { + sink.error(new FileNotFoundException(fileName)); + return this; + } + this.inputStream = new BufferedInputStream(in); + this.chunkBytes = new byte[chunkSizeBytes]; + } + try { + int consumedBytes = inputStream.read(chunkBytes); + if (consumedBytes == -1) { + sink.complete(); + } + else { + sink.next(Unpooled.copiedBuffer(chunkBytes, 0, consumedBytes)); + } + } + catch (IOException e) { + sink.error(e); + } + return this; + } + + public void dispose() { + if (inputStream != null) { + try { + inputStream.close(); + } + catch (IOException e) { + } + } + } + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/resume/ResumeFileTransfer.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/resume/ResumeFileTransfer.java new file mode 100644 index 0000000..60dc6f8 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/resume/ResumeFileTransfer.java @@ -0,0 +1,115 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.resume; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Duration; + +import infra.remoting.Channel; +import infra.remoting.ChannelAcceptor; +import infra.remoting.Payload; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.core.Resume; +import infra.remoting.transport.netty.client.TcpClientTransport; +import infra.remoting.transport.netty.server.CloseableChannel; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.remoting.util.DefaultPayload; +import reactor.core.publisher.Flux; +import reactor.util.retry.Retry; + +public class ResumeFileTransfer { + + /*amount of file chunks requested by subscriber: n, refilled on n/2 of received items*/ + private static final int PREFETCH_WINDOW_SIZE = 4; + private static final Logger logger = LoggerFactory.getLogger(ResumeFileTransfer.class); + + public static void main(String[] args) { + + Resume resume = new Resume() + .sessionDuration(Duration.ofMinutes(5)) + .retry(Retry.fixedDelay(Long.MAX_VALUE, Duration.ofSeconds(1)) + .doBeforeRetry(s -> logger.debug("Disconnected. Trying to resume..."))); + + RequestCodec codec = new RequestCodec(); + + CloseableChannel server = RemotingServer.create(ChannelAcceptor.forRequestStream(payload -> { + Request request = codec.decode(payload); + payload.release(); + String fileName = request.getFileName(); + int chunkSize = request.getChunkSize(); + + Flux ticks = Flux.interval(Duration.ofMillis(500)).onBackpressureDrop(); + + return Files.fileSource(fileName, chunkSize) + .map(DefaultPayload::create) + .zipWith(ticks, (p, tick) -> p) + .log("server"); + })) + .resume(resume) + .bindNow(TcpServerTransport.create("localhost", 8000)); + + Channel client = + ChannelConnector.create() + .resume(resume) + .connect(TcpClientTransport.create("localhost", 8001)) + .block(); + + client.requestStream(codec.encode(new Request(16, "lorem.txt"))) + .log("client") + .doFinally(s -> server.dispose()) + .subscribe(Files.fileSink("today-remoting/build/lorem_output.txt", PREFETCH_WINDOW_SIZE)); + + server.onClose().block(); + } + + private static class RequestCodec { + + public Payload encode(Request request) { + String encoded = request.getChunkSize() + ":" + request.getFileName(); + return DefaultPayload.create(encoded); + } + + public Request decode(Payload payload) { + String encoded = payload.getDataUtf8(); + String[] chunkSizeAndFileName = encoded.split(":"); + int chunkSize = Integer.parseInt(chunkSizeAndFileName[0]); + String fileName = chunkSizeAndFileName[1]; + return new Request(chunkSize, fileName); + } + } + + private static class Request { + private final int chunkSize; + private final String fileName; + + public Request(int chunkSize, String fileName) { + this.chunkSize = chunkSize; + this.fileName = fileName; + } + + public int getChunkSize() { + return chunkSize; + } + + public String getFileName() { + return fileName; + } + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/resume/readme.md b/today-remoting/src/test/java/infra/remoting/examples/tcp/resume/readme.md new file mode 100644 index 0000000..55e761f --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/resume/readme.md @@ -0,0 +1,29 @@ +1. Start socat. It is used for emulation of transport disconnects + +`socat -d TCP-LISTEN:8001,fork,reuseaddr TCP:localhost:8000` + +2. start `ResumeFileTransfer.main` + +3. terminate/start socat periodically for session resumption + +`ResumeFileTransfer` output is as follows + +``` +Received file chunk: 7. Total size: 112 +Received file chunk: 8. Total size: 128 +Received file chunk: 9. Total size: 144 +Received file chunk: 10. Total size: 160 +Disconnected. Trying to resume connection... +Disconnected. Trying to resume connection... +Disconnected. Trying to resume connection... +Disconnected. Trying to resume connection... +Disconnected. Trying to resume connection... +Received file chunk: 11. Total size: 176 +Received file chunk: 12. Total size: 192 +Received file chunk: 13. Total size: 208 +Received file chunk: 14. Total size: 224 +Received file chunk: 15. Total size: 240 +Received file chunk: 16. Total size: 256 +``` + +It transfers file from `resources/lorem.txt` to `build/out/lorem_output.txt` in chunks of 16 bytes every 500 millis diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/stream/ClientStreamingToServer.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/stream/ClientStreamingToServer.java new file mode 100644 index 0000000..36b0557 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/stream/ClientStreamingToServer.java @@ -0,0 +1,64 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.stream; + +import java.time.Duration; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.Channel; +import infra.remoting.ChannelAcceptor; +import infra.remoting.Payload; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.transport.netty.client.TcpClientTransport; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.remoting.util.DefaultPayload; +import reactor.core.publisher.Flux; + +public final class ClientStreamingToServer { + + private static final Logger logger = LoggerFactory.getLogger(ClientStreamingToServer.class); + + public static void main(String[] args) throws InterruptedException { + RemotingServer.create( + ChannelAcceptor.forRequestStream( + payload -> + Flux.interval(Duration.ofMillis(100)) + .map(aLong -> DefaultPayload.create("Interval: " + aLong)))) + .bindNow(TcpServerTransport.create("localhost", 7000)); + + Channel channel = + ChannelConnector.create() + .setupPayload(DefaultPayload.create("test", "test")) + .connect(TcpClientTransport.create("localhost", 7000)) + .block(); + + final Payload payload = DefaultPayload.create("Hello"); + channel + .requestStream(payload) + .map(Payload::getDataUtf8) + .doOnNext(logger::debug) + .take(10) + .then() + .doFinally(signalType -> channel.dispose()) + .then() + .block(); + + Thread.sleep(1000000); + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/tcp/stream/ServerStreamingToClient.java b/today-remoting/src/test/java/infra/remoting/examples/tcp/stream/ServerStreamingToClient.java new file mode 100644 index 0000000..66b4dbe --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/tcp/stream/ServerStreamingToClient.java @@ -0,0 +1,58 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.tcp.stream; + +import java.time.Duration; + +import infra.remoting.Channel; +import infra.remoting.Payload; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.transport.netty.client.TcpClientTransport; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.remoting.util.DefaultPayload; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import static infra.remoting.ChannelAcceptor.forRequestStream; + +public final class ServerStreamingToClient { + + public static void main(String[] args) { + + RemotingServer.create((setup, channel) -> { + channel + .requestStream(DefaultPayload.create("Hello-Bidi")) + .map(Payload::getDataUtf8) + .log() + .subscribe(); + + return Mono.just(new Channel() { }); + }) + .bindNow(TcpServerTransport.create("localhost", 7000)); + + Channel channel = + ChannelConnector.create() + .acceptor(forRequestStream(payload -> + Flux.interval(Duration.ofSeconds(1)) + .map(aLong -> DefaultPayload.create("Bi-di Response => " + aLong)))) + .connect(TcpClientTransport.create("localhost", 7000)) + .block(); + + channel.onClose().block(); + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/ws/WebSocketAggregationSample.java b/today-remoting/src/test/java/infra/remoting/examples/ws/WebSocketAggregationSample.java new file mode 100644 index 0000000..2913855 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/ws/WebSocketAggregationSample.java @@ -0,0 +1,76 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.ws; + +import java.time.Duration; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.Channel; +import infra.remoting.ChannelAcceptor; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.frame.decoder.PayloadDecoder; +import infra.remoting.transport.ConnectionAcceptor; +import infra.remoting.transport.websocket.WebsocketClientTransport; +import infra.remoting.transport.websocket.WebsocketConnection; +import infra.remoting.util.ByteBufPayload; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.netty.Connection; +import reactor.netty.DisposableServer; +import reactor.netty.http.server.HttpServer; + +public class WebSocketAggregationSample { + + private static final Logger logger = LoggerFactory.getLogger(WebSocketAggregationSample.class); + + public static void main(String[] args) { + + ConnectionAcceptor connectionAcceptor = + RemotingServer.create(ChannelAcceptor.forRequestResponse(Mono::just)) + .payloadDecoder(PayloadDecoder.ZERO_COPY) + .asConnectionAcceptor(); + + DisposableServer server = + HttpServer.create() + .host("localhost") + .port(0) + .handle((req, res) -> res.sendWebsocket((in, out) -> connectionAcceptor + .accept(new WebsocketConnection( + (Connection) in.aggregateFrames())) + .then(out.neverComplete()))) + .bindNow(); + + WebsocketClientTransport transport = + WebsocketClientTransport.create(server.host(), server.port()); + + Channel clientChannel = + ChannelConnector.create() + .keepAlive(Duration.ofMinutes(10), Duration.ofMinutes(10)) + .payloadDecoder(PayloadDecoder.ZERO_COPY) + .connect(transport) + .block(); + + Flux.range(1, 100) + .concatMap(i -> clientChannel.requestResponse(ByteBufPayload.create("Hello " + i))) + .doOnNext(payload -> logger.debug("Processed " + payload.getDataUtf8())) + .blockLast(); + clientChannel.dispose(); + server.dispose(); + } +} diff --git a/today-remoting/src/test/java/infra/remoting/examples/ws/WebSocketHeadersSample.java b/today-remoting/src/test/java/infra/remoting/examples/ws/WebSocketHeadersSample.java new file mode 100644 index 0000000..c2cc321 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/examples/ws/WebSocketHeadersSample.java @@ -0,0 +1,97 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.examples.ws; + +import java.time.Duration; + +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.Channel; +import infra.remoting.ChannelAcceptor; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.frame.decoder.PayloadDecoder; +import infra.remoting.transport.ConnectionAcceptor; +import infra.remoting.transport.websocket.WebsocketClientTransport; +import infra.remoting.transport.websocket.WebsocketConnection; +import infra.remoting.util.ByteBufPayload; +import io.netty.handler.codec.http.HttpResponseStatus; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.netty.Connection; +import reactor.netty.DisposableServer; +import reactor.netty.http.server.HttpServer; + +public class WebSocketHeadersSample { + + private static final Logger logger = LoggerFactory.getLogger(WebSocketHeadersSample.class); + + public static void main(String[] args) { + + ConnectionAcceptor connectionAcceptor = + RemotingServer.create(ChannelAcceptor.forRequestResponse(Mono::just)) + .payloadDecoder(PayloadDecoder.ZERO_COPY) + .asConnectionAcceptor(); + + DisposableServer server = + HttpServer.create() + .host("localhost") + .port(0) + .route(routes -> routes.get( + "/", + (req, res) -> { + if (req.requestHeaders().containsValue("Authorization", "test", true)) { + return res.sendWebsocket((in, out) -> + connectionAcceptor + .accept(new WebsocketConnection((Connection) in)) + .then(out.neverComplete())); + } + res.status(HttpResponseStatus.UNAUTHORIZED); + return res.send(); + })) + .bindNow(); + + logger.debug( + "\n\nStart of Authorized WebSocket Connection\n----------------------------------\n"); + + WebsocketClientTransport transport = + WebsocketClientTransport.create(server.host(), server.port()) + .header("Authorization", "test"); + + Channel clientChannel = + ChannelConnector.create() + .keepAlive(Duration.ofMinutes(10), Duration.ofMinutes(10)) + .payloadDecoder(PayloadDecoder.ZERO_COPY) + .connect(transport) + .block(); + + Flux.range(1, 100) + .concatMap(i -> clientChannel.requestResponse(ByteBufPayload.create("Hello " + i))) + .doOnNext(payload -> logger.debug("Processed " + payload.getDataUtf8())) + .blockLast(); + clientChannel.dispose(); + + logger.debug( + "\n\nStart of Unauthorized WebSocket Upgrade\n----------------------------------\n"); + + ChannelConnector.create() + .keepAlive(Duration.ofMinutes(10), Duration.ofMinutes(10)) + .payloadDecoder(PayloadDecoder.ZERO_COPY) + .connect(WebsocketClientTransport.create(server.host(), server.port())) + .block(); + } +} diff --git a/today-remoting/src/test/java/infra/remoting/exceptions/ApplicationErrorExceptionTests.java b/today-remoting/src/test/java/infra/remoting/exceptions/ApplicationErrorExceptionTests.java deleted file mode 100644 index 15370c2..0000000 --- a/today-remoting/src/test/java/infra/remoting/exceptions/ApplicationErrorExceptionTests.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.exceptions; - -final class ApplicationErrorExceptionTests - implements ChannelExceptionTests { - - @Override - public ApplicationErrorException getException(String message) { - return new ApplicationErrorException(message); - } - - @Override - public ApplicationErrorException getException(String message, Throwable cause) { - return new ApplicationErrorException(message, cause); - } - - @Override - public int getSpecifiedErrorCode() { - return 0x00000201; - } -} diff --git a/today-remoting/src/test/java/infra/remoting/exceptions/CanceledExceptionTests.java b/today-remoting/src/test/java/infra/remoting/exceptions/CanceledExceptionTests.java deleted file mode 100644 index f376062..0000000 --- a/today-remoting/src/test/java/infra/remoting/exceptions/CanceledExceptionTests.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.exceptions; - -final class CanceledExceptionTests implements ChannelExceptionTests { - - @Override - public CanceledException getException(String message) { - return new CanceledException(message); - } - - @Override - public CanceledException getException(String message, Throwable cause) { - return new CanceledException(message, cause); - } - - @Override - public int getSpecifiedErrorCode() { - return 0x00000203; - } -} diff --git a/today-remoting/src/test/java/infra/remoting/exceptions/ConnectionCloseExceptionTests.java b/today-remoting/src/test/java/infra/remoting/exceptions/ConnectionCloseExceptionTests.java deleted file mode 100644 index bfc8a3c..0000000 --- a/today-remoting/src/test/java/infra/remoting/exceptions/ConnectionCloseExceptionTests.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.exceptions; - -final class ConnectionCloseExceptionTests implements ChannelExceptionTests { - - @Override - public ConnectionCloseException getException(String message) { - return new ConnectionCloseException(message); - } - - @Override - public ConnectionCloseException getException(String message, Throwable cause) { - return new ConnectionCloseException(message, cause); - } - - @Override - public int getSpecifiedErrorCode() { - return 0x00000102; - } -} diff --git a/today-remoting/src/test/java/infra/remoting/exceptions/ConnectionErrorExceptionTests.java b/today-remoting/src/test/java/infra/remoting/exceptions/ConnectionErrorExceptionTests.java deleted file mode 100644 index db08cba..0000000 --- a/today-remoting/src/test/java/infra/remoting/exceptions/ConnectionErrorExceptionTests.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.exceptions; - -final class ConnectionErrorExceptionTests implements ChannelExceptionTests { - - @Override - public ConnectionErrorException getException(String message) { - return new ConnectionErrorException(message); - } - - @Override - public ConnectionErrorException getException(String message, Throwable cause) { - return new ConnectionErrorException(message, cause); - } - - @Override - public int getSpecifiedErrorCode() { - return 0x00000101; - } -} diff --git a/today-remoting/src/test/java/infra/remoting/exceptions/InvalidExceptionTests.java b/today-remoting/src/test/java/infra/remoting/exceptions/InvalidExceptionTests.java deleted file mode 100644 index bc3487e..0000000 --- a/today-remoting/src/test/java/infra/remoting/exceptions/InvalidExceptionTests.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.exceptions; - -final class InvalidExceptionTests implements ChannelExceptionTests { - - @Override - public InvalidException getException(String message) { - return new InvalidException(message); - } - - @Override - public InvalidException getException(String message, Throwable cause) { - return new InvalidException(message, cause); - } - - @Override - public int getSpecifiedErrorCode() { - return 0x00000204; - } -} diff --git a/today-remoting/src/test/java/infra/remoting/exceptions/InvalidSetupExceptionTests.java b/today-remoting/src/test/java/infra/remoting/exceptions/InvalidSetupExceptionTests.java deleted file mode 100644 index a2ae4f9..0000000 --- a/today-remoting/src/test/java/infra/remoting/exceptions/InvalidSetupExceptionTests.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.exceptions; - -final class InvalidSetupExceptionTests implements ChannelExceptionTests { - - @Override - public InvalidSetupException getException(String message) { - return new InvalidSetupException(message); - } - - @Override - public InvalidSetupException getException(String message, Throwable cause) { - return new InvalidSetupException(message, cause); - } - - @Override - public int getSpecifiedErrorCode() { - return 0x00000001; - } -} diff --git a/today-remoting/src/test/java/infra/remoting/exceptions/RejectedExceptionTests.java b/today-remoting/src/test/java/infra/remoting/exceptions/RejectedExceptionTests.java deleted file mode 100644 index 781d2a2..0000000 --- a/today-remoting/src/test/java/infra/remoting/exceptions/RejectedExceptionTests.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.exceptions; - -final class RejectedExceptionTests implements ChannelExceptionTests { - - @Override - public RejectedException getException(String message) { - return new RejectedException(message); - } - - @Override - public RejectedException getException(String message, Throwable cause) { - return new RejectedException(message, cause); - } - - @Override - public int getSpecifiedErrorCode() { - return 0x00000202; - } -} diff --git a/today-remoting/src/test/java/infra/remoting/exceptions/RejectedResumeExceptionTests.java b/today-remoting/src/test/java/infra/remoting/exceptions/RejectedResumeExceptionTests.java deleted file mode 100644 index f503d8d..0000000 --- a/today-remoting/src/test/java/infra/remoting/exceptions/RejectedResumeExceptionTests.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.exceptions; - -final class RejectedResumeExceptionTests implements ChannelExceptionTests { - - @Override - public RejectedResumeException getException(String message) { - return new RejectedResumeException(message); - } - - @Override - public RejectedResumeException getException(String message, Throwable cause) { - return new RejectedResumeException(message, cause); - } - - @Override - public int getSpecifiedErrorCode() { - return 0x00000004; - } -} diff --git a/today-remoting/src/test/java/infra/remoting/exceptions/RejectedSetupExceptionTests.java b/today-remoting/src/test/java/infra/remoting/exceptions/RejectedSetupExceptionTests.java deleted file mode 100644 index 4c0c25b..0000000 --- a/today-remoting/src/test/java/infra/remoting/exceptions/RejectedSetupExceptionTests.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.exceptions; - -final class RejectedSetupExceptionTests implements ChannelExceptionTests { - - @Override - public RejectedSetupException getException(String message) { - return new RejectedSetupException(message); - } - - @Override - public RejectedSetupException getException(String message, Throwable cause) { - return new RejectedSetupException(message, cause); - } - - @Override - public int getSpecifiedErrorCode() { - return 0x00000003; - } -} diff --git a/today-remoting/src/test/java/infra/remoting/exceptions/UnsupportedSetupExceptionTests.java b/today-remoting/src/test/java/infra/remoting/exceptions/UnsupportedSetupExceptionTests.java deleted file mode 100644 index f530802..0000000 --- a/today-remoting/src/test/java/infra/remoting/exceptions/UnsupportedSetupExceptionTests.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.exceptions; - -final class UnsupportedSetupExceptionTests - implements ChannelExceptionTests { - - @Override - public UnsupportedSetupException getException(String message) { - return new UnsupportedSetupException(message); - } - - @Override - public UnsupportedSetupException getException(String message, Throwable cause) { - return new UnsupportedSetupException(message, cause); - } - - @Override - public int getSpecifiedErrorCode() { - return 0x00000002; - } -} diff --git a/today-remoting/src/test/java/infra/remoting/frame/ByteBufRepresentation.java b/today-remoting/src/test/java/infra/remoting/frame/ByteBufRepresentation.java index 3dab653..0ebb92e 100644 --- a/today-remoting/src/test/java/infra/remoting/frame/ByteBufRepresentation.java +++ b/today-remoting/src/test/java/infra/remoting/frame/ByteBufRepresentation.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/test/java/infra/remoting/frame/ErrorFrameCodecTests.java b/today-remoting/src/test/java/infra/remoting/frame/ErrorFrameCodecTests.java index 8f53590..07c833e 100644 --- a/today-remoting/src/test/java/infra/remoting/frame/ErrorFrameCodecTests.java +++ b/today-remoting/src/test/java/infra/remoting/frame/ErrorFrameCodecTests.java @@ -1,28 +1,27 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; import org.junit.jupiter.api.Test; +import infra.remoting.error.ApplicationErrorException; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufUtil; -import infra.remoting.exceptions.ApplicationErrorException; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/today-remoting/src/test/java/infra/remoting/frame/ExtensionFrameCodecTests.java b/today-remoting/src/test/java/infra/remoting/frame/ExtensionFrameCodecTests.java index df8d25a..ffa468a 100644 --- a/today-remoting/src/test/java/infra/remoting/frame/ExtensionFrameCodecTests.java +++ b/today-remoting/src/test/java/infra/remoting/frame/ExtensionFrameCodecTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/test/java/infra/remoting/frame/FrameHeaderCodecTests.java b/today-remoting/src/test/java/infra/remoting/frame/FrameHeaderCodecTests.java index 56fde1b..16f5cbb 100644 --- a/today-remoting/src/test/java/infra/remoting/frame/FrameHeaderCodecTests.java +++ b/today-remoting/src/test/java/infra/remoting/frame/FrameHeaderCodecTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/test/java/infra/remoting/frame/GenericFrameCodecTests.java b/today-remoting/src/test/java/infra/remoting/frame/GenericFrameCodecTests.java index 028f8b8..0a72669 100644 --- a/today-remoting/src/test/java/infra/remoting/frame/GenericFrameCodecTests.java +++ b/today-remoting/src/test/java/infra/remoting/frame/GenericFrameCodecTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/test/java/infra/remoting/frame/KeepaliveFrameFlyweightTests.java b/today-remoting/src/test/java/infra/remoting/frame/KeepaliveFrameFlyweightTests.java index 6ad7371..83f1953 100644 --- a/today-remoting/src/test/java/infra/remoting/frame/KeepaliveFrameFlyweightTests.java +++ b/today-remoting/src/test/java/infra/remoting/frame/KeepaliveFrameFlyweightTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/test/java/infra/remoting/frame/LeaseFrameCodecTests.java b/today-remoting/src/test/java/infra/remoting/frame/LeaseFrameCodecTests.java index 4e87866..da4a07e 100644 --- a/today-remoting/src/test/java/infra/remoting/frame/LeaseFrameCodecTests.java +++ b/today-remoting/src/test/java/infra/remoting/frame/LeaseFrameCodecTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/test/java/infra/remoting/frame/PayloadFlyweightTests.java b/today-remoting/src/test/java/infra/remoting/frame/PayloadFlyweightTests.java index 959d6c7..45122d3 100644 --- a/today-remoting/src/test/java/infra/remoting/frame/PayloadFlyweightTests.java +++ b/today-remoting/src/test/java/infra/remoting/frame/PayloadFlyweightTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; @@ -22,11 +21,11 @@ import java.nio.charset.StandardCharsets; +import infra.remoting.Payload; +import infra.remoting.util.DefaultPayload; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; -import infra.remoting.Payload; -import infra.remoting.util.DefaultPayload; public class PayloadFlyweightTests { diff --git a/today-remoting/src/test/java/infra/remoting/frame/RequestNFrameCodecTests.java b/today-remoting/src/test/java/infra/remoting/frame/RequestNFrameCodecTests.java index 5146a4f..275187b 100644 --- a/today-remoting/src/test/java/infra/remoting/frame/RequestNFrameCodecTests.java +++ b/today-remoting/src/test/java/infra/remoting/frame/RequestNFrameCodecTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/test/java/infra/remoting/frame/ResumeFrameCodecTests.java b/today-remoting/src/test/java/infra/remoting/frame/ResumeFrameCodecTests.java index 9e691cd..2ebc72b 100644 --- a/today-remoting/src/test/java/infra/remoting/frame/ResumeFrameCodecTests.java +++ b/today-remoting/src/test/java/infra/remoting/frame/ResumeFrameCodecTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/test/java/infra/remoting/frame/ResumeOkFrameCodecTests.java b/today-remoting/src/test/java/infra/remoting/frame/ResumeOkFrameCodecTests.java index 6f1811f..6b6a3ad 100644 --- a/today-remoting/src/test/java/infra/remoting/frame/ResumeOkFrameCodecTests.java +++ b/today-remoting/src/test/java/infra/remoting/frame/ResumeOkFrameCodecTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/test/java/infra/remoting/frame/SetupFrameCodecTests.java b/today-remoting/src/test/java/infra/remoting/frame/SetupFrameCodecTests.java index 3d9deb5..099c9b6 100644 --- a/today-remoting/src/test/java/infra/remoting/frame/SetupFrameCodecTests.java +++ b/today-remoting/src/test/java/infra/remoting/frame/SetupFrameCodecTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; @@ -21,11 +20,11 @@ import java.util.Arrays; +import infra.remoting.Payload; +import infra.remoting.util.DefaultPayload; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; -import infra.remoting.Payload; -import infra.remoting.util.DefaultPayload; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/today-remoting/src/test/java/infra/remoting/frame/VersionCodecTests.java b/today-remoting/src/test/java/infra/remoting/frame/VersionCodecTests.java index fecc14f..95ebc0f 100644 --- a/today-remoting/src/test/java/infra/remoting/frame/VersionCodecTests.java +++ b/today-remoting/src/test/java/infra/remoting/frame/VersionCodecTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.frame; diff --git a/today-remoting/src/test/java/infra/remoting/internal/SchedulerUtils.java b/today-remoting/src/test/java/infra/remoting/internal/SchedulerUtils.java index 90e9945..856beda 100644 --- a/today-remoting/src/test/java/infra/remoting/internal/SchedulerUtils.java +++ b/today-remoting/src/test/java/infra/remoting/internal/SchedulerUtils.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.internal; diff --git a/today-remoting/src/test/java/infra/remoting/internal/UnboundedProcessorTests.java b/today-remoting/src/test/java/infra/remoting/internal/UnboundedProcessorTests.java index 3beceeb..c2ac2a5 100644 --- a/today-remoting/src/test/java/infra/remoting/internal/UnboundedProcessorTests.java +++ b/today-remoting/src/test/java/infra/remoting/internal/UnboundedProcessorTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.internal; @@ -24,14 +23,14 @@ import java.time.Duration; +import infra.remoting.RaceTestConstants; +import infra.remoting.buffer.LeaksTrackingByteBufAllocator; +import infra.remoting.internal.subscriber.AssertSubscriber; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; import io.netty.util.CharsetUtil; import io.netty.util.ReferenceCountUtil; -import infra.remoting.RaceTestConstants; -import infra.remoting.buffer.LeaksTrackingByteBufAllocator; -import infra.remoting.internal.subscriber.AssertSubscriber; import reactor.core.Fuseable; import reactor.core.publisher.Hooks; import reactor.core.scheduler.Schedulers; diff --git a/today-remoting/src/test/java/infra/remoting/internal/subscriber/AssertSubscriber.java b/today-remoting/src/test/java/infra/remoting/internal/subscriber/AssertSubscriber.java index baceb24..e8c50e5 100644 --- a/today-remoting/src/test/java/infra/remoting/internal/subscriber/AssertSubscriber.java +++ b/today-remoting/src/test/java/infra/remoting/internal/subscriber/AssertSubscriber.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.internal.subscriber; @@ -38,7 +37,6 @@ import java.util.function.Consumer; import java.util.function.Supplier; -import infra.lang.NonNull; import reactor.core.CoreSubscriber; import reactor.core.Fuseable; import reactor.core.Scannable; @@ -1082,7 +1080,6 @@ public void request(long n) { } @Override - @NonNull public Context currentContext() { return context; } diff --git a/today-remoting/src/test/java/infra/remoting/lb/LoadbalanceChannelClientTests.java b/today-remoting/src/test/java/infra/remoting/lb/LoadbalanceChannelClientTests.java index 9bba704..16de46a 100644 --- a/today-remoting/src/test/java/infra/remoting/lb/LoadbalanceChannelClientTests.java +++ b/today-remoting/src/test/java/infra/remoting/lb/LoadbalanceChannelClientTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; @@ -26,10 +25,10 @@ import java.time.Duration; import java.util.concurrent.atomic.AtomicInteger; -import infra.remoting.Payload; import infra.remoting.Channel; -import infra.remoting.core.RemotingClient; +import infra.remoting.Payload; import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingClient; import infra.remoting.transport.ClientTransport; import infra.remoting.util.DefaultPayload; import reactor.core.publisher.Flux; @@ -78,17 +77,14 @@ public Flux requestChannel(Publisher payloads) { void testChannelReconnection() { when(channelConnector.connect(clientTransport)).thenReturn(PROGRESSING_HANDLER); - RemotingClient client = - LoadBalanceRemotingClient.create( - channelConnector, - Mono.just(singletonList(LoadBalanceTarget.of("key", clientTransport)))); - - Publisher result = - client - .requestChannel(SOURCE) - .repeatWhen(longFlux -> longFlux.delayElements(LONG_DURATION).take(5)) - .map(Payload::getDataUtf8) - .log(); + RemotingClient client = RemotingClient.forLoadBalance(channelConnector, + Mono.just(singletonList(LoadBalanceTarget.of("key", clientTransport)))); + + Publisher result = client + .requestChannel(SOURCE) + .repeatWhen(longFlux -> longFlux.delayElements(LONG_DURATION).take(5)) + .map(Payload::getDataUtf8) + .log(); StepVerifier.create(result) .expectSubscription() diff --git a/today-remoting/src/test/java/infra/remoting/lb/LoadbalanceTests.java b/today-remoting/src/test/java/infra/remoting/lb/LoadbalanceTests.java index d59c0f6..4867b05 100644 --- a/today-remoting/src/test/java/infra/remoting/lb/LoadbalanceTests.java +++ b/today-remoting/src/test/java/infra/remoting/lb/LoadbalanceTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; @@ -30,15 +29,15 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; -import infra.remoting.Payload; import infra.remoting.Channel; +import infra.remoting.DecoratingChannel; +import infra.remoting.Payload; import infra.remoting.RaceTestConstants; import infra.remoting.core.ChannelConnector; import infra.remoting.internal.subscriber.AssertSubscriber; import infra.remoting.test.util.TestClientTransport; import infra.remoting.transport.ClientTransport; import infra.remoting.util.EmptyPayload; -import infra.remoting.util.ChannelDecorator; import reactor.core.CoreSubscriber; import reactor.core.publisher.Flux; import reactor.core.publisher.Hooks; @@ -130,14 +129,14 @@ public void shouldDeliverAllTheRequestsWithWeightedStrategy() throws Interrupted final LoadBalanceTarget target1 = LoadBalanceTarget.of("1", mockTransport1); final LoadBalanceTarget target2 = LoadBalanceTarget.of("2", mockTransport2); - final WeightedChannel weightedRSocket1 = new WeightedChannel(counter); - final WeightedChannel weightedRSocket2 = new WeightedChannel(counter); + final WeightedChannel weightedChannel1 = new WeightedChannel(counter); + final WeightedChannel weightedChannel2 = new WeightedChannel(counter); final ChannelConnector channelConnectorMock = Mockito.mock(ChannelConnector.class); Mockito.when(channelConnectorMock.connect(mockTransport1)) - .then(im -> Mono.just(new TestChannel(weightedRSocket1))); + .then(im -> Mono.just(new TestChannel(weightedChannel1))); Mockito.when(channelConnectorMock.connect(mockTransport2)) - .then(im -> Mono.just(new TestChannel(weightedRSocket2))); + .then(im -> Mono.just(new TestChannel(weightedChannel2))); final List collectionOfDestination1 = Collections.singletonList(target1); final List collectionOfDestination2 = Collections.singletonList(target2); final List collectionOfDestinations1And2 = Arrays.asList(target1, target2); @@ -151,13 +150,13 @@ public void shouldDeliverAllTheRequestsWithWeightedStrategy() throws Interrupted source.asFlux(), WeightedLoadBalanceStrategy.builder() .weightedStatsResolver( - rsocket -> { - if (rsocket instanceof TestChannel) { - return (WeightedChannel) ((TestChannel) rsocket).source(); + channel -> { + if (channel instanceof TestChannel) { + return (WeightedChannel) ((TestChannel) channel).source(); } - return ((PooledChannel) rsocket).target() == target1 - ? weightedRSocket1 - : weightedRSocket2; + return ((PooledChannel) channel).target() == target1 + ? weightedChannel1 + : weightedChannel2; }) .build()); final Mono fnfSource = @@ -188,12 +187,12 @@ public void shouldDeliverAllTheRequestsWithWeightedStrategy() throws Interrupted } @Test - public void ensureRSocketIsCleanedFromThePoolIfSourceRSocketIsDisposed() { + public void ensureChannelIsCleanedFromThePoolIfSourceChannelIsDisposed() { final AtomicInteger counter = new AtomicInteger(); final ClientTransport mockTransport = Mockito.mock(ClientTransport.class); final ChannelConnector channelConnectorMock = Mockito.mock(ChannelConnector.class); - final TestChannel testRSocket = + final TestChannel testChannel = new TestChannel( new Channel() { @Override @@ -204,7 +203,7 @@ public Mono fireAndForget(Payload payload) { }); Mockito.when(channelConnectorMock.connect(Mockito.any(ClientTransport.class))) - .then(im -> Mono.delay(Duration.ofMillis(200)).map(__ -> testRSocket)); + .then(im -> Mono.delay(Duration.ofMillis(200)).map(__ -> testChannel)); final TestPublisher> source = TestPublisher.create(); final ChannelPool channelPool = @@ -217,7 +216,7 @@ public Mono fireAndForget(Payload payload) { .expectComplete() .verify(Duration.ofSeconds(2)); - testRSocket.dispose(); + testChannel.dispose(); Assertions.assertThatThrownBy( () -> @@ -256,7 +255,7 @@ public Flux requestChannel(Publisher source) { final ChannelPool channelPool = new ChannelPool(channelConnectorMock, source, new RoundRobinLoadBalanceStrategy()); - // check that context is propagated when there is no rsocket + // check that context is propagated when there is no channel StepVerifier.create( channelPool .select() @@ -281,7 +280,7 @@ public Flux requestChannel(Publisher source) { .verify(Duration.ofSeconds(2)); source.next(Collections.singletonList(LoadBalanceTarget.of("2", mockTransport))); - // check that context is propagated when there is an RSocket but it is unresolved + // check that context is propagated when there is a Channel but it is unresolved StepVerifier.create( channelPool .select() @@ -302,7 +301,7 @@ public Flux requestChannel(Publisher source) { .expectComplete() .verify(Duration.ofSeconds(2)); - // check that context is propagated when there is an RSocket and it is resolved + // check that context is propagated when there is a Channel and it is resolved StepVerifier.create( channelPool .select() @@ -405,7 +404,7 @@ public Mono fireAndForget(Payload payload) { Assertions.assertThat(counter.get()).isOne(); } - static class TestChannel extends ChannelDecorator { + static class TestChannel extends DecoratingChannel { final Sinks.Empty sink = Sinks.empty(); diff --git a/today-remoting/src/test/java/infra/remoting/lb/RoundRobinLoadBalanceStrategyTests.java b/today-remoting/src/test/java/infra/remoting/lb/RoundRobinLoadBalanceStrategyTests.java index 61d0285..bf8b518 100644 --- a/today-remoting/src/test/java/infra/remoting/lb/RoundRobinLoadBalanceStrategyTests.java +++ b/today-remoting/src/test/java/infra/remoting/lb/RoundRobinLoadBalanceStrategyTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; @@ -29,8 +28,8 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; -import infra.remoting.Payload; import infra.remoting.Channel; +import infra.remoting.Payload; import infra.remoting.RaceTestConstants; import infra.remoting.core.ChannelConnector; import infra.remoting.transport.ClientTransport; diff --git a/today-remoting/src/test/java/infra/remoting/lb/WeightedLoadBalanceStrategyTests.java b/today-remoting/src/test/java/infra/remoting/lb/WeightedLoadBalanceStrategyTests.java index 671d45b..173f98d 100644 --- a/today-remoting/src/test/java/infra/remoting/lb/WeightedLoadBalanceStrategyTests.java +++ b/today-remoting/src/test/java/infra/remoting/lb/WeightedLoadBalanceStrategyTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lb; @@ -29,8 +28,8 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; -import infra.remoting.Payload; import infra.remoting.Channel; +import infra.remoting.Payload; import infra.remoting.RaceTestConstants; import infra.remoting.core.ChannelConnector; import infra.remoting.transport.ClientTransport; @@ -59,7 +58,7 @@ public void allRequestsShouldGoToTheSocketWithHigherWeight() { final AtomicInteger counter2 = new AtomicInteger(); final ClientTransport mockTransport = Mockito.mock(ClientTransport.class); final ChannelConnector channelConnectorMock = Mockito.mock(ChannelConnector.class); - final WeightedTestChannel rSocket1 = + final WeightedTestChannel channel = new WeightedTestChannel( new Channel() { @Override @@ -68,7 +67,7 @@ public Mono fireAndForget(Payload payload) { return Mono.empty(); } }); - final WeightedTestChannel rSocket2 = + final WeightedTestChannel channel1 = new WeightedTestChannel( new Channel() { @Override @@ -78,8 +77,8 @@ public Mono fireAndForget(Payload payload) { } }); Mockito.when(channelConnectorMock.connect(Mockito.any(ClientTransport.class))) - .then(im -> Mono.just(rSocket1)) - .then(im -> Mono.just(rSocket2)); + .then(im -> Mono.just(channel)) + .then(im -> Mono.just(channel1)); final TestPublisher> source = TestPublisher.create(); final ChannelPool channelPool = @@ -115,7 +114,7 @@ public void shouldDeliverValuesToTheSocketWithTheHighestCalculatedWeight() { final ClientTransport mockTransport1 = Mockito.mock(ClientTransport.class); final ClientTransport mockTransport2 = Mockito.mock(ClientTransport.class); final ChannelConnector channelConnectorMock = Mockito.mock(ChannelConnector.class); - final WeightedTestChannel rSocket1 = + final WeightedTestChannel channel1 = new WeightedTestChannel( new Channel() { @Override @@ -124,7 +123,7 @@ public Mono fireAndForget(Payload payload) { return Mono.empty(); } }); - final WeightedTestChannel rSocket2 = + final WeightedTestChannel channel2 = new WeightedTestChannel( new Channel() { @Override @@ -133,7 +132,7 @@ public Mono fireAndForget(Payload payload) { return Mono.empty(); } }); - final WeightedTestChannel rSocket3 = + final WeightedTestChannel channel3 = new WeightedTestChannel( new Channel() { @Override @@ -144,9 +143,9 @@ public Mono fireAndForget(Payload payload) { }); Mockito.when(channelConnectorMock.connect(Mockito.any(ClientTransport.class))) - .then(im -> Mono.just(rSocket1)) - .then(im -> Mono.just(rSocket2)) - .then(im -> Mono.just(rSocket3)); + .then(im -> Mono.just(channel1)) + .then(im -> Mono.just(channel2)) + .then(im -> Mono.just(channel3)); final TestPublisher> source = TestPublisher.create(); final ChannelPool channelPool = @@ -171,7 +170,7 @@ public Mono fireAndForget(Payload payload) { channelPool.select().fireAndForget(EmptyPayload.INSTANCE).subscribe(); } - rSocket1.updateAvailability(0.0); + channel1.updateAvailability(0.0); source.next(Collections.singletonList(LoadBalanceTarget.of("1", mockTransport1))); @@ -195,7 +194,7 @@ public Mono fireAndForget(Payload payload) { Assertions.assertThat(counter2.get()) .isCloseTo(0, Offset.offset(Math.round(RaceTestConstants.REPEATS * 3 * 0.1f))); - rSocket2.updateAvailability(0.0); + channel2.updateAvailability(0.0); source.next(Collections.singletonList(LoadBalanceTarget.of("2", mockTransport1))); diff --git a/today-remoting/src/test/java/infra/remoting/lease/LeaseImplTests.java b/today-remoting/src/test/java/infra/remoting/lease/LeaseImplTests.java index e879920..b7e7a73 100644 --- a/today-remoting/src/test/java/infra/remoting/lease/LeaseImplTests.java +++ b/today-remoting/src/test/java/infra/remoting/lease/LeaseImplTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.lease; diff --git a/today-remoting/src/test/java/infra/remoting/plugins/RequestInterceptorTests.java b/today-remoting/src/test/java/infra/remoting/plugins/RequestInterceptorTests.java index fe16336..3ccf358 100644 --- a/today-remoting/src/test/java/infra/remoting/plugins/RequestInterceptorTests.java +++ b/today-remoting/src/test/java/infra/remoting/plugins/RequestInterceptorTests.java @@ -1,22 +1,22 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.plugins; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -27,13 +27,10 @@ import java.util.concurrent.TimeUnit; import java.util.function.Function; -import infra.lang.Nullable; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import infra.remoting.Closeable; -import infra.remoting.Payload; import infra.remoting.Channel; import infra.remoting.ChannelAcceptor; +import infra.remoting.Closeable; +import infra.remoting.Payload; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; import infra.remoting.core.ChannelConnector; import infra.remoting.core.RemotingServer; @@ -41,6 +38,8 @@ import infra.remoting.transport.local.LocalClientTransport; import infra.remoting.transport.local.LocalServerTransport; import infra.remoting.util.DefaultPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -172,28 +171,28 @@ void interceptorShouldBeInstalledProperlyOnTheClientResponderSide(boolean errorO CountDownLatch latch = new CountDownLatch(1); final Closeable closeable = RemotingServer.create( - (setup, rSocket) -> + (setup, channel) -> Mono.just(new Channel() { }) .doAfterTerminate( () -> { new Thread( () -> { - rSocket + channel .fireAndForget(DefaultPayload.create("test")) .onErrorResume(__ -> Mono.empty()) .block(); - rSocket + channel .requestResponse(DefaultPayload.create("test")) .onErrorResume(__ -> Mono.empty()) .block(); - rSocket + channel .requestStream(DefaultPayload.create("test")) .onErrorResume(__ -> Mono.empty()) .blockLast(); - rSocket + channel .requestChannel( Flux.just(DefaultPayload.create("test"))) .onErrorResume(__ -> Mono.empty()) @@ -412,25 +411,25 @@ void interceptorShouldBeInstalledProperlyOnTheServerResponderSide(boolean errorO CountDownLatch latch = new CountDownLatch(1); final TestRequestInterceptor testRequestInterceptor = new TestRequestInterceptor(); - final Closeable closeable = RemotingServer.create((setup, rSocket) -> Mono.just(new Channel() { }) + final Closeable closeable = RemotingServer.create((setup, channel) -> Mono.just(new Channel() { }) .doAfterTerminate(() -> { new Thread(() -> { - rSocket + channel .fireAndForget(DefaultPayload.create("test")) .onErrorResume(__ -> Mono.empty()) .block(); - rSocket + channel .requestResponse(DefaultPayload.create("test")) .onErrorResume(__ -> Mono.empty()) .block(); - rSocket + channel .requestStream(DefaultPayload.create("test")) .onErrorResume(__ -> Mono.empty()) .blockLast(); - rSocket + channel .requestChannel( Flux.just(DefaultPayload.create("test"))) .onErrorResume(__ -> Mono.empty()) diff --git a/today-remoting/src/test/java/infra/remoting/plugins/TestRequestInterceptor.java b/today-remoting/src/test/java/infra/remoting/plugins/TestRequestInterceptor.java index 64ad6c5..6d5b9ab 100644 --- a/today-remoting/src/test/java/infra/remoting/plugins/TestRequestInterceptor.java +++ b/today-remoting/src/test/java/infra/remoting/plugins/TestRequestInterceptor.java @@ -1,32 +1,31 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.plugins; import org.assertj.core.api.Assertions; import org.assertj.core.api.Condition; +import org.jspecify.annotations.Nullable; import java.util.Queue; import java.util.function.Consumer; -import infra.lang.Nullable; -import io.netty.buffer.ByteBuf; import infra.remoting.frame.FrameType; import infra.remoting.internal.jctools.queues.MpscUnboundedArrayQueue; +import io.netty.buffer.ByteBuf; public class TestRequestInterceptor implements RequestInterceptor { @@ -42,9 +41,8 @@ public void onStart(int streamId, FrameType requestType, @Nullable ByteBuf metad @Override public void onTerminate(int streamId, FrameType requestType, @Nullable Throwable t) { - events.add( - new Event( - t == null ? EventType.ON_COMPLETE : EventType.ON_ERROR, streamId, requestType, t)); + events.add(new Event( + t == null ? EventType.ON_COMPLETE : EventType.ON_ERROR, streamId, requestType, t)); } @Override diff --git a/today-remoting/src/test/java/infra/remoting/protocol/MetadataTests.java b/today-remoting/src/test/java/infra/remoting/protocol/MetadataTests.java index 15a9daf..a157a8d 100644 --- a/today-remoting/src/test/java/infra/remoting/protocol/MetadataTests.java +++ b/today-remoting/src/test/java/infra/remoting/protocol/MetadataTests.java @@ -1,24 +1,21 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.protocol; -import static org.junit.jupiter.api.Assertions.*; - /** * @author 海子 Yang * @since 1.0 2025/7/31 17:05 diff --git a/today-remoting/src/test/java/infra/remoting/resume/ClientChannelSessionTests.java b/today-remoting/src/test/java/infra/remoting/resume/ClientChannelSessionTests.java index ebfbc12..e0bf3f5 100644 --- a/today-remoting/src/test/java/infra/remoting/resume/ClientChannelSessionTests.java +++ b/today-remoting/src/test/java/infra/remoting/resume/ClientChannelSessionTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.resume; @@ -22,19 +21,19 @@ import java.time.Duration; import java.util.concurrent.atomic.AtomicBoolean; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.ReferenceCounted; import infra.remoting.FrameAssert; -import infra.remoting.exceptions.ConnectionCloseException; -import infra.remoting.exceptions.RejectedResumeException; +import infra.remoting.error.ConnectionCloseException; +import infra.remoting.error.RejectedResumeException; import infra.remoting.frame.ErrorFrameCodec; import infra.remoting.frame.FrameType; import infra.remoting.frame.KeepAliveFrameCodec; import infra.remoting.frame.ResumeOkFrameCodec; import infra.remoting.keepalive.KeepAliveSupport; import infra.remoting.test.util.TestClientTransport; -import infra.remoting.test.util.TestDuplexConnection; +import infra.remoting.test.util.TestConnection; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.util.ReferenceCounted; import reactor.core.publisher.Operators; import reactor.test.StepVerifier; import reactor.test.scheduler.VirtualTimeScheduler; @@ -43,7 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat; -public class ClientChannelSessionTests { +class ClientChannelSessionTests { @Test void sessionTimeoutSmokeTest() { @@ -55,20 +54,20 @@ void sessionTimeoutSmokeTest() { transport.connect().subscribe(); - final ResumableDuplexConnection resumableDuplexConnection = - new ResumableDuplexConnection( + final ResumableConnection resumableConnection = + new ResumableConnection( "test", Unpooled.EMPTY_BUFFER, transport.testConnection(), framesStore); - resumableDuplexConnection.receive().subscribe(); + resumableConnection.receive().subscribe(); final ClientChannelSession session = new ClientChannelSession( Unpooled.EMPTY_BUFFER, - resumableDuplexConnection, + resumableConnection, transport.connect().delaySubscription(Duration.ofMillis(1)), c -> { AtomicBoolean firstHandled = new AtomicBoolean(); - return ((TestDuplexConnection) c) + return ((TestConnection) c) .receive() .next() .doOnNext(__ -> firstHandled.set(true)) @@ -177,7 +176,7 @@ void sessionTimeoutSmokeTest() { assertThat(session.isDisposed()).isTrue(); - resumableDuplexConnection.onClose().as(StepVerifier::create).expectComplete().verify(); + resumableConnection.onClose().as(StepVerifier::create).expectComplete().verify(); keepAliveSupport.dispose(); transport.alloc().assertHasNoLeaks(); } @@ -197,20 +196,20 @@ void sessionTerminationOnWrongFrameTest() { transport.connect().subscribe(); - final ResumableDuplexConnection resumableDuplexConnection = - new ResumableDuplexConnection( + final ResumableConnection resumableConnection = + new ResumableConnection( "test", Unpooled.EMPTY_BUFFER, transport.testConnection(), framesStore); - resumableDuplexConnection.receive().subscribe(); + resumableConnection.receive().subscribe(); final ClientChannelSession session = new ClientChannelSession( Unpooled.EMPTY_BUFFER, - resumableDuplexConnection, + resumableConnection, transport.connect().delaySubscription(Duration.ofMillis(1)), c -> { AtomicBoolean firstHandled = new AtomicBoolean(); - return ((TestDuplexConnection) c) + return ((TestConnection) c) .receive() .next() .doOnNext(__ -> firstHandled.set(true)) @@ -307,7 +306,7 @@ void sessionTerminationOnWrongFrameTest() { .typeOf(FrameType.ERROR) .matches(ReferenceCounted::release); - resumableDuplexConnection + resumableConnection .onClose() .as(StepVerifier::create) .expectErrorMessage("RESUME_OK frame must be received before any others") @@ -330,20 +329,20 @@ void shouldErrorWithNoRetriesOnErrorFrameTest() { transport.connect().subscribe(); - final ResumableDuplexConnection resumableDuplexConnection = - new ResumableDuplexConnection( + final ResumableConnection resumableConnection = + new ResumableConnection( "test", Unpooled.EMPTY_BUFFER, transport.testConnection(), framesStore); - resumableDuplexConnection.receive().subscribe(); + resumableConnection.receive().subscribe(); final ClientChannelSession session = new ClientChannelSession( Unpooled.EMPTY_BUFFER, - resumableDuplexConnection, + resumableConnection, transport.connect().delaySubscription(Duration.ofMillis(1)), c -> { AtomicBoolean firstHandled = new AtomicBoolean(); - return ((TestDuplexConnection) c) + return ((TestConnection) c) .receive() .next() .doOnNext(__ -> firstHandled.set(true)) @@ -404,7 +403,7 @@ void shouldErrorWithNoRetriesOnErrorFrameTest() { assertThat(session.s).isNotNull(); assertThat(session.isDisposed()).isTrue(); - resumableDuplexConnection + resumableConnection .onClose() .as(StepVerifier::create) .expectError(RejectedResumeException.class) @@ -427,20 +426,20 @@ void shouldTerminateConnectionOnIllegalStateInKeepAliveFrame() { transport.connect().subscribe(); - final ResumableDuplexConnection resumableDuplexConnection = - new ResumableDuplexConnection( + final ResumableConnection resumableConnection = + new ResumableConnection( "test", Unpooled.EMPTY_BUFFER, transport.testConnection(), framesStore); - resumableDuplexConnection.receive().subscribe(); + resumableConnection.receive().subscribe(); final ClientChannelSession session = new ClientChannelSession( Unpooled.EMPTY_BUFFER, - resumableDuplexConnection, + resumableConnection, transport.connect().delaySubscription(Duration.ofMillis(1)), c -> { AtomicBoolean firstHandled = new AtomicBoolean(); - return ((TestDuplexConnection) c) + return ((TestConnection) c) .receive() .next() .doOnNext(__ -> firstHandled.set(true)) @@ -482,7 +481,7 @@ void shouldTerminateConnectionOnIllegalStateInKeepAliveFrame() { .typeOf(FrameType.ERROR) .matches(ReferenceCounted::release); - resumableDuplexConnection.onClose().as(StepVerifier::create).expectError().verify(); + resumableConnection.onClose().as(StepVerifier::create).expectError().verify(); keepAliveSupport.dispose(); transport.alloc().assertHasNoLeaks(); } diff --git a/today-remoting/src/test/java/infra/remoting/resume/DisconnectableClientTransport.java b/today-remoting/src/test/java/infra/remoting/resume/DisconnectableClientTransport.java new file mode 100644 index 0000000..444729e --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/resume/DisconnectableClientTransport.java @@ -0,0 +1,77 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.resume; + +import java.nio.channels.ClosedChannelException; +import java.time.Duration; +import java.util.concurrent.atomic.AtomicReference; + +import infra.remoting.Connection; +import infra.remoting.transport.ClientTransport; +import reactor.core.publisher.Mono; + +class DisconnectableClientTransport implements ClientTransport { + + private final ClientTransport clientTransport; + + private final AtomicReference curConnection = new AtomicReference<>(); + + private long nextConnectPermitMillis; + + public DisconnectableClientTransport(ClientTransport clientTransport) { + this.clientTransport = clientTransport; + } + + @Override + public Mono connect() { + return Mono.defer(() -> + now() < nextConnectPermitMillis + ? Mono.error(new ClosedChannelException()) + : clientTransport.connect().map(c -> { + if (curConnection.compareAndSet(null, c)) { + return c; + } + else { + throw new IllegalStateException( + "Transport supports at most 1 connection"); + } + })); + } + + public void disconnect() { + disconnectFor(Duration.ZERO); + } + + public void disconnectPermanently() { + disconnectFor(Duration.ofDays(42)); + } + + public void disconnectFor(Duration cooldown) { + Connection cur = curConnection.getAndSet(null); + if (cur != null) { + nextConnectPermitMillis = now() + cooldown.toMillis(); + cur.dispose(); + } + else { + throw new IllegalStateException("Trying to disconnect while not connected"); + } + } + + private static long now() { + return System.currentTimeMillis(); + } +} diff --git a/today-remoting/src/test/java/infra/remoting/resume/InMemoryResumeStoreTests.java b/today-remoting/src/test/java/infra/remoting/resume/InMemoryResumeStoreTests.java index 549a13a..8dcf9aa 100644 --- a/today-remoting/src/test/java/infra/remoting/resume/InMemoryResumeStoreTests.java +++ b/today-remoting/src/test/java/infra/remoting/resume/InMemoryResumeStoreTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.resume; @@ -28,12 +27,12 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.ReferenceCounted; import infra.remoting.RaceTestConstants; import infra.remoting.internal.UnboundedProcessor; import infra.remoting.internal.subscriber.AssertSubscriber; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.util.ReferenceCounted; import reactor.core.Disposable; import reactor.core.publisher.Hooks; import reactor.test.util.RaceTestUtils; @@ -294,7 +293,7 @@ void sendingVsReconnectRaceTest(boolean withLateSubscriber) { final Consumer consumer = f -> { - if (ResumableDuplexConnection.isResumableFrame(f)) { + if (ResumableConnection.isResumableFrame(f)) { receivedPosition.addAndGet(f.readableBytes()); receivedFrames.offer(f); return; diff --git a/today-remoting/src/test/java/infra/remoting/resume/ResumeIntegrationTests.java b/today-remoting/src/test/java/infra/remoting/resume/ResumeIntegrationTests.java new file mode 100644 index 0000000..d358c25 --- /dev/null +++ b/today-remoting/src/test/java/infra/remoting/resume/ResumeIntegrationTests.java @@ -0,0 +1,224 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.remoting.resume; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.reactivestreams.Publisher; + +import java.net.InetSocketAddress; +import java.nio.channels.ClosedChannelException; +import java.time.Duration; +import java.util.concurrent.atomic.AtomicInteger; + +import infra.remoting.Channel; +import infra.remoting.ChannelAcceptor; +import infra.remoting.Payload; +import infra.remoting.core.ChannelConnector; +import infra.remoting.core.RemotingServer; +import infra.remoting.core.Resume; +import infra.remoting.error.RejectedResumeException; +import infra.remoting.error.UnsupportedSetupException; +import infra.remoting.test.SlowTest; +import infra.remoting.transport.ClientTransport; +import infra.remoting.transport.ServerTransport; +import infra.remoting.transport.netty.client.TcpClientTransport; +import infra.remoting.transport.netty.server.CloseableChannel; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.remoting.util.DefaultPayload; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; +import reactor.test.StepVerifier; +import reactor.util.retry.Retry; + +@SlowTest +public class ResumeIntegrationTests { + + private static final String SERVER_HOST = "localhost"; + + private static final int SERVER_PORT = 0; + + @Test + void timeoutOnPermanentDisconnect() { + CloseableChannel closeable = newServerChannel().block(); + + DisconnectableClientTransport clientTransport = + new DisconnectableClientTransport(clientTransport(closeable.address())); + + int sessionDurationSeconds = 5; + Channel channel = newClientChannel(clientTransport, sessionDurationSeconds).block(); + + Mono.delay(Duration.ofSeconds(1)).subscribe(v -> clientTransport.disconnectPermanently()); + + StepVerifier.create(channel.requestChannel(testRequest()).then().doFinally(s -> closeable.dispose())) + .expectError(ClosedChannelException.class) + .verify(Duration.ofSeconds(7)); + } + + @Test + public void reconnectOnDisconnect() { + CloseableChannel closeable = newServerChannel().block(); + + DisconnectableClientTransport clientTransport = + new DisconnectableClientTransport(clientTransport(closeable.address())); + + int sessionDurationSeconds = 15; + Channel channel = newClientChannel(clientTransport, sessionDurationSeconds).block(); + + Flux.just(3, 20, 40, 75) + .flatMap(v -> Mono.delay(Duration.ofSeconds(v))) + .subscribe(v -> clientTransport.disconnectFor(Duration.ofSeconds(7))); + + AtomicInteger counter = new AtomicInteger(-1); + StepVerifier.create( + channel + .requestChannel(testRequest()) + .take(Duration.ofSeconds(600)) + .map(Payload::getDataUtf8) + .timeout(Duration.ofSeconds(12)) + .doOnNext(x -> throwOnNonContinuous(counter, x)) + .then() + .doFinally(s -> closeable.dispose())) + .expectComplete() + .verify(); + } + + @Test + public void reconnectOnMissingSession() { + + int serverSessionDuration = 2; + + CloseableChannel closeable = newServerChannel(serverSessionDuration).block(); + + DisconnectableClientTransport clientTransport = + new DisconnectableClientTransport(clientTransport(closeable.address())); + int clientSessionDurationSeconds = 10; + + Channel channel = newClientChannel(clientTransport, clientSessionDurationSeconds).block(); + + Mono.delay(Duration.ofSeconds(1)) + .subscribe(v -> clientTransport.disconnectFor(Duration.ofSeconds(3))); + + StepVerifier.create( + channel.requestChannel(testRequest()).then().doFinally(s -> closeable.dispose())) + .expectError() + .verify(Duration.ofSeconds(5)); + + StepVerifier.create(channel.onClose()) + .expectErrorMatches( + err -> + err instanceof RejectedResumeException + && "unknown resume token".equals(err.getMessage())) + .verify(Duration.ofSeconds(5)); + } + + @Test + void serverMissingResume() { + CloseableChannel closeableChannel = + RemotingServer.create(ChannelAcceptor.with(new TestResponderChannel())) + .bind(serverTransport(SERVER_HOST, SERVER_PORT)) + .block(); + + Channel channel = ChannelConnector.create() + .resume(new Resume()) + .connect(clientTransport(closeableChannel.address())) + .block(); + + StepVerifier.create(channel.onClose().doFinally(s -> closeableChannel.dispose())) + .expectErrorMatches(err -> err instanceof UnsupportedSetupException + && "resume not supported".equals(err.getMessage())) + .verify(Duration.ofSeconds(5)); + + Assertions.assertThat(channel.isDisposed()).isTrue(); + } + + static ClientTransport clientTransport(InetSocketAddress address) { + return TcpClientTransport.create(address); + } + + static ServerTransport serverTransport(String host, int port) { + return TcpServerTransport.create(host, port); + } + + private static Flux testRequest() { + return Flux.interval(Duration.ofMillis(500)) + .map(v -> DefaultPayload.create("client_request")) + .onBackpressureDrop(); + } + + private void throwOnNonContinuous(AtomicInteger counter, String x) { + int curValue = Integer.parseInt(x); + int prevValue = counter.get(); + if (prevValue >= 0) { + int dif = curValue - prevValue; + if (dif != 1) { + throw new IllegalStateException(String.format( + "Payload values are expected to be continuous numbers: %d %d", prevValue, curValue)); + } + } + counter.set(curValue); + } + + private static Mono newClientChannel( + DisconnectableClientTransport clientTransport, int sessionDurationSeconds) { + return ChannelConnector.create() + .resume(new Resume() + .sessionDuration(Duration.ofSeconds(sessionDurationSeconds)) + .storeFactory(new InMemoryResumableFramesStoreFactory("client", 500_000)) + .cleanupStoreOnKeepAlive(true) + .retry(Retry.fixedDelay(Long.MAX_VALUE, Duration.ofSeconds(1)))) + .keepAlive(Duration.ofSeconds(5), Duration.ofMinutes(5)) + .connect(clientTransport); + } + + private static Mono newServerChannel() { + return newServerChannel(15); + } + + private static Mono newServerChannel(int sessionDurationSeconds) { + return RemotingServer.create(ChannelAcceptor.with(new TestResponderChannel())) + .resume(new Resume().cleanupStoreOnKeepAlive() + .sessionDuration(Duration.ofSeconds(sessionDurationSeconds)) + .storeFactory(new InMemoryResumableFramesStoreFactory("server", 500_000))) + .bind(serverTransport(SERVER_HOST, SERVER_PORT)); + } + + private static class TestResponderChannel implements Channel { + + AtomicInteger counter = new AtomicInteger(); + + @Override + public Flux requestChannel(Publisher payloads) { + return duplicate( + Flux.interval(Duration.ofMillis(1)) + .onBackpressureLatest() + .publishOn(Schedulers.boundedElastic()), + 20) + .map(v -> DefaultPayload.create(String.valueOf(counter.getAndIncrement()))) + .takeUntilOther(Flux.from(payloads).then()); + } + + private Flux duplicate(Flux f, int n) { + Flux r = Flux.empty(); + for (int i = 0; i < n; i++) { + r = r.mergeWith(f); + } + return r; + } + } +} diff --git a/today-remoting/src/test/java/infra/remoting/resume/ServerChannelSessionTests.java b/today-remoting/src/test/java/infra/remoting/resume/ServerChannelSessionTests.java index 1bd2004..3123b83 100644 --- a/today-remoting/src/test/java/infra/remoting/resume/ServerChannelSessionTests.java +++ b/today-remoting/src/test/java/infra/remoting/resume/ServerChannelSessionTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.resume; @@ -21,15 +20,15 @@ import java.time.Duration; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.ReferenceCounted; import infra.remoting.FrameAssert; import infra.remoting.frame.FrameType; import infra.remoting.frame.KeepAliveFrameCodec; import infra.remoting.frame.ResumeFrameCodec; import infra.remoting.keepalive.KeepAliveSupport; import infra.remoting.test.util.TestClientTransport; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.util.ReferenceCounted; import reactor.core.publisher.Operators; import reactor.test.StepVerifier; import reactor.test.scheduler.VirtualTimeScheduler; @@ -48,16 +47,16 @@ void sessionTimeoutSmokeTest() { transport.connect().subscribe(); - final ResumableDuplexConnection resumableDuplexConnection = - new ResumableDuplexConnection( + final ResumableConnection resumableConnection = + new ResumableConnection( "test", Unpooled.EMPTY_BUFFER, transport.testConnection(), framesStore); - resumableDuplexConnection.receive().subscribe(); + resumableConnection.receive().subscribe(); final ServerChannelSession session = new ServerChannelSession( Unpooled.EMPTY_BUFFER, - resumableDuplexConnection, + resumableConnection, transport.testConnection(), framesStore, Duration.ofMinutes(1), @@ -142,7 +141,7 @@ void sessionTimeoutSmokeTest() { .typeOf(FrameType.ERROR) .matches(ReferenceCounted::release); - resumableDuplexConnection.onClose().as(StepVerifier::create).expectComplete().verify(); + resumableConnection.onClose().as(StepVerifier::create).expectComplete().verify(); transport.alloc().assertHasNoLeaks(); } finally { @@ -160,16 +159,16 @@ void shouldTerminateConnectionOnIllegalStateInKeepAliveFrame() { transport.connect().subscribe(); - final ResumableDuplexConnection resumableDuplexConnection = - new ResumableDuplexConnection( + final ResumableConnection resumableConnection = + new ResumableConnection( "test", Unpooled.EMPTY_BUFFER, transport.testConnection(), framesStore); - resumableDuplexConnection.receive().subscribe(); + resumableConnection.receive().subscribe(); final ServerChannelSession session = new ServerChannelSession( Unpooled.EMPTY_BUFFER, - resumableDuplexConnection, + resumableConnection, transport.testConnection(), framesStore, Duration.ofMinutes(1), @@ -200,7 +199,7 @@ void shouldTerminateConnectionOnIllegalStateInKeepAliveFrame() { .typeOf(FrameType.ERROR) .matches(ReferenceCounted::release); - resumableDuplexConnection.onClose().as(StepVerifier::create).expectError().verify(); + resumableConnection.onClose().as(StepVerifier::create).expectError().verify(); keepAliveSupport.dispose(); transport.alloc().assertHasNoLeaks(); } diff --git a/today-remoting/src/test/java/infra/remoting/test/util/ByteBufUtils.java b/today-remoting/src/test/java/infra/remoting/test/util/ByteBufUtils.java index 11cf136..413b54a 100644 --- a/today-remoting/src/test/java/infra/remoting/test/util/ByteBufUtils.java +++ b/today-remoting/src/test/java/infra/remoting/test/util/ByteBufUtils.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test.util; diff --git a/today-remoting/src/test/java/infra/remoting/test/util/LocalDuplexConnection.java b/today-remoting/src/test/java/infra/remoting/test/util/LocalConnection.java similarity index 79% rename from today-remoting/src/test/java/infra/remoting/test/util/LocalDuplexConnection.java rename to today-remoting/src/test/java/infra/remoting/test/util/LocalConnection.java index e3a788c..3c4b6b8 100644 --- a/today-remoting/src/test/java/infra/remoting/test/util/LocalDuplexConnection.java +++ b/today-remoting/src/test/java/infra/remoting/test/util/LocalConnection.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test.util; @@ -21,11 +20,11 @@ import java.net.SocketAddress; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.ProtocolErrorException; import infra.remoting.frame.ErrorFrameCodec; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; import reactor.core.CoreSubscriber; import reactor.core.Scannable; import reactor.core.publisher.Flux; @@ -33,14 +32,14 @@ import reactor.core.publisher.Operators; import reactor.core.publisher.Sinks; -public class LocalDuplexConnection implements DuplexConnection { +public class LocalConnection implements Connection { private final ByteBufAllocator allocator; private final Sinks.Many send; private final Sinks.Many receive; private final Sinks.Empty onClose; private final String name; - public LocalDuplexConnection( + public LocalConnection( String name, ByteBufAllocator allocator, Sinks.Many send, diff --git a/today-remoting/src/test/java/infra/remoting/test/util/MockChannel.java b/today-remoting/src/test/java/infra/remoting/test/util/MockChannel.java index 0ea12ce..93a08a4 100644 --- a/today-remoting/src/test/java/infra/remoting/test/util/MockChannel.java +++ b/today-remoting/src/test/java/infra/remoting/test/util/MockChannel.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test.util; @@ -21,8 +20,8 @@ import java.util.concurrent.atomic.AtomicInteger; -import infra.remoting.Payload; import infra.remoting.Channel; +import infra.remoting.Payload; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; diff --git a/today-remoting/src/test/java/infra/remoting/test/util/StringUtils.java b/today-remoting/src/test/java/infra/remoting/test/util/StringUtils.java index 14242d1..8c20318 100644 --- a/today-remoting/src/test/java/infra/remoting/test/util/StringUtils.java +++ b/today-remoting/src/test/java/infra/remoting/test/util/StringUtils.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test.util; diff --git a/today-remoting/src/test/java/infra/remoting/test/util/TestClientTransport.java b/today-remoting/src/test/java/infra/remoting/test/util/TestClientTransport.java index 67acf86..dac86e4 100644 --- a/today-remoting/src/test/java/infra/remoting/test/util/TestClientTransport.java +++ b/today-remoting/src/test/java/infra/remoting/test/util/TestClientTransport.java @@ -1,48 +1,48 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test.util; import java.time.Duration; -import io.netty.buffer.ByteBufAllocator; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; import infra.remoting.transport.ClientTransport; +import io.netty.buffer.ByteBufAllocator; import reactor.core.publisher.Mono; import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; public class TestClientTransport implements ClientTransport { + private final LeaksTrackingByteBufAllocator allocator = LeaksTrackingByteBufAllocator.instrument( ByteBufAllocator.DEFAULT, Duration.ofSeconds(1), "client"); - private volatile TestDuplexConnection testDuplexConnection; + private volatile TestConnection testConnection; int maxFrameLength = FRAME_LENGTH_MASK; @Override - public Mono connect() { - return Mono.fromSupplier(() -> testDuplexConnection = new TestDuplexConnection(allocator)); + public Mono connect() { + return Mono.fromSupplier(() -> testConnection = new TestConnection(allocator)); } - public TestDuplexConnection testConnection() { - return testDuplexConnection; + public TestConnection testConnection() { + return testConnection; } public LeaksTrackingByteBufAllocator alloc() { diff --git a/today-remoting/src/test/java/infra/remoting/test/util/TestDuplexConnection.java b/today-remoting/src/test/java/infra/remoting/test/util/TestConnection.java similarity index 61% rename from today-remoting/src/test/java/infra/remoting/test/util/TestDuplexConnection.java rename to today-remoting/src/test/java/infra/remoting/test/util/TestConnection.java index e02a98b..b906b61 100644 --- a/today-remoting/src/test/java/infra/remoting/test/util/TestDuplexConnection.java +++ b/today-remoting/src/test/java/infra/remoting/test/util/TestConnection.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test.util; @@ -24,14 +23,13 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; -import infra.lang.NonNull; import infra.logging.Logger; import infra.logging.LoggerFactory; -import io.netty.buffer.ByteBuf; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.ProtocolErrorException; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; import infra.remoting.frame.ErrorFrameCodec; +import io.netty.buffer.ByteBuf; import reactor.core.CoreSubscriber; import reactor.core.publisher.DirectProcessor; import reactor.core.publisher.Flux; @@ -41,12 +39,12 @@ import reactor.core.publisher.Operators; /** - * An implementation of {@link DuplexConnection} that provides functionality to modify the behavior + * An implementation of {@link Connection} that provides functionality to modify the behavior * dynamically. */ -public class TestDuplexConnection implements DuplexConnection { +public class TestConnection implements Connection { - private static final Logger logger = LoggerFactory.getLogger(TestDuplexConnection.class); + private static final Logger logger = LoggerFactory.getLogger(TestConnection.class); private final LinkedBlockingQueue sent; @@ -59,7 +57,7 @@ public class TestDuplexConnection implements DuplexConnection { private volatile double availability = 1; private volatile int initialSendRequestN = Integer.MAX_VALUE; - public TestDuplexConnection(LeaksTrackingByteBufAllocator allocator) { + public TestConnection(LeaksTrackingByteBufAllocator allocator) { this.allocator = allocator; this.sent = new LinkedBlockingQueue<>(); this.received = DirectProcessor.create(); @@ -72,7 +70,7 @@ public TestDuplexConnection(LeaksTrackingByteBufAllocator allocator) { @Override public void sendFrame(int streamId, ByteBuf frame) { if (availability <= 0) { - throw new IllegalStateException("RSocket not available. Availability: " + availability); + throw new IllegalStateException("Channel not available. Availability: " + availability); } sendSink.next(frame); @@ -81,31 +79,29 @@ public void sendFrame(int streamId, ByteBuf frame) { @Override public Flux receive() { - return received.transform( - Operators.lift( - (__, actual) -> - new CoreSubscriber() { - @Override - public void onSubscribe(Subscription s) { - actual.onSubscribe(s); - } - - @Override - public void onNext(ByteBuf byteBuf) { - actual.onNext(byteBuf); - byteBuf.release(); - } - - @Override - public void onError(Throwable t) { - actual.onError(t); - } - - @Override - public void onComplete() { - actual.onComplete(); - } - })); + return received.transform(Operators.lift((__, actual) -> + new CoreSubscriber<>() { + @Override + public void onSubscribe(Subscription s) { + actual.onSubscribe(s); + } + + @Override + public void onNext(ByteBuf byteBuf) { + actual.onNext(byteBuf); + byteBuf.release(); + } + + @Override + public void onError(Throwable t) { + actual.onError(t); + } + + @Override + public void onComplete() { + actual.onComplete(); + } + })); } @Override @@ -130,7 +126,7 @@ public LeaksTrackingByteBufAllocator alloc() { @Override public SocketAddress remoteAddress() { - return new TestLocalSocketAddress("TestDuplexConnection"); + return new TestLocalSocketAddress("TestConnection"); } @Override @@ -157,7 +153,6 @@ public boolean isEmpty() { return sent.isEmpty(); } - @NonNull public ByteBuf awaitFrame() { try { return sent.take(); diff --git a/today-remoting/src/test/java/infra/remoting/test/util/TestLocalSocketAddress.java b/today-remoting/src/test/java/infra/remoting/test/util/TestLocalSocketAddress.java index 1b08dae..d41d36e 100644 --- a/today-remoting/src/test/java/infra/remoting/test/util/TestLocalSocketAddress.java +++ b/today-remoting/src/test/java/infra/remoting/test/util/TestLocalSocketAddress.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test.util; diff --git a/today-remoting/src/test/java/infra/remoting/test/util/TestServerTransport.java b/today-remoting/src/test/java/infra/remoting/test/util/TestServerTransport.java index 836fb55..36dd226 100644 --- a/today-remoting/src/test/java/infra/remoting/test/util/TestServerTransport.java +++ b/today-remoting/src/test/java/infra/remoting/test/util/TestServerTransport.java @@ -1,26 +1,25 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test.util; -import io.netty.buffer.ByteBufAllocator; import infra.remoting.Closeable; import infra.remoting.buffer.LeaksTrackingByteBufAllocator; import infra.remoting.transport.ConnectionAcceptor; import infra.remoting.transport.ServerTransport; +import io.netty.buffer.ByteBufAllocator; import reactor.core.Scannable; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; @@ -28,8 +27,8 @@ import static infra.remoting.frame.FrameLengthCodec.FRAME_LENGTH_MASK; public class TestServerTransport implements ServerTransport { - private final Sinks.One connSink = Sinks.one(); - private TestDuplexConnection connection; + private final Sinks.One connSink = Sinks.one(); + private TestConnection connection; private final LeaksTrackingByteBufAllocator allocator = LeaksTrackingByteBufAllocator.instrument(ByteBufAllocator.DEFAULT); @@ -63,14 +62,14 @@ public boolean isDisposed() { } private void disposeConnection() { - TestDuplexConnection c = connection; + TestConnection c = connection; if (c != null) { c.dispose(); } } - public TestDuplexConnection connect() { - TestDuplexConnection c = new TestDuplexConnection(allocator); + public TestConnection connect() { + TestConnection c = new TestConnection(allocator); connection = c; connSink.tryEmitValue(c); return c; diff --git a/today-remoting/src/test/java/infra/remoting/test/util/TestSubscriber.java b/today-remoting/src/test/java/infra/remoting/test/util/TestSubscriber.java index df46bac..237c049 100644 --- a/today-remoting/src/test/java/infra/remoting/test/util/TestSubscriber.java +++ b/today-remoting/src/test/java/infra/remoting/test/util/TestSubscriber.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test.util; diff --git a/today-remoting/src/test/java/infra/remoting/util/ByteBufPayloadTests.java b/today-remoting/src/test/java/infra/remoting/util/ByteBufPayloadTests.java index 924f086..5cfdd7d 100644 --- a/today-remoting/src/test/java/infra/remoting/util/ByteBufPayloadTests.java +++ b/today-remoting/src/test/java/infra/remoting/util/ByteBufPayloadTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.util; @@ -20,9 +19,9 @@ import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; +import infra.remoting.Payload; import io.netty.buffer.Unpooled; import io.netty.util.IllegalReferenceCountException; -import infra.remoting.Payload; public class ByteBufPayloadTests { diff --git a/today-remoting/src/test/java/infra/remoting/util/DefaultPayloadTests.java b/today-remoting/src/test/java/infra/remoting/util/DefaultPayloadTests.java index 658cd20..ff4d06f 100644 --- a/today-remoting/src/test/java/infra/remoting/util/DefaultPayloadTests.java +++ b/today-remoting/src/test/java/infra/remoting/util/DefaultPayloadTests.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.util; @@ -22,11 +21,11 @@ import java.nio.ByteBuffer; import java.util.concurrent.ThreadLocalRandom; +import infra.remoting.Payload; +import infra.remoting.buffer.LeaksTrackingByteBufAllocator; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; -import infra.remoting.Payload; -import infra.remoting.buffer.LeaksTrackingByteBufAllocator; import static org.assertj.core.api.Assertions.assertThat; diff --git a/today-remoting/src/test/java/infra/remoting/util/NumberUtilsTests.java b/today-remoting/src/test/java/infra/remoting/util/NumberUtilsTests.java deleted file mode 100644 index 89135e0..0000000 --- a/today-remoting/src/test/java/infra/remoting/util/NumberUtilsTests.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.remoting.util; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.assertj.core.api.Assertions.assertThatNullPointerException; - -final class NumberUtilsTests { - - @DisplayName("returns int value with postitive int") - @Test - void requireNonNegativeInt() { - assertThat(NumberUtils.requireNonNegative(Integer.MAX_VALUE, "test-message")) - .isEqualTo(Integer.MAX_VALUE); - } - - @DisplayName( - "requireNonNegative with int argument throws IllegalArgumentException with negative value") - @Test - void requireNonNegativeIntNegative() { - assertThatIllegalArgumentException() - .isThrownBy(() -> NumberUtils.requireNonNegative(Integer.MIN_VALUE, "test-message")) - .withMessage("test-message"); - } - - @DisplayName("requireNonNegative with int argument throws NullPointerException with null message") - @Test - void requireNonNegativeIntNullMessage() { - assertThatNullPointerException() - .isThrownBy(() -> NumberUtils.requireNonNegative(Integer.MIN_VALUE, null)) - .withMessage("message is required"); - } - - @DisplayName("requireNonNegative returns int value with zero") - @Test - void requireNonNegativeIntZero() { - assertThat(NumberUtils.requireNonNegative(0, "test-message")).isEqualTo(0); - } - - @DisplayName("requirePositive returns int value with positive int") - @Test - void requirePositiveInt() { - assertThat(NumberUtils.requirePositive(Integer.MAX_VALUE, "test-message")) - .isEqualTo(Integer.MAX_VALUE); - } - - @DisplayName( - "requirePositive with int argument throws IllegalArgumentException with negative value") - @Test - void requirePositiveIntNegative() { - assertThatIllegalArgumentException() - .isThrownBy(() -> NumberUtils.requirePositive(Integer.MIN_VALUE, "test-message")) - .withMessage("test-message"); - } - - @DisplayName("requirePositive with int argument throws NullPointerException with null message") - @Test - void requirePositiveIntNullMessage() { - assertThatNullPointerException() - .isThrownBy(() -> NumberUtils.requirePositive(Integer.MIN_VALUE, null)) - .withMessage("message is required"); - } - - @DisplayName("requirePositive with int argument throws IllegalArgumentException with zero value") - @Test - void requirePositiveIntZero() { - assertThatIllegalArgumentException() - .isThrownBy(() -> NumberUtils.requirePositive(0, "test-message")) - .withMessage("test-message"); - } - - @DisplayName("requirePositive returns long value with positive long") - @Test - void requirePositiveLong() { - assertThat(NumberUtils.requirePositive(Long.MAX_VALUE, "test-message")) - .isEqualTo(Long.MAX_VALUE); - } - - @DisplayName( - "requirePositive with long argument throws IllegalArgumentException with negative value") - @Test - void requirePositiveLongNegative() { - assertThatIllegalArgumentException() - .isThrownBy(() -> NumberUtils.requirePositive(Long.MIN_VALUE, "test-message")) - .withMessage("test-message"); - } - - @DisplayName("requirePositive with long argument throws NullPointerException with null message") - @Test - void requirePositiveLongNullMessage() { - assertThatNullPointerException() - .isThrownBy(() -> NumberUtils.requirePositive(Long.MIN_VALUE, null)) - .withMessage("message is required"); - } - - @DisplayName("requirePositive with long argument throws IllegalArgumentException with zero value") - @Test - void requirePositiveLongZero() { - assertThatIllegalArgumentException() - .isThrownBy(() -> NumberUtils.requirePositive(0L, "test-message")) - .withMessage("test-message"); - } - - @DisplayName("requireUnsignedByte returns length if 255") - @Test - void requireUnsignedByte() { - assertThat(NumberUtils.requireUnsignedByte((1 << 8) - 1)).isEqualTo(255); - } - - @DisplayName("requireUnsignedByte throws IllegalArgumentException if larger than 255") - @Test - void requireUnsignedByteOverFlow() { - assertThatIllegalArgumentException() - .isThrownBy(() -> NumberUtils.requireUnsignedByte(1 << 8)) - .withMessage("%d is larger than 8 bits", 1 << 8); - } - - @DisplayName("requireUnsignedMedium returns length if 16_777_215") - @Test - void requireUnsignedMedium() { - assertThat(NumberUtils.requireUnsignedMedium((1 << 24) - 1)).isEqualTo(16_777_215); - } - - @DisplayName("requireUnsignedMedium throws IllegalArgumentException if larger than 16_777_215") - @Test - void requireUnsignedMediumOverFlow() { - assertThatIllegalArgumentException() - .isThrownBy(() -> NumberUtils.requireUnsignedMedium(1 << 24)) - .withMessage("%d is larger than 24 bits", 1 << 24); - } - - @DisplayName("requireUnsignedShort returns length if 65_535") - @Test - void requireUnsignedShort() { - assertThat(NumberUtils.requireUnsignedShort((1 << 16) - 1)).isEqualTo(65_535); - } - - @DisplayName("requireUnsignedShort throws IllegalArgumentException if larger than 65_535") - @Test - void requireUnsignedShortOverFlow() { - assertThatIllegalArgumentException() - .isThrownBy(() -> NumberUtils.requireUnsignedShort(1 << 16)) - .withMessage("%d is larger than 16 bits", 1 << 16); - } - - @Test - void encodeUnsignedMedium() { - ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(); - NumberUtils.encodeUnsignedMedium(buffer, 129); - buffer.markReaderIndex(); - - assertThat(buffer.readUnsignedMedium()).as("reading as unsigned medium").isEqualTo(129); - - buffer.resetReaderIndex(); - assertThat(buffer.readMedium()).as("reading as signed medium").isEqualTo(129); - } - - @Test - void encodeUnsignedMediumLarge() { - ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(); - NumberUtils.encodeUnsignedMedium(buffer, 0xFFFFFC); - buffer.markReaderIndex(); - - assertThat(buffer.readUnsignedMedium()).as("reading as unsigned medium").isEqualTo(16777212); - - buffer.resetReaderIndex(); - assertThat(buffer.readMedium()).as("reading as signed medium").isEqualTo(-4); - } -} diff --git a/today-remoting/src/test/resources/logback-test.xml b/today-remoting/src/test/resources/logback-test.xml index 7d459cb..dd39c4a 100644 --- a/today-remoting/src/test/resources/logback-test.xml +++ b/today-remoting/src/test/resources/logback-test.xml @@ -1,20 +1,4 @@ - @@ -24,7 +8,10 @@ - + + + + diff --git a/today-remoting/src/test/resources/lorem.txt b/today-remoting/src/test/resources/lorem.txt new file mode 100644 index 0000000..e035ea8 --- /dev/null +++ b/today-remoting/src/test/resources/lorem.txt @@ -0,0 +1,32 @@ +Alteration literature to or an sympathize mr imprudence. Of is ferrars subject as enjoyed or tedious cottage. +Procuring as in resembled by in agreeable. Next long no gave mr eyes. Admiration advantages no he celebrated so pianoforte unreserved. +Not its herself forming charmed amiable. Him why feebly expect future now. + +Situation admitting promotion at or to perceived be. Mr acuteness we as estimable enjoyment up. +An held late as felt know. Learn do allow solid to grave. Middleton suspicion age her attention. +Chiefly several bed its wishing. Is so moments on chamber pressed to. Doubtful yet way properly answered humanity its desirous. + Minuter believe service arrived civilly add all. Acuteness allowance an at eagerness favourite in extensive exquisite ye. + + Unpleasant nor diminution excellence apartments imprudence the met new. Draw part them he an to he roof only. + Music leave say doors him. Tore bred form if sigh case as do. Staying he no looking if do opinion. + Sentiments way understood end partiality and his. + + Ladyship it daughter securing procured or am moreover mr. Put sir she exercise vicinity cheerful wondered. + Continual say suspicion provision you neglected sir curiosity unwilling. Simplicity end themselves increasing led day sympathize yet. + General windows effects not are drawing man garrets. Common indeed garden you his ladies out yet. Preference imprudence contrasted to remarkably in on. + Taken now you him trees tears any. Her object giving end sister except oppose. + + No comfort do written conduct at prevent manners on. Celebrated contrasted discretion him sympathize her collecting occasional. + Do answered bachelor occasion in of offended no concerns. Supply worthy warmth branch of no ye. Voice tried known to as my to. + Though wished merits or be. Alone visit use these smart rooms ham. No waiting in on enjoyed placing it inquiry. + + So insisted received is occasion advanced honoured. Among ready to which up. Attacks smiling and may out assured moments man nothing outward. + Thrown any behind afford either the set depend one temper. Instrument melancholy in acceptance collecting frequently be if. + Zealously now pronounce existence add you instantly say offending. Merry their far had widen was. Concerns no in expenses raillery formerly. + + As am hastily invited settled at limited civilly fortune me. Really spring in extent an by. Judge but built gay party world. + Of so am he remember although required. Bachelor unpacked be advanced at. Confined in declared marianne is vicinity. + + In alteration insipidity impression by travelling reasonable up motionless. Of regard warmth by unable sudden garden ladies. + No kept hung am size spot no. Likewise led and dissuade rejoiced welcomed husbands boy. Do listening on he suspected resembled. + Water would still if to. Position boy required law moderate was may. \ No newline at end of file diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/BaseClientServerTest.java b/today-remoting/src/testFixtures/java/infra/remoting/test/BaseClientServerTest.java index 56d3b58..1c926ae 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/BaseClientServerTest.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/BaseClientServerTest.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; @@ -51,7 +50,7 @@ public void teardown() { public void testFireNForget10() { long outputCount = Flux.range(1, 10) - .flatMap(i -> setup.getRSocket().fireAndForget(testPayload(i))) + .flatMap(i -> setup.getChannel().fireAndForget(testPayload(i))) .doOnError(Throwable::printStackTrace) .count() .block(); @@ -64,7 +63,7 @@ public void testFireNForget10() { public void testPushMetadata10() { long outputCount = Flux.range(1, 10) - .flatMap(i -> setup.getRSocket().metadataPush(DefaultPayload.create("", "metadata"))) + .flatMap(i -> setup.getChannel().metadataPush(DefaultPayload.create("", "metadata"))) .doOnError(Throwable::printStackTrace) .count() .block(); @@ -77,7 +76,7 @@ public void testRequestResponse1() { long outputCount = Flux.range(1, 1) .flatMap( - i -> setup.getRSocket().requestResponse(testPayload(i)).map(Payload::getDataUtf8)) + i -> setup.getChannel().requestResponse(testPayload(i)).map(Payload::getDataUtf8)) .doOnError(Throwable::printStackTrace) .count() .block(); @@ -91,7 +90,7 @@ public void testRequestResponse10() { long outputCount = Flux.range(1, 10) .flatMap( - i -> setup.getRSocket().requestResponse(testPayload(i)).map(Payload::getDataUtf8)) + i -> setup.getChannel().requestResponse(testPayload(i)).map(Payload::getDataUtf8)) .doOnError(Throwable::printStackTrace) .count() .block(); @@ -121,7 +120,7 @@ public void testRequestResponse100() { long outputCount = Flux.range(1, 100) .flatMap( - i -> setup.getRSocket().requestResponse(testPayload(i)).map(Payload::getDataUtf8)) + i -> setup.getChannel().requestResponse(testPayload(i)).map(Payload::getDataUtf8)) .doOnError(Throwable::printStackTrace) .count() .block(); @@ -135,7 +134,7 @@ public void testRequestResponse10_000() { long outputCount = Flux.range(1, 10_000) .flatMap( - i -> setup.getRSocket().requestResponse(testPayload(i)).map(Payload::getDataUtf8)) + i -> setup.getChannel().requestResponse(testPayload(i)).map(Payload::getDataUtf8)) .doOnError(Throwable::printStackTrace) .count() .block(); @@ -146,7 +145,7 @@ public void testRequestResponse10_000() { @Test @Timeout(10000) public void testRequestStream() { - Flux publisher = setup.getRSocket().requestStream(testPayload(3)); + Flux publisher = setup.getChannel().requestStream(testPayload(3)); long count = publisher.take(5).count().block(); @@ -156,7 +155,7 @@ public void testRequestStream() { @Test @Timeout(10000) public void testRequestStreamAll() { - Flux publisher = setup.getRSocket().requestStream(testPayload(3)); + Flux publisher = setup.getChannel().requestStream(testPayload(3)); long count = publisher.count().block(); @@ -169,7 +168,7 @@ public void testRequestStreamWithRequestN() { CountdownBaseSubscriber ts = new CountdownBaseSubscriber(); ts.expect(5); - setup.getRSocket().requestStream(testPayload(3)).subscribe(ts); + setup.getChannel().requestStream(testPayload(3)).subscribe(ts); ts.await(); assertThat(ts.count()).isEqualTo(5); @@ -186,7 +185,7 @@ public void testRequestStreamWithRequestN() { public void testRequestStreamWithDelayedRequestN() { CountdownBaseSubscriber ts = new CountdownBaseSubscriber(); - setup.getRSocket().requestStream(testPayload(3)).subscribe(ts); + setup.getChannel().requestStream(testPayload(3)).subscribe(ts); ts.expect(5); @@ -203,7 +202,7 @@ public void testRequestStreamWithDelayedRequestN() { @Test @Timeout(10000) public void testChannel0() { - Flux publisher = setup.getRSocket().requestChannel(Flux.empty()); + Flux publisher = setup.getChannel().requestChannel(Flux.empty()); long count = publisher.count().block(); @@ -213,7 +212,7 @@ public void testChannel0() { @Test @Timeout(10000) public void testChannel1() { - Flux publisher = setup.getRSocket().requestChannel(Flux.just(testPayload(0))); + Flux publisher = setup.getChannel().requestChannel(Flux.just(testPayload(0))); long count = publisher.count().block(); @@ -225,7 +224,7 @@ public void testChannel1() { public void testChannel3() { Flux publisher = setup - .getRSocket() + .getChannel() .requestChannel(Flux.just(testPayload(0), testPayload(1), testPayload(2))); long count = publisher.count().block(); @@ -238,7 +237,7 @@ public void testChannel3() { public void testChannel512() { Flux payloads = Flux.range(1, 512).map(i -> DefaultPayload.create("hello " + i)); - long count = setup.getRSocket().requestChannel(payloads).count().block(); + long count = setup.getChannel().requestChannel(payloads).count().block(); assertThat(count).isEqualTo(512); } @@ -248,7 +247,7 @@ public void testChannel512() { public void testChannel20_000() { Flux payloads = Flux.range(1, 20_000).map(i -> DefaultPayload.create("hello " + i)); - long count = setup.getRSocket().requestChannel(payloads).count().block(); + long count = setup.getChannel().requestChannel(payloads).count().block(); assertThat(count).isEqualTo(20_000); } @@ -258,7 +257,7 @@ public void testChannel20_000() { public void testChannel200_000() { Flux payloads = Flux.range(1, 200_000).map(i -> DefaultPayload.create("hello " + i)); - long count = setup.getRSocket().requestChannel(payloads).count().block(); + long count = setup.getChannel().requestChannel(payloads).count().block(); assertThat(count).isEqualTo(200_000); } @@ -270,7 +269,7 @@ public void testChannel2_000_000() { AtomicInteger counter = new AtomicInteger(0); Flux payloads = Flux.range(1, 2_000_000).map(i -> DefaultPayload.create("hello " + i)); - long count = setup.getRSocket().requestChannel(payloads).count().block(); + long count = setup.getChannel().requestChannel(payloads).count().block(); assertThat(count).isEqualTo(2_000_000); } diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/ByteBufRepresentation.java b/today-remoting/src/testFixtures/java/infra/remoting/test/ByteBufRepresentation.java index 2f6e203..562b545 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/ByteBufRepresentation.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/ByteBufRepresentation.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/ClientSetupRule.java b/today-remoting/src/testFixtures/java/infra/remoting/test/ClientSetupRule.java index e153296..cfddf80 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/ClientSetupRule.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/ClientSetupRule.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; @@ -21,8 +20,8 @@ import java.util.function.Function; import java.util.function.Supplier; -import infra.remoting.Closeable; import infra.remoting.Channel; +import infra.remoting.Closeable; import infra.remoting.core.ChannelConnector; import infra.remoting.core.RemotingServer; import infra.remoting.transport.ClientTransport; @@ -48,7 +47,7 @@ public ClientSetupRule( this.serverInit = address -> - RemotingServer.create((setup, rsocket) -> Mono.just(new TestChannel(data, metadata))) + RemotingServer.create((setup, channel) -> Mono.just(new TestChannel(data, metadata))) .bind(serverTransportSupplier.apply(address)) .block(); @@ -69,7 +68,7 @@ public void tearDown() { server.dispose(); } - public Channel getRSocket() { + public Channel getChannel() { return client; } diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/CountdownBaseSubscriber.java b/today-remoting/src/testFixtures/java/infra/remoting/test/CountdownBaseSubscriber.java index 4edbb62..4a22d3d 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/CountdownBaseSubscriber.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/CountdownBaseSubscriber.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/LeaksTrackingByteBufAllocator.java b/today-remoting/src/testFixtures/java/infra/remoting/test/LeaksTrackingByteBufAllocator.java index d26120a..89da69b 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/LeaksTrackingByteBufAllocator.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/LeaksTrackingByteBufAllocator.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/PayloadPredicate.java b/today-remoting/src/testFixtures/java/infra/remoting/test/PayloadPredicate.java index 5c39624..62f3bbe 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/PayloadPredicate.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/PayloadPredicate.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/PerfTest.java b/today-remoting/src/testFixtures/java/infra/remoting/test/PerfTest.java index c1edf50..8942577 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/PerfTest.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/PerfTest.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/PingClient.java b/today-remoting/src/testFixtures/java/infra/remoting/test/PingClient.java index 7215bd4..c65ec60 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/PingClient.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/PingClient.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; @@ -23,8 +22,8 @@ import java.time.Duration; import java.util.function.BiFunction; -import infra.remoting.Payload; import infra.remoting.Channel; +import infra.remoting.Payload; import infra.remoting.util.ByteBufPayload; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -68,12 +67,12 @@ Flux pingPong( final Recorder histogram) { return Flux.usingWhen( client, - rsocket -> + channel -> Flux.range(1, count) .flatMap( i -> { long start = System.nanoTime(); - return Flux.from(interaction.apply(rsocket, payload.retain())) + return Flux.from(interaction.apply(channel, payload.retain())) .doOnNext(Payload::release) .doFinally( signalType -> { @@ -82,9 +81,9 @@ Flux pingPong( }); }, 64), - rsocket -> { - rsocket.dispose(); - return rsocket.onClose(); + channel -> { + channel.dispose(); + return channel.onClose(); }) .doOnError(Throwable::printStackTrace); } diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/PingHandler.java b/today-remoting/src/testFixtures/java/infra/remoting/test/PingHandler.java index c9d4581..e2c3436 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/PingHandler.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/PingHandler.java @@ -1,28 +1,27 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; import java.util.concurrent.ThreadLocalRandom; -import infra.remoting.ConnectionSetupPayload; -import infra.remoting.Payload; import infra.remoting.Channel; import infra.remoting.ChannelAcceptor; +import infra.remoting.ConnectionSetupPayload; +import infra.remoting.Payload; import infra.remoting.util.ByteBufPayload; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -42,7 +41,7 @@ public PingHandler(byte[] data) { } @Override - public Mono accept(ConnectionSetupPayload setup, Channel sendingSocket) { + public Mono accept(ConnectionSetupPayload setup, Channel channel) { return Mono.just( new Channel() { @Override diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/SlowTest.java b/today-remoting/src/testFixtures/java/infra/remoting/test/SlowTest.java index c0040d5..5ffd1bf 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/SlowTest.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/SlowTest.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/TestChannel.java b/today-remoting/src/testFixtures/java/infra/remoting/test/TestChannel.java index 62cee17..875871f 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/TestChannel.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/TestChannel.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; @@ -22,8 +21,8 @@ import java.time.Duration; import java.util.concurrent.atomic.AtomicLong; -import infra.remoting.Payload; import infra.remoting.Channel; +import infra.remoting.Payload; import infra.remoting.util.ByteBufPayload; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/TestDuplexConnection.java b/today-remoting/src/testFixtures/java/infra/remoting/test/TestConnection.java similarity index 83% rename from today-remoting/src/testFixtures/java/infra/remoting/test/TestDuplexConnection.java rename to today-remoting/src/testFixtures/java/infra/remoting/test/TestConnection.java index f10891f..b240122 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/TestDuplexConnection.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/TestConnection.java @@ -1,33 +1,32 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Subscription; import java.net.SocketAddress; import java.util.function.BiFunction; -import infra.lang.Nullable; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import infra.remoting.DuplexConnection; +import infra.remoting.Connection; import infra.remoting.ProtocolErrorException; import infra.remoting.frame.PayloadFrameCodec; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; import reactor.core.CoreSubscriber; import reactor.core.Fuseable; import reactor.core.Scannable; @@ -36,14 +35,14 @@ import reactor.core.publisher.Operators; import reactor.core.publisher.Sinks; -public class TestDuplexConnection implements DuplexConnection { +public class TestConnection implements Connection { final ByteBufAllocator allocator; final Sinks.Many inbound = Sinks.unsafe().many().unicast().onBackpressureError(); final Sinks.Many outbound = Sinks.unsafe().many().unicast().onBackpressureError(); final Sinks.One close = Sinks.one(); - public TestDuplexConnection( + public TestConnection( CoreSubscriber outboundSubscriber, boolean trackLeaks) { this.outbound.asFlux().subscribe(outboundSubscriber); this.allocator = diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/TestFrames.java b/today-remoting/src/testFixtures/java/infra/remoting/test/TestFrames.java index 7f594c4..da98e9b 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/TestFrames.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/TestFrames.java @@ -1,25 +1,21 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; import infra.remoting.Payload; import infra.remoting.frame.CancelFrameCodec; import infra.remoting.frame.ErrorFrameCodec; @@ -36,6 +32,9 @@ import infra.remoting.frame.SetupFrameCodec; import infra.remoting.util.DefaultPayload; import infra.remoting.util.EmptyPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.Unpooled; /** Test instances of all frame types. */ public final class TestFrames { diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/TestSubscriber.java b/today-remoting/src/testFixtures/java/infra/remoting/test/TestSubscriber.java index 3ab3be1..336e6d2 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/TestSubscriber.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/TestSubscriber.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/TransportPair.java b/today-remoting/src/testFixtures/java/infra/remoting/test/TransportPair.java index e272744..25541f5 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/TransportPair.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/TransportPair.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; @@ -25,23 +24,23 @@ import java.util.function.BiFunction; import java.util.function.Supplier; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.ReferenceCounted; -import io.netty.util.ResourceLeakDetector; -import infra.remoting.Closeable; -import infra.remoting.DuplexConnection; import infra.remoting.Channel; +import infra.remoting.Closeable; +import infra.remoting.Connection; import infra.remoting.ProtocolErrorException; import infra.remoting.core.ChannelConnector; import infra.remoting.core.RemotingServer; import infra.remoting.core.Resume; import infra.remoting.frame.decoder.PayloadDecoder; -import infra.remoting.plugins.ConnectionInterceptor; -import infra.remoting.resume.InMemoryResumableFramesStore; +import infra.remoting.plugins.ConnectionDecorator; +import infra.remoting.resume.InMemoryResumableFramesStoreFactory; import infra.remoting.transport.ClientTransport; import infra.remoting.transport.ServerTransport; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.util.ReferenceCountUtil; +import io.netty.util.ReferenceCounted; +import io.netty.util.ResourceLeakDetector; import reactor.core.CoreSubscriber; import reactor.core.Disposable; import reactor.core.Disposables; @@ -121,20 +120,20 @@ public TransportPair(Supplier addressSupplier, allocatorToSupply2 = ByteBufAllocator.DEFAULT; } responder = new TestChannel(TransportPair.data, metadata); - final RemotingServer remotingServer = RemotingServer.create((setup, sendingSocket) -> Mono.just(responder)) + final RemotingServer remotingServer = RemotingServer.create((setup, channel) -> Mono.just(responder)) .payloadDecoder(PayloadDecoder.ZERO_COPY) .interceptors(registry -> { if (runServerWithAsyncInterceptors && !withResumability) { logger.info("Perform Integration Test with Async Interceptors Enabled For Server"); - registry.forConnection((type, duplexConnection) -> new AsyncDuplexConnection(duplexConnection, "server")) - .forChannelAcceptor(delegate -> (connectionSetupPayload, sendingSocket) -> delegate.accept(connectionSetupPayload, sendingSocket) + registry.forConnection((type, duplexConnection) -> new AsyncConnection(duplexConnection, "server")) + .forChannelAcceptor(delegate -> (connectionSetupPayload, channel) -> delegate.accept(connectionSetupPayload, channel) .subscribeOn(Schedulers.parallel())); } if (withResumability) { registry.forConnection((type, duplexConnection) -> - type == ConnectionInterceptor.Type.SOURCE - ? new DisconnectingDuplexConnection( + type == ConnectionDecorator.Type.SOURCE + ? new DisconnectingConnection( "Server", duplexConnection, Duration.ofMillis(ThreadLocalRandom.current().nextInt(100, 1000))) @@ -144,7 +143,7 @@ public TransportPair(Supplier addressSupplier, if (withResumability) { remotingServer.resume(new Resume() - .storeFactory(token -> new InMemoryResumableFramesStore("server", token, Integer.MAX_VALUE))); + .storeFactory(new InMemoryResumableFramesStoreFactory("server", Integer.MAX_VALUE))); } if (withRandomFragmentation) { @@ -162,16 +161,16 @@ public TransportPair(Supplier addressSupplier, if (runClientWithAsyncInterceptors && !withResumability) { logger.info("Perform Integration Test with Async Interceptors Enabled For Client"); registry.forConnection((type, duplexConnection) -> - new AsyncDuplexConnection(duplexConnection, "client")) - .forChannelAcceptor(delegate -> (connectionSetupPayload, sendingSocket) -> - delegate.accept(connectionSetupPayload, sendingSocket) + new AsyncConnection(duplexConnection, "client")) + .forChannelAcceptor(delegate -> (connectionSetupPayload, channel) -> + delegate.accept(connectionSetupPayload, channel) .subscribeOn(Schedulers.parallel())); } if (withResumability) { registry.forConnection((type, duplexConnection) -> - type == ConnectionInterceptor.Type.SOURCE - ? new DisconnectingDuplexConnection( + type == ConnectionDecorator.Type.SOURCE + ? new DisconnectingConnection( "Client", duplexConnection, Duration.ofMillis( @@ -181,20 +180,18 @@ public TransportPair(Supplier addressSupplier, }); if (withResumability) { - channelConnector.resume( - new Resume().storeFactory( - token -> new InMemoryResumableFramesStore("client", token, Integer.MAX_VALUE))); + channelConnector.resume(new Resume() + .storeFactory(new InMemoryResumableFramesStoreFactory("client", Integer.MAX_VALUE))); } if (withRandomFragmentation) { channelConnector.fragment(ThreadLocalRandom.current().nextInt(256, 512)); } - client = - channelConnector - .connect(clientTransportSupplier.apply(address, server, allocatorToSupply1)) - .doOnError(Throwable::printStackTrace) - .block(); + client = channelConnector + .connect(clientTransportSupplier.apply(address, server, allocatorToSupply1)) + .doOnError(Throwable::printStackTrace) + .block(); } @Override @@ -243,31 +240,31 @@ public void awaitClosed(Duration timeout) { logger.info("TransportPair has been terminated"); } - private static class AsyncDuplexConnection implements DuplexConnection { + private static class AsyncConnection implements Connection { - private final DuplexConnection duplexConnection; + private final Connection connection; private String tag; private final ByteBufReleaserOperator bufReleaserOperator; - public AsyncDuplexConnection(DuplexConnection duplexConnection, String tag) { - this.duplexConnection = duplexConnection; + public AsyncConnection(Connection connection, String tag) { + this.connection = connection; this.tag = tag; this.bufReleaserOperator = new ByteBufReleaserOperator(); } @Override public void sendFrame(int streamId, ByteBuf frame) { - duplexConnection.sendFrame(streamId, frame); + connection.sendFrame(streamId, frame); } @Override public void sendErrorAndClose(ProtocolErrorException e) { - duplexConnection.sendErrorAndClose(e); + connection.sendErrorAndClose(e); } @Override public Flux receive() { - return duplexConnection + return connection .receive() .doOnTerminate(() -> logger.info("[" + this + "] Receive is done before PO")) .subscribeOn(Schedulers.boundedElastic()) @@ -283,17 +280,17 @@ public Flux receive() { @Override public ByteBufAllocator alloc() { - return duplexConnection.alloc(); + return connection.alloc(); } @Override public SocketAddress remoteAddress() { - return duplexConnection.remoteAddress(); + return connection.remoteAddress(); } @Override public Mono onClose() { - return Mono.whenDelayError(duplexConnection.onClose() + return Mono.whenDelayError(connection.onClose() .doOnTerminate(() -> logger.info("[" + this + "] Source Connection is done")), bufReleaserOperator .onClose() @@ -302,14 +299,14 @@ public Mono onClose() { @Override public void dispose() { - duplexConnection.dispose(); + connection.dispose(); } @Override public String toString() { - return "AsyncDuplexConnection{" + return "AsyncConnection{" + "duplexConnection=" - + duplexConnection + + connection + ", tag='" + tag + '\'' @@ -319,14 +316,14 @@ public String toString() { } } - private static class DisconnectingDuplexConnection implements DuplexConnection { + private static class DisconnectingConnection implements Connection { private final String tag; - final DuplexConnection source; + final Connection source; final Duration delay; final Swap disposables = Disposables.swap(); - DisconnectingDuplexConnection(String tag, DuplexConnection source, Duration delay) { + DisconnectingConnection(String tag, Connection source, Duration delay) { this.tag = tag; this.source = source; this.delay = delay; @@ -388,7 +385,7 @@ public SocketAddress remoteAddress() { @Override public String toString() { - return "DisconnectingDuplexConnection{" + return "DisconnectingConnection{" + "tag='" + tag + '\'' diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/TransportTest.java b/today-remoting/src/testFixtures/java/infra/remoting/test/TransportTest.java index 8429abb..04fbebd 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/TransportTest.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/TransportTest.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; @@ -35,10 +34,10 @@ import java.util.stream.Collectors; import java.util.zip.GZIPInputStream; -import io.netty.util.ReferenceCounted; -import infra.remoting.Payload; import infra.remoting.Channel; +import infra.remoting.Payload; import infra.remoting.util.ByteBufPayload; +import io.netty.util.ReferenceCounted; import reactor.core.Exceptions; import reactor.core.publisher.Flux; import reactor.core.publisher.Hooks; diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/TriFunction.java b/today-remoting/src/testFixtures/java/infra/remoting/test/TriFunction.java index bfee944..e2ddda7 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/TriFunction.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/TriFunction.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.remoting.test; diff --git a/today-remoting/src/testFixtures/java/infra/remoting/test/package-info.java b/today-remoting/src/testFixtures/java/infra/remoting/test/package-info.java index 15d905a..c19fc14 100644 --- a/today-remoting/src/testFixtures/java/infra/remoting/test/package-info.java +++ b/today-remoting/src/testFixtures/java/infra/remoting/test/package-info.java @@ -1,22 +1,21 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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. */ -/** Utilities for testing RSocket components. */ -@NonNullApi +/** Utilities for testing components. */ +@NullMarked package infra.remoting.test; -import infra.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-remoting/src/testFixtures/resources/services/org.assertj.core.presentation.Representation b/today-remoting/src/testFixtures/resources/services/org.assertj.core.presentation.Representation index 4085e23..9c87471 100644 --- a/today-remoting/src/testFixtures/resources/services/org.assertj.core.presentation.Representation +++ b/today-remoting/src/testFixtures/resources/services/org.assertj.core.presentation.Representation @@ -1 +1 @@ -io.rsocket.test.ByteBufRepresentation \ No newline at end of file +infra.remoting.test.ByteBufRepresentation \ No newline at end of file diff --git a/today-service-api/build.gradle b/today-service-api/build.gradle new file mode 100644 index 0000000..9fbc979 --- /dev/null +++ b/today-service-api/build.gradle @@ -0,0 +1,15 @@ +description = "TODAY Service API" + + +dependencies { + api project(":today-remoting") + api project(":today-cloud-core") + api project(":today-service-serialization") + + implementation 'cn.taketoday:infra-beans' + implementation 'cn.taketoday:infra-context' + + optional "com.google.protobuf:protobuf-java" + +// testImplementation project(":today-cloud-samples:demo-tests:demo-user-api") +} \ No newline at end of file diff --git a/today-service-client/src/main/java/infra/cloud/RpcRequest.java b/today-service-api/src/main/java/infra/cloud/RpcRequest.java similarity index 51% rename from today-service-client/src/main/java/infra/cloud/RpcRequest.java rename to today-service-api/src/main/java/infra/cloud/RpcRequest.java index 25e35f5..487bd89 100644 --- a/today-service-client/src/main/java/infra/cloud/RpcRequest.java +++ b/today-service-api/src/main/java/infra/cloud/RpcRequest.java @@ -1,18 +1,17 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.cloud; @@ -22,23 +21,28 @@ import java.util.Arrays; import java.util.Objects; +import infra.cloud.serialize.Readable; +import infra.cloud.serialize.Message; +import infra.cloud.serialize.Writable; +import infra.cloud.service.ServiceMethod; + /** * @author TODAY 2021/7/4 01:19 */ -public class RpcRequest implements Serializable { +public class RpcRequest implements Serializable, Message { @Serial private static final long serialVersionUID = 1L; - private String methodName; + private String serviceClass; - private String serviceName; + private String methodName; private Object[] arguments; private String[] paramTypes; - private RpcMethod rpcMethod; + private ServiceMethod method; public void setArguments(Object[] arguments) { this.arguments = arguments; @@ -64,20 +68,34 @@ public String getMethodName() { return methodName; } - public void setRpcMethod(RpcMethod rpcMethod) { - this.rpcMethod = rpcMethod; + public void setMethod(ServiceMethod method) { + this.method = method; + } + + public ServiceMethod getMethod() { + return method; } - public RpcMethod getRpcMethod() { - return rpcMethod; + public void setServiceClass(String serviceName) { + this.serviceClass = serviceName; } - public void setServiceName(String serviceName) { - this.serviceName = serviceName; + public String getServiceClass() { + return serviceClass; } - public String getServiceName() { - return serviceName; + @Override + public void writeTo(Writable writable) { + writable.write(serviceClass); + writable.write(methodName); + writable.write(paramTypes, Writable::write); + } + + @Override + public void readFrom(Readable readable) { + this.serviceClass = readable.readString(); + this.methodName = readable.readString(); + this.paramTypes = readable.read(String.class, Readable::readString); } @Override @@ -87,14 +105,14 @@ public boolean equals(Object o) { if (!(o instanceof RpcRequest request)) return false; return Objects.equals(methodName, request.methodName) - && Objects.equals(serviceName, request.serviceName) + && Objects.equals(serviceClass, request.serviceClass) && Arrays.equals(paramTypes, request.paramTypes) && Arrays.equals(arguments, request.arguments); } @Override public int hashCode() { - int result = Objects.hash(methodName, serviceName); + int result = Objects.hash(methodName, serviceClass); result = 31 * result + Arrays.hashCode(paramTypes); result = 31 * result + Arrays.hashCode(arguments); return result; @@ -104,7 +122,7 @@ public int hashCode() { public String toString() { return "RpcRequest{" + "method='" + methodName + '\'' + - ", serviceName='" + serviceName + '\'' + + ", serviceName='" + serviceClass + '\'' + ", paramTypes=" + Arrays.toString(paramTypes) + ", arguments=" + Arrays.toString(arguments) + '}'; diff --git a/today-service-api/src/main/java/infra/cloud/RpcResponse.java b/today-service-api/src/main/java/infra/cloud/RpcResponse.java new file mode 100644 index 0000000..61f1dda --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/RpcResponse.java @@ -0,0 +1,80 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud; + +import org.jspecify.annotations.Nullable; + +import java.io.Serial; +import java.io.Serializable; + +import infra.cloud.service.ServiceMethod; + +/** + * @author TODAY 2021/7/4 22:31 + */ +public class RpcResponse implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** service result */ + + @Nullable + private Object result; + + @Nullable + private Throwable exception; + + @Nullable + private ServiceMethod method; + + public RpcResponse() { + } + + public RpcResponse(@Nullable ServiceMethod method, Object result) { + this.method = method; + this.result = result; + } + + public void setMethod(@Nullable ServiceMethod rpcMethod) { + this.method = rpcMethod; + } + + @Nullable + public ServiceMethod getMethod() { + return method; + } + + public void setResult(@Nullable Object result) { + this.result = result; + } + + @Nullable + public Object getResult() { + return result; + } + + @Nullable + public Throwable getException() { + return exception; + } + + public void setException(@Nullable Throwable exception) { + this.exception = exception; + } + +} diff --git a/today-service-api/src/main/java/infra/cloud/serialize/ArgumentSerialization.java b/today-service-api/src/main/java/infra/cloud/serialize/ArgumentSerialization.java new file mode 100644 index 0000000..1220314 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/serialize/ArgumentSerialization.java @@ -0,0 +1,70 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize; + +import org.jspecify.annotations.Nullable; + +import infra.core.MethodParameter; + +/** + * A strategy interface for serializing and deserializing method arguments. + *

    + * Implementations of this interface define how specific argument types are converted + * to a wire format (serialization) and reconstructed from it (deserialization). + *

    + * + * @param the type of the argument being serialized or deserialized + * @author 海子 Yang + * @since 1.0 2024/12/20 16:29 + */ +public interface ArgumentSerialization { + + /** + * Checks whether this serializer supports the given method parameter. + *

    This method is typically used to determine if the current implementation + * can handle the serialization/deserialization logic for the specified parameter. + * + * @param parameter the method parameter to check + * @return {@code true} if this serializer supports the parameter; {@code false} otherwise + */ + boolean supportsArgument(MethodParameter parameter); + + /** + * Serializes the given value based on the provided method parameter. + *

    The serialized data is written to the provided {@link Writable} instance. + * + * @param parameter the method parameter associated with the value + * @param value the value to serialize, may be {@code null} + * @param writable the destination to write the serialized data to + * @throws SerializationException if an error occurs during serialization + */ + void serialize(MethodParameter parameter, @Nullable T value, Writable writable) + throws SerializationException; + + /** + * Deserializes a value from the provided readable source based on the method parameter. + *

    Reads data from the {@link Readable} instance and reconstructs the original object. + * + * @param parameter the method parameter describing the expected type + * @param readable the source to read the serialized data from + * @return the deserialized value, or {@code null} if applicable + * @throws SerializationException if an error occurs during deserialization + */ + @Nullable + T deserialize(MethodParameter parameter, Readable readable) throws SerializationException; + +} diff --git a/today-service-api/src/main/java/infra/cloud/serialize/MessagePackReader.java b/today-service-api/src/main/java/infra/cloud/serialize/MessagePackReader.java new file mode 100644 index 0000000..9df5249 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/serialize/MessagePackReader.java @@ -0,0 +1,773 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize; + +import org.jspecify.annotations.Nullable; + +import java.lang.reflect.Array; +import java.math.BigInteger; +import java.nio.charset.Charset; +import java.time.Instant; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; + +import infra.cloud.serialize.format.ExtensionTypeHeader; +import infra.cloud.serialize.format.MessageFormat; +import infra.cloud.serialize.format.MessageFormatException; +import infra.cloud.serialize.format.MessageInsufficientBufferException; +import infra.cloud.serialize.format.MessageIntegerOverflowException; +import infra.cloud.serialize.format.MessageNeverUsedFormatException; +import infra.cloud.serialize.format.MessagePackException; +import infra.cloud.serialize.format.MessageSizeException; +import infra.cloud.serialize.format.MessageTypeException; +import infra.lang.Constant; +import infra.lang.Enumerable; +import infra.lang.TodayStrategies; +import infra.util.CollectionUtils; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; + +import static infra.cloud.serialize.format.MessagePackCode.ARRAY16; +import static infra.cloud.serialize.format.MessagePackCode.ARRAY32; +import static infra.cloud.serialize.format.MessagePackCode.BIN16; +import static infra.cloud.serialize.format.MessagePackCode.BIN32; +import static infra.cloud.serialize.format.MessagePackCode.BIN8; +import static infra.cloud.serialize.format.MessagePackCode.EXT16; +import static infra.cloud.serialize.format.MessagePackCode.EXT32; +import static infra.cloud.serialize.format.MessagePackCode.EXT8; +import static infra.cloud.serialize.format.MessagePackCode.EXT_TIMESTAMP; +import static infra.cloud.serialize.format.MessagePackCode.FALSE; +import static infra.cloud.serialize.format.MessagePackCode.FIXEXT1; +import static infra.cloud.serialize.format.MessagePackCode.FIXEXT16; +import static infra.cloud.serialize.format.MessagePackCode.FIXEXT2; +import static infra.cloud.serialize.format.MessagePackCode.FIXEXT4; +import static infra.cloud.serialize.format.MessagePackCode.FIXEXT8; +import static infra.cloud.serialize.format.MessagePackCode.FLOAT32; +import static infra.cloud.serialize.format.MessagePackCode.FLOAT64; +import static infra.cloud.serialize.format.MessagePackCode.INT16; +import static infra.cloud.serialize.format.MessagePackCode.INT32; +import static infra.cloud.serialize.format.MessagePackCode.INT64; +import static infra.cloud.serialize.format.MessagePackCode.INT8; +import static infra.cloud.serialize.format.MessagePackCode.MAP16; +import static infra.cloud.serialize.format.MessagePackCode.MAP32; +import static infra.cloud.serialize.format.MessagePackCode.NIL; +import static infra.cloud.serialize.format.MessagePackCode.STR16; +import static infra.cloud.serialize.format.MessagePackCode.STR32; +import static infra.cloud.serialize.format.MessagePackCode.STR8; +import static infra.cloud.serialize.format.MessagePackCode.TRUE; +import static infra.cloud.serialize.format.MessagePackCode.UINT16; +import static infra.cloud.serialize.format.MessagePackCode.UINT32; +import static infra.cloud.serialize.format.MessagePackCode.UINT64; +import static infra.cloud.serialize.format.MessagePackCode.UINT8; +import static infra.cloud.serialize.format.MessagePackCode.isFixInt; +import static infra.cloud.serialize.format.MessagePackCode.isFixedArray; +import static infra.cloud.serialize.format.MessagePackCode.isFixedMap; +import static infra.cloud.serialize.format.MessagePackCode.isFixedRaw; + +/** + * MessagePack specification reader implementation + * + * @author 海子 Yang + * @since 1.0 2025/8/18 14:52 + */ +public class MessagePackReader implements Readable { + + private static final int stringSizeLimit = TodayStrategies.getInt( + "infra.cloud.serialize.stringSizeLimit", Integer.MAX_VALUE / 2); + + private static final Charset stringCharset = Charset.forName(TodayStrategies.getProperty( + "infra.cloud.serialize.stringCharset", "UTF-8")); + + private final ByteBuf buffer; + + public MessagePackReader(ByteBuf buffer) { + this.buffer = buffer; + } + + @Override + public byte[] readFully() { + return ByteBufUtil.getBytes(buffer); + } + + @Override + public void read(byte[] b) { + buffer.readBytes(b); + } + + @Override + public byte[] read() { + return read(readBinaryHeader()); + } + + @Override + public byte[] read(int len) { + if (len == 0) { + return Constant.EMPTY_BYTES; + } + return ByteBufUtil.getBytes(buffer, buffer.readerIndex(), len); + } + + @Override + public void read(byte[] b, int off, int len) { + buffer.readBytes(b, off, len); + } + + @Override + public void read(Message message) { + message.readFrom(this); + } + + @Override + public > V readEnum(Class type) { + return Enumerable.of(type, readInt()); + } + + @Nullable + @Override + public V readNullable(Function valueMapper) { + if (tryReadNull()) { + return null; + } + return valueMapper.apply(this); + } + + @Override + public int skipBytes(int n) { + int start = buffer.readerIndex(); + buffer.skipBytes(n); + return start - buffer.readerIndex(); + } + + @Override + public boolean readBoolean() { + byte b = buffer.readByte(); + if (b == FALSE) { + return false; + } + else if (b == TRUE) { + return true; + } + throw unexpected("boolean", b); + } + + @Override + public byte readByte() { + byte b = readInt8(); + if (isFixInt(b)) { + return b; + } + switch (b) { + case UINT8: // unsigned int 8 + byte u8 = readInt8(); + if (u8 < (byte) 0) { + throw overflowU8(u8); + } + return u8; + case UINT16: // unsigned int 16 + short u16 = readInt16(); + if (u16 < 0 || u16 > Byte.MAX_VALUE) { + throw overflowU16(u16); + } + return (byte) u16; + case UINT32: // unsigned int 32 + int u32 = readInt32(); + if (u32 < 0 || u32 > Byte.MAX_VALUE) { + throw overflowU32(u32); + } + return (byte) u32; + case UINT64: // unsigned int 64 + long u64 = readInt64(); + if (u64 < 0L || u64 > Byte.MAX_VALUE) { + throw overflowU64(u64); + } + return (byte) u64; + case INT8: // signed int 8 + return readInt8(); + case INT16: // signed int 16 + short i16 = readInt16(); + if (i16 < Byte.MIN_VALUE || i16 > Byte.MAX_VALUE) { + throw overflowI16(i16); + } + return (byte) i16; + case INT32: // signed int 32 + int i32 = readInt32(); + if (i32 < Byte.MIN_VALUE || i32 > Byte.MAX_VALUE) { + throw overflowI32(i32); + } + return (byte) i32; + case INT64: // signed int 64 + long i64 = readInt64(); + if (i64 < Byte.MIN_VALUE || i64 > Byte.MAX_VALUE) { + throw overflowI64(i64); + } + return (byte) i64; + } + throw unexpected("Integer", b); + } + + @Override + public int readUnsignedByte() { + return readByte() & 0xff; + } + + @Override + public short readShort() { + byte b = readInt8(); + if (isFixInt(b)) { + return b; + } + switch (b) { + case UINT8: // unsigned int 8 + byte u8 = readInt8(); + return (short) (u8 & 0xff); + case UINT16: // unsigned int 16 + short u16 = readInt16(); + if (u16 < (short) 0) { + throw overflowU16(u16); + } + return u16; + case UINT32: // unsigned int 32 + int u32 = readInt32(); + if (u32 < 0 || u32 > Short.MAX_VALUE) { + throw overflowU32(u32); + } + return (short) u32; + case UINT64: // unsigned int 64 + long u64 = readInt64(); + if (u64 < 0L || u64 > Short.MAX_VALUE) { + throw overflowU64(u64); + } + return (short) u64; + case INT8: // signed int 8 + return readInt8(); + case INT16: // signed int 16 + return readInt16(); + case INT32: // signed int 32 + int i32 = readInt32(); + if (i32 < Short.MIN_VALUE || i32 > Short.MAX_VALUE) { + throw overflowI32(i32); + } + return (short) i32; + case INT64: // signed int 64 + long i64 = readInt64(); + if (i64 < Short.MIN_VALUE || i64 > Short.MAX_VALUE) { + throw overflowI64(i64); + } + return (short) i64; + } + throw unexpected("Integer", b); + } + + @Override + public int readUnsignedShort() { + return readShort() & 0xffff; + } + + @Override + public int readInt() { + byte b = readInt8(); + if (isFixInt(b)) { + return b; + } + switch (b) { + case UINT8: // unsigned int 8 + byte u8 = readInt8(); + return u8 & 0xff; + case UINT16: // unsigned int 16 + short u16 = readInt16(); + return u16 & 0xffff; + case UINT32: // unsigned int 32 + int u32 = readInt32(); + if (u32 < 0) { + throw overflowU32(u32); + } + return u32; + case UINT64: // unsigned int 64 + long u64 = readInt64(); + if (u64 < 0L || u64 > (long) Integer.MAX_VALUE) { + throw overflowU64(u64); + } + return (int) u64; + case INT8: // signed int 8 + return readInt8(); + case INT16: // signed int 16 + return readInt16(); + case INT32: // signed int 32 + return readInt32(); + case INT64: // signed int 64 + long i64 = readInt64(); + if (i64 < (long) Integer.MIN_VALUE || i64 > (long) Integer.MAX_VALUE) { + throw overflowI64(i64); + } + return (int) i64; + } + throw unexpected("Integer", b); + } + + @Override + public long readLong() { + byte b = readInt8(); + if (isFixInt(b)) { + return b; + } + switch (b) { + case UINT8: // unsigned int 8 + byte u8 = readInt8(); + return u8 & 0xff; + case UINT16: // unsigned int 16 + short u16 = readInt16(); + return u16 & 0xffff; + case UINT32: // unsigned int 32 + int u32 = readInt32(); + if (u32 < 0) { + return (long) (u32 & 0x7fffffff) + 0x80000000L; + } + else { + return u32; + } + case UINT64: // unsigned int 64 + long u64 = readInt64(); + if (u64 < 0L) { + throw overflowU64(u64); + } + return u64; + case INT8: // signed int 8 + return readInt8(); + case INT16: // signed int 16 + return readInt16(); + case INT32: // signed int 32 + return readInt32(); + case INT64: // signed int 64 + return readInt64(); + } + throw unexpected("Integer", b); + } + + @Override + public float readFloat() { + byte b = readInt8(); + return switch (b) { + case FLOAT32 -> readFloat32(); + case FLOAT64 -> (float) readFloat64(); + default -> throw unexpected("Float", b); + }; + } + + @Override + public double readDouble() { + byte b = readInt8(); + return switch (b) { + case FLOAT32 -> readFloat32(); + case FLOAT64 -> readFloat64(); + default -> throw unexpected("Float", b); + }; + } + + @Override + public String readString() { + int len = readStringHeader(); + if (len == 0) { + return Constant.BLANK; + } + if (len > stringSizeLimit) { + throw new MessageSizeException("Cannot reading a String of size larger than %,d: %,d" + .formatted(stringSizeLimit, len), len); + } + return buffer.readString(len, stringCharset); + } + + @Override + public Instant readTimestamp() { + ExtensionTypeHeader ext = readExtensionTypeHeader(); + if (!ext.isTimestampType()) { + throw unexpectedExtension("Timestamp", EXT_TIMESTAMP, ext.getType()); + } + switch (ext.getLength()) { + case 4: { + // Need to convert Java's int (int32) to uint32 + long u32 = readInt32() & 0xffffffffL; + return Instant.ofEpochSecond(u32); + } + case 8: { + long data64 = readInt64(); + int nsec = (int) (data64 >>> 34); + long sec = data64 & 0x00000003ffffffffL; + return Instant.ofEpochSecond(sec, nsec); + } + case 12: { + // Need to convert Java's int (int32) to uint32 + long nsecU32 = readInt32() & 0xffffffffL; + long sec = readInt64(); + return Instant.ofEpochSecond(sec, nsecU32); + } + default: + throw new MessageFormatException("Timestamp extension type (%d) expects 4, 8, or 12 bytes of payload but got %d bytes" + .formatted(EXT_TIMESTAMP, ext.getLength())); + } + } + + @Override + @SuppressWarnings("unchecked") + public T[] read(Class type, Function mapper) { + int size = readArrayHeader(); + T[] array = (T[]) Array.newInstance(type, size); + for (int i = 0; i < size; i++) { + array[i] = mapper.apply(this); + } + return array; + } + + @Override + @SuppressWarnings("unchecked") + public T[] read(Class type, Supplier supplier) { + int size = readArrayHeader(); + T[] array = (T[]) Array.newInstance(type, size); + for (int i = 0; i < size; i++) { + array[i] = supplier.get(); + } + return array; + } + + @Override + public List read(Function mapper) { + int size = readArrayHeader(); + ArrayList result = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + result.add(mapper.apply(this)); + } + return result; + } + + @Override + public List read(Supplier supplier) { + int size = readArrayHeader(); + ArrayList result = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + result.add(supplier.get()); + } + return result; + } + + @Override + public Map read(Function keyMapper, Function valueMapper) { + int size = readMapHeader(); + LinkedHashMap result = CollectionUtils.newLinkedHashMap(size); + for (int i = 0; i < size; i++) { + result.put(keyMapper.apply(this), valueMapper.apply(this)); + } + return result; + } + + /** + * Reads header of an array. + * + *

    + * This method returns number of elements to be read. After this method call, you call unpacker methods for + * each element. You don't have to call anything at the end of iteration. + * + * @return the size of the array to be read + * @throws MessageTypeException when value is not MessagePack Array type + * @throws MessageSizeException when size of the array is larger than 2^31 - 1 + */ + public int readArrayHeader() { + byte b = readInt8(); + if (isFixedArray(b)) { // fixarray + return b & 0x0f; + } + return switch (b) { + // array 16 + case ARRAY16 -> readNextLength16(); + // array 32 + case ARRAY32 -> readNextLength32(); + default -> throw unexpected("Array", b); + }; + } + + /** + * Reads header of a map. + * + *

    + * This method returns number of pairs to be read. After this method call, for each pair, you call unpacker + * methods for key first, and then value. You will call unpacker methods twice as many time as the returned + * count. You don't have to call anything at the end of iteration. + * + * @return the size of the map to be read + * @throws MessageTypeException when value is not MessagePack Map type + * @throws MessageSizeException when size of the map is larger than 2^31 - 1 + */ + public int readMapHeader() { + byte b = readInt8(); + if (isFixedMap(b)) { // fixmap + return b & 0x0f; + } + return switch (b) { + case MAP16 -> readNextLength16();// map 16 + case MAP32 -> readNextLength32();// map 32 + default -> throw unexpected("Map", b); + }; + } + + public ExtensionTypeHeader readExtensionTypeHeader() { + byte b = readInt8(); + switch (b) { + case FIXEXT1: { + byte type = readInt8(); + return new ExtensionTypeHeader(type, 1); + } + case FIXEXT2: { + byte type = readInt8(); + return new ExtensionTypeHeader(type, 2); + } + case FIXEXT4: { + byte type = readInt8(); + return new ExtensionTypeHeader(type, 4); + } + case FIXEXT8: { + byte type = readInt8(); + return new ExtensionTypeHeader(type, 8); + } + case FIXEXT16: { + byte type = readInt8(); + return new ExtensionTypeHeader(type, 16); + } + case EXT8: { + int length = readNextLength8(); + byte type = readInt8(); + return new ExtensionTypeHeader(type, length); + } + case EXT16: { + int length = readNextLength16(); + byte type = readInt8(); + return new ExtensionTypeHeader(type, length); + } + case EXT32: { + int length = readNextLength32(); + byte type = readInt8(); + return new ExtensionTypeHeader(type, length); + } + } + + throw unexpected("Ext", b); + } + + /** + * Read a byte value at the cursor and proceed the cursor. + */ + private byte readInt8() { + return buffer.readByte(); + } + + private short readInt16() { + return buffer.readShort(); + } + + private int readInt32() { + return buffer.readInt(); + } + + private long readInt64() { + return buffer.readLong(); + } + + private float readFloat32() { + return buffer.readFloat(); + } + + private double readFloat64() { + return buffer.readDouble(); + } + + private int readNextLength8() { + byte u8 = readInt8(); + return u8 & 0xff; + } + + private int readNextLength16() { + short u16 = readInt16(); + return u16 & 0xffff; + } + + private int readNextLength32() { + int u32 = readInt32(); + if (u32 < 0) { + throw overflowU32Size(u32); + } + return u32; + } + + private int tryReadStringHeader(byte b) { + return switch (b) { + case STR8 -> readNextLength8(); // str 8 + case STR16 -> readNextLength16(); // str 16 + case STR32 -> readNextLength32(); // str 32 + default -> -1; + }; + } + + private int tryReadBinaryHeader(byte b) { + return switch (b) { + case BIN8 -> readNextLength8(); // bin 8 + case BIN16 -> readNextLength16(); // bin 16 + case BIN32 -> readNextLength32(); // bin 32 + default -> -1; + }; + } + + public int readStringHeader() { + byte b = readInt8(); + if (isFixedRaw(b)) { // FixRaw + return b & 0x1f; + } + int len = tryReadStringHeader(b); + if (len >= 0) { + return len; + } + + throw unexpected("String", b); + } + + /** + * Reads header of a binary. + * + *

    + * This method returns number of bytes to be read. After this method call, you call a readPayload method such as + * {@link #read(int)} with the returned count. + * + *

    + * You can divide readPayload method into multiple calls. In this case, you must repeat readPayload methods + * until total amount of bytes becomes equal to the returned count. + * + * @return the size of the map to be read + * @throws MessageTypeException when value is not MessagePack Map type + * @throws MessageSizeException when size of the map is larger than 2^31 - 1 + * when underlying input + */ + public int readBinaryHeader() { + byte b = readInt8(); + if (isFixedRaw(b)) { // FixRaw + return b & 0x1f; + } + int len = tryReadBinaryHeader(b); + if (len >= 0) { + return len; + } + + throw unexpected("Binary", b); + } + + /** + * Reads a Nil byte. + * + * @throws MessageTypeException when value is not MessagePack Nil type + */ + public void readNull() { + byte b = readInt8(); + if (b == NIL) { + return; + } + throw unexpected("Nil", b); + } + + /** + * Peeks a Nil byte and reads it if next byte is a nil value. + *

    + * The difference from {@link #readNull()} is that readNull throws an exception if the next byte is not nil value + * while this tryReadNull method returns false without changing position. + * + * @return true if a {@code null} value is read + * @throws MessageInsufficientBufferException when the end of file reached + */ + public boolean tryReadNull() { + // makes sure that buffer has at least 1 byte + ByteBuf buffer = this.buffer; + if (!buffer.isReadable(1)) { + throw new MessageInsufficientBufferException(); + } + int index = buffer.readerIndex(); + byte b = buffer.getByte(index); + if (b == NIL) { + buffer.readerIndex(index + 1); + return true; + } + return false; + } + + /** + * Create an exception for the case when an unexpected byte value is read + */ + private static MessagePackException unexpected(String expected, byte b) { + MessageFormat format = MessageFormat.valueOf(b); + if (format == MessageFormat.NEVER_USED) { + return new MessageNeverUsedFormatException(String.format("Expected %s, but encountered 0xC1 \"NEVER_USED\" byte", expected)); + } + else { + String name = format.getValueType().name(); + String typeName = name.charAt(0) + name.substring(1).toLowerCase(); + return new MessageTypeException(String.format("Expected %s, but got %s (%02x)", expected, typeName, b)); + } + } + + private static MessagePackException unexpectedExtension(String expected, int expectedType, int actualType) { + return new MessageTypeException(String.format("Expected extension type %s (%d), but got extension type %d", + expected, expectedType, actualType)); + } + + private static MessageIntegerOverflowException overflowU8(byte u8) { + BigInteger bi = BigInteger.valueOf(u8 & 0xff); + return new MessageIntegerOverflowException(bi); + } + + private static MessageIntegerOverflowException overflowU16(short u16) { + BigInteger bi = BigInteger.valueOf(u16 & 0xffff); + return new MessageIntegerOverflowException(bi); + } + + private static MessageIntegerOverflowException overflowU32(int u32) { + BigInteger bi = BigInteger.valueOf((long) (u32 & 0x7fffffff) + 0x80000000L); + return new MessageIntegerOverflowException(bi); + } + + private static MessageIntegerOverflowException overflowU64(long u64) { + BigInteger bi = BigInteger.valueOf(u64 + Long.MAX_VALUE + 1L).setBit(63); + return new MessageIntegerOverflowException(bi); + } + + private static MessageIntegerOverflowException overflowI16(short i16) { + BigInteger bi = BigInteger.valueOf(i16); + return new MessageIntegerOverflowException(bi); + } + + private static MessageIntegerOverflowException overflowI32(int i32) { + BigInteger bi = BigInteger.valueOf(i32); + return new MessageIntegerOverflowException(bi); + } + + private static MessageIntegerOverflowException overflowI64(long i64) { + BigInteger bi = BigInteger.valueOf(i64); + return new MessageIntegerOverflowException(bi); + } + + private static MessageSizeException overflowU32Size(int u32) { + long lv = (long) (u32 & 0x7fffffff) + 0x80000000L; + return new MessageSizeException(lv); + } + +} diff --git a/today-service-api/src/main/java/infra/cloud/serialize/MessagePackWriter.java b/today-service-api/src/main/java/infra/cloud/serialize/MessagePackWriter.java new file mode 100644 index 0000000..91eb349 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/serialize/MessagePackWriter.java @@ -0,0 +1,545 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize; + +import org.jspecify.annotations.Nullable; + +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import infra.lang.Enumerable; +import io.netty.buffer.ByteBuf; + +import static infra.cloud.serialize.format.MessagePackCode.ARRAY16; +import static infra.cloud.serialize.format.MessagePackCode.ARRAY32; +import static infra.cloud.serialize.format.MessagePackCode.BIN16; +import static infra.cloud.serialize.format.MessagePackCode.BIN32; +import static infra.cloud.serialize.format.MessagePackCode.BIN8; +import static infra.cloud.serialize.format.MessagePackCode.EXT8; +import static infra.cloud.serialize.format.MessagePackCode.EXT_TIMESTAMP; +import static infra.cloud.serialize.format.MessagePackCode.FALSE; +import static infra.cloud.serialize.format.MessagePackCode.FIXARRAY_PREFIX; +import static infra.cloud.serialize.format.MessagePackCode.FIXEXT4; +import static infra.cloud.serialize.format.MessagePackCode.FIXEXT8; +import static infra.cloud.serialize.format.MessagePackCode.FIXMAP_PREFIX; +import static infra.cloud.serialize.format.MessagePackCode.FIXSTR_PREFIX; +import static infra.cloud.serialize.format.MessagePackCode.FLOAT32; +import static infra.cloud.serialize.format.MessagePackCode.FLOAT64; +import static infra.cloud.serialize.format.MessagePackCode.INT16; +import static infra.cloud.serialize.format.MessagePackCode.INT32; +import static infra.cloud.serialize.format.MessagePackCode.INT64; +import static infra.cloud.serialize.format.MessagePackCode.INT8; +import static infra.cloud.serialize.format.MessagePackCode.MAP16; +import static infra.cloud.serialize.format.MessagePackCode.MAP32; +import static infra.cloud.serialize.format.MessagePackCode.NIL; +import static infra.cloud.serialize.format.MessagePackCode.STR16; +import static infra.cloud.serialize.format.MessagePackCode.STR32; +import static infra.cloud.serialize.format.MessagePackCode.STR8; +import static infra.cloud.serialize.format.MessagePackCode.TRUE; +import static infra.cloud.serialize.format.MessagePackCode.UINT16; +import static infra.cloud.serialize.format.MessagePackCode.UINT32; +import static infra.cloud.serialize.format.MessagePackCode.UINT64; +import static infra.cloud.serialize.format.MessagePackCode.UINT8; + +/** + * A writer for serializing data into MessagePack format. + * This class provides methods to write various data types according to the MessagePack specification, + * automatically choosing the most efficient encoding based on the value being written. + * + * @author TAKETODAY + * @since 1.0 2025/8/18 17:50 + */ +public class MessagePackWriter implements Writable { + + private static final long NANOS_PER_SECOND = 1000000000L; + + private final ByteBuf buffer; + + public MessagePackWriter(ByteBuf buffer) { + this.buffer = buffer; + } + + /** + * Writes a byte array to the output. + *

    + * This method is used with {@link #writeStringHeader(int)} or {@link #writeBinaryHeader(int)} methods. + *

    + * Unlike {@link #writeFully(byte[])} method, this method does not make a defensive copy of the given byte + * array + * + * @param b the data to add + * @throws SerializationException if an I/O error occurs. + * @see #writeFully(byte[]) + */ + @Override + public void write(byte @Nullable [] b) { + if (b == null || b.length == 0) { + writeBinaryHeader(0); + } + else { + writeBinaryHeader(b.length); + buffer.writeBytes(b); + } + } + + @Override + public void writeFully(byte[] b) { + buffer.writeBytes(b); + } + + @Override + public void writeFully(byte[] b, int off, int len) { + buffer.writeBytes(b, off, len); + } + + @Override + public void write(byte[] b, int off, int len) { + writeBinaryHeader(len); + buffer.writeBytes(b, off, len); + } + + public void writeNull() { + writeByte(NIL); + } + + @Override + public void write(boolean v) { + writeByte(v ? TRUE : FALSE); + } + + @Override + public void write(byte b) { + if (b < -(1 << 5)) { + writeByteAndByte(INT8, b); + } + else { + writeByte(b); + } + } + + /** + * Writes an Integer value. + * + *

    + * This method writes an integer using the smallest format from the int format family. + * + * @param v the integer to be written + */ + @Override + public void write(short v) { + if (v < -(1 << 5)) { + if (v < -(1 << 7)) { + writeByteAndShort(INT16, v); + } + else { + writeByteAndByte(INT8, (byte) v); + } + } + else if (v < (1 << 7)) { + writeByte((byte) v); + } + else { + if (v < (1 << 8)) { + writeByteAndByte(UINT8, (byte) v); + } + else { + writeByteAndShort(UINT16, v); + } + } + } + + /** + * Writes an Integer value. + * + *

    + * This method writes an integer using the smallest format from the int format family. + * + * @param v the integer to be written + */ + @Override + public void write(int v) { + if (v < -(1 << 5)) { + if (v < -(1 << 15)) { + writeByteAndInt(INT32, v); + } + else if (v < -(1 << 7)) { + writeByteAndShort(INT16, (short) v); + } + else { + writeByteAndByte(INT8, (byte) v); + } + } + else if (v < (1 << 7)) { + writeByte((byte) v); + } + else { + if (v < (1 << 8)) { + writeByteAndByte(UINT8, (byte) v); + } + else if (v < (1 << 16)) { + writeByteAndShort(UINT16, (short) v); + } + else { + // unsigned 32 + writeByteAndInt(UINT32, v); + } + } + } + + /** + * Writes an Integer value. + * + *

    + * This method writes an integer using the smallest format from the int format family. + * + * @param v the integer to be written + */ + @Override + public void write(long v) { + if (v < -(1L << 5)) { + if (v < -(1L << 15)) { + if (v < -(1L << 31)) { + writeByteAndLong(INT64, v); + } + else { + writeByteAndInt(INT32, (int) v); + } + } + else { + if (v < -(1 << 7)) { + writeByteAndShort(INT16, (short) v); + } + else { + writeByteAndByte(INT8, (byte) v); + } + } + } + else if (v < (1 << 7)) { + // fixnum + writeByte((byte) v); + } + else { + if (v < (1L << 16)) { + if (v < (1 << 8)) { + writeByteAndByte(UINT8, (byte) v); + } + else { + writeByteAndShort(UINT16, (short) v); + } + } + else { + if (v < (1L << 32)) { + writeByteAndInt(UINT32, (int) v); + } + else { + writeByteAndLong(UINT64, v); + } + } + } + } + + @Override + public void write(float v) { + writeByteAndFloat(FLOAT32, v); + } + + @Override + public void write(double v) { + writeByteAndDouble(FLOAT64, v); + } + + @Override + public void write(@Nullable String v) { + if (v == null || v.isEmpty()) { + writeStringHeader(0); + } + else { + // JVM performs various optimizations (memory allocation, reusing encoder etc.) when String.getBytes is used + byte[] bytes = v.getBytes(StandardCharsets.UTF_8); + writeStringHeader(bytes.length); + writeFully(bytes); + } + } + + @Override + public void write(Enumerable v) { + write(v.getValue()); + } + + @Override + public void write(Instant v) { + writeTimestamp(v.getEpochSecond(), v.getNano()); + } + + @Override + public void writeTimestamp(long millis) { + write(Instant.ofEpochMilli(millis)); + } + + @Override + public void write(Message v) { + v.writeTo(this); + } + + @Override + public boolean writeNullable(@Nullable V v, BiConsumer valueMapper) { + if (v == null) { + writeNull(); + return true; + } + else { + valueMapper.accept(this, v); + return false; + } + } + + @Override + public void writeTimestamp(long epochSecond, int nanoAdjustment) { + long sec = Math.addExact(epochSecond, Math.floorDiv(nanoAdjustment, NANOS_PER_SECOND)); + long nsec = Math.floorMod(nanoAdjustment, NANOS_PER_SECOND); + + if (sec >>> 34 == 0) { + // sec can be serialized in 34 bits. + long data64 = (nsec << 34) | sec; + if ((data64 & 0xffffffff00000000L) == 0L) { + // sec can be serialized in 32 bits and nsec is 0. + // use timestamp 32 + writeTimestamp32((int) sec); + } + else { + // sec exceeded 32 bits or nsec is not 0. + // use timestamp 64 + writeTimestamp64(data64); + } + } + else { + // use timestamp 96 format + writeTimestamp96(sec, (int) nsec); + } + } + + @Override + public void write(T[] v, Consumer mapper) { + writeArrayHeader(v.length); + for (T t : v) { + mapper.accept(t); + } + } + + @Override + public void write(T[] v, BiConsumer mapper) { + writeArrayHeader(v.length); + for (T t : v) { + mapper.accept(this, t); + } + } + + @Override + public void write(List v, Consumer mapper) { + int size = v.size(); + writeArrayHeader(size); + for (T t : v) { + mapper.accept(t); + } + } + + @Override + public void write(List v, BiConsumer mapper) { + int size = v.size(); + writeArrayHeader(size); + for (T t : v) { + mapper.accept(this, t); + } + } + + @Override + public void write(Map v, BiConsumer keyMapper, BiConsumer valueMapper) { + int size = v.size(); + writeMapHeader(size); + for (Map.Entry entry : v.entrySet()) { + keyMapper.accept(this, entry.getKey()); + valueMapper.accept(this, entry.getValue()); + } + } + + @Override + public void write(Map v, Consumer keyMapper, Consumer valueMapper) { + int size = v.size(); + writeMapHeader(size); + for (Map.Entry entry : v.entrySet()) { + keyMapper.accept(entry.getKey()); + valueMapper.accept(entry.getValue()); + } + } + + /** + * Writes header of a Binary value. + *

    + * You MUST call {@link #writeFully(byte[])} method to write body binary. + * + * @param len number of bytes of a binary to be written + */ + public void writeBinaryHeader(int len) { + if (len < (1 << 8)) { + writeByteAndByte(BIN8, (byte) len); + } + else if (len < (1 << 16)) { + writeByteAndShort(BIN16, (short) len); + } + else { + writeByteAndInt(BIN32, len); + } + } + + /** + * Writes header of a String value. + *

    + * Length must be number of bytes of a string in UTF-8 encoding. + *

    + * You MUST call {@link #writeFully(byte[])} method to write body of the + * UTF-8 encoded string. + * + * @param len number of bytes of a UTF-8 string to be written + */ + public void writeStringHeader(int len) { + if (len < (1 << 5)) { + writeByte((byte) (FIXSTR_PREFIX | len)); + } + else if (len < (1 << 8)) { + writeByteAndByte(STR8, (byte) len); + } + else if (len < (1 << 16)) { + writeByteAndShort(STR16, (short) len); + } + else { + writeByteAndInt(STR32, len); + } + } + + /** + * Writes header of an Array value. + *

    + * You will call other packer methods for each element after this method call. + *

    + * You don't have to call anything at the end of iteration. + * + * @param arraySize number of elements to be written + */ + public void writeArrayHeader(int arraySize) { + if (arraySize < 0) { + throw new IllegalArgumentException("array size must be >= 0"); + } + + if (arraySize < (1 << 4)) { + writeByte((byte) (FIXARRAY_PREFIX | arraySize)); + } + else if (arraySize < (1 << 16)) { + writeByteAndShort(ARRAY16, (short) arraySize); + } + else { + writeByteAndInt(ARRAY32, arraySize); + } + } + + /** + * Writes header of a Map value. + *

    + * After this method call, for each key-value pair, you will call packer methods for key first, and then value. + * You will call packer methods twice as many time as the size of the map. + *

    + * You don't have to call anything at the end of iteration. + * + * @param mapSize number of pairs to be written + */ + public void writeMapHeader(int mapSize) { + if (mapSize < 0) { + throw new IllegalArgumentException("map size must be >= 0"); + } + + if (mapSize < (1 << 4)) { + writeByte((byte) (FIXMAP_PREFIX | mapSize)); + } + else if (mapSize < (1 << 16)) { + writeByteAndShort(MAP16, (short) mapSize); + } + else { + writeByteAndInt(MAP32, mapSize); + } + } + + private void writeByte(byte b) { + buffer.writeByte(b); + } + + private void writeByteAndByte(byte b, byte v) { + buffer.writeByte(b); + buffer.writeByte(v); + } + + private void writeByteAndShort(byte b, short v) { + buffer.writeByte(b); + buffer.writeShort(v); + } + + private void writeByteAndInt(byte b, int v) { + buffer.writeByte(b); + buffer.writeInt(v); + } + + private void writeByteAndFloat(byte b, float v) { + buffer.writeByte(b); + buffer.writeFloat(v); + } + + private void writeByteAndDouble(byte b, double v) { + buffer.writeByte(b); + buffer.writeDouble(v); + } + + private void writeByteAndLong(byte b, long v) { + buffer.writeByte(b); + buffer.writeLong(v); + } + + private void writeTimestamp32(int sec) { + // timestamp 32 in fixext 4 + buffer.writeByte(FIXEXT4); + buffer.writeByte(EXT_TIMESTAMP); + buffer.writeInt(sec); + } + + private void writeTimestamp64(long data64) { + // timestamp 64 in fixext 8 + buffer.writeByte(FIXEXT8); + buffer.writeByte(EXT_TIMESTAMP); + buffer.writeLong(data64); + } + + private void writeTimestamp96(long sec, int nsec) { + // timestamp 96 in ext 8 + buffer.writeByte(EXT8); + buffer.writeByte((byte) 12); + buffer.writeByte(EXT_TIMESTAMP); + buffer.writeInt(nsec); + buffer.writeLong(sec); + } + +} diff --git a/today-service-api/src/main/java/infra/cloud/serialize/ReturnValueDeserializer.java b/today-service-api/src/main/java/infra/cloud/serialize/ReturnValueDeserializer.java new file mode 100644 index 0000000..14b0713 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/serialize/ReturnValueDeserializer.java @@ -0,0 +1,32 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize; + +import infra.cloud.service.ServiceMethod; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/20 22:01 + */ +public interface ReturnValueDeserializer { + + boolean supportsReturnValue(ServiceMethod method); + + T deserialize(ServiceMethod method, Readable readable) + throws SerializationException; + +} diff --git a/today-service-api/src/main/java/infra/cloud/serialize/ReturnValueSerializer.java b/today-service-api/src/main/java/infra/cloud/serialize/ReturnValueSerializer.java new file mode 100644 index 0000000..9ca7171 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/serialize/ReturnValueSerializer.java @@ -0,0 +1,52 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize; + +import infra.cloud.service.ServiceMethod; + +/** + * A serializer responsible for converting the return value of a service method into a writable format. + *

    + * Implementations of this interface determine whether they support serializing the return value + * for a given {@link ServiceMethod} and perform the actual serialization process. + * + * @param the type of the return value to be serialized + * @author 海子 Yang + * @since 1.0 2025/8/20 21:56 + */ +public interface ReturnValueSerializer { + + /** + * Checks if this serializer supports serializing the return value for the specified service method. + * + * @param method the service method to check + * @return {@code true} if this serializer can handle the return value of the given method, {@code false} otherwise + */ + boolean supportsReturnValue(ServiceMethod method); + + /** + * Serializes the return value of a service method into the provided writable target. + * + * @param method the service method whose return value is being serialized + * @param returnValue the return value to serialize + * @param writable the target to write the serialized data to + * @throws SerializationException if an error occurs during serialization + */ + void serialize(ServiceMethod method, T returnValue, Writable writable) + throws SerializationException; + +} diff --git a/today-service-api/src/main/java/infra/cloud/serialize/ThrowableSerialization.java b/today-service-api/src/main/java/infra/cloud/serialize/ThrowableSerialization.java new file mode 100644 index 0000000..a8eabd0 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/serialize/ThrowableSerialization.java @@ -0,0 +1,37 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize; + +import java.io.IOException; + +import io.netty.buffer.ByteBuf; + +/** + * @author 海子 Yang + * @since 1.0 2024/12/25 17:36 + */ +public class ThrowableSerialization { + + public void serialize(Throwable throwable, ByteBuf payload) throws IOException { + + } + + public Throwable deserialize(ByteBuf body) throws SerializationException { + return null; + } + +} diff --git a/today-service-api/src/main/java/infra/cloud/serialize/package-info.java b/today-service-api/src/main/java/infra/cloud/serialize/package-info.java new file mode 100644 index 0000000..5b28edc --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/serialize/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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. + */ + +/** + * serialize + */ +@NullMarked +package infra.cloud.serialize; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-service-api/src/main/java/infra/cloud/serialize/support/ProtobufArgumentSerialization.java b/today-service-api/src/main/java/infra/cloud/serialize/support/ProtobufArgumentSerialization.java new file mode 100644 index 0000000..298baa5 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/serialize/support/ProtobufArgumentSerialization.java @@ -0,0 +1,106 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize.support; + +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Message; + +import org.jspecify.annotations.Nullable; + +import java.lang.reflect.Method; + +import infra.cloud.serialize.ArgumentSerialization; +import infra.cloud.serialize.Readable; +import infra.cloud.serialize.ReturnValueSerializer; +import infra.cloud.serialize.SerializationException; +import infra.cloud.serialize.Writable; +import infra.cloud.service.ServiceMethod; +import infra.core.MethodParameter; +import infra.util.ConcurrentReferenceHashMap; + +/** + * Serialization for protobuf + * + * @author 海子 Yang + * @since 1.0 2024/12/20 17:46 + */ +public class ProtobufArgumentSerialization implements ArgumentSerialization, ReturnValueSerializer { + + private static final ConcurrentReferenceHashMap, Method> methodCache = new ConcurrentReferenceHashMap<>(); + + @Override + public boolean supportsArgument(MethodParameter parameter) { + return Message.class.isAssignableFrom(parameter.getParameterType()); + } + + @Override + public void serialize(MethodParameter parameter, @Nullable Message value, Writable writable) throws SerializationException { + writable.write(value == null ? null : value.toByteArray()); + } + + @Override + public Message deserialize(MethodParameter parameter, Readable readable) throws SerializationException { + Class parameterType = parameter.getParameterType(); + Message.Builder messageBuilder = getMessageBuilder(parameterType); + try { + byte[] bytes = readable.read(); + return messageBuilder.mergeFrom(bytes).build(); + } + catch (InvalidProtocolBufferException e) { + throw new SerializationException("Invalid protocol buffer", e); + } + } + + /** + * Create a new {@code Message.Builder} instance for the given class. + *

    This method uses a ConcurrentReferenceHashMap for caching method lookups. + */ + private Message.Builder getMessageBuilder(Class clazz) { + try { + Method method = methodCache.get(clazz); + if (method == null) { + method = clazz.getMethod("newBuilder"); + methodCache.put(clazz, method); + } + return (Message.Builder) method.invoke(clazz); + } + catch (Exception ex) { + throw new SerializationException( + "Invalid Protobuf Message type: no invocable newBuilder() method on " + clazz, ex); + } + } + + // ---------------------------------------------------------------------------------------- + // ReturnValueSerialization + // ---------------------------------------------------------------------------------------- + + @Override + public boolean supportsReturnValue(ServiceMethod method) { + return Message.class.isAssignableFrom(method.getReturnType().getParameterType()); + } + + @Override + public void serialize(ServiceMethod method, Message value, Writable writable) { + writable.write(value.toByteArray()); + } + +// @Override +// public Message deserialize(RpcMethod method, ByteBuf payload, Input input) throws SerializationException { +// return deserialize(method.getReturnType(), payload, input); +// } + +} diff --git a/today-service-api/src/main/java/infra/cloud/serialize/support/SerializableReturnValueSerialization.java b/today-service-api/src/main/java/infra/cloud/serialize/support/SerializableReturnValueSerialization.java new file mode 100644 index 0000000..a83b692 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/serialize/support/SerializableReturnValueSerialization.java @@ -0,0 +1,75 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize.support; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +import infra.cloud.serialize.Readable; +import infra.cloud.serialize.ReturnValueDeserializer; +import infra.cloud.serialize.ReturnValueSerializer; +import infra.cloud.serialize.SerializationException; +import infra.cloud.serialize.Writable; +import infra.cloud.service.ServiceMethod; +import infra.core.Ordered; + +/** + * @author 海子 Yang + * @since 1.0 2026/3/18 11:42 + */ +public class SerializableReturnValueSerialization implements ReturnValueDeserializer, ReturnValueSerializer, Ordered { + + @Override + public boolean supportsReturnValue(ServiceMethod method) { + return method.isReturnTypeAssignableTo(Serializable.class); + } + + @Override + public void serialize(ServiceMethod method, Serializable value, Writable writable) throws SerializationException { + try { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + try (ObjectOutputStream stream = new ObjectOutputStream(output)) { + stream.writeObject(value); + stream.flush(); + } + writable.write(output.toByteArray()); + } + catch (IOException e) { + throw new SerializationException(value + " serialize failed", e); + } + } + + @Override + public Serializable deserialize(ServiceMethod method, Readable readable) throws SerializationException { + try (ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(readable.read()))) { + return (Serializable) stream.readObject(); + } + catch (Exception e) { + throw new SerializationException(method + " return value deserialize failed", e); + } + } + + @Override + public int getOrder() { + return LOWEST_PRECEDENCE; + } + +} diff --git a/today-service-api/src/main/java/infra/cloud/serialize/support/SimpleValueArgumentSerialization.java b/today-service-api/src/main/java/infra/cloud/serialize/support/SimpleValueArgumentSerialization.java new file mode 100644 index 0000000..19b70cc --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/serialize/support/SimpleValueArgumentSerialization.java @@ -0,0 +1,89 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize.support; + +import org.jspecify.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +import infra.cloud.serialize.ArgumentSerialization; +import infra.cloud.serialize.Readable; +import infra.cloud.serialize.Writable; +import infra.cloud.serialize.value.ValueSerialization; +import infra.core.MethodParameter; + +import static infra.cloud.serialize.value.ValueSerialization.map; + +/** + * A simple implementation of {@link ArgumentSerialization} that handles basic value types. + *

    + * This class provides serialization and deserialization support for primitive types + * and their corresponding wrapper classes, including {@code int}, {@code long}, and {@code short}. + * It maintains an internal map to associate each supported type with its specific + * {@link ValueSerialization} logic. + * + * @author 海子 Yang + * @since 1.0 2025/3/8 21:15 + */ +public class SimpleValueArgumentSerialization implements ArgumentSerialization { + + private final Map, ValueSerialization> serializationMap = new HashMap<>(); + + public SimpleValueArgumentSerialization() { + serializationMap.put(int.class, map(infra.cloud.serialize.Readable::readInt, Writable::write)); + serializationMap.put(Integer.class, map(infra.cloud.serialize.Readable::readInt, Writable::write)); + + serializationMap.put(long.class, map(infra.cloud.serialize.Readable::readLong, Writable::write)); + serializationMap.put(Long.class, map(infra.cloud.serialize.Readable::readLong, Writable::write)); + + serializationMap.put(short.class, map(infra.cloud.serialize.Readable::readShort, Writable::write)); + serializationMap.put(Short.class, map(infra.cloud.serialize.Readable::readShort, Writable::write)); + } + + @Override + public boolean supportsArgument(MethodParameter parameter) { + return serializationMap.containsKey(parameter.getParameterType()); + } + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void serialize(MethodParameter parameter, @Nullable Object value, Writable writable) { + ValueSerialization serialization = findSerialization(parameter.getParameterType()); + serialization.serialize(parameter, value, writable); + } + + @Override + public @Nullable Object deserialize(MethodParameter parameter, Readable readable) { + var serialization = findSerialization(parameter.getParameterType()); + return serialization.deserialize(parameter, readable); + } + + @SuppressWarnings({ "rawtypes" }) + private ValueSerialization findSerialization(Class type) { + ValueSerialization serialization = serializationMap.get(type); + if (serialization == null) { + Class superclass = type.getSuperclass(); + if (superclass == null || superclass == Object.class) { + throw new IllegalStateException("ValueSerialization for type %s not found".formatted(type)); + } + return findSerialization(superclass); + } + return serialization; + } + +} diff --git a/today-service-api/src/main/java/infra/cloud/serialize/support/package-info.java b/today-service-api/src/main/java/infra/cloud/serialize/support/package-info.java new file mode 100644 index 0000000..80679af --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/serialize/support/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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. + */ + +/** + * + */ +@NullMarked +package infra.cloud.serialize.support; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-service-api/src/main/java/infra/cloud/serialize/value/FuncValueSerialization.java b/today-service-api/src/main/java/infra/cloud/serialize/value/FuncValueSerialization.java new file mode 100644 index 0000000..41133ac --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/serialize/value/FuncValueSerialization.java @@ -0,0 +1,52 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize.value; + +import java.util.function.BiConsumer; +import java.util.function.Function; + +import infra.cloud.serialize.Readable; +import infra.cloud.serialize.Writable; +import infra.cloud.serialize.SerializationException; +import infra.core.MethodParameter; + +/** + * @author 海子 Yang + * @since 1.0 2025/3/8 21:37 + */ +final class FuncValueSerialization implements ValueSerialization { + + private final Function reader; + + private final BiConsumer writer; + + FuncValueSerialization(Function reader, BiConsumer writer) { + this.reader = reader; + this.writer = writer; + } + + @Override + public void serialize(MethodParameter parameter, T value, Writable writable) { + writer.accept(writable, value); + } + + @Override + public T deserialize(MethodParameter parameter, Readable readable) throws SerializationException { + return reader.apply(readable); + } + +} diff --git a/today-service-api/src/main/java/infra/cloud/serialize/value/ValueSerialization.java b/today-service-api/src/main/java/infra/cloud/serialize/value/ValueSerialization.java new file mode 100644 index 0000000..a459d38 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/serialize/value/ValueSerialization.java @@ -0,0 +1,80 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize.value; + +import java.util.function.BiConsumer; +import java.util.function.Function; + +import infra.cloud.serialize.Readable; +import infra.cloud.serialize.SerializationException; +import infra.cloud.serialize.Writable; +import infra.core.MethodParameter; +import infra.lang.Assert; + +/** + * Defines a strategy for serializing and deserializing values of type {@code T}. + *

    + * This interface provides methods to convert a Java object into a writable format + * and to reconstruct an object from a readable format, typically used in RPC or + * data persistence scenarios. It also offers a static factory method to create + * instances based on functional definitions. + * + * @param the type of value to be serialized and deserialized + * @author 海子 Yang + * @since 1.0 2025/3/8 21:31 + */ +public interface ValueSerialization { + + /** + * Serializes the given value into the provided writable output. + * + * @param parameter the method parameter context associated with this serialization + * @param value the value to serialize + * @param writable the target output to write the serialized data + * @throws SerializationException if an error occurs during serialization + */ + void serialize(MethodParameter parameter, T value, Writable writable) + throws SerializationException; + + /** + * Deserializes a value from the provided readable input. + * + * @param parameter the method parameter context associated with this deserialization + * @param readable the source input to read the serialized data + * @return the deserialized value of type {@code T} + * @throws SerializationException if an error occurs during deserialization + */ + T deserialize(MethodParameter parameter, Readable readable) + throws SerializationException; + + /** + * Creates a {@code ValueSerialization} instance using the provided reader and writer functions. + * + * @param the type of value to handle + * @param reader a function to read and construct an object from {@link Readable} + * @param writer a consumer to write an object to {@link Writable} + * @return a new {@code ValueSerialization} instance + * @throws IllegalArgumentException if either reader or writer is null + */ + static ValueSerialization map(Function reader, BiConsumer writer) { + Assert.notNull(reader, "reader Function is required"); + Assert.notNull(writer, "writer BiConsumer is required"); + return new FuncValueSerialization<>(reader, writer); + } + +} + diff --git a/today-service-api/src/main/java/infra/cloud/service/AbstractServiceInterfaceMetadataProvider.java b/today-service-api/src/main/java/infra/cloud/service/AbstractServiceInterfaceMetadataProvider.java new file mode 100644 index 0000000..79bd263 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/service/AbstractServiceInterfaceMetadataProvider.java @@ -0,0 +1,86 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import java.lang.reflect.Method; +import java.util.List; + +import infra.core.MethodIntrospector; +import infra.lang.Assert; +import infra.util.ReflectionUtils; + +/** + * Abstract base class for providing metadata about service interfaces. + *

    This class implements the {@link ServiceInterfaceMetadataProvider} interface and provides + * a common implementation for extracting service method metadata from a given service interface. + * It relies on a {@link ServiceMetadataProvider} to obtain general service metadata and uses + * reflection to identify and process service methods. + * + * @param the type of the service method, which must extend {@link ServiceMethod} + * @author 海子 Yang + * @since 1.0 2025/8/10 16:53 + */ +public abstract class AbstractServiceInterfaceMetadataProvider implements ServiceInterfaceMetadataProvider { + + private final ServiceMetadataProvider serviceMetadataProvider; + + /** + * Constructs a new {@code AbstractServiceInterfaceMetadataProvider} with the specified + * {@link ServiceMetadataProvider}. + * + * @param serviceMetadataProvider the provider for general service metadata; must not be null + * @throws IllegalArgumentException if {@code serviceMetadataProvider} is null + */ + protected AbstractServiceInterfaceMetadataProvider(ServiceMetadataProvider serviceMetadataProvider) { + Assert.notNull(serviceMetadataProvider, "serviceMetadataProvider is required"); + this.serviceMetadataProvider = serviceMetadataProvider; + } + + @Override + public ServiceInterfaceMetadata getMetadata(Class serviceInterface) { + ServiceMetadata serviceMetadata = serviceMetadataProvider.getMetadata(serviceInterface); + + List serviceMethods = MethodIntrospector.filterMethods(serviceInterface, this::isServiceMethod).stream() + .map(method -> createServiceMethod(serviceMetadata, serviceInterface, method)) + .toList(); + + return new ServiceInterfaceMetadata<>(serviceInterface, serviceMetadata, serviceMethods); + } + + /** + * Creates a specific service method instance based on the provided service metadata, + * service interface, and reflected method. + * + * @param serviceMetadata the general metadata of the service + * @param serviceInterface the service interface class + * @param method the reflected method to be wrapped + * @return a new instance of {@code M} representing the service method + */ + protected abstract M createServiceMethod(ServiceMetadata serviceMetadata, Class serviceInterface, Method method); + + /** + * Determines whether the given method should be considered a service method. + *

    By default, this implementation excludes standard {@link Object} methods. + * + * @param method the method to check + * @return {@code true} if the method is a service method, {@code false} otherwise + */ + protected boolean isServiceMethod(Method method) { + return !ReflectionUtils.isObjectMethod(method); + } + +} diff --git a/today-service-api/src/main/java/infra/cloud/service/DefaultServiceMetadataProvider.java b/today-service-api/src/main/java/infra/cloud/service/DefaultServiceMetadataProvider.java new file mode 100644 index 0000000..3a61e42 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/service/DefaultServiceMetadataProvider.java @@ -0,0 +1,161 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import org.jspecify.annotations.Nullable; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import infra.lang.Assert; +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.util.ClassUtils; +import infra.util.CollectionUtils; +import infra.util.MultiValueMap; +import infra.util.StringUtils; + +import static infra.lang.TodayStrategies.readStrategies; + +/** + * Default implementation of {@link ServiceMetadataProvider} that loads service metadata + * from properties files located on the classpath. + *

    + * By default, this provider searches for configuration files at + * {@code META-INF/service-metadata.properties}. It supports loading multiple + * resources with the same name from different locations in the classpath and + * aggregates the metadata into an internal cache keyed by interface name. + * + * @author 海子 Yang + * @since 1.0 2025/8/9 21:55 + */ +public class DefaultServiceMetadataProvider implements ServiceMetadataProvider { + + private static final Logger log = LoggerFactory.getLogger(DefaultServiceMetadataProvider.class); + + public static final String DEFAULT_METADATA_LOCATION = "META-INF/service-metadata.properties"; + + public static final String KEY_PREFIX = "service."; + + public static final String KEY_SERVICE_ID = "service.id"; + public static final String KEY_SERVICE_VERSION = "service.version"; + public static final String KEY_SERVICE_INTERFACES = "service.interfaces"; + + private final Map metadataCache; + + /** + * Constructs a provider with the default metadata location. + */ + public DefaultServiceMetadataProvider() { + this(DEFAULT_METADATA_LOCATION); + } + + /** + * Constructs a provider with a custom metadata location. + * + * @param metadataLocation the classpath location of the metadata file + */ + public DefaultServiceMetadataProvider(String metadataLocation) { + Assert.notNull(metadataLocation, "metadata-location is required"); + this.metadataCache = loadMetadata(metadataLocation); + } + + @Override + public ServiceMetadata getMetadata(Class serviceInterface) { + ServiceMetadata metadata = metadataCache.get(serviceInterface.getName()); + Assert.state(metadata != null, "metadata not found"); + return metadata; + } + + private Map loadMetadata(String metadataLocation) { + Map metadataCache = new HashMap<>(); + List serviceMetadata = loadResources(ClassUtils.getDefaultClassLoader(), metadataLocation); + for (ServiceMetadata metadata : serviceMetadata) { + for (String ifc : metadata.getInterfaces()) { + metadataCache.put(ifc, metadata); + } + } + return metadataCache; + } + + /** + * Creates ServiceMetadata from loaded properties. + * + * @param properties the loaded properties + * @return a new ServiceMetadata instance + */ + protected @Nullable ServiceMetadata createMetadata(Properties properties) { + MultiValueMap props = MultiValueMap.forLinkedHashMap(); + readStrategies(props, properties); + + List interfaces = props.remove(KEY_SERVICE_INTERFACES); + + if (interfaces == null) { + return null; + } + + String serviceId = CollectionUtils.firstElement(props.remove(KEY_SERVICE_ID)); + String version = CollectionUtils.firstElement(props.remove(KEY_SERVICE_VERSION)); + + Set keys = props.keySet(); + var metadata = CollectionUtils.newLinkedHashMap(keys.size()); + for (String key : keys) { + String value = props.getFirst(key); + if (StringUtils.hasText(value)) { + metadata.put(StringUtils.delete(key, KEY_PREFIX), value); + } + } + + return new ServiceMetadata(serviceId, version, interfaces, metadata); + } + + protected List loadResources(ClassLoader classLoader, String metadataLocation) { + List serviceMetadataList = new ArrayList<>(); + try { + log.debug("Detecting service-metadata location '{}'", metadataLocation); + Enumeration urls = classLoader.getResources(metadataLocation); + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + Properties properties = new Properties(); + + log.debug("Reading service-metadata file '{}'", url); + try (InputStream inputStream = url.openStream()) { + properties.load(inputStream); + } + + ServiceMetadata metadata = createMetadata(properties); + if (metadata != null) { + serviceMetadataList.add(metadata); + } + } + } + catch (IOException ex) { + throw new IllegalArgumentException( + "Unable to load service-metadata from location [%s]".formatted(metadataLocation), ex); + } + return serviceMetadataList; + } + +} diff --git a/today-service-api/src/main/java/infra/cloud/service/InvocationResult.java b/today-service-api/src/main/java/infra/cloud/service/InvocationResult.java new file mode 100644 index 0000000..4f0e8e5 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/service/InvocationResult.java @@ -0,0 +1,61 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import org.jspecify.annotations.Nullable; +import org.reactivestreams.Publisher; + +import infra.core.AttributeAccessor; +import infra.util.concurrent.Future; + +/** + * Remote service invocation result + * + * @author 海子 Yang + * @since 1.0 2025/8/9 12:10 + */ +public interface InvocationResult extends AttributeAccessor { + + @Nullable + Object getBlockingValue(); + + boolean isFailed(); + + @Nullable + Throwable getException(); + + InvocationType getType(); + + default boolean isRequestResponse() { + return getType() == InvocationType.REQUEST_RESPONSE; + } + + default boolean isStreaming() { + return !getType().serverSendsOneMessage(); + } + + /** + * Only for {@link InvocationType#REQUEST_RESPONSE} + * + * @see InvocationType#REQUEST_RESPONSE + * @see #isRequestResponse() + */ + Future future(); + + Publisher publisher(); + +} diff --git a/today-service-api/src/main/java/infra/cloud/service/InvocationType.java b/today-service-api/src/main/java/infra/cloud/service/InvocationType.java new file mode 100644 index 0000000..8fba7b6 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/service/InvocationType.java @@ -0,0 +1,61 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/9 15:45 + */ +public enum InvocationType { + + /** + * One request message without response. + */ + FIRE_AND_FORGET, + + /** + * One request message followed by one response message. + */ + REQUEST_RESPONSE, + + /** + * One request message followed by zero or more response messages. + */ + RESPONSE_STREAMING, + + /** + * Zero or more request and response messages arbitrarily interleaved in time. + */ + DUPLEX_STREAMING; + + /** + * Returns {@code true} for {@code REQUEST_RESPONSE} and {@code RESPONSE_STREAMING}, which do not permit the + * client to stream. + */ + public final boolean clientSendsOneMessage() { + return this == REQUEST_RESPONSE || this == RESPONSE_STREAMING; + } + + /** + * Returns {@code true} for {@code REQUEST_RESPONSE}, which do not permit the + * server to stream. + */ + public final boolean serverSendsOneMessage() { + return this == REQUEST_RESPONSE; + } + +} diff --git a/today-service-api/src/main/java/infra/cloud/service/Metadata.java b/today-service-api/src/main/java/infra/cloud/service/Metadata.java new file mode 100644 index 0000000..f2362ad --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/service/Metadata.java @@ -0,0 +1,31 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +/** + * @author 海子 Yang + * @since 1.0 2025/7/29 11:41 + */ +public class Metadata { + + public static final Metadata EMPTY = new Metadata(); + + public T get(int key) { + return null; + } + +} diff --git a/today-service-api/src/main/java/infra/cloud/service/MethodIdGenerator.java b/today-service-api/src/main/java/infra/cloud/service/MethodIdGenerator.java new file mode 100644 index 0000000..cfcaf6e --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/service/MethodIdGenerator.java @@ -0,0 +1,74 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class MethodIdGenerator { + + public static Map generateMethodIds(Set> serviceInterfaces) { + List allMethods = new ArrayList<>(); + for (Class serviceInterface : serviceInterfaces) { + collectMethods(serviceInterface, allMethods); + } + + allMethods.sort((m1, m2) -> { + int cmp = m1.getDeclaringClass().getName().compareTo(m2.getDeclaringClass().getName()); + if (cmp != 0) + return cmp; + + cmp = m1.getName().compareTo(m2.getName()); + if (cmp != 0) + return cmp; + + Class[] params1 = m1.getParameterTypes(); + Class[] params2 = m2.getParameterTypes(); + cmp = Integer.compare(params1.length, params2.length); + if (cmp != 0) + return cmp; + + for (int i = 0; i < params1.length; i++) { + cmp = params1[i].getName().compareTo(params2[i].getName()); + if (cmp != 0) + return cmp; + } + return 0; + }); + + Map methodIds = new HashMap<>(); + int id = 1; + for (Method method : allMethods) { + methodIds.put(method, id++); + } + return methodIds; + } + + private static void collectMethods(Class interfaceClass, List methods) { + methods.addAll(Arrays.asList(interfaceClass.getDeclaredMethods())); + + for (Class parent : interfaceClass.getInterfaces()) { + collectMethods(parent, methods); + } + } + +} \ No newline at end of file diff --git a/today-service-api/src/main/java/infra/cloud/service/ServiceInterfaceMetadata.java b/today-service-api/src/main/java/infra/cloud/service/ServiceInterfaceMetadata.java new file mode 100644 index 0000000..76d9733 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/service/ServiceInterfaceMetadata.java @@ -0,0 +1,78 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import java.util.Collections; +import java.util.List; + +/** + * Metadata container for a service interface, holding the associated service metadata, + * the service interface class, and an immutable list of its service methods. + * + * @param the type of service method, which must extend {@link ServiceMethod} + * @author 海子 Yang + * @since 1.0 2025/8/9 21:59 + */ +public class ServiceInterfaceMetadata { + + private final ServiceMetadata serviceMetadata; + + private final Class serviceInterface; + + private final List serviceMethods; + + /** + * Constructs a new {@code ServiceInterfaceMetadata} instance. + * + * @param serviceInterface the class representing the service interface + * @param serviceMetadata the metadata associated with the service + * @param serviceMethods the list of service methods belonging to this interface + */ + public ServiceInterfaceMetadata(Class serviceInterface, ServiceMetadata serviceMetadata, List serviceMethods) { + this.serviceInterface = serviceInterface; + this.serviceMetadata = serviceMetadata; + this.serviceMethods = serviceMethods; + } + + /** + * Returns the class representing the service interface. + * + * @return the service interface class + */ + public Class getServiceInterface() { + return serviceInterface; + } + + /** + * Returns the metadata associated with the service. + * + * @return the service metadata + */ + public ServiceMetadata getServiceMetadata() { + return serviceMetadata; + } + + /** + * Returns an unmodifiable list of service methods belonging to this interface. + * + * @return an immutable list of service methods + */ + public List getServiceMethods() { + return Collections.unmodifiableList(serviceMethods); + } + +} diff --git a/today-service-api/src/main/java/infra/cloud/service/ServiceInterfaceMetadataProvider.java b/today-service-api/src/main/java/infra/cloud/service/ServiceInterfaceMetadataProvider.java new file mode 100644 index 0000000..a409d1b --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/service/ServiceInterfaceMetadataProvider.java @@ -0,0 +1,41 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +/** + * Provider interface for retrieving metadata associated with a service interface. + *

    + * This interface is responsible for extracting and providing structural or descriptive + * information about a given service interface, typically used in RPC or cloud service + * discovery scenarios. The metadata is generic over {@link ServiceMethod} to allow + * flexible method-level descriptions. + * + * @param the type of service method metadata, which must extend {@link ServiceMethod} + * @author 海子 Yang + * @since 1.0 2025/8/10 08:22 + */ +public interface ServiceInterfaceMetadataProvider { + + /** + * Retrieves the metadata for the specified service interface. + * + * @param serviceInterface the class object of the service interface to inspect + * @return the metadata associated with the given service interface + */ + ServiceInterfaceMetadata getMetadata(Class serviceInterface); + +} diff --git a/today-service-api/src/main/java/infra/cloud/service/ServiceMetadata.java b/today-service-api/src/main/java/infra/cloud/service/ServiceMetadata.java new file mode 100644 index 0000000..5b99f41 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/service/ServiceMetadata.java @@ -0,0 +1,181 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import org.jspecify.annotations.Nullable; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import infra.core.style.ToStringBuilder; +import infra.lang.Assert; + +/** + * Represents metadata for a service artifact (e.g., a JAR file) that may contain + * one or more service interfaces. + *

    + * This class encapsulates the service's identity information including its unique identifier, + * version, and service-level properties. + *

    + * + * @author 海子 Yang + * @since 1.0 2025/8/9 21:51 + */ +public class ServiceMetadata { + + private final String id; + + private final @Nullable String version; + + private final List interfaces; + + private final Map properties; + + /** + * Constructs a new {@code ServiceMetadata} with the specified id and version. + * + * @param id the unique identifier of the service + * @param version the version of the service, may be {@code null} + */ + public ServiceMetadata(String id, @Nullable String version, List interfaces) { + this(id, version, interfaces, null); + } + + /** + * Constructs a new {@code ServiceMetadata} with the specified id, version, and properties. + * + * @param id the unique identifier of the service + * @param version the version of the service, may be {@code null} + * @param properties the properties + * @param interfaces interfaces class names + */ + public ServiceMetadata(String id, @Nullable String version, List interfaces, @Nullable Map properties) { + Assert.notNull(id, "service id is required"); + Assert.notNull(interfaces, "interfaces is required"); + this.id = id; + this.version = version; + this.interfaces = List.copyOf(interfaces); + this.properties = properties == null ? Map.of() : Map.copyOf(properties); + } + + /** + * Returns the unique identifier of the service. + * + * @return the service ID + */ + public String getId() { + return id; + } + + /** + * Returns the version of the service, which may be {@code null} if not specified. + * + * @return the service version, or {@code null} + */ + public @Nullable String getVersion() { + return version; + } + + /** + * Returns the list of service interface class names contained in this artifact. + *

    + * The returned list is unmodifiable and represents a snapshot of the interfaces + * at construction time. This ensures thread-safety and prevents accidental modification. + *

    + * + * @return the list of interface class names (never null) + */ + public List getInterfaces() { + return interfaces; + } + + /** + * Returns the properties associated with this service artifact. + *

    + * The returned map is unmodifiable and represents a snapshot of the properties + * at construction time. This ensures thread-safety and prevents accidental modification. + *

    + * + * @return the properties map (never null) + */ + public Map getProperties() { + return properties; + } + + /** + * Creates a new {@code ServiceMetadata} instance with the specified properties, + * while retaining the current {@code id} and {@code version}. + *

    + * This method follows an immutable pattern, returning a new instance rather than + * modifying the existing one. If the provided properties map is {@code null}, + * the resulting instance will have an empty properties map. + *

    + * + * @param properties the new properties to associate with the service, may be {@code null} + * @return a new {@code ServiceMetadata} instance with the updated properties + */ + public ServiceMetadata withProperties(@Nullable Map properties) { + return new ServiceMetadata(id, version, interfaces, properties); + } + + /** + * Convenience method to retrieve a specific property value. + * + * @param key the property key + * @return the property value, or {@code null} if not present + */ + public @Nullable String getProperty(String key) { + return this.properties.get(key); + } + + /** + * Checks if a property exists for the given key. + * + * @param key the property key + * @return {@code true} if the property exists, {@code false} otherwise + */ + public boolean hasProperty(String key) { + return this.properties.containsKey(key); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof ServiceMetadata that)) + return false; + return Objects.equals(id, that.id) + && Objects.equals(version, that.version) + && Objects.equals(interfaces, that.interfaces) + && Objects.equals(properties, that.properties); + } + + @Override + public int hashCode() { + return Objects.hash(id, version, interfaces, properties); + } + + @Override + public String toString() { + return ToStringBuilder.forInstance(this) + .append("id", id) + .append("version", version) + .append("interfaces", interfaces) + .append("properties", properties) + .toString(); + } + +} diff --git a/today-service-api/src/main/java/infra/cloud/service/ServiceMetadataProvider.java b/today-service-api/src/main/java/infra/cloud/service/ServiceMetadataProvider.java new file mode 100644 index 0000000..db4b834 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/service/ServiceMetadataProvider.java @@ -0,0 +1,40 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +/** + * Provider interface for retrieving metadata associated with service interfaces. + *

    + * Implementations of this interface are responsible for supplying {@link ServiceMetadata} + * for a given service interface class. This metadata typically includes configuration, + * routing information, or other descriptive attributes required by the service infrastructure. + *

    + * + * @author 海子 Yang + * @since 1.0 2025/8/9 21:50 + */ +public interface ServiceMetadataProvider { + + /** + * Retrieves the metadata for the specified service interface. + * + * @param serviceInterface the class object representing the service interface + * @return the {@link ServiceMetadata} associated with the given interface + */ + ServiceMetadata getMetadata(Class serviceInterface); + +} diff --git a/today-service-api/src/main/java/infra/cloud/service/ServiceMethod.java b/today-service-api/src/main/java/infra/cloud/service/ServiceMethod.java new file mode 100644 index 0000000..d1c00ae --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/service/ServiceMethod.java @@ -0,0 +1,55 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import java.lang.reflect.Method; + +import infra.core.annotation.AnnotatedMethod; + +/** + * Represents a method within a service, encapsulating metadata such as the service interface, + * the underlying {@link Method}, and its parameters. This class provides access to service + * identification, metadata, and reflection-based method details. + * + * @author 海子 Yang + * @since 1.0 2025/8/10 08:20 + */ +public class ServiceMethod extends AnnotatedMethod { + + protected final ServiceMetadata serviceMetadata; + + protected final Class serviceInterface; + + public ServiceMethod(ServiceMetadata serviceMetadata, Class serviceInterface, Method method) { + super(method); + this.serviceInterface = serviceInterface; + this.serviceMetadata = serviceMetadata; + } + + public String getServiceId() { + return serviceMetadata.getId(); + } + + public ServiceMetadata getServiceMetadata() { + return serviceMetadata; + } + + public Class getServiceInterface() { + return serviceInterface; + } + +} diff --git a/today-service-api/src/main/java/infra/cloud/service/config/ResumeProperties.java b/today-service-api/src/main/java/infra/cloud/service/config/ResumeProperties.java new file mode 100644 index 0000000..6b32701 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/service/config/ResumeProperties.java @@ -0,0 +1,131 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service.config; + +import java.time.Duration; + +import infra.remoting.core.Resume; +import infra.remoting.resume.ResumableFramesStoreFactory; +import infra.util.DataSize; + +/** + * Resume config properties + * + * @author 海子 Yang + * @see Resume + * @since 1.0 2025/8/22 21:32 + */ +public class ResumeProperties { + + /** + * Whether connection resume is enabled. Defaults to true. + */ + private boolean enabled = true; + + /** + * The maximum time for a client to keep trying to reconnect. + */ + private Duration sessionDuration = Duration.ofMinutes(2); + + /** + * A timeout value to apply to the resumed session stream obtained from the store after a reconnect. + */ + private Duration streamTimeout = Duration.ofSeconds(10); + + /** + * Memory cache limit bytes + */ + private DataSize memoryCacheLimit = DataSize.ofBytes(100_000); + + private boolean cleanupStoreOnKeepAlive = false; + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isEnabled() { + return enabled; + } + + /** + * A {@link reactor.core.publisher.Flux#timeout(Duration) timeout} value to apply to the resumed + * session stream obtained from the {@link Resume#storeFactory(ResumableFramesStoreFactory) store} after a reconnect. + * The resume stream must not take longer than the specified time to emit each frame. + * + *

    By default, this is set to 10 seconds. + * + * @param streamTimeout the timeout value for resuming a session stream + */ + public void setStreamTimeout(Duration streamTimeout) { + this.streamTimeout = streamTimeout; + } + + public Duration getStreamTimeout() { + return streamTimeout; + } + + /** + * The maximum time for a client to keep trying to reconnect. During this time client and server + * continue to store unsent frames to keep the session warm and ready to resume. + * + *

    By default, this is set to 2 minutes. + * + * @param sessionDuration the max duration for a session + */ + public void setSessionDuration(Duration sessionDuration) { + this.sessionDuration = sessionDuration; + } + + public Duration getSessionDuration() { + return sessionDuration; + } + + public DataSize getMemoryCacheLimit() { + return memoryCacheLimit; + } + + public void setMemoryCacheLimit(DataSize memoryCacheLimit) { + this.memoryCacheLimit = memoryCacheLimit; + } + + /** + * When this property is enabled, hints from {@code KEEPALIVE} frames about how much data has been + * received by the other side, is used to proactively clean frames from the {@link + * Resume#storeFactory(ResumableFramesStoreFactory) store}. + * + *

    By default, this is set to {@code false} in which case information from {@code KEEPALIVE} is + * ignored and old frames from the store are removed only when the store runs out of space. + */ + public void setCleanupStoreOnKeepAlive(boolean cleanupStoreOnKeepAlive) { + this.cleanupStoreOnKeepAlive = cleanupStoreOnKeepAlive; + } + + /** + * When this property is enabled, hints from {@code KEEPALIVE} frames about how much data has been + * received by the other side, is used to proactively clean frames from the {@link + * Resume#storeFactory(ResumableFramesStoreFactory) store}. + * + *

    By default, this is set to {@code false} in which case information from {@code KEEPALIVE} is + * ignored and old frames from the store are removed only when the store runs out of space. + * + * @return the same instance for method chaining + */ + public boolean isCleanupStoreOnKeepAlive() { + return cleanupStoreOnKeepAlive; + } + +} diff --git a/today-service-api/src/main/java/infra/cloud/service/config/package-info.java b/today-service-api/src/main/java/infra/cloud/service/config/package-info.java new file mode 100644 index 0000000..b58da61 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/service/config/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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. + */ + +@NullMarked +package infra.cloud.service.config; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-service-api/src/main/java/infra/cloud/service/package-info.java b/today-service-api/src/main/java/infra/cloud/service/package-info.java new file mode 100644 index 0000000..22ef3b6 --- /dev/null +++ b/today-service-api/src/main/java/infra/cloud/service/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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. + */ + +@NullMarked +package infra.cloud.service; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-service-api/src/main/resources/META-INF/today.strategies b/today-service-api/src/main/resources/META-INF/today.strategies new file mode 100644 index 0000000..64e57c3 --- /dev/null +++ b/today-service-api/src/main/resources/META-INF/today.strategies @@ -0,0 +1,5 @@ +infra.cloud.serialize.ReturnValueDeserializer=\ + infra.cloud.serialize.support.SerializableReturnValueSerialization + +infra.cloud.serialize.ReturnValueSerializer=\ + infra.cloud.serialize.support.SerializableReturnValueSerialization diff --git a/today-service-api/src/test/java/infra/cloud/service/MethodIdGeneratorTests.java b/today-service-api/src/test/java/infra/cloud/service/MethodIdGeneratorTests.java new file mode 100644 index 0000000..bd464ba --- /dev/null +++ b/today-service-api/src/test/java/infra/cloud/service/MethodIdGeneratorTests.java @@ -0,0 +1,39 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Set; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/11 15:20 + */ +class MethodIdGeneratorTests { + + @Test + void test() { + Map methodIntegerMap = MethodIdGenerator.generateMethodIds(Set.of(UserService.class)); + methodIntegerMap.forEach((key, value) -> { + System.out.println(value + " -> " + key); + }); + } + +} \ No newline at end of file diff --git a/today-service-api/src/test/java/infra/cloud/service/ServiceMetadataTests.java b/today-service-api/src/test/java/infra/cloud/service/ServiceMetadataTests.java new file mode 100644 index 0000000..8b59b7d --- /dev/null +++ b/today-service-api/src/test/java/infra/cloud/service/ServiceMetadataTests.java @@ -0,0 +1,301 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author 海子 Yang + * @since 1.0 2026/3/10 16:17 + */ +class ServiceMetadataTests { + + @Test + void testService() { + ServiceMetadata metadata = new ServiceMetadata("test-service", "1.0", List.of()); + + ServiceMetadata withProps = metadata.withProperties(Map.of( + "env", "test" + )); + + assertThat(metadata.getProperties()).isEmpty(); + assertThat(withProps.getProperties()).hasSize(1); + + } + + @Test + void createWithRequiredFields() { + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", List.of("com.example.UserService")); + + assertThat(metadata.getId()).isEqualTo("user-service"); + assertThat(metadata.getVersion()).isEqualTo("1.0.0"); + assertThat(metadata.getInterfaces()).containsExactly("com.example.UserService"); + assertThat(metadata.getProperties()).isEmpty(); + } + + @Test + void createWithNullVersion() { + ServiceMetadata metadata = new ServiceMetadata("user-service", null, List.of("com.example.UserService")); + + assertThat(metadata.getId()).isEqualTo("user-service"); + assertThat(metadata.getVersion()).isNull(); + assertThat(metadata.getInterfaces()).containsExactly("com.example.UserService"); + } + + @Test + void createWithEmptyInterfaces() { + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", List.of()); + + assertThat(metadata.getId()).isEqualTo("user-service"); + assertThat(metadata.getVersion()).isEqualTo("1.0.0"); + assertThat(metadata.getInterfaces()).isEmpty(); + } + + @Test + void createWithProperties() { + Map props = Map.of("env", "test", "region", "us-west-2"); + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", List.of(), props); + + assertThat(metadata.getProperties()).hasSize(2) + .containsEntry("env", "test") + .containsEntry("region", "us-west-2"); + } + + @Test + void createWithNullProperties() { + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", List.of(), null); + + assertThat(metadata.getProperties()).isEmpty(); + } + + @Test + void createWithMultipleInterfaces() { + List interfaces = List.of("com.example.UserService", "com.example.UserAdminService"); + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", interfaces); + + assertThat(metadata.getInterfaces()).hasSize(2) + .containsExactly("com.example.UserService", "com.example.UserAdminService"); + } + + @Test + void withPropertiesReturnsNewInstance() { + ServiceMetadata original = new ServiceMetadata("user-service", "1.0.0", List.of()); + Map newProps = Map.of("env", "prod"); + + ServiceMetadata modified = original.withProperties(newProps); + + assertThat(modified).isNotSameAs(original); + assertThat(original.getProperties()).isEmpty(); + assertThat(modified.getProperties()).containsEntry("env", "prod"); + } + + @Test + void withPropertiesWithNullMap() { + ServiceMetadata original = new ServiceMetadata("user-service", "1.0.0", List.of(), Map.of("env", "test")); + ServiceMetadata modified = original.withProperties(null); + + assertThat(modified.getProperties()).isEmpty(); + } + + @Test + void withPropertiesPreservesOtherFields() { + ServiceMetadata original = new ServiceMetadata("user-service", "2.0.0", List.of("com.example.UserService")); + ServiceMetadata modified = original.withProperties(Map.of("new-key", "new-value")); + + assertThat(modified.getId()).isEqualTo("user-service"); + assertThat(modified.getVersion()).isEqualTo("2.0.0"); + assertThat(modified.getInterfaces()).containsExactly("com.example.UserService"); + assertThat(modified.getProperties()).containsEntry("new-key", "new-value"); + } + + @Test + void getPropertyReturnsValue() { + Map props = Map.of("env", "production", "timeout", "30s"); + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", List.of(), props); + + assertThat(metadata.getProperty("env")).isEqualTo("production"); + assertThat(metadata.getProperty("timeout")).isEqualTo("30s"); + } + + @Test + void getPropertyReturnsNullForMissingKey() { + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", List.of()); + + assertThat(metadata.getProperty("nonexistent")).isNull(); + } + + @Test + void hasPropertyReturnsTrueWhenExists() { + Map props = Map.of("env", "test"); + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", List.of(), props); + + assertThat(metadata.hasProperty("env")).isTrue(); + } + + @Test + void hasPropertyReturnsFalseWhenNotExists() { + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", List.of()); + + assertThat(metadata.hasProperty("env")).isFalse(); + } + + @Test + void hasPropertyReturnsFalseForEmptyMetadata() { + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", List.of()); + + assertThat(metadata.hasProperty("any-key")).isFalse(); + } + + @Test + void equalsReturnsTrueForSameObject() { + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", List.of()); + + assertThat(metadata).isEqualTo(metadata); + } + + @Test + void equalsReturnsFalseForNull() { + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", List.of()); + + assertThat(metadata).isNotEqualTo(null); + } + + @Test + void equalsReturnsFalseForDifferentType() { + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", List.of()); + + assertThat(metadata).isNotEqualTo("not a ServiceMetadata"); + } + + @Test + void equalsReturnsTrueForSameValues() { + ServiceMetadata metadata1 = new ServiceMetadata("user-service", "1.0.0", List.of("com.example.UserService")); + ServiceMetadata metadata2 = new ServiceMetadata("user-service", "1.0.0", List.of("com.example.UserService")); + + assertThat(metadata1).isEqualTo(metadata2); + } + + @Test + void equalsReturnsFalseForDifferentId() { + ServiceMetadata metadata1 = new ServiceMetadata("user-service", "1.0.0", List.of()); + ServiceMetadata metadata2 = new ServiceMetadata("order-service", "1.0.0", List.of()); + + assertThat(metadata1).isNotEqualTo(metadata2); + } + + @Test + void equalsReturnsFalseForDifferentVersion() { + ServiceMetadata metadata1 = new ServiceMetadata("user-service", "1.0.0", List.of()); + ServiceMetadata metadata2 = new ServiceMetadata("user-service", "2.0.0", List.of()); + + assertThat(metadata1).isNotEqualTo(metadata2); + } + + @Test + void equalsReturnsFalseForDifferentProperties() { + ServiceMetadata metadata1 = new ServiceMetadata("user-service", "1.0.0", List.of(), Map.of("env", "test")); + ServiceMetadata metadata2 = new ServiceMetadata("user-service", "1.0.0", List.of(), Map.of("env", "prod")); + + assertThat(metadata1).isNotEqualTo(metadata2); + } + + @Test + void equalsReturnsTrueForNullVersionBoth() { + ServiceMetadata metadata1 = new ServiceMetadata("user-service", null, List.of()); + ServiceMetadata metadata2 = new ServiceMetadata("user-service", null, List.of()); + + assertThat(metadata1).isEqualTo(metadata2); + } + + @Test + void equalsReturnsTrueForEmptyPropertiesBoth() { + ServiceMetadata metadata1 = new ServiceMetadata("user-service", "1.0.0", List.of()); + ServiceMetadata metadata2 = new ServiceMetadata("user-service", "1.0.0", List.of()); + + assertThat(metadata1).isEqualTo(metadata2); + } + + @Test + void hashCodeIsConsistent() { + ServiceMetadata metadata1 = new ServiceMetadata("user-service", "1.0.0", List.of()); + ServiceMetadata metadata2 = new ServiceMetadata("user-service", "1.0.0", List.of()); + + assertThat(metadata1.hashCode()).isEqualTo(metadata2.hashCode()); + } + + @Test + void hashCodeDiffersForDifferentId() { + ServiceMetadata metadata1 = new ServiceMetadata("user-service", "1.0.0", List.of()); + ServiceMetadata metadata2 = new ServiceMetadata("order-service", "1.0.0", List.of()); + + assertThat(metadata1.hashCode()).isNotEqualTo(metadata2.hashCode()); + } + + @Test + void hashCodeDiffersForDifferentProperties() { + ServiceMetadata metadata1 = new ServiceMetadata("user-service", "1.0.0", List.of(), Map.of("env", "test")); + ServiceMetadata metadata2 = new ServiceMetadata("user-service", "1.0.0", List.of(), Map.of("env", "prod")); + + assertThat(metadata1.hashCode()).isNotEqualTo(metadata2.hashCode()); + } + + @Test + void toStringContainsId() { + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", List.of()); + + assertThat(metadata.toString()).contains("user-service"); + } + + @Test + void toStringContainsVersion() { + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", List.of()); + + assertThat(metadata.toString()).contains("1.0.0"); + } + + @Test + void unmodifiableInterfaces() { + List interfaces = new ArrayList<>(List.of("com.example.UserService")); + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", interfaces); + + interfaces.add("com.example.AnotherService"); + + assertThat(metadata.getInterfaces()).hasSize(1) + .containsExactly("com.example.UserService"); + } + + @Test + void propertiesAreDefensivelyCopied() { + Map mutableProps = new HashMap<>(); + mutableProps.put("env", "test"); + ServiceMetadata metadata = new ServiceMetadata("user-service", "1.0.0", List.of(), mutableProps); + + mutableProps.put("new-key", "new-value"); + mutableProps.remove("env"); + + assertThat(metadata.getProperties()).hasSize(1) + .containsEntry("env", "test"); + } + +} \ No newline at end of file diff --git a/today-service-api/src/test/java/infra/cloud/service/UserService.java b/today-service-api/src/test/java/infra/cloud/service/UserService.java new file mode 100644 index 0000000..1b40c48 --- /dev/null +++ b/today-service-api/src/test/java/infra/cloud/service/UserService.java @@ -0,0 +1,26 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +/** + * @author 海子 Yang + * @since 1.0 2026/3/15 22:36 + */ +public interface UserService { + + Object getById(long id); +} diff --git a/today-service-client/build.gradle b/today-service-client/build.gradle index b51d195..36e049c 100644 --- a/today-service-client/build.gradle +++ b/today-service-client/build.gradle @@ -1,42 +1,16 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - description = "TODAY Service Client" dependencies { api project(":today-remoting") - api project(":today-cloud-core") - api project(":today-service-registry") - - optional 'io.netty:netty-transport' - optional 'io.netty:netty-codec' - optional 'io.netty:netty-handler' - - implementation 'cn.taketoday:today-web' - implementation 'cn.taketoday:today-framework' - implementation 'io.projectreactor:reactor-core' // :3.6.1 - - implementation 'org.apache.commons:commons-pool2:2.12.0' + api project(":today-service-api") + api project(":today-service-discovery") - implementation 'io.protostuff:protostuff-core:1.7.4' - implementation 'io.protostuff:protostuff-runtime:1.7.4' + optional "cn.taketoday:infra-context" + optional project(":today-remoting-transport-tcp") - optional "com.google.protobuf:protobuf-java" + testImplementation "cn.taketoday:infra-app-test" +// testImplementation project(":today-cloud-samples:demo-tests:demo-user-api") } \ No newline at end of file diff --git a/today-service-client/src/main/java/infra/cloud/JdkServiceProxy.java b/today-service-client/src/main/java/infra/cloud/JdkServiceProxy.java deleted file mode 100644 index 21341c5..0000000 --- a/today-service-client/src/main/java/infra/cloud/JdkServiceProxy.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -import infra.cloud.registry.ServiceNotFoundException; - -/** - * @author TODAY 2021/7/4 22:58 - */ -public class JdkServiceProxy implements ServiceProxy { - - @Override - @SuppressWarnings("unchecked") - public T getProxy(Class serviceInterface, DiscoveryClient discoveryClient, ServiceMethodInvoker rpcInvoker) { - return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class[] { serviceInterface }, - new ServiceInvocationHandler(serviceInterface, discoveryClient, rpcInvoker)); - } - - static final class ServiceInvocationHandler implements InvocationHandler { - private final CopyOnWriteArrayList serviceInstances = new CopyOnWriteArrayList<>(); - - private final Class serviceInterface; - - private final DiscoveryClient discoveryClient; - - private final ServiceMethodInvoker rpcInvoker; - - public ServiceInvocationHandler(Class serviceInterface, DiscoveryClient discoveryClient, ServiceMethodInvoker rpcInvoker) { - this.serviceInterface = serviceInterface; - this.discoveryClient = discoveryClient; - this.rpcInvoker = rpcInvoker; - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - CopyOnWriteArrayList serviceInstances = this.serviceInstances; - if (serviceInstances.isEmpty()) { - List instances = discoveryClient.getInstances(serviceInterface.getName()); - serviceInstances.addAll(instances); - if (serviceInstances.isEmpty()) { - throw new ServiceNotFoundException(serviceInterface); - } - } - return rpcInvoker.invoke(serviceInstances, method, args); - } - } -} diff --git a/today-service-client/src/main/java/infra/cloud/RemoteExceptionHandler.java b/today-service-client/src/main/java/infra/cloud/RemoteExceptionHandler.java deleted file mode 100644 index 298c37e..0000000 --- a/today-service-client/src/main/java/infra/cloud/RemoteExceptionHandler.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud; - -import infra.util.concurrent.Future; - -/** - * @author TODAY 2021/7/9 21:54 - */ -public interface RemoteExceptionHandler { - - Object handle(Future throwable) throws Throwable; -} diff --git a/today-service-client/src/main/java/infra/cloud/RpcMethod.java b/today-service-client/src/main/java/infra/cloud/RpcMethod.java deleted file mode 100644 index 0fd579d..0000000 --- a/today-service-client/src/main/java/infra/cloud/RpcMethod.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud; - -import java.lang.reflect.Method; - -import infra.core.MethodParameter; -import infra.core.style.ToStringBuilder; -import infra.lang.Nullable; - -/** - * @author 海子 Yang - * @since 1.0 2024/12/20 16:39 - */ -public class RpcMethod { - - private final Method method; - - private final MethodParameter[] parameters; - - @Nullable - private MethodParameter returnTypeParameter; - - public RpcMethod(Method method) { - this.method = method; - this.parameters = initMethodParameters(method); - } - - private MethodParameter[] initMethodParameters(Method method) { - int count = method.getParameterCount(); - MethodParameter[] result = new MethodParameter[count]; - for (int i = 0; i < count; i++) { - result[i] = new MethodParameter(method, i); - } - return result; - } - - public Method getMethod() { - return method; - } - - public MethodParameter[] getParameters() { - return parameters; - } - - public MethodParameter getReturnType() { - MethodParameter returnType = returnTypeParameter; - if (returnType == null) { - returnType = MethodParameter.forExecutable(method, -1); - this.returnTypeParameter = returnType; - } - return returnType; - } - - @Override - public String toString() { - return ToStringBuilder.forInstance(this) - .append("parameters", parameters) - .append("method", method) - .toString(); - } - -} diff --git a/today-service-client/src/main/java/infra/cloud/RpcResponse.java b/today-service-client/src/main/java/infra/cloud/RpcResponse.java deleted file mode 100644 index 9f0a357..0000000 --- a/today-service-client/src/main/java/infra/cloud/RpcResponse.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud; - -import java.io.Serial; -import java.io.Serializable; - -import infra.lang.Nullable; - -/** - * @author TODAY 2021/7/4 22:31 - */ -public class RpcResponse implements Serializable { - - public static final RpcResponse empty = new RpcResponse(); - - @Serial - private static final long serialVersionUID = 1L; - - /** service result */ - private Object result; - - @Nullable - private Throwable exception; - - @Nullable - private RpcMethod rpcMethod; - - public RpcResponse() { } - - public RpcResponse(@Nullable RpcMethod rpcMethod, Object result) { - this.rpcMethod = rpcMethod; - this.result = result; - } - - public void setRpcMethod(@Nullable RpcMethod rpcMethod) { - this.rpcMethod = rpcMethod; - } - - @Nullable - public RpcMethod getRpcMethod() { - return rpcMethod; - } - - public void setResult(Object result) { - this.result = result; - } - - public Object getResult() { - return result; - } - - @Nullable - public Throwable getException() { - return exception; - } - - public void setException(@Nullable Throwable exception) { - this.exception = exception; - } - - // static - - public static RpcResponse ofThrowable(@Nullable Throwable throwable) { - final RpcResponse rpcResponse = new RpcResponse(); - rpcResponse.setException(throwable); - return rpcResponse; - } - - public static RpcResponse ofThrowable(RpcMethod rpcMethod, @Nullable Throwable throwable) { - final RpcResponse rpcResponse = new RpcResponse(); - rpcResponse.setException(throwable); - rpcResponse.setRpcMethod(rpcMethod); - return rpcResponse; - } - -} diff --git a/today-service-client/src/main/java/infra/cloud/ServiceMethod.java b/today-service-client/src/main/java/infra/cloud/ServiceMethod.java deleted file mode 100644 index 0cc61a1..0000000 --- a/today-service-client/src/main/java/infra/cloud/ServiceMethod.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud; - -import java.lang.reflect.Method; - -/** - * @author Harry Yang - * @since 1.0 2024/1/7 21:03 - */ -public class ServiceMethod { - - public final Method method; - - public ServiceMethod(Method method) { - this.method = method; - } - -} diff --git a/today-service-client/src/main/java/infra/cloud/ServiceMethodInvoker.java b/today-service-client/src/main/java/infra/cloud/ServiceMethodInvoker.java deleted file mode 100644 index f2abe92..0000000 --- a/today-service-client/src/main/java/infra/cloud/ServiceMethodInvoker.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; - -import infra.cloud.registry.InstanceSelector; -import infra.cloud.registry.RandomInstanceSelector; -import infra.lang.Assert; -import infra.util.concurrent.Future; -import reactor.core.publisher.Mono; - -/** - * Service Method Invoker - * - * @author TODAY 2021/7/4 01:58 - */ -public abstract class ServiceMethodInvoker { - - protected InstanceSelector instanceSelector = new RandomInstanceSelector(); - - protected RemoteExceptionHandler exceptionHandler = new SimpleRemoteExceptionHandler(); - - private final ArrayList resolvers = new ArrayList<>(); - - public ServiceMethodInvoker() { - resolvers.add(new ListenableFutureReturnValueResolver()); - resolvers.add(new MonoFutureReturnValueResolver()); - resolvers.add(new BlockFutureReturnValueResolver()); - } - - public Object invoke(List instances, Method method, Object[] args) throws Throwable { - ServiceInstance selected = instanceSelector.select(instances); - try { - Future response = invokeInternal(selected, method, args); - return resolveReturnValue(response, method); - } - catch (Throwable e) { - return handleException(e, instances, selected, method, args); - } - } - - private Object resolveReturnValue(Future response, Method method) throws Throwable { - for (ReturnValueResolver resolver : resolvers) { - if (resolver.supports(method)) { - return resolver.resolve(response, method); - } - } - return null; - } - - protected RpcResponse handleException(Throwable e, List instances, - ServiceInstance selected, Method method, Object[] args) throws Throwable { - - throw e; - } - - protected abstract Future invokeInternal(ServiceInstance selected, Method method, Object[] args) - throws Throwable; - - public void setExceptionHandler(RemoteExceptionHandler exceptionHandler) { - Assert.notNull(exceptionHandler, "exceptionHandler is required"); - this.exceptionHandler = exceptionHandler; - } - - public RemoteExceptionHandler getExceptionHandler() { - return exceptionHandler; - } - - public void setServiceSelector(InstanceSelector instanceSelector) { - Assert.notNull(instanceSelector, "serviceSelector is required"); - this.instanceSelector = instanceSelector; - } - - public InstanceSelector getServiceSelector() { - return instanceSelector; - } - - interface ReturnValueResolver { - - boolean supports(Method method); - - Object resolve(Future response, Method method) throws Throwable; - - } - - static class ListenableFutureReturnValueResolver implements ReturnValueResolver { - - @Override - public boolean supports(Method method) { - return method.getReturnType() == Future.class; - } - - @Override - public Object resolve(Future response, Method method) { - return response; - } - - } - - static class BlockFutureReturnValueResolver implements ReturnValueResolver { - - @Override - public boolean supports(Method method) { - return true; - } - - @Override - public Object resolve(Future response, Method method) { - response.syncUninterruptibly(); - return response.getNow(); - } - - } - - static class MonoFutureReturnValueResolver implements ReturnValueResolver { - - @Override - public boolean supports(Method method) { - return method.getReturnType() == Mono.class; - } - - @Override - public Object resolve(Future response, Method method) { - return Mono.fromFuture(response.completable()); - } - - } - -} diff --git a/today-service-client/src/main/java/infra/cloud/ServiceProvider.java b/today-service-client/src/main/java/infra/cloud/ServiceProvider.java deleted file mode 100644 index 7f44447..0000000 --- a/today-service-client/src/main/java/infra/cloud/ServiceProvider.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud; - -import infra.cloud.registry.ServiceNotFoundException; - -/** - * @author Harry Yang - * @since 1.0 2023/8/14 21:53 - */ -public interface ServiceProvider { - - /** - * Lookup a service - * - * @param serviceInterface service interface type - * @param Service type - * @return service - * @throws ServiceNotFoundException service not found - */ - T getService(Class serviceInterface); - -} diff --git a/today-service-client/src/main/java/infra/cloud/ServiceProxy.java b/today-service-client/src/main/java/infra/cloud/ServiceProxy.java deleted file mode 100644 index ed237eb..0000000 --- a/today-service-client/src/main/java/infra/cloud/ServiceProxy.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud; - -/** - * @author TODAY 2021/7/4 22:58 - */ -public interface ServiceProxy { - - T getProxy(Class serviceInterface, DiscoveryClient discoveryClient, ServiceMethodInvoker methodInvoker); -} diff --git a/today-service-client/src/main/java/infra/cloud/ServiceTimeoutException.java b/today-service-client/src/main/java/infra/cloud/ServiceTimeoutException.java index 48012da..17aed48 100644 --- a/today-service-client/src/main/java/infra/cloud/ServiceTimeoutException.java +++ b/today-service-client/src/main/java/infra/cloud/ServiceTimeoutException.java @@ -1,23 +1,22 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.cloud; -import infra.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * @author Harry Yang diff --git a/today-service-client/src/main/java/infra/cloud/SimpleRemoteExceptionHandler.java b/today-service-client/src/main/java/infra/cloud/SimpleRemoteExceptionHandler.java deleted file mode 100644 index 2f5caf0..0000000 --- a/today-service-client/src/main/java/infra/cloud/SimpleRemoteExceptionHandler.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud; - -import java.util.concurrent.ExecutionException; - -import infra.util.concurrent.Future; - -/** - * @author TODAY 2021/7/9 21:55 - */ -public class SimpleRemoteExceptionHandler implements RemoteExceptionHandler { - - @Override - public Object handle(Future response) throws Throwable { - throw exceptionNow(response); - } - - Throwable exceptionNow(Future response) { - if (!response.isDone()) - throw new IllegalStateException("Task has not completed"); - if (response.isCancelled()) - throw new IllegalStateException("Task was cancelled"); - boolean interrupted = false; - try { - while (true) { - try { - response.get(); - throw new IllegalStateException("Task completed with a result"); - } - catch (InterruptedException e) { - interrupted = true; - } - catch (ExecutionException e) { - return e.getCause(); - } - } - } - finally { - if (interrupted) - Thread.currentThread().interrupt(); - } - } - -} diff --git a/today-service-client/src/main/java/infra/cloud/client/ServiceReference.java b/today-service-client/src/main/java/infra/cloud/client/ServiceReference.java deleted file mode 100644 index 332b60f..0000000 --- a/today-service-client/src/main/java/infra/cloud/client/ServiceReference.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.client; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author Harry Yang - * @since 1.0 2023/11/20 23:07 - */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD }) -public @interface ServiceReference { - -} diff --git a/today-service-client/src/main/java/infra/cloud/protocol/ServiceDependencyResolvingStrategy.java b/today-service-client/src/main/java/infra/cloud/client/annotation/ServiceDependencyResolvingStrategy.java similarity index 59% rename from today-service-client/src/main/java/infra/cloud/protocol/ServiceDependencyResolvingStrategy.java rename to today-service-client/src/main/java/infra/cloud/client/annotation/ServiceDependencyResolvingStrategy.java index 7f18422..bd2ce40 100644 --- a/today-service-client/src/main/java/infra/cloud/protocol/ServiceDependencyResolvingStrategy.java +++ b/today-service-client/src/main/java/infra/cloud/client/annotation/ServiceDependencyResolvingStrategy.java @@ -1,28 +1,27 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.cloud.protocol; +package infra.cloud.client.annotation; + +import org.jspecify.annotations.Nullable; import infra.beans.factory.config.DependencyDescriptor; import infra.beans.factory.support.DependencyResolvingStrategy; -import infra.cloud.ServiceProvider; -import infra.cloud.client.ServiceReference; +import infra.cloud.service.ServiceProvider; import infra.context.ApplicationContext; -import infra.lang.Nullable; import infra.stereotype.Service; /** diff --git a/today-service-client/src/main/java/infra/cloud/client/annotation/ServiceReference.java b/today-service-client/src/main/java/infra/cloud/client/annotation/ServiceReference.java new file mode 100644 index 0000000..232d22b --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/client/annotation/ServiceReference.java @@ -0,0 +1,32 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Harry Yang + * @since 1.0 2023/11/20 23:07 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD }) +public @interface ServiceReference { + +} diff --git a/today-service-client/src/main/java/infra/cloud/client/annotation/config/ServiceClientAutoConfiguration.java b/today-service-client/src/main/java/infra/cloud/client/annotation/config/ServiceClientAutoConfiguration.java new file mode 100644 index 0000000..cfc9580 --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/client/annotation/config/ServiceClientAutoConfiguration.java @@ -0,0 +1,114 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client.annotation.config; + +import java.util.List; + +import infra.beans.factory.ObjectProvider; +import infra.cloud.client.DiscoveryClient; +import infra.cloud.client.annotation.ConditionalOnDiscoveryEnabled; +import infra.cloud.client.simple.SimpleDiscoveryProperties; +import infra.cloud.serialize.ArgumentSerialization; +import infra.cloud.serialize.ReturnValueDeserializer; +import infra.cloud.serialize.ThrowableSerialization; +import infra.cloud.service.ClientInterceptor; +import infra.cloud.service.DefaultRemotingOperationsProvider; +import infra.cloud.service.DefaultServiceInterfaceMetadataProvider; +import infra.cloud.service.DefaultServiceMetadataProvider; +import infra.cloud.service.DefaultServiceProxyFactory; +import infra.cloud.service.RemotingOperationsProvider; +import infra.cloud.service.ReturnValueResolver; +import infra.cloud.service.ServiceInterfaceMetadataProvider; +import infra.cloud.service.ServiceInterfaceMethod; +import infra.cloud.service.ServiceInvoker; +import infra.cloud.service.ServiceMetadataProvider; +import infra.cloud.service.ServiceMethodInvoker; +import infra.cloud.service.serialize.RequestSerializer; +import infra.cloud.service.serialize.ResponseDeserializer; +import infra.context.annotation.config.DisableDIAutoConfiguration; +import infra.context.condition.ConditionalOnMissingBean; +import infra.context.properties.EnableConfigurationProperties; +import infra.core.io.ResourceLoader; +import infra.lang.TodayStrategies; +import infra.stereotype.Component; +import io.netty.buffer.ByteBufAllocator; + +/** + * Auto-configuration for remote service client. + * + * @author 海子 Yang + * @since 1.0 2025/8/9 22:14 + */ +@SuppressWarnings("rawtypes") +@DisableDIAutoConfiguration +@ConditionalOnDiscoveryEnabled +@EnableConfigurationProperties(SimpleDiscoveryProperties.class) +public final class ServiceClientAutoConfiguration { + + @Component + public static DefaultServiceProxyFactory serviceProxyFactory( + ServiceInterfaceMetadataProvider metadataProvider, ServiceInvoker serviceInvoker) { + return new DefaultServiceProxyFactory(metadataProvider, serviceInvoker); + } + + @Component + public static ServiceInvoker serviceInvoker(List interceptors, RemotingOperationsProvider remotingOperationsProvider, + RequestSerializer requestSerializer, ResponseDeserializer responseDeserializer) { + return new ServiceMethodInvoker(interceptors, remotingOperationsProvider, ByteBufAllocator.DEFAULT, + requestSerializer, responseDeserializer); + } + + @Component + public static ThrowableSerialization throwableSerialization() { + return new ThrowableSerialization(); + } + + @Component + public static ResponseDeserializer responseDeserializer(List serializations, + ThrowableSerialization throwableSerialization, ResourceLoader resourceLoader) { + // order after ReturnValueDeserializer beans + serializations.addAll(TodayStrategies.find(ReturnValueDeserializer.class, resourceLoader.getClassLoader())); + return new ResponseDeserializer(serializations, throwableSerialization); + } + + @Component + public static RequestSerializer requestSerializer(List argumentSerializations, ResourceLoader resourceLoader) { + List serializations = TodayStrategies.find(ArgumentSerialization.class, resourceLoader.getClassLoader()); + argumentSerializations.addAll(serializations); // order after ArgumentSerialization beans + return new RequestSerializer(argumentSerializations); + } + + @Component + @ConditionalOnMissingBean + public static RemotingOperationsProvider remotingOperationsProvider(DiscoveryClient discoveryClient) { + return new DefaultRemotingOperationsProvider(discoveryClient); + } + + @Component + @ConditionalOnMissingBean + public static ServiceMetadataProvider serviceMetadataProvider() { + return new DefaultServiceMetadataProvider(); + } + + @Component + @ConditionalOnMissingBean + public static ServiceInterfaceMetadataProvider serviceInterfaceMetadataProvider( + ServiceMetadataProvider serviceMetadataProvider, ObjectProvider resolvers) { + return new DefaultServiceInterfaceMetadataProvider(serviceMetadataProvider, resolvers.orderedList()); + } + +} diff --git a/today-service-client/src/main/java/infra/cloud/http/EnableHttpServiceClient.java b/today-service-client/src/main/java/infra/cloud/http/EnableHttpServiceClient.java deleted file mode 100644 index 3b10c2f..0000000 --- a/today-service-client/src/main/java/infra/cloud/http/EnableHttpServiceClient.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.http; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import infra.context.annotation.Import; - -/** - * @author Harry Yang - * @since 1.0 2023/9/5 10:03 - */ -@Import(HttpServiceClientConfig.class) -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE, ElementType.METHOD }) -public @interface EnableHttpServiceClient { - -} diff --git a/today-service-client/src/main/java/infra/cloud/http/HttpServiceClientConfig.java b/today-service-client/src/main/java/infra/cloud/http/HttpServiceClientConfig.java deleted file mode 100644 index 3109457..0000000 --- a/today-service-client/src/main/java/infra/cloud/http/HttpServiceClientConfig.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.http; - -import java.util.List; - -import infra.beans.factory.ObjectProvider; -import infra.cloud.core.serialize.JdkSerialization; -import infra.cloud.core.serialize.Serialization; -import infra.cloud.registry.RegistryProperties; -import infra.cloud.serialize.ReturnValueSerialization; -import infra.cloud.serialize.RpcArgumentSerialization; -import infra.cloud.serialize.RpcRequestSerialization; -import infra.cloud.serialize.RpcResponseSerialization; -import infra.cloud.serialize.ThrowableSerialization; -import infra.context.annotation.Configuration; -import infra.context.annotation.MissingBean; -import infra.context.properties.EnableConfigurationProperties; -import infra.lang.TodayStrategies; -import infra.stereotype.Component; - -/** - * @author Harry Yang - * @since 1.0 2023/9/5 09:56 - */ -@Configuration(proxyBeanMethods = false) -@EnableConfigurationProperties(RegistryProperties.class) -public class HttpServiceClientConfig { - - @Component - @SuppressWarnings({ "rawtypes" }) - static RpcRequestSerialization rpcRequestSerialization(ObjectProvider serializations) { - List list = TodayStrategies.find(RpcArgumentSerialization.class); - serializations.addOrderedTo(list); - return new RpcRequestSerialization(list); - } - - @Component - @SuppressWarnings({ "rawtypes" }) - static RpcResponseSerialization responseSerialization(ObjectProvider serializations) { - List list = TodayStrategies.find(ReturnValueSerialization.class); - serializations.addOrderedTo(list); - return new RpcResponseSerialization(list, new ThrowableSerialization()); - } - - @MissingBean - static Serialization requestSerialization() { - return new JdkSerialization<>(); - } - -} diff --git a/today-service-client/src/main/java/infra/cloud/protocol/http/HttpOperations.java b/today-service-client/src/main/java/infra/cloud/protocol/http/HttpOperations.java deleted file mode 100644 index 56dfc95..0000000 --- a/today-service-client/src/main/java/infra/cloud/protocol/http/HttpOperations.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.protocol.http; - -import java.util.List; -import java.util.Map; - -import infra.cloud.DefaultServiceInstance; -import infra.cloud.RpcRequest; -import infra.cloud.RpcResponse; -import infra.cloud.ServiceInstance; -import infra.cloud.core.serialize.Serialization; -import infra.cloud.registry.ServiceNotFoundException; -import infra.core.ParameterizedTypeReference; -import infra.core.style.ToStringBuilder; -import infra.http.HttpEntity; -import infra.http.HttpMethod; -import infra.web.client.HttpClientErrorException; -import infra.web.client.RestClient; -import infra.web.client.RestClientException; -import infra.web.client.RestTemplate; - -/** - * @author Harry Yang - * @since 1.0 2023/8/14 17:46 - */ -final class HttpOperations { - private static final ParameterizedTypeReference> reference = new ParameterizedTypeReference<>() { }; - - private final Serialization serialization; - - private final RestTemplate restOperations = new RestTemplate(); - - private final RestClient restClient = RestClient.create(restOperations); - - private final String registryURL; - - public HttpOperations(String registryURL, Serialization serialization) { - this.registryURL = registryURL; - this.serialization = serialization; - } - - public RpcResponse execute(ServiceInstance selected, RpcRequest rpcRequest) { - return restOperations.execute(selected.getHttpURI(), HttpMethod.POST, - request -> serialization.serialize(rpcRequest, request.getBody()), - response -> { - try { - return serialization.deserialize(response.getBody()); - } - catch (ClassNotFoundException e) { - throw new ServiceNotFoundException(e); - } - }); - } - - @SuppressWarnings("rawtypes") - public List getInstances(String name) { - try { - return restOperations.exchange(buildGetServiceDefinitionURL(name), HttpMethod.GET, HttpEntity.EMPTY, reference).getBody(); - } - catch (HttpClientErrorException.NotFound e) { - throw new ServiceNotFoundException(name, e); - } - } - - private String buildGetServiceDefinitionURL(String serviceInterface) { - return registryURL + '/' + serviceInterface; - } - - public void register(Object body) throws RestClientException { - restClient.post() - .uri(registryURL) - .body(body) - .execute(); - } - - public void delete(Object body) { - restClient.delete() - .uri(registryURL) - .body(body) - .execute(); - } - - public Map getServices() { - return restClient.get() - .uri(registryURL) - .retrieve().body(new ParameterizedTypeReference>() { }); - } - - @Override - public String toString() { - return ToStringBuilder.forInstance(this) - .append("registryURL", registryURL) - .toString(); - } -} diff --git a/today-service-client/src/main/java/infra/cloud/protocol/http/HttpServiceMethodInvoker.java b/today-service-client/src/main/java/infra/cloud/protocol/http/HttpServiceMethodInvoker.java deleted file mode 100644 index 9202141..0000000 --- a/today-service-client/src/main/java/infra/cloud/protocol/http/HttpServiceMethodInvoker.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.protocol.http; - -import java.lang.reflect.Method; - -import infra.cloud.RpcRequest; -import infra.cloud.RpcResponse; -import infra.cloud.ServiceInstance; -import infra.cloud.ServiceMethodInvoker; -import infra.util.concurrent.Future; - -/** - * @author Harry Yang - * @since 2021/7/4 23:10 - */ -final class HttpServiceMethodInvoker extends ServiceMethodInvoker { - - private final HttpOperations httpOperations; - - HttpServiceMethodInvoker(HttpOperations httpOperations) { - this.httpOperations = httpOperations; - } - - @Override - protected Future invokeInternal(ServiceInstance selected, Method method, Object[] args) { - RpcRequest rpcRequest = new RpcRequest(); - rpcRequest.setMethodName(method.getName()); - rpcRequest.setServiceName(selected.getServiceId()); - rpcRequest.setParameterTypes(method.getParameterTypes()); - rpcRequest.setArguments(args); - RpcResponse execute = httpOperations.execute(selected, rpcRequest); - Throwable exception = execute.getException(); - if (exception != null) { - return Future.failed(exception); - } - return Future.ok(execute.getResult()); - } - -} diff --git a/today-service-client/src/main/java/infra/cloud/protocol/http/HttpServiceRegistry.java b/today-service-client/src/main/java/infra/cloud/protocol/http/HttpServiceRegistry.java deleted file mode 100644 index c1b7ab5..0000000 --- a/today-service-client/src/main/java/infra/cloud/protocol/http/HttpServiceRegistry.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.protocol.http; - -import java.util.ArrayList; -import java.util.List; - -import infra.cloud.DiscoveryClient; -import infra.cloud.JdkServiceProxy; -import infra.cloud.RpcResponse; -import infra.cloud.ServiceInstance; -import infra.cloud.ServiceMethodInvoker; -import infra.cloud.ServiceProvider; -import infra.cloud.ServiceProxy; -import infra.cloud.core.serialize.JdkSerialization; -import infra.cloud.core.serialize.Serialization; -import infra.cloud.registry.HttpRegistration; -import infra.cloud.registry.ServiceRegisterFailedException; -import infra.cloud.registry.ServiceRegistry; -import infra.core.style.ToStringBuilder; -import infra.web.client.RestClientException; - -/** - * @author TODAY 2021/7/3 23:48 - */ -public class HttpServiceRegistry implements ServiceRegistry, ServiceProvider, DiscoveryClient { - - private ServiceProxy serviceProxy; - - private final HttpOperations httpOperations; - - private final ServiceMethodInvoker methodInvoker; - - public HttpServiceRegistry(String registryURL) { - this.httpOperations = new HttpOperations(registryURL, new JdkSerialization<>()); - this.methodInvoker = new HttpServiceMethodInvoker(httpOperations); - } - - HttpServiceRegistry(HttpOperations httpOperations) { - this.httpOperations = httpOperations; - this.methodInvoker = new HttpServiceMethodInvoker(httpOperations); - } - - HttpServiceRegistry(HttpOperations httpOperations, ServiceMethodInvoker methodInvoker) { - this.httpOperations = httpOperations; - this.methodInvoker = methodInvoker; - } - - public void setServiceProxy(ServiceProxy serviceProxy) { - this.serviceProxy = serviceProxy; - } - - public ServiceProxy getServiceProxy() { - if (serviceProxy == null) { - serviceProxy = createServiceProxy(); - } - return serviceProxy; - } - - protected JdkServiceProxy createServiceProxy() { - return new JdkServiceProxy(); - } - - @Override - public void register(HttpRegistration registration) { - try { - httpOperations.register(registration); - } - catch (RestClientException e) { - throw new ServiceRegisterFailedException(registration, e); - } - } - - @Override - public void unregister(HttpRegistration registration) { - httpOperations.delete(registration); - } - - @Override - public List getServices() { - return new ArrayList<>(httpOperations.getServices().keySet()); - } - - @Override - @SuppressWarnings("unchecked") - public List getInstances(String serviceId) { - return httpOperations.getInstances(serviceId); - } - - /** - * lookup for a target service - * - * @param serviceInterface target service interface - * @param service type - * @return target service interface - */ - @Override - public T getService(Class serviceInterface) { - return getServiceProxy().getProxy(serviceInterface, this, methodInvoker); - } - - @Override - public String toString() { - return ToStringBuilder.forInstance(this) - .append("httpOperations", httpOperations) - .toString(); - } - - // static - - public static HttpServiceRegistry ofURL(String registryURL) { - return new HttpServiceRegistry(registryURL); - } - - public static HttpServiceRegistry ofURL(String registryURL, Serialization serialization) { - return new HttpServiceRegistry(new HttpOperations(registryURL, serialization)); - } - - public static HttpServiceRegistry ofURL(String registryURL, Serialization serialization, ServiceMethodInvoker methodInvoker) { - return new HttpServiceRegistry(new HttpOperations(registryURL, serialization), methodInvoker); - } - -} diff --git a/today-service-client/src/main/java/infra/cloud/serialize/ByteBufInput.java b/today-service-client/src/main/java/infra/cloud/serialize/ByteBufInput.java deleted file mode 100644 index 142fa92..0000000 --- a/today-service-client/src/main/java/infra/cloud/serialize/ByteBufInput.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.serialize; - -import java.io.IOException; -import java.nio.ByteBuffer; - -import io.netty.buffer.ByteBuf; -import io.protostuff.ByteString; -import io.protostuff.Input; -import io.protostuff.Output; -import io.protostuff.Schema; - -/** - * @author 海子 Yang - * @since 1.0 2024/12/20 21:35 - */ -public class ByteBufInput implements Input { - - private final ByteBuf buffer; - - public ByteBufInput(ByteBuf buffer) { - this.buffer = buffer; - } - - @Override - public void handleUnknownField(int fieldNumber, Schema schema) throws IOException { - - } - - @Override - public int readFieldNumber(Schema schema) throws IOException { - return 0; - } - - @Override - public int readInt32() throws IOException { - return 0; - } - - @Override - public int readUInt32() throws IOException { - return 0; - } - - @Override - public int readSInt32() throws IOException { - return 0; - } - - @Override - public int readFixed32() throws IOException { - return 0; - } - - @Override - public int readSFixed32() throws IOException { - return 0; - } - - @Override - public long readInt64() throws IOException { - return 0; - } - - @Override - public long readUInt64() throws IOException { - return 0; - } - - @Override - public long readSInt64() throws IOException { - return 0; - } - - @Override - public long readFixed64() throws IOException { - return 0; - } - - @Override - public long readSFixed64() throws IOException { - return 0; - } - - @Override - public float readFloat() throws IOException { - return 0; - } - - @Override - public double readDouble() throws IOException { - return 0; - } - - @Override - public boolean readBool() throws IOException { - return false; - } - - @Override - public int readEnum() throws IOException { - return 0; - } - - @Override - public String readString() throws IOException { - return ""; - } - - @Override - public ByteString readBytes() throws IOException { - return null; - } - - @Override - public void readBytes(ByteBuffer bb) throws IOException { - - } - - @Override - public byte[] readByteArray() throws IOException { - return new byte[0]; - } - - @Override - public ByteBuffer readByteBuffer() throws IOException { - return null; - } - - @Override - public T mergeObject(T value, Schema schema) throws IOException { - return null; - } - - @Override - public void transferByteRangeTo(Output output, boolean utf8String, int fieldNumber, boolean repeated) throws IOException { - - } -} diff --git a/today-service-client/src/main/java/infra/cloud/serialize/ByteBufOutput.java b/today-service-client/src/main/java/infra/cloud/serialize/ByteBufOutput.java deleted file mode 100644 index b73bb5b..0000000 --- a/today-service-client/src/main/java/infra/cloud/serialize/ByteBufOutput.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.serialize; - -import java.io.IOException; -import java.nio.ByteBuffer; - -import io.netty.buffer.ByteBuf; -import io.protostuff.ByteString; -import io.protostuff.Output; -import io.protostuff.Schema; - -/** - * @author 海子 Yang - * @since 1.0 2024/12/20 21:28 - */ -public class ByteBufOutput implements Output { - private final ByteBuf buffer; - - public ByteBufOutput(ByteBuf buffer) { - this.buffer = buffer; - } - - @Override - public void writeInt32(int fieldNumber, int value, boolean repeated) throws IOException { - - } - - @Override - public void writeUInt32(int fieldNumber, int value, boolean repeated) throws IOException { - - } - - @Override - public void writeSInt32(int fieldNumber, int value, boolean repeated) throws IOException { - - } - - @Override - public void writeFixed32(int fieldNumber, int value, boolean repeated) throws IOException { - - } - - @Override - public void writeSFixed32(int fieldNumber, int value, boolean repeated) throws IOException { - - } - - @Override - public void writeInt64(int fieldNumber, long value, boolean repeated) throws IOException { - - } - - @Override - public void writeUInt64(int fieldNumber, long value, boolean repeated) throws IOException { - - } - - @Override - public void writeSInt64(int fieldNumber, long value, boolean repeated) throws IOException { - - } - - @Override - public void writeFixed64(int fieldNumber, long value, boolean repeated) throws IOException { - - } - - @Override - public void writeSFixed64(int fieldNumber, long value, boolean repeated) throws IOException { - - } - - @Override - public void writeFloat(int fieldNumber, float value, boolean repeated) throws IOException { - - } - - @Override - public void writeDouble(int fieldNumber, double value, boolean repeated) throws IOException { - - } - - @Override - public void writeBool(int fieldNumber, boolean value, boolean repeated) throws IOException { - - } - - @Override - public void writeEnum(int fieldNumber, int value, boolean repeated) throws IOException { - - } - - @Override - public void writeString(int fieldNumber, CharSequence value, boolean repeated) throws IOException { - - } - - @Override - public void writeBytes(int fieldNumber, ByteString value, boolean repeated) throws IOException { - - } - - @Override - public void writeByteArray(int fieldNumber, byte[] value, boolean repeated) throws IOException { - - } - - @Override - public void writeByteRange(boolean utf8String, int fieldNumber, byte[] value, int offset, int length, boolean repeated) throws IOException { - - } - - @Override - public void writeObject(int fieldNumber, T value, Schema schema, boolean repeated) throws IOException { - - } - - @Override - public void writeBytes(int fieldNumber, ByteBuffer value, boolean repeated) throws IOException { - - } -} diff --git a/today-service-client/src/main/java/infra/cloud/serialize/ProtobufArgumentSerialization.java b/today-service-client/src/main/java/infra/cloud/serialize/ProtobufArgumentSerialization.java deleted file mode 100644 index cf9e3fd..0000000 --- a/today-service-client/src/main/java/infra/cloud/serialize/ProtobufArgumentSerialization.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.serialize; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.google.protobuf.Message; - -import java.io.IOException; -import java.lang.reflect.Method; - -import infra.cloud.RpcMethod; -import infra.cloud.core.serialize.DeserializeFailedException; -import infra.core.MethodParameter; -import infra.http.converter.HttpMessageConversionException; -import infra.lang.Nullable; -import infra.util.ConcurrentReferenceHashMap; -import io.netty.buffer.ByteBuf; -import io.protostuff.Input; -import io.protostuff.Output; - -/** - * Serialization for protobuf - * - * @author 海子 Yang - * @since 1.0 2024/12/20 17:46 - */ -public class ProtobufArgumentSerialization implements RpcArgumentSerialization, ReturnValueSerialization { - - private static final ConcurrentReferenceHashMap, Method> methodCache = new ConcurrentReferenceHashMap<>(); - - @Override - public boolean supportsArgument(MethodParameter parameter) { - return Message.class.isAssignableFrom(parameter.getParameterType()); - } - - @Override - public void serialize(MethodParameter parameter, @Nullable Message value, ByteBuf payload, Output output) throws IOException { - - } - - @Override - public Message deserialize(MethodParameter parameter, ByteBuf payload, Input input) throws DeserializeFailedException { - Class parameterType = parameter.getParameterType(); - Message.Builder messageBuilder = getMessageBuilder(parameterType); - -// return messageBuilder.mergeFrom(); - return null; - } - - /** - * Create a new {@code Message.Builder} instance for the given class. - *

    This method uses a ConcurrentReferenceHashMap for caching method lookups. - */ - private Message.Builder getMessageBuilder(Class clazz) { - try { - Method method = methodCache.get(clazz); - if (method == null) { - method = clazz.getMethod("newBuilder"); - methodCache.put(clazz, method); - } - return (Message.Builder) method.invoke(clazz); - } - catch (Exception ex) { - throw new HttpMessageConversionException( - "Invalid Protobuf Message type: no invocable newBuilder() method on " + clazz, ex); - } - } - - // ---------------------------------------------------------------------------------------- - // ReturnValueSerialization - // ---------------------------------------------------------------------------------------- - - @Override - public boolean supportsArgument(RpcMethod method) { - return Message.class.isAssignableFrom(method.getReturnType().getParameterType()); - } - - @Override - public void serialize(RpcMethod method, Message value, ByteBuf payload, Output output) throws IOException { - - } - - @Override - public Message deserialize(RpcMethod method, ByteBuf payload, Input input) throws DeserializeFailedException { - Class parameterType = method.getReturnType().getParameterType(); - Message.Builder messageBuilder = getMessageBuilder(parameterType); - - try { - return messageBuilder.mergeFrom(payload.array()).build(); - } - catch (InvalidProtocolBufferException e) { - throw new DeserializeFailedException(e); - } - } - -} diff --git a/today-service-client/src/main/java/infra/cloud/serialize/ReturnValueSerialization.java b/today-service-client/src/main/java/infra/cloud/serialize/ReturnValueSerialization.java deleted file mode 100644 index 4046971..0000000 --- a/today-service-client/src/main/java/infra/cloud/serialize/ReturnValueSerialization.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.serialize; - -import java.io.IOException; - -import infra.cloud.RpcMethod; -import infra.cloud.core.serialize.DeserializeFailedException; -import io.netty.buffer.ByteBuf; -import io.protostuff.Input; -import io.protostuff.Output; - -/** - * @author 海子 Yang - * @since 1.0 2024/12/20 21:42 - */ -public interface ReturnValueSerialization { - - /** - * Whether the given parameter is supported by this resolver. - *

    - * static match - *

    - */ - boolean supportsArgument(RpcMethod method); - - void serialize(RpcMethod method, T returnValue, ByteBuf payload, Output output) throws IOException; - - T deserialize(RpcMethod method, ByteBuf payload, Input input) throws DeserializeFailedException; - -} diff --git a/today-service-client/src/main/java/infra/cloud/serialize/RpcArgumentSerialization.java b/today-service-client/src/main/java/infra/cloud/serialize/RpcArgumentSerialization.java deleted file mode 100644 index 255ee64..0000000 --- a/today-service-client/src/main/java/infra/cloud/serialize/RpcArgumentSerialization.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.serialize; - -import java.io.IOException; - -import infra.cloud.core.serialize.DeserializeFailedException; -import infra.core.MethodParameter; -import infra.lang.Nullable; -import io.netty.buffer.ByteBuf; -import io.protostuff.Input; -import io.protostuff.Output; - -/** - * @author 海子 Yang - * @since 1.0 2024/12/20 16:29 - */ -public interface RpcArgumentSerialization { - - /** - * Whether the given parameter is supported by this resolver. - *

    - * static match - *

    - */ - boolean supportsArgument(MethodParameter parameter); - - void serialize(MethodParameter parameter, @Nullable T value, ByteBuf payload, Output output) throws IOException; - - @Nullable - T deserialize(MethodParameter parameter, ByteBuf payload, Input input) throws DeserializeFailedException; - -} diff --git a/today-service-client/src/main/java/infra/cloud/serialize/RpcRequestSerialization.java b/today-service-client/src/main/java/infra/cloud/serialize/RpcRequestSerialization.java deleted file mode 100644 index ea50662..0000000 --- a/today-service-client/src/main/java/infra/cloud/serialize/RpcRequestSerialization.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.serialize; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.List; - -import infra.cloud.RpcMethod; -import infra.cloud.RpcRequest; -import infra.core.MethodParameter; -import io.netty.buffer.ByteBuf; -import io.protostuff.Output; - -/** - * @author 海子 Yang - * @since 1.0 2024/12/20 15:59 - */ -@SuppressWarnings({ "unchecked", "rawtypes" }) -public class RpcRequestSerialization { - - private final List argumentSerializations; - - public RpcRequestSerialization(List argumentSerializations) { - this.argumentSerializations = argumentSerializations; - } - - @SuppressWarnings("unchecked") - public void serialize(RpcRequest request, ByteBuf payload) throws IOException { -// output.writeString(1, request.getMethodName(), true); -// output.writeString(2, request.getServiceName(), true); - - payload.writeInt(request.getMethodName().length()); - payload.writeCharSequence(request.getMethodName(), StandardCharsets.UTF_8); - - payload.writeInt(request.getServiceName().length()); - payload.writeCharSequence(request.getServiceName(), StandardCharsets.UTF_8); - - RpcMethod rpcMethod = request.getRpcMethod(); - - int idx = 0; - Object[] arguments = request.getArguments(); - - Output output = new ByteBufOutput(payload); - beforeSerializeArguments(output, arguments); - for (MethodParameter parameter : rpcMethod.getParameters()) { - var serialization = findArgumentSerialization(parameter); - serialization.serialize(parameter, arguments[idx++], payload, output); - } - afterSerializeArguments(output, arguments); - } - - private RpcArgumentSerialization findArgumentSerialization(MethodParameter parameter) { - for (var argumentSerialization : argumentSerializations) { - if (argumentSerialization.supportsArgument(parameter)) { - return argumentSerialization; - } - } - throw new IllegalStateException("RpcArgumentSerialization for parameter %s not found".formatted(parameter)); - } - - protected void afterSerializeArguments(Output output, Object[] arguments) { - - } - - protected void beforeSerializeArguments(Output output, Object[] arguments) { - - } - -} diff --git a/today-service-client/src/main/java/infra/cloud/serialize/RpcResponseSerialization.java b/today-service-client/src/main/java/infra/cloud/serialize/RpcResponseSerialization.java deleted file mode 100644 index 10bcd35..0000000 --- a/today-service-client/src/main/java/infra/cloud/serialize/RpcResponseSerialization.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.serialize; - -import java.io.IOException; -import java.util.List; - -import infra.cloud.RpcMethod; -import infra.cloud.RpcRequest; -import infra.cloud.RpcResponse; -import infra.cloud.core.serialize.DeserializeFailedException; -import io.netty.buffer.ByteBuf; - -/** - * @author 海子 Yang - * @since 1.0 2024/12/20 21:22 - */ -@SuppressWarnings({ "unchecked", "rawtypes" }) -public class RpcResponseSerialization { - - private final List serializations; - - private final ThrowableSerialization throwableSerialization; - - public RpcResponseSerialization(List serializations, ThrowableSerialization throwableSerialization) { - this.serializations = serializations; - this.throwableSerialization = throwableSerialization; - } - - public void serialize(RpcResponse response, ByteBuf payload) throws IOException { - Throwable exception = response.getException(); - if (exception != null) { - // has error - payload.writeBoolean(true); - throwableSerialization.serialize(exception, payload); - } - else { - RpcMethod rpcMethod = response.getRpcMethod(); - payload.writeBoolean(false); - - var serialization = findSerialization(rpcMethod); - Object result = response.getResult(); - serialization.serialize(rpcMethod, result, payload, new ByteBufOutput(payload)); - } - } - - public RpcResponse deserialize(RpcRequest rpcRequest, ByteBuf body) throws DeserializeFailedException { - RpcMethod rpcMethod = rpcRequest.getRpcMethod(); - RpcResponse response = new RpcResponse(); - response.setRpcMethod(rpcMethod); - boolean hasError = body.readBoolean(); - if (hasError) { - Throwable deserialize = throwableSerialization.deserialize(body); - response.setException(deserialize); - } - else { - var serialization = findSerialization(rpcMethod); - Object result = serialization.deserialize(rpcMethod, body, new ByteBufInput(body)); - response.setResult(result); - } - return response; - } - - private ReturnValueSerialization findSerialization(RpcMethod rpcMethod) { - for (ReturnValueSerialization serialization : serializations) { - if (serialization.supportsArgument(rpcMethod)) { - return serialization; - } - } - throw new IllegalStateException("ReturnValueSerialization for method %s not found".formatted(rpcMethod)); - } - -} diff --git a/today-service-client/src/main/java/infra/cloud/serialize/SimpleValueArgumentSerialization.java b/today-service-client/src/main/java/infra/cloud/serialize/SimpleValueArgumentSerialization.java deleted file mode 100644 index 2f2c253..0000000 --- a/today-service-client/src/main/java/infra/cloud/serialize/SimpleValueArgumentSerialization.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.serialize; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import infra.beans.BeanUtils; -import infra.cloud.core.serialize.DeserializeFailedException; -import infra.cloud.serialize.value.ValueSerialization; -import infra.core.MethodParameter; -import infra.lang.Nullable; -import io.netty.buffer.ByteBuf; -import io.protostuff.Input; -import io.protostuff.Output; - -import static infra.cloud.serialize.value.ValueSerialization.map; - -/** - * @author 海子 Yang - * @since 1.0 2025/3/8 21:15 - */ -public class SimpleValueArgumentSerialization implements RpcArgumentSerialization { - - private final Map, ValueSerialization> serializationMap = new HashMap<>(); - - public SimpleValueArgumentSerialization() { - serializationMap.put(int.class, map(ByteBuf::readInt, ByteBuf::writeInt)); - serializationMap.put(Integer.class, map(ByteBuf::readInt, ByteBuf::writeInt)); - - serializationMap.put(long.class, map(ByteBuf::readLong, ByteBuf::writeLong)); - serializationMap.put(Long.class, map(ByteBuf::readLong, ByteBuf::writeLong)); - -// serializationMap.put(short.class, map(ByteBuf::readShort, ByteBuf::writeShort)); -// serializationMap.put(Short.class, map(ByteBuf::readShort, ByteBuf::writeShort)); - - } - - @Override - public boolean supportsArgument(MethodParameter parameter) { - return BeanUtils.isSimpleProperty(parameter.getParameterType()); - } - - @Override - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void serialize(MethodParameter parameter, @Nullable Object value, ByteBuf payload, Output output) throws IOException { - payload.writeBoolean(value != null); - if (value != null) { - ValueSerialization serialization = findSerialization(parameter.getParameterType()); - serialization.serialize(parameter, value, payload); - } - } - - @Nullable - @Override - public Object deserialize(MethodParameter parameter, ByteBuf payload, Input input) throws DeserializeFailedException { - boolean isNull = payload.readBoolean(); - if (isNull) { - return null; - } - - var serialization = findSerialization(parameter.getParameterType()); - return serialization.deserialize(parameter, payload); - } - - @SuppressWarnings({ "rawtypes" }) - private ValueSerialization findSerialization(Class type) { - ValueSerialization serialization = serializationMap.get(type); - if (serialization == null) { - Class superclass = type.getSuperclass(); - if (superclass == null || superclass == Object.class) { - throw new IllegalStateException("ValueSerialization for type %s not found".formatted(type)); // todo type - } - return findSerialization(superclass); - } - return serialization; - } - -} diff --git a/today-service-client/src/main/java/infra/cloud/serialize/ThrowableSerialization.java b/today-service-client/src/main/java/infra/cloud/serialize/ThrowableSerialization.java deleted file mode 100644 index 3925bf0..0000000 --- a/today-service-client/src/main/java/infra/cloud/serialize/ThrowableSerialization.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.serialize; - -import java.io.IOException; - -import infra.cloud.core.serialize.DeserializeFailedException; -import io.netty.buffer.ByteBuf; - -/** - * @author 海子 Yang - * @since 1.0 2024/12/25 17:36 - */ -public class ThrowableSerialization { - - public void serialize(Throwable throwable, ByteBuf payload) throws IOException { - - } - - public Throwable deserialize(ByteBuf body) throws DeserializeFailedException { - return null; - } - -} diff --git a/today-service-client/src/main/java/infra/cloud/serialize/package-info.java b/today-service-client/src/main/java/infra/cloud/serialize/package-info.java deleted file mode 100644 index e0afbce..0000000 --- a/today-service-client/src/main/java/infra/cloud/serialize/package-info.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -/** - * serialize - */ -@NonNullApi -@NonNullFields -package infra.cloud.serialize; - -import infra.lang.NonNullApi; -import infra.lang.NonNullFields; \ No newline at end of file diff --git a/today-service-client/src/main/java/infra/cloud/serialize/value/FuncValueSerialization.java b/today-service-client/src/main/java/infra/cloud/serialize/value/FuncValueSerialization.java deleted file mode 100644 index e76a579..0000000 --- a/today-service-client/src/main/java/infra/cloud/serialize/value/FuncValueSerialization.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.serialize.value; - -import java.io.IOException; -import java.util.function.BiConsumer; -import java.util.function.Function; - -import infra.cloud.core.serialize.DeserializeFailedException; -import infra.core.MethodParameter; -import io.netty.buffer.ByteBuf; - -/** - * @author 海子 Yang - * @since 1.0 2025/3/8 21:37 - */ -final class FuncValueSerialization implements ValueSerialization { - - private final Function reader; - - private final BiConsumer writer; - - FuncValueSerialization(Function reader, BiConsumer writer) { - this.reader = reader; - this.writer = writer; - } - - @Override - public void serialize(MethodParameter parameter, T value, ByteBuf payload) throws IOException { - writer.accept(payload, value); - } - - @Override - public T deserialize(MethodParameter parameter, ByteBuf payload) throws DeserializeFailedException { - return reader.apply(payload); - } - -} diff --git a/today-service-client/src/main/java/infra/cloud/serialize/value/ValueSerialization.java b/today-service-client/src/main/java/infra/cloud/serialize/value/ValueSerialization.java deleted file mode 100644 index 11d156e..0000000 --- a/today-service-client/src/main/java/infra/cloud/serialize/value/ValueSerialization.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.serialize.value; - -import java.io.IOException; -import java.util.function.BiConsumer; -import java.util.function.Function; - -import infra.cloud.core.serialize.DeserializeFailedException; -import infra.core.MethodParameter; -import infra.lang.Assert; -import io.netty.buffer.ByteBuf; - -/** - * @author 海子 Yang - * @since 1.0 2025/3/8 21:31 - */ -public interface ValueSerialization { - - void serialize(MethodParameter parameter, T value, ByteBuf payload) throws IOException; - - T deserialize(MethodParameter parameter, ByteBuf payload) throws DeserializeFailedException; - - static ValueSerialization map(Function reader, BiConsumer writer) { - Assert.notNull(reader, "reader Function is required"); - Assert.notNull(writer, "writer BiConsumer is required"); - return new FuncValueSerialization<>(reader, writer); - } - -} - diff --git a/today-service-client/src/main/java/infra/cloud/service/AbstractInvocationResult.java b/today-service-client/src/main/java/infra/cloud/service/AbstractInvocationResult.java new file mode 100644 index 0000000..dc9ee25 --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/AbstractInvocationResult.java @@ -0,0 +1,32 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import infra.core.AttributeAccessorSupport; +import infra.remoting.Payload; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/9 16:17 + */ +abstract class AbstractInvocationResult extends AttributeAccessorSupport implements InvocationResult { + + protected final Object deserialize(Payload payload) { + return null; + } + +} diff --git a/today-service-client/src/main/java/infra/cloud/service/ClientInterceptor.java b/today-service-client/src/main/java/infra/cloud/service/ClientInterceptor.java new file mode 100644 index 0000000..d2d1556 --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/ClientInterceptor.java @@ -0,0 +1,46 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +/** + * Intercepts calls on an interface on its way to the target. These are nested + * "on top" of the target. + * + *

    + * The user should implement the {@link #intercept(ServiceInvocation)} method to + * modify the original behavior. E.g. the following class implements a tracing + * interceptor (traces all the calls on the intercepted method(s)): + * + *

    {@code
    + * class TracingInterceptor implements ClientInterceptor {
    + *    public InvocationResult intercept(ServiceInvocation i) throws Throwable {
    + *         System.out.println("before service " + i + " with args " + i.getArguments());
    + *         Object ret = i.proceed();
    + *         System.out.println("after service " + i + " returns " + ret);
    + *         return ret;
    + *     }
    + * }
    + * }
    + * + * @author 海子 Yang + * @since 1.0 2025/8/9 15:39 + */ +public interface ClientInterceptor { + + InvocationResult intercept(ServiceInvocation invocation) throws Throwable; + +} diff --git a/today-service-client/src/main/java/infra/cloud/service/DefaultRemotingOperationsProvider.java b/today-service-client/src/main/java/infra/cloud/service/DefaultRemotingOperationsProvider.java new file mode 100644 index 0000000..db79514 --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/DefaultRemotingOperationsProvider.java @@ -0,0 +1,81 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + +import infra.cloud.client.DiscoveryClient; +import infra.cloud.client.ServiceInstance; +import infra.remoting.RemotingOperations; +import infra.remoting.core.RemotingClient; +import infra.remoting.lb.LoadBalanceTarget; +import infra.remoting.transport.netty.client.TcpClientTransport; +import reactor.core.publisher.Flux; + +/** + * Default implementation of {@link RemotingOperationsProvider} that provides remoting operations + * based on service discovery. It maintains a cache of {@link RemotingClient} instances keyed by + * service ID and periodically refreshes the list of available service instances for load balancing. + *

    + * This class also acts as a function to convert {@link ServiceInstance} lists into + * {@link LoadBalanceTarget} lists for the load balancer. + * + * @author 海子 Yang + * @since 1.0 2025/8/10 22:08 + */ +public class DefaultRemotingOperationsProvider implements RemotingOperationsProvider { + + private final DiscoveryClient discoveryClient; + + private final ConcurrentHashMap remotingClientMap = new ConcurrentHashMap<>(); + + private Duration discoveryPeriod = Duration.ofSeconds(10); + + public DefaultRemotingOperationsProvider(DiscoveryClient discoveryClient) { + this.discoveryClient = discoveryClient; + } + + /** + * service discovery reload period + */ + public void setDiscoveryPeriod(Duration discoveryPeriod) { + this.discoveryPeriod = discoveryPeriod; + } + + @Override + public RemotingOperations getRemotingOperations(ServiceMethod serviceMethod) { + return getRemotingOperations(serviceMethod.getServiceId()); + } + + public RemotingOperations getRemotingOperations(String serviceId) { + return remotingClientMap.computeIfAbsent(serviceId, name -> RemotingClient.forLoadBalance(Flux.interval(Duration.ZERO, discoveryPeriod) + .map(i -> discoveryClient.getInstances(name)) + .map(instances -> { + var targets = new ArrayList(instances.size()); + for (ServiceInstance instance : instances) { + targets.add(LoadBalanceTarget.of(instance.getInstanceId(), + TcpClientTransport.create(instance.getHost(), instance.getPort()))); + } + return targets; + })) + .weightedLoadBalanceStrategy() + .build()); + } + +} diff --git a/today-service-client/src/main/java/infra/cloud/service/DefaultServiceInterfaceMetadataProvider.java b/today-service-client/src/main/java/infra/cloud/service/DefaultServiceInterfaceMetadataProvider.java new file mode 100644 index 0000000..93ee3ae --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/DefaultServiceInterfaceMetadataProvider.java @@ -0,0 +1,150 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import org.jspecify.annotations.Nullable; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import infra.util.concurrent.Future; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/10 08:22 + */ +public class DefaultServiceInterfaceMetadataProvider extends AbstractServiceInterfaceMetadataProvider { + + private final ArrayList resolvers = new ArrayList<>(); + + public DefaultServiceInterfaceMetadataProvider(ServiceMetadataProvider serviceMetadataProvider, + List returnValueResolvers) { + super(serviceMetadataProvider); + resolvers.addAll(returnValueResolvers); + + resolvers.add(new MonoReturnValueResolver()); + resolvers.add(new FluxReturnValueResolver()); + resolvers.add(new BlockReturnValueResolver()); + resolvers.add(new FutureReturnValueResolver()); + } + + @Override + protected ServiceInterfaceMethod createServiceMethod(ServiceMetadata serviceMetadata, Class serviceInterface, Method method) { + return new ServiceInterfaceMethod(serviceMetadata, serviceInterface, method, resolvers); + } + + static class FutureReturnValueResolver implements ReturnValueResolver { + + @Override + public boolean supportsMethod(ServiceInterfaceMethod invocation) { + return invocation.getMethod().getReturnType() == Future.class; + } + + @Override + public InvocationType getInvocationType(ServiceInterfaceMethod method) { + return InvocationType.REQUEST_RESPONSE; + } + + @Override + public Object resolve(ServiceInterfaceMethod method, InvocationResult result) throws Throwable { + return result.future(); + } + + @Override + public boolean isBlocking() { + return false; + } + } + + static class BlockReturnValueResolver implements ReturnValueResolver { + + @Override + public boolean supportsMethod(ServiceInterfaceMethod method) { + return true; + } + + @Override + public InvocationType getInvocationType(ServiceInterfaceMethod method) { + return InvocationType.REQUEST_RESPONSE; + } + + @Override + public @Nullable Object resolve(ServiceInterfaceMethod method, InvocationResult result) throws Throwable { + return result.getBlockingValue(); + } + + @Override + public boolean isBlocking() { + return true; + } + + } + + static class MonoReturnValueResolver implements ReturnValueResolver { + + @Override + public boolean supportsMethod(ServiceInterfaceMethod method) { + return method.getMethod().getReturnType() == Mono.class; + } + + @Override + public InvocationType getInvocationType(ServiceInterfaceMethod method) { + return InvocationType.REQUEST_RESPONSE; + } + + @Override + public Mono resolve(ServiceInterfaceMethod method, InvocationResult result) throws Throwable { + return Mono.from(result.publisher()); + } + + @Override + public boolean isBlocking() { + return false; + } + + } + + static class FluxReturnValueResolver implements ReturnValueResolver { + + @Override + public boolean supportsMethod(ServiceInterfaceMethod method) { + return method.getMethod().getReturnType() == Flux.class; + } + + @Override + public InvocationType getInvocationType(ServiceInterfaceMethod method) { + if (method.getParameters().length == 1 && method.getParameters()[0].getParameterType() == Flux.class) { + return InvocationType.DUPLEX_STREAMING; + } + return InvocationType.RESPONSE_STREAMING; + } + + @Override + public Flux resolve(ServiceInterfaceMethod method, InvocationResult result) throws Throwable { + return Flux.from(result.publisher()); + } + + @Override + public boolean isBlocking() { + return false; + } + + } +} diff --git a/today-service-client/src/main/java/infra/cloud/service/DefaultServiceProxyFactory.java b/today-service-client/src/main/java/infra/cloud/service/DefaultServiceProxyFactory.java new file mode 100644 index 0000000..d3fa67d --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/DefaultServiceProxyFactory.java @@ -0,0 +1,108 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import org.jspecify.annotations.Nullable; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import infra.lang.Assert; +import infra.util.ReflectionUtils; + +/** + * Default implementation of {@link ServiceProxyFactory} that creates service proxies + * using dynamic proxy mechanisms. This factory configures proxies with the necessary + * interceptors, remoting operations provider, and service interface metadata to handle + * remote method invocations. + * + * @author 海子 Yang + * @since 2021/7/4 22:58 + */ +public class DefaultServiceProxyFactory implements ServiceProxyFactory { + + private final ServiceInterfaceMetadataProvider metadataProvider; + + private final ServiceInvoker serviceInvoker; + + public DefaultServiceProxyFactory(ServiceInterfaceMetadataProvider metadataProvider, ServiceInvoker serviceInvoker) { + Assert.notNull(metadataProvider, "metadataProvider is required"); + Assert.notNull(serviceInvoker, "serviceInvoker is required"); + this.metadataProvider = metadataProvider; + this.serviceInvoker = serviceInvoker; + } + + @Override + @SuppressWarnings("unchecked") + public S getService(Class serviceInterface) { + Assert.isTrue(serviceInterface.isInterface(), "service must be an interface"); + var metadata = metadataProvider.getMetadata(serviceInterface); + List serviceMethods = metadata.getServiceMethods(); + + return (S) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class[] { serviceInterface }, + new ServiceInvocationHandler(serviceInterface, serviceMethods, serviceInvoker)); + } + + static final class ServiceInvocationHandler implements InvocationHandler { + + private final Class serviceInterface; + + private final ServiceInvoker serviceInvoker; + + private final Map serviceMethods; + + public ServiceInvocationHandler(Class serviceInterface, List methods, ServiceInvoker serviceInvoker) { + this.serviceInterface = serviceInterface; + this.serviceInvoker = serviceInvoker; + this.serviceMethods = methods.stream() + .collect(Collectors.toMap(ServiceInterfaceMethod::getMethod, Function.identity())); + } + + @Override + public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + ServiceInterfaceMethod serviceMethod = serviceMethods.get(method); + if (serviceMethod != null) { + var result = serviceInvoker.invoke(serviceMethod, args); + return serviceMethod.resolveResult(result); + } + + if (method.isDefault()) { + return InvocationHandler.invokeDefault(proxy, method, args); + } + + if (ReflectionUtils.isEqualsMethod(method)) { + // Only consider equal when proxies are identical. + return (proxy == args[0]); + } + else if (ReflectionUtils.isHashCodeMethod(method)) { + // Use hashCode of service proxy. + return System.identityHashCode(proxy); + } + else if (ReflectionUtils.isToStringMethod(method)) { + return "Remote service proxy: " + serviceInterface; + } + + throw new IllegalStateException("Unexpected method invocation: " + method); + } + } + +} diff --git a/today-service-client/src/main/java/infra/cloud/service/DuplexStreamingResult.java b/today-service-client/src/main/java/infra/cloud/service/DuplexStreamingResult.java new file mode 100644 index 0000000..95546d0 --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/DuplexStreamingResult.java @@ -0,0 +1,76 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import org.reactivestreams.Publisher; + +import infra.remoting.Payload; +import infra.util.concurrent.Future; +import reactor.core.publisher.Flux; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/15 20:35 + */ +class DuplexStreamingResult extends AbstractInvocationResult { + + private final Flux payloadFlux; + + public DuplexStreamingResult(Flux payloadFlux) { + this.payloadFlux = payloadFlux; + } + + @Override + public Object getBlockingValue() { + return null; + } + + @Override + public boolean isFailed() { + return false; + } + + @Override + public Throwable getException() { + return null; + } + + @Override + public InvocationType getType() { + return InvocationType.DUPLEX_STREAMING; + } + + @Override + public boolean isRequestResponse() { + return false; + } + + @Override + public boolean isStreaming() { + return true; + } + + @Override + public Future future() { + return null; + } + + @Override + public Publisher publisher() { + return null; + } +} diff --git a/today-service-client/src/main/java/infra/cloud/service/FireAndForgetResult.java b/today-service-client/src/main/java/infra/cloud/service/FireAndForgetResult.java new file mode 100644 index 0000000..e74287a --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/FireAndForgetResult.java @@ -0,0 +1,76 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import org.reactivestreams.Publisher; + +import infra.util.concurrent.Future; +import reactor.core.publisher.Mono; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/15 20:36 + */ +class FireAndForgetResult extends AbstractInvocationResult { + + private final Mono fireAndForgetMono; + + public FireAndForgetResult(Mono fireAndForgetMono) { + this.fireAndForgetMono = fireAndForgetMono; + } + + @Override + public Object getBlockingValue() { + return null; + } + + @Override + public boolean isFailed() { + return false; + } + + @Override + public Throwable getException() { + return null; + } + + @Override + public InvocationType getType() { + return InvocationType.FIRE_AND_FORGET; + } + + @Override + public boolean isRequestResponse() { + return false; + } + + @Override + public boolean isStreaming() { + return false; + } + + @Override + public Future future() { + return null; + } + + @Override + public Publisher publisher() { + return fireAndForgetMono; + } + +} diff --git a/today-service-client/src/main/java/infra/cloud/service/MethodServiceInvocation.java b/today-service-client/src/main/java/infra/cloud/service/MethodServiceInvocation.java new file mode 100644 index 0000000..101c74b --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/MethodServiceInvocation.java @@ -0,0 +1,80 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import infra.core.AttributeAccessorSupport; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/9 14:11 + */ +public abstract class MethodServiceInvocation extends AttributeAccessorSupport implements ServiceInvocation { + + protected final ServiceInterfaceMethod serviceMethod; + + private final Object[] args; + + private final ClientInterceptor[] interceptors; + + private int currentIndex = 0; + + private final int interceptorSize; + + public MethodServiceInvocation(ServiceInterfaceMethod serviceMethod, Object[] args, ClientInterceptor[] interceptors) { + this.serviceMethod = serviceMethod; + this.args = args; + this.interceptors = interceptors; + this.interceptorSize = interceptors.length; + } + + @Override + public InvocationResult proceed() throws Throwable { + if (currentIndex < interceptorSize) { + return interceptors[currentIndex++].intercept(this); + } + + return invokeRemoting(); + } + + protected abstract InvocationResult invokeRemoting(); + + @Override + public ServiceInterfaceMethod getServiceMethod() { + return serviceMethod; + } + + @Override + public ServiceMetadata getServiceMetadata() { + return serviceMethod.serviceMetadata; + } + + @Override + public String getServiceId() { + return serviceMethod.getServiceId(); + } + + @Override + public Object[] getArguments() { + return args; + } + + @Override + public InvocationType getType() { + return serviceMethod.getInvocationType(); + } + +} diff --git a/today-service-client/src/main/java/infra/cloud/service/RemotingOperationsProvider.java b/today-service-client/src/main/java/infra/cloud/service/RemotingOperationsProvider.java new file mode 100644 index 0000000..90f5d9c --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/RemotingOperationsProvider.java @@ -0,0 +1,42 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import infra.remoting.RemotingOperations; + +/** + * Provider for obtaining {@link RemotingOperations} instances based on a given service method. + *

    + * This interface defines a contract for retrieving remote operation handlers dynamically + * according to the specified {@link ServiceMethod}. Implementations are responsible for + * returning the appropriate remoting operations capable of handling the requested service. + *

    + * + * @author 海子 Yang + * @since 1.0 2025/8/10 22:10 + */ +public interface RemotingOperationsProvider { + + /** + * Retrieves the {@link RemotingOperations} instance associated with the given service method. + * + * @param serviceMethod the specific service method to obtain remoting operations for + * @return the corresponding {@link RemotingOperations} instance; must not be null + */ + RemotingOperations getRemotingOperations(ServiceMethod serviceMethod); + +} diff --git a/today-service-client/src/main/java/infra/cloud/service/RequestResponseResult.java b/today-service-client/src/main/java/infra/cloud/service/RequestResponseResult.java new file mode 100644 index 0000000..ea6786b --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/RequestResponseResult.java @@ -0,0 +1,97 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import org.jspecify.annotations.Nullable; + +import java.util.function.Function; + +import infra.cloud.service.serialize.ResponseDeserializer; +import infra.remoting.Payload; +import infra.util.concurrent.Future; +import infra.util.concurrent.PublisherFuture; +import reactor.core.publisher.Mono; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/15 19:55 + */ +final class RequestResponseResult extends AbstractInvocationResult implements Function { + + private final Mono resultPublisher; + + private final ServiceInterfaceMethod method; + + private final ResponseDeserializer responseDeserializer; + + private Future future; + + RequestResponseResult(ServiceInterfaceMethod method, Mono resultPublisher, ResponseDeserializer responseDeserializer) { + this.resultPublisher = resultPublisher; + this.method = method; + this.responseDeserializer = responseDeserializer; + } + + @Override + public @Nullable Object getBlockingValue() { + return publisher().block(); + } + + @Override + public Object apply(Payload payload) { + return responseDeserializer.deserialize(method, payload.data()); + } + + @Override + public boolean isFailed() { + return false; + } + + @Override + public Throwable getException() { + return null; + } + + @Override + public InvocationType getType() { + return InvocationType.REQUEST_RESPONSE; + } + + @Override + public boolean isRequestResponse() { + return true; + } + + @Override + public boolean isStreaming() { + return false; + } + + @Override + public Future future() { + if (future == null) { + future = PublisherFuture.of(publisher()); + } + return future; + } + + @Override + public Mono publisher() { + return resultPublisher.map(this); + } + +} diff --git a/today-service-client/src/main/java/infra/cloud/service/ResponseStreamingResult.java b/today-service-client/src/main/java/infra/cloud/service/ResponseStreamingResult.java new file mode 100644 index 0000000..61d8daf --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/ResponseStreamingResult.java @@ -0,0 +1,91 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import org.reactivestreams.Publisher; + +import java.util.function.Function; + +import infra.cloud.service.serialize.ResponseDeserializer; +import infra.remoting.Payload; +import infra.util.concurrent.Future; +import reactor.core.publisher.Flux; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/15 20:34 + */ +final class ResponseStreamingResult extends AbstractInvocationResult implements Function { + + private final Flux payloadFlux; + + private final ServiceInterfaceMethod method; + + private final ResponseDeserializer responseDeserializer; + + public ResponseStreamingResult(ServiceInterfaceMethod method, Flux payloadFlux, ResponseDeserializer responseDeserializer) { + this.method = method; + this.payloadFlux = payloadFlux; + this.responseDeserializer = responseDeserializer; + } + + @Override + public Object getBlockingValue() { + return null; + } + + @Override + public boolean isFailed() { + return false; + } + + @Override + public Throwable getException() { + return null; + } + + @Override + public InvocationType getType() { + return InvocationType.RESPONSE_STREAMING; + } + + @Override + public boolean isRequestResponse() { + return false; + } + + @Override + public boolean isStreaming() { + return true; + } + + @Override + public Future future() { + return null; + } + + @Override + public Publisher publisher() { + return payloadFlux.map(this); + } + + @Override + public Object apply(Payload payload) { + return responseDeserializer.deserialize(method, payload.data()); + } + +} diff --git a/today-service-client/src/main/java/infra/cloud/service/ReturnValueResolver.java b/today-service-client/src/main/java/infra/cloud/service/ReturnValueResolver.java new file mode 100644 index 0000000..a8b6b55 --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/ReturnValueResolver.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import org.jspecify.annotations.Nullable; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/9 14:17 + */ +public interface ReturnValueResolver { + + boolean supportsMethod(ServiceInterfaceMethod method); + + InvocationType getInvocationType(ServiceInterfaceMethod method); + + @Nullable + Object resolve(ServiceInterfaceMethod method, InvocationResult result) throws Throwable; + + boolean isBlocking(); +} diff --git a/today-service-client/src/main/java/infra/cloud/service/ServiceInterfaceMethod.java b/today-service-client/src/main/java/infra/cloud/service/ServiceInterfaceMethod.java new file mode 100644 index 0000000..c1c21a9 --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/ServiceInterfaceMethod.java @@ -0,0 +1,73 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import org.jspecify.annotations.Nullable; + +import java.lang.reflect.Method; +import java.util.ArrayList; + +/** + * Represents a method defined in a service interface, extending {@link ServiceMethod} + * with specific invocation logic and return value resolution strategies. + *

    + * This class determines the invocation type (e.g., blocking or non-blocking) based on + * the method's return type and configures the appropriate {@link ReturnValueResolver} + * to handle the result of the method invocation. + *

    + * + * @author Harry Yang + * @since 1.0 2024/1/7 21:03 + */ +public class ServiceInterfaceMethod extends ServiceMethod { + + private final InvocationType invocationType; + + private final ReturnValueResolver returnValueResolver; + + private final boolean blocking; + + ServiceInterfaceMethod(ServiceMetadata serviceMetadata, Class serviceInterface, Method method, ArrayList resolvers) { + super(serviceMetadata, serviceInterface, method); + this.returnValueResolver = findReturnValueResolver(resolvers); + this.blocking = returnValueResolver.isBlocking(); + this.invocationType = returnValueResolver.getInvocationType(this); + } + + public boolean isBlocking() { + return blocking; + } + + public InvocationType getInvocationType() { + return invocationType; + } + + @Nullable + public Object resolveResult(InvocationResult result) throws Throwable { + return returnValueResolver.resolve(this, result); + } + + private ReturnValueResolver findReturnValueResolver(ArrayList resolvers) { + for (ReturnValueResolver resolver : resolvers) { + if (resolver.supportsMethod(this)) { + return resolver; + } + } + throw new IllegalArgumentException("No ReturnValueResolver found for method: " + method); + } + +} diff --git a/today-service-client/src/main/java/infra/cloud/service/ServiceInvocation.java b/today-service-client/src/main/java/infra/cloud/service/ServiceInvocation.java new file mode 100644 index 0000000..91dcc2c --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/ServiceInvocation.java @@ -0,0 +1,62 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import infra.core.AttributeAccessor; + +/** + * This interface represents a service invocation. + * + * @author 海子 Yang + * @since 1.0 2025/8/9 10:12 + */ +public interface ServiceInvocation extends AttributeAccessor { + + /** + * Proceeds to the next interceptor in the chain. + * + * @return see the children interfaces' proceed definition. + * @throws Throwable if the invocation throws an exception. + */ + InvocationResult proceed() throws Throwable; + + /** + * Service ID + */ + String getServiceId(); + + /** + * Service metadata + */ + ServiceMetadata getServiceMetadata(); + + /** + * service method metadata + */ + ServiceInterfaceMethod getServiceMethod(); + + /** + * Get the arguments as an array object. It is possible to change element values + * within this array to change the arguments. + * + * @return the argument of the invocation + */ + Object[] getArguments(); + + InvocationType getType(); + +} diff --git a/today-service-client/src/main/java/infra/cloud/service/ServiceInvoker.java b/today-service-client/src/main/java/infra/cloud/service/ServiceInvoker.java new file mode 100644 index 0000000..b6cbd3d --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/ServiceInvoker.java @@ -0,0 +1,38 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/9 12:01 + */ +public interface ServiceInvoker { + + /** + * Implement this method to perform extra treatments before and after the + * invocation. Polite implementations would certainly like to invoke + * {@link ServiceInvocation#proceed()}. + * + * @param serviceMethod the service method + * @param args invocation args + * @return the result of the call to {@link ServiceInvocation#proceed()}, might be + * intercepted by the interceptor. + * @throws Throwable if the interceptors or the target-object throws an exception. + */ + InvocationResult invoke(ServiceInterfaceMethod serviceMethod, Object[] args) throws Throwable; + +} diff --git a/today-service-client/src/main/java/infra/cloud/service/ServiceMethodInvoker.java b/today-service-client/src/main/java/infra/cloud/service/ServiceMethodInvoker.java new file mode 100644 index 0000000..102ae0d --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/ServiceMethodInvoker.java @@ -0,0 +1,237 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import org.jspecify.annotations.Nullable; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + +import java.util.List; + +import infra.cloud.serialize.MessagePackWriter; +import infra.cloud.service.serialize.RequestSerializer; +import infra.cloud.service.serialize.ResponseDeserializer; +import infra.remoting.Payload; +import infra.remoting.RemotingOperations; +import infra.remoting.util.ByteBufPayload; +import infra.util.concurrent.Future; +import infra.util.concurrent.FutureListener; +import infra.util.concurrent.Promise; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.Operators; + +/** + * Invokes service methods by handling different invocation types such as fire-and-forget, + * request-response, response streaming, and duplex streaming. This class manages the + * serialization of requests, deserialization of responses, and interacts with remoting + * operations to execute remote procedure calls (RPC). + * + * @author 海子 Yang + * @since 1.0 2021/7/4 01:58 + */ +public class ServiceMethodInvoker implements ServiceInvoker { + + private final ClientInterceptor[] interceptors; + + private final RemotingOperationsProvider remotingOperationsProvider; + + private final ByteBufAllocator allocator; + + private final RequestSerializer requestSerializer; + + private final ResponseDeserializer responseDeserializer; + + public ServiceMethodInvoker(List interceptors, RemotingOperationsProvider remotingOperationsProvider, + ByteBufAllocator allocator, RequestSerializer requestSerializer, ResponseDeserializer responseDeserializer) { + this.interceptors = interceptors.toArray(new ClientInterceptor[0]); + this.remotingOperationsProvider = remotingOperationsProvider; + this.allocator = allocator; + this.requestSerializer = requestSerializer; + this.responseDeserializer = responseDeserializer; + } + + @Override + public InvocationResult invoke(ServiceInterfaceMethod serviceMethod, Object[] args) throws Throwable { + MethodServiceInvocation invocation = new MethodServiceInvocation0(serviceMethod, args, interceptors); + return invocation.proceed(); + } + + class MethodServiceInvocation0 extends MethodServiceInvocation { + + public MethodServiceInvocation0(ServiceInterfaceMethod serviceMethod, Object[] args, ClientInterceptor[] interceptors) { + super(serviceMethod, args, interceptors); + } + + @Override + protected InvocationResult invokeRemoting() { + RemotingOperations operations = remotingOperationsProvider.getRemotingOperations(getServiceMethod()); + return switch (getType()) { + case FIRE_AND_FORGET -> new FireAndForgetResult(operations.fireAndForget(createMonoPayload())); + case REQUEST_RESPONSE -> new RequestResponseResult(getServiceMethod(), operations.requestResponse(createMonoPayload()), responseDeserializer); + case RESPONSE_STREAMING -> new ResponseStreamingResult(getServiceMethod(), operations.requestStream(createMonoPayload()), responseDeserializer); + case DUPLEX_STREAMING -> new DuplexStreamingResult(operations.requestChannel(createChannelPayload())); + }; + } + + private Mono createMonoPayload() { + return Mono.defer(() -> { + ByteBuf buffer = allocator.ioBuffer(); + requestSerializer.serialize(serviceMethod, getArguments(), new MessagePackWriter(buffer)); + return Mono.just(ByteBufPayload.create(buffer)); + }); + } + + @SuppressWarnings("unchecked") + private Publisher createChannelPayload() { + Flux flux = (Flux) getArguments()[0]; + + return Flux.empty(); + } + + } + + class InvocationResult0 extends AbstractInvocationResult implements Publisher, + Subscriber, FutureListener>, Subscription { + + @Nullable + private Throwable throwable; + + private final InvocationType invocationType; + + private final Publisher payloadPublisher; + + @Nullable + private Promise resultPromise; + + @Nullable + private Subscription payloadSubscription; + + private Subscriber downstream; + + public InvocationResult0(InvocationType invocationType, Publisher publisher) { + this.invocationType = invocationType; + this.payloadPublisher = publisher; + } + + @Nullable + @Override + public Object getBlockingValue() { + return future().join(); + } + + @Override + public boolean isFailed() { + return throwable != null; + } + + @Nullable + @Override + public Throwable getException() { + return throwable; + } + + @Override + public InvocationType getType() { + return invocationType; + } + + @Override + public Future future() { + if (resultPromise == null) { + resultPromise = Future.forPromise(); + } + return resultPromise; + } + + @Override + public Publisher publisher() { + return this; + } + + @Override + public void subscribe(Subscriber downstream) { + this.downstream = downstream; + downstream.onSubscribe(this); + payloadPublisher.subscribe(this); + if (resultPromise != null) { + resultPromise.onCompleted(this); + } + } + + @Override + public void request(long n) { + if (Operators.validate(n) && payloadSubscription != null) { + payloadSubscription.request(n); + } + } + + @Override + public void cancel() { + if (payloadSubscription != null) { + payloadSubscription.cancel(); + payloadSubscription = null; + } + + if (resultPromise != null) { + resultPromise.cancel(); + } + } + + @Override + public void onSubscribe(Subscription s) { + if (Operators.validate(payloadSubscription, s)) { + this.payloadSubscription = s; + } + } + + @Override + public void onNext(Payload payload) { + Object result = deserialize(payload); + downstream.onNext(result); + if (resultPromise != null) { + resultPromise.trySuccess(result); + } + } + + @Override + public void onError(Throwable t) { + downstream.onError(t); + } + + @Override + public void onComplete() { + downstream.onComplete(); + if (resultPromise != null && !resultPromise.isDone()) { + resultPromise.trySuccess(null); + } + } + + @Override + public void operationComplete(Future completed) { + if (completed.isCancelled()) { + if (payloadSubscription != null) { + payloadSubscription.cancel(); + } + } + } + } + +} diff --git a/today-service-client/src/main/java/infra/cloud/service/ServiceProvider.java b/today-service-client/src/main/java/infra/cloud/service/ServiceProvider.java new file mode 100644 index 0000000..42e6055 --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/ServiceProvider.java @@ -0,0 +1,34 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +/** + * @author Harry Yang + * @since 1.0 2023/8/14 21:53 + */ +public interface ServiceProvider { + + /** + * Lookup a service + * + * @param serviceInterface service interface type + * @param Service type + * @return service + */ + T getService(Class serviceInterface); + +} diff --git a/today-service-client/src/main/java/infra/cloud/service/ServiceProxyFactory.java b/today-service-client/src/main/java/infra/cloud/service/ServiceProxyFactory.java new file mode 100644 index 0000000..ae260f3 --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/ServiceProxyFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +/** + * Factory to create a client proxy from a REMOTE service interface + * + * @author 海子 Yang + * @since 2021/7/4 22:58 + */ +public interface ServiceProxyFactory extends ServiceProvider { + + /** + * Return a proxy that implements the given service interface to perform + * requests and retrieve responses through a client. + * + * @param service the service to create a proxy for + * @param the service type + * @return the created proxy + */ + @Override + S getService(Class service); + +} diff --git a/today-service-client/src/main/java/infra/cloud/service/serialize/RequestSerializer.java b/today-service-client/src/main/java/infra/cloud/service/serialize/RequestSerializer.java new file mode 100644 index 0000000..1602742 --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/serialize/RequestSerializer.java @@ -0,0 +1,76 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service.serialize; + +import java.util.List; + +import infra.cloud.serialize.ArgumentSerialization; +import infra.cloud.serialize.Writable; +import infra.cloud.service.ServiceInterfaceMethod; +import infra.core.MethodParameter; + +/** + * Serializer for RPC request. + * + * @author 海子 Yang + * @since 1.0 2024/12/20 15:59 + */ +@SuppressWarnings({ "unchecked", "rawtypes" }) +public class RequestSerializer { + + private final List argumentSerializations; + + public RequestSerializer(List argumentSerializations) { + this.argumentSerializations = argumentSerializations; + } + + @SuppressWarnings("unchecked") + public void serialize(ServiceInterfaceMethod serviceMethod, Object[] arguments, Writable writable) { + writable.write(serviceMethod.getServiceInterface().getName()); + writable.write(serviceMethod.getMethod().getName()); + writable.write(serviceMethod.getParameters(), parameter -> { + writable.write(parameter.getParameterType().getName()); + }); + + int idx = 0; + + beforeSerializeArguments(writable, arguments); + for (MethodParameter parameter : serviceMethod.getParameters()) { + var serialization = findArgumentSerialization(parameter); + serialization.serialize(parameter, arguments[idx++], writable); + } + afterSerializeArguments(writable, arguments); + } + + private ArgumentSerialization findArgumentSerialization(MethodParameter parameter) { + for (var argumentSerialization : argumentSerializations) { + if (argumentSerialization.supportsArgument(parameter)) { + return argumentSerialization; + } + } + throw new IllegalStateException("ArgumentSerialization for parameter %s not found".formatted(parameter)); + } + + protected void afterSerializeArguments(Writable writable, Object[] arguments) { + + } + + protected void beforeSerializeArguments(Writable writable, Object[] arguments) { + + } + +} diff --git a/today-service-client/src/main/java/infra/cloud/service/serialize/ResponseDeserializer.java b/today-service-client/src/main/java/infra/cloud/service/serialize/ResponseDeserializer.java new file mode 100644 index 0000000..2bf82c9 --- /dev/null +++ b/today-service-client/src/main/java/infra/cloud/service/serialize/ResponseDeserializer.java @@ -0,0 +1,64 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service.serialize; + +import org.jspecify.annotations.Nullable; + +import java.util.List; + +import infra.cloud.serialize.MessagePackReader; +import infra.cloud.serialize.ReturnValueDeserializer; +import infra.cloud.serialize.SerializationException; +import infra.cloud.serialize.ThrowableSerialization; +import infra.cloud.service.ServiceInterfaceMethod; +import infra.cloud.service.ServiceMethod; +import io.netty.buffer.ByteBuf; + +/** + * @author 海子 Yang + * @since 1.0 2024/12/20 21:22 + */ +@SuppressWarnings({ "rawtypes" }) +public class ResponseDeserializer { + + private final List serializations; + + private final ThrowableSerialization throwableSerialization; + + public ResponseDeserializer(List serializations, ThrowableSerialization throwableSerialization) { + this.serializations = serializations; + this.throwableSerialization = throwableSerialization; + } + + public @Nullable Object deserialize(ServiceInterfaceMethod method, ByteBuf body) throws SerializationException { + MessagePackReader input = new MessagePackReader(body); + return input.readNullable(in -> { + var deserializer = findDeserializer(method); + return deserializer.deserialize(method, in); + }); + } + + private ReturnValueDeserializer findDeserializer(ServiceMethod method) { + for (ReturnValueDeserializer deserializer : serializations) { + if (deserializer.supportsReturnValue(method)) { + return deserializer; + } + } + throw new IllegalStateException("ReturnValueDeserializer for method %s not found".formatted(method)); + } + +} diff --git a/today-service-client/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports b/today-service-client/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports new file mode 100644 index 0000000..21835ff --- /dev/null +++ b/today-service-client/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports @@ -0,0 +1 @@ +infra.cloud.client.annotation.config.ServiceClientAutoConfiguration \ No newline at end of file diff --git a/today-service-client/src/main/resources/META-INF/today.strategies b/today-service-client/src/main/resources/META-INF/today.strategies index fbb92ce..70a677a 100644 --- a/today-service-client/src/main/resources/META-INF/today.strategies +++ b/today-service-client/src/main/resources/META-INF/today.strategies @@ -1,25 +1,9 @@ -# -# Copyright 2021 - 2024 the original author or authors. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see [http://www.gnu.org/licenses/] -# infra.beans.factory.support.DependencyResolvingStrategy=\ - infra.remoting.ServiceDependencyResolvingStrategy + infra.cloud.client.annotation.ServiceDependencyResolvingStrategy -infra.cloud.serialize.RpcArgumentSerialization=\ - infra.cloud.serialize.ProtobufArgumentSerialization,\ - infra.cloud.serialize.SimpleValueArgumentSerialization +infra.cloud.serialize.ArgumentSerialization=\ + infra.cloud.serialize.support.ProtobufArgumentSerialization,\ + infra.cloud.serialize.support.SimpleValueArgumentSerialization diff --git a/today-service-client/src/test/java/infra/cloud/client/annotation/config/ServiceClientAutoConfigurationTests.java b/today-service-client/src/test/java/infra/cloud/client/annotation/config/ServiceClientAutoConfigurationTests.java new file mode 100644 index 0000000..adf2faf --- /dev/null +++ b/today-service-client/src/test/java/infra/cloud/client/annotation/config/ServiceClientAutoConfigurationTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client.annotation.config; + +import org.junit.jupiter.api.Test; + +import cn.taketoday.demo.service.UserService; +import infra.app.test.context.InfraTest; +import infra.beans.factory.annotation.Autowired; +import infra.cloud.service.ServiceProxyFactory; +import infra.context.annotation.Configuration; +import infra.context.annotation.config.EnableAutoConfiguration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/9 22:20 + */ +@InfraTest +class ServiceClientAutoConfigurationTests { + + @Autowired + ServiceProxyFactory serviceProxyFactory; + + @Test + void serviceProxyFactory() { + UserService userService = serviceProxyFactory.getService(UserService.class); + assertThat(userService).isNotNull(); + } + + @EnableAutoConfiguration + @Configuration(proxyBeanMethods = false) + public static class Config { + + } + +} \ No newline at end of file diff --git a/today-service-client/src/test/java/infra/cloud/service/DefaultServiceInterfaceMetadataProviderTests.java b/today-service-client/src/test/java/infra/cloud/service/DefaultServiceInterfaceMetadataProviderTests.java new file mode 100644 index 0000000..2e84707 --- /dev/null +++ b/today-service-client/src/test/java/infra/cloud/service/DefaultServiceInterfaceMetadataProviderTests.java @@ -0,0 +1,111 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import infra.core.ResolvableType; +import infra.util.concurrent.Future; +import reactor.core.publisher.Flux; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/10 16:56 + */ +class DefaultServiceInterfaceMetadataProviderTests { + + @Test + void getMetadata() { + + ServiceMetadataProvider serviceMetadataProvider = serviceInterface -> { + return new ServiceMetadata("demo-user-service", "1.0"); + }; + + var metadataProvider = new DefaultServiceInterfaceMetadataProvider(serviceMetadataProvider, List.of()); + + var metadata = metadataProvider.getMetadata(DemoUserService.class); + List serviceMethods = metadata.getServiceMethods(); + + assertThat(metadata.getServiceMetadata().getVersion()).isEqualTo("1.0"); + assertThat(metadata.getServiceMetadata().getId()).isEqualTo("demo-user-service"); + assertThat(metadata.getServiceInterface()).isEqualTo(DemoUserService.class); + + assertThat(serviceMethods).hasSize(4); + assertThatThrownBy(() -> serviceMethods.remove(1)) + .isInstanceOf(UnsupportedOperationException.class); + + Map methodMap = serviceMethods.stream().collect(Collectors.toMap(me -> me.getMethod().getName(), me -> me)); + + ServiceInterfaceMethod getById = methodMap.get("getById"); + assertThat(getById.getServiceInterface()).isSameAs(metadata.getServiceInterface()); + assertThat(getById.getInvocationType()).isEqualTo(InvocationType.REQUEST_RESPONSE); + assertThat(getById.getParameters().length).isEqualTo(1); + assertThat(getById.getReturnType().getParameterType()).isEqualTo(User.class); + + ServiceInterfaceMethod listUsers = methodMap.get("listUsers"); + assertThat(listUsers.getInvocationType()).isEqualTo(InvocationType.REQUEST_RESPONSE); + assertThat(listUsers.getParameters().length).isEqualTo(0); + assertThat(listUsers.getReturnType().getParameterType()).isEqualTo(List.class); + assertThat(ResolvableType.forMethodParameter(listUsers.getReturnType()).getGeneric().resolve()).isEqualTo(User.class); + + ServiceInterfaceMethod listUsersFuture = methodMap.get("listUsersFuture"); + assertThat(listUsersFuture.getInvocationType()).isEqualTo(InvocationType.REQUEST_RESPONSE); + assertThat(listUsersFuture.getParameters().length).isEqualTo(0); + assertThat(listUsersFuture.getReturnType().getParameterType()).isEqualTo(Future.class); + + assertThat(listUsersFuture.getInvocationType().clientSendsOneMessage()).isTrue(); + assertThat(listUsersFuture.getInvocationType().serverSendsOneMessage()).isTrue(); + + ResolvableType resolvableType = ResolvableType.forMethodParameter(listUsersFuture.getReturnType()); + assertThat(resolvableType.getGeneric().resolve()).isEqualTo(List.class); + assertThat(resolvableType.getGeneric().getGeneric().resolve()).isEqualTo(User.class); + // + + ServiceInterfaceMethod listUsersFlux = methodMap.get("listUsersFlux"); + assertThat(listUsersFlux.getInvocationType()).isEqualTo(InvocationType.RESPONSE_STREAMING); + assertThat(listUsersFlux.getParameters().length).isEqualTo(0); + assertThat(listUsersFlux.getReturnType().getParameterType()).isEqualTo(Flux.class); + assertThat(ResolvableType.forMethodParameter(listUsers.getReturnType()).getGeneric().resolve()).isEqualTo(User.class); + + assertThat(listUsersFlux.getInvocationType().clientSendsOneMessage()).isTrue(); + assertThat(listUsersFlux.getInvocationType().serverSendsOneMessage()).isFalse(); + + } + + interface DemoUserService { + + User getById(int id); + + List listUsers(); + + Future> listUsersFuture(); + + Flux listUsersFlux(); + } + + static class User { + + } + +} \ No newline at end of file diff --git a/today-service-client/src/test/java/infra/cloud/service/ServiceInvokerFactoryTests.java b/today-service-client/src/test/java/infra/cloud/service/ServiceInvokerFactoryTests.java new file mode 100644 index 0000000..0c31a7e --- /dev/null +++ b/today-service-client/src/test/java/infra/cloud/service/ServiceInvokerFactoryTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import org.junit.jupiter.api.Test; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/9 20:43 + */ +class ServiceInvokerFactoryTests { + + @Test + void create() { + +// ServiceInvokerFactory factory = new ServiceInvokerFactory(new SimpleDiscoveryClient(new SimpleDiscoveryProperties())); +// ServiceInvoker serviceInvoker = factory.create(UserService.class); +// assertThat(serviceInvoker).isNotNull(); + } + +} \ No newline at end of file diff --git a/today-service-client/src/test/java/infra/cloud/service/TracingInterceptor.java b/today-service-client/src/test/java/infra/cloud/service/TracingInterceptor.java new file mode 100644 index 0000000..0c3fb49 --- /dev/null +++ b/today-service-client/src/test/java/infra/cloud/service/TracingInterceptor.java @@ -0,0 +1,45 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.service; + +import java.util.Arrays; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/13 16:56 + */ +class TracingInterceptor implements ClientInterceptor { + + @Override + public InvocationResult intercept(ServiceInvocation invocation) throws Throwable { + System.out.printf("before service %s with args %s%n", invocation, Arrays.toString(invocation.getArguments())); + InvocationResult result = invocation.proceed(); + + if (result.isRequestResponse()) { + result.future().onCompleted(future -> { + if (future.isSuccess()) { + System.out.printf("after service %s returns %s%n", invocation, future.getNow()); + } + else { + System.out.printf("service %s failed %s%n", invocation, future.getCause()); + } + }); + } + return result; + } + +} diff --git a/today-service-discovery/build.gradle b/today-service-discovery/build.gradle new file mode 100644 index 0000000..4074e82 --- /dev/null +++ b/today-service-discovery/build.gradle @@ -0,0 +1,12 @@ +description = "TODAY Service discovery client" + +dependencies { + api project(":today-cloud-core") + + implementation 'cn.taketoday:infra-beans' + implementation 'cn.taketoday:infra-context' + + testImplementation 'cn.taketoday:infra-app-test' +// testImplementation project(":today-cloud-samples:demo-tests:demo-user-api") + +} \ No newline at end of file diff --git a/today-service-discovery/src/main/java/infra/cloud/client/CompositeDiscoveryClient.java b/today-service-discovery/src/main/java/infra/cloud/client/CompositeDiscoveryClient.java new file mode 100644 index 0000000..0119855 --- /dev/null +++ b/today-service-discovery/src/main/java/infra/cloud/client/CompositeDiscoveryClient.java @@ -0,0 +1,88 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; + +import infra.lang.Assert; +import infra.util.CollectionUtils; + +/** + * A {@link DiscoveryClient} that is composed of other discovery clients and delegates + * calls to each of them in order. + * + * @author 海子 Yang + * @since 1.0 2025/8/8 14:21 + */ +public class CompositeDiscoveryClient implements DiscoveryClient { + + private final List discoveryClients; + + public CompositeDiscoveryClient(List discoveryClients) { + Assert.notEmpty(discoveryClients, "discoveryClients must not be empty"); + this.discoveryClients = discoveryClients; + } + + @Override + public String getDescription() { + return "Composite Discovery Client"; + } + + @Override + public List getInstances(String serviceId) { + if (this.discoveryClients != null) { + for (DiscoveryClient discoveryClient : this.discoveryClients) { + List instances = discoveryClient.getInstances(serviceId); + if (CollectionUtils.isNotEmpty(instances)) { + return instances; + } + } + } + return Collections.emptyList(); + } + + @Override + public List getServices() { + LinkedHashSet services = new LinkedHashSet<>(); + if (this.discoveryClients != null) { + for (DiscoveryClient discoveryClient : this.discoveryClients) { + List serviceForClient = discoveryClient.getServices(); + if (serviceForClient != null) { + services.addAll(serviceForClient); + } + } + } + return new ArrayList<>(services); + } + + @Override + public void probe() { + if (this.discoveryClients != null) { + for (DiscoveryClient discoveryClient : this.discoveryClients) { + discoveryClient.probe(); + } + } + } + + public List getDiscoveryClients() { + return discoveryClients; + } + +} diff --git a/today-service-discovery/src/main/java/infra/cloud/client/DiscoveryClient.java b/today-service-discovery/src/main/java/infra/cloud/client/DiscoveryClient.java new file mode 100644 index 0000000..908cc5a --- /dev/null +++ b/today-service-discovery/src/main/java/infra/cloud/client/DiscoveryClient.java @@ -0,0 +1,82 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client; + +import java.util.List; + +import infra.core.Ordered; +import infra.lang.Descriptive; + +/** + * Represents read operations commonly available to discovery + * services such as Netflix Eureka or consul.io. + * + * @author Harry Yang + * @since 1.0 + */ +public interface DiscoveryClient extends Descriptive, Ordered { + + /** + * Default order of the discovery client. + */ + int DEFAULT_ORDER = 0; + + /** + * A human-readable description of the implementation. + * + * @return The description. + */ + @Override + String getDescription(); + + /** + * Gets all ServiceInstances associated with a particular serviceId. + * + * @param serviceId The serviceId to query. + * @return A List of ServiceInstance. + */ + List getInstances(String serviceId); + + /** + * @return All known service IDs. + */ + List getServices(); + + /** + * Can be used to verify the client is valid and able to make calls. + *

    + * A successful invocation with no exception thrown implies the client is able to make + * calls. + *

    + * The default implementation simply calls {@link #getServices()} - client + * implementations can override with a lighter weight operation if they choose to. + */ + default void probe() { + getServices(); + } + + /** + * Default implementation for getting order of discovery clients. + * + * @return order + */ + @Override + default int getOrder() { + return DEFAULT_ORDER; + } + +} diff --git a/today-service-discovery/src/main/java/infra/cloud/client/config/DiscoveryClientAutoConfiguration.java b/today-service-discovery/src/main/java/infra/cloud/client/config/DiscoveryClientAutoConfiguration.java new file mode 100644 index 0000000..aa7ed2e --- /dev/null +++ b/today-service-discovery/src/main/java/infra/cloud/client/config/DiscoveryClientAutoConfiguration.java @@ -0,0 +1,57 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client.config; + +import infra.beans.factory.ObjectProvider; +import infra.cloud.client.CompositeDiscoveryClient; +import infra.cloud.client.DiscoveryClient; +import infra.cloud.client.annotation.ConditionalOnDiscoveryEnabled; +import infra.cloud.client.simple.SimpleDiscoveryClient; +import infra.cloud.client.simple.SimpleDiscoveryProperties; +import infra.context.annotation.MissingBean; +import infra.context.annotation.Primary; +import infra.context.annotation.config.DisableDIAutoConfiguration; +import infra.context.properties.EnableConfigurationProperties; +import infra.stereotype.Component; + +/** + * Auto-configuration for discovery client. + * + * @author Biju Kunjummen + * @author 海子 Yang + */ +@DisableDIAutoConfiguration +@ConditionalOnDiscoveryEnabled +@EnableConfigurationProperties(SimpleDiscoveryProperties.class) +public class DiscoveryClientAutoConfiguration { + + @Primary + @Component + public static DiscoveryClient primaryDiscoveryClient(ObjectProvider discoveryClients) { + var discoveryClientList = discoveryClients.orderedList(); + if (discoveryClientList.size() == 1) { + return discoveryClientList.get(0); + } + return new CompositeDiscoveryClient(discoveryClientList); + } + + @MissingBean + public static SimpleDiscoveryClient simpleDiscoveryClient(SimpleDiscoveryProperties properties) { + return new SimpleDiscoveryClient(properties); + } + +} diff --git a/today-service-discovery/src/main/java/infra/cloud/client/simple/SimpleDiscoveryClient.java b/today-service-discovery/src/main/java/infra/cloud/client/simple/SimpleDiscoveryClient.java new file mode 100644 index 0000000..d70a6fb --- /dev/null +++ b/today-service-discovery/src/main/java/infra/cloud/client/simple/SimpleDiscoveryClient.java @@ -0,0 +1,68 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client.simple; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import infra.cloud.client.DiscoveryClient; +import infra.cloud.client.ServiceInstance; + +/** + * A {@link infra.cloud.client.DiscoveryClient} that will use the + * properties file as a source of service instances. + * + * @author Biju Kunjummen + * @author Olga Maciaszek-Sharma + * @author Charu Covindane + * @author 海子 Yang + */ +public class SimpleDiscoveryClient implements DiscoveryClient { + + private final SimpleDiscoveryProperties properties; + + public SimpleDiscoveryClient(SimpleDiscoveryProperties properties) { + this.properties = properties; + } + + @Override + public String getDescription() { + return "Simple Discovery Client"; + } + + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + public List getInstances(String serviceId) { + List instances = properties.getInstances().get(serviceId); + if (instances != null) { + return Collections.unmodifiableList(instances); + } + return Collections.emptyList(); + } + + @Override + public List getServices() { + return new ArrayList<>(properties.getInstances().keySet()); + } + + @Override + public int getOrder() { + return properties.getOrder(); + } + +} diff --git a/today-service-discovery/src/main/java/infra/cloud/client/simple/SimpleDiscoveryProperties.java b/today-service-discovery/src/main/java/infra/cloud/client/simple/SimpleDiscoveryProperties.java new file mode 100644 index 0000000..8727b3a --- /dev/null +++ b/today-service-discovery/src/main/java/infra/cloud/client/simple/SimpleDiscoveryProperties.java @@ -0,0 +1,62 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client.simple; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import infra.beans.factory.InitializingBean; +import infra.cloud.client.DefaultServiceInstance; +import infra.context.properties.ConfigurationProperties; +import infra.core.OrderedSupport; + +/** + * Properties to hold the details of a {@link infra.cloud.client.DiscoveryClient} + * service instances for a given service. It also holds the user-configurable order + * that will be used to establish the precedence of this client in the list of clients + * used by {@link infra.cloud.client.CompositeDiscoveryClient}. + * + * @author Biju Kunjummen + * @author Olga Maciaszek-Sharma + * @author Tim Ysewyn + * @author Charu Covindane + * @author 海子 Yang + */ +@ConfigurationProperties(prefix = "infra.cloud.discovery.simple") +public class SimpleDiscoveryProperties extends OrderedSupport implements InitializingBean { + + private Map> instances = new HashMap<>(); + + public Map> getInstances() { + return this.instances; + } + + public void setInstances(Map> instances) { + this.instances = instances; + } + + @Override + public void afterPropertiesSet() { + for (String key : this.instances.keySet()) { + for (DefaultServiceInstance instance : this.instances.get(key)) { + instance.setServiceId(key); + } + } + } + +} diff --git a/today-service-discovery/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports b/today-service-discovery/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports new file mode 100644 index 0000000..78effe7 --- /dev/null +++ b/today-service-discovery/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports @@ -0,0 +1 @@ +infra.cloud.client.config.DiscoveryClientAutoConfiguration \ No newline at end of file diff --git a/today-service-discovery/src/main/resources/META-INF/today.strategies b/today-service-discovery/src/main/resources/META-INF/today.strategies new file mode 100644 index 0000000..e69de29 diff --git a/today-service-discovery/src/test/java/infra/cloud/client/CompositeDiscoveryClientAutoConfigurationTests.java b/today-service-discovery/src/test/java/infra/cloud/client/CompositeDiscoveryClientAutoConfigurationTests.java new file mode 100644 index 0000000..1ef5a88 --- /dev/null +++ b/today-service-discovery/src/test/java/infra/cloud/client/CompositeDiscoveryClientAutoConfigurationTests.java @@ -0,0 +1,88 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import infra.app.test.context.InfraTest; +import infra.beans.factory.annotation.Autowired; +import infra.cloud.client.simple.SimpleDiscoveryClient; +import infra.context.annotation.Bean; +import infra.context.annotation.Configuration; +import infra.context.annotation.config.EnableAutoConfiguration; + +import static org.assertj.core.api.BDDAssertions.then; + +/** + * Composite Discovery Client should be the one found by default. + * + * @author Biju Kunjummen + */ +@InfraTest +class CompositeDiscoveryClientAutoConfigurationTests { + + @Autowired + private DiscoveryClient discoveryClient; + + @Test + void compositeDiscoveryClientShouldBeTheDefault() { + then(this.discoveryClient).isInstanceOf(CompositeDiscoveryClient.class); + CompositeDiscoveryClient compositeDiscoveryClient = (CompositeDiscoveryClient) this.discoveryClient; + then(compositeDiscoveryClient.getDiscoveryClients()).hasSize(2); + then(compositeDiscoveryClient.getDiscoveryClients().get(0).getDescription()) + .isEqualTo("A custom discovery client"); + } + + @Test + void simpleDiscoveryClientShouldBeHaveTheLowestPrecedence() { + CompositeDiscoveryClient compositeDiscoveryClient = (CompositeDiscoveryClient) this.discoveryClient; + then(compositeDiscoveryClient.getDiscoveryClients().get(0).getDescription()) + .isEqualTo("A custom discovery client"); + then(compositeDiscoveryClient.getDiscoveryClients().get(1)).isInstanceOf(SimpleDiscoveryClient.class); + } + + @EnableAutoConfiguration + @Configuration(proxyBeanMethods = false) + public static class Config { + + @Bean + public DiscoveryClient customDiscoveryClient1() { + return new DiscoveryClient() { + + @Override + public String getDescription() { + return "A custom discovery client"; + } + + @Override + public List getInstances(String serviceId) { + return null; + } + + @Override + public List getServices() { + return null; + } + + }; + } + + } + +} diff --git a/today-service-discovery/src/test/java/infra/cloud/client/CompositeDiscoveryClientOrderTest.java b/today-service-discovery/src/test/java/infra/cloud/client/CompositeDiscoveryClientOrderTest.java new file mode 100644 index 0000000..59fa14c --- /dev/null +++ b/today-service-discovery/src/test/java/infra/cloud/client/CompositeDiscoveryClientOrderTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import infra.app.test.context.InfraTest; +import infra.beans.factory.annotation.Autowired; + +import static infra.cloud.client.CompositeDiscoveryClientTestsConfig.CUSTOM_DISCOVERY_CLIENT; +import static infra.cloud.client.CompositeDiscoveryClientTestsConfig.CUSTOM_SERVICE_ID; +import static infra.cloud.client.CompositeDiscoveryClientTestsConfig.DEFAULT_ORDER_DISCOVERY_CLIENT; +import static infra.cloud.client.CompositeDiscoveryClientTestsConfig.FOURTH_DISCOVERY_CLIENT; +import static org.assertj.core.api.BDDAssertions.then; + +/** + * Tests for the support of ordered {@link DiscoveryClient} instances in + * {@link CompositeDiscoveryClient}. + * + * @author Olga Maciaszek-Sharma + */ +@InfraTest(properties = "infra.cloud.discovery.simple.order=2", + classes = CompositeDiscoveryClientTestsConfig.class) +class CompositeDiscoveryClientOrderTest { + + @Autowired + CompositeDiscoveryClient discoveryClient; + + @Test + void shouldGetOrderedDiscoveryClients() { + List discoveryClients = this.discoveryClient.getDiscoveryClients(); + + then(discoveryClients.get(0).getDescription()).isEqualTo(CUSTOM_DISCOVERY_CLIENT); + then(discoveryClients.get(1).getDescription()).isEqualTo(DEFAULT_ORDER_DISCOVERY_CLIENT); + then(discoveryClients.get(2).getDescription()).isEqualTo("Simple Discovery Client"); + then(discoveryClients.get(3).getDescription()).isEqualTo(FOURTH_DISCOVERY_CLIENT); + } + + @Test + void shouldOnlyReturnServiceInstancesForTheHighestPrecedenceDiscoveryClient() { + List serviceInstances = this.discoveryClient.getInstances(CUSTOM_SERVICE_ID); + + then(serviceInstances).hasSize(1); + then(serviceInstances.get(0).getPort()).isEqualTo(123); + } + +} diff --git a/today-service-discovery/src/test/java/infra/cloud/client/CompositeDiscoveryClientTests.java b/today-service-discovery/src/test/java/infra/cloud/client/CompositeDiscoveryClientTests.java new file mode 100644 index 0000000..a0e1dd9 --- /dev/null +++ b/today-service-discovery/src/test/java/infra/cloud/client/CompositeDiscoveryClientTests.java @@ -0,0 +1,76 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client; + +import org.junit.jupiter.api.Test; + +import infra.app.test.context.InfraTest; +import infra.beans.factory.annotation.Autowired; + +import static infra.cloud.client.CompositeDiscoveryClientTestsConfig.CUSTOM_SERVICE_ID; +import static org.assertj.core.api.BDDAssertions.then; + +/** + * Tests for behavior of Composite Discovery Client. + * + * @author Biju Kunjummen + */ +@InfraTest(properties = { "app.name=service0", + "infra.cloud.discovery.simple.instances.service1[0].uri=http://s11:8080", + "infra.cloud.discovery.simple.instances.service1[1].uri=https://s12:8443", + "infra.cloud.discovery.simple.instances.service2[0].uri=https://s21:8080", + "infra.cloud.discovery.simple.instances.service2[1].uri=https://s22:443" }, + classes = CompositeDiscoveryClientTestsConfig.class) +class CompositeDiscoveryClientTests { + + @Autowired + private DiscoveryClient discoveryClient; + + @Test + void getInstancesByServiceIdShouldDelegateCall() { + then(this.discoveryClient).isInstanceOf(CompositeDiscoveryClient.class); + + then(this.discoveryClient.getInstances("service1")).hasSize(2); + + ServiceInstance s1 = this.discoveryClient.getInstances("service1").get(0); + then(s1.getHost()).isEqualTo("s11"); + then(s1.getPort()).isEqualTo(8080); + then(s1.isSecure()).isEqualTo(false); + } + + @Test + void getServicesShouldAggregateAllServiceNames() { + then(this.discoveryClient.getServices()).containsOnlyOnce("service1", "service2", "custom"); + } + + @Test + void getDescriptionShouldBeComposite() { + then(this.discoveryClient.getDescription()).isEqualTo("Composite Discovery Client"); + } + + @Test + void getInstancesShouldRespectOrder() { + then(this.discoveryClient.getInstances(CUSTOM_SERVICE_ID)).hasSize(1); + then(this.discoveryClient.getInstances(CUSTOM_SERVICE_ID)).hasSize(1); + } + + @Test + void getInstancesByUnknownServiceIdShouldReturnAnEmptyList() { + then(this.discoveryClient.getInstances("unknown")).hasSize(0); + } + +} diff --git a/today-service-discovery/src/test/java/infra/cloud/client/CompositeDiscoveryClientTestsConfig.java b/today-service-discovery/src/test/java/infra/cloud/client/CompositeDiscoveryClientTestsConfig.java new file mode 100644 index 0000000..572244a --- /dev/null +++ b/today-service-discovery/src/test/java/infra/cloud/client/CompositeDiscoveryClientTestsConfig.java @@ -0,0 +1,92 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client; + +import java.util.Collections; +import java.util.List; + +import infra.context.annotation.Bean; +import infra.context.annotation.Configuration; +import infra.context.annotation.config.EnableAutoConfiguration; + +import static java.util.Collections.singletonList; + +/** + * Test configuration for {@link CompositeDiscoveryClient} tests. + * + * @author Olga Maciaszek-Sharma + * @author Tim Ysewyn + */ +@EnableAutoConfiguration +@Configuration(proxyBeanMethods = false) +public class CompositeDiscoveryClientTestsConfig { + + static final String DEFAULT_ORDER_DISCOVERY_CLIENT = "Default order discovery client"; + static final String CUSTOM_DISCOVERY_CLIENT = "A custom discovery client"; + static final String FOURTH_DISCOVERY_CLIENT = "Fourth discovery client"; + static final String CUSTOM_SERVICE_ID = "custom"; + + @Bean + public DiscoveryClient customDiscoveryClient() { + return aDiscoveryClient(-1, CUSTOM_DISCOVERY_CLIENT); + } + + @Bean + public DiscoveryClient thirdOrderCustomDiscoveryClient() { + return aDiscoveryClient(3, FOURTH_DISCOVERY_CLIENT); + } + + @Bean + public DiscoveryClient defaultOrderDiscoveryClient() { + return aDiscoveryClient(null, DEFAULT_ORDER_DISCOVERY_CLIENT); + } + + private DiscoveryClient aDiscoveryClient(Integer order, String description) { + class TestDiscoveryClient implements DiscoveryClient { + @Override + public String getDescription() { + return description; + } + + @Override + public List getInstances(String serviceId) { + if (serviceId.equals(CUSTOM_SERVICE_ID)) { + ServiceInstance s1 = new DefaultServiceInstance("customInstance", CUSTOM_SERVICE_ID, "host", 123, false); + return List.of(s1); + } + return Collections.emptyList(); + } + + @Override + public List getServices() { + return singletonList(CUSTOM_SERVICE_ID); + } + + @Override + public int getOrder() { + return order != null ? order : DiscoveryClient.super.getOrder(); + } + + @Override + public String toString() { + return description; + } + } + return new TestDiscoveryClient(); + } + +} diff --git a/today-service-discovery/src/test/java/infra/cloud/client/CompositeDiscoveryClientUnitTests.java b/today-service-discovery/src/test/java/infra/cloud/client/CompositeDiscoveryClientUnitTests.java new file mode 100644 index 0000000..1e1d376 --- /dev/null +++ b/today-service-discovery/src/test/java/infra/cloud/client/CompositeDiscoveryClientUnitTests.java @@ -0,0 +1,97 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.BDDAssertions.then; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * Mockito tests for Composite Discovery Client + * + * @author Sean Ruffatti + */ +@ExtendWith(MockitoExtension.class) +class CompositeDiscoveryClientUnitTests { + + private CompositeDiscoveryClient underTest; + + @Mock + private DiscoveryClient client1; + + @Mock + private DiscoveryClient client2; + + @BeforeEach + void setUp() { + underTest = new CompositeDiscoveryClient(Arrays.asList(client1, client2)); + } + + @Test + void shouldRetrieveInstancesByServiceId() { + ServiceInstance serviceInstance1 = new DefaultServiceInstance("instance1", "serviceId", "https://s1", 8443, + true); + when(client1.getInstances("serviceId")).thenReturn(Collections.singletonList(serviceInstance1)); + + List serviceInstances = underTest.getInstances("serviceId"); + + then(serviceInstances.get(0).getInstanceId()).isEqualTo("instance1"); + then(serviceInstances.get(0).getServiceId()).isEqualTo("serviceId"); + then(serviceInstances.get(0).getHost()).isEqualTo("https://s1"); + then(serviceInstances.get(0).getPort()).isEqualTo(8443); + } + + @Test + void shouldReturnServiceIds() { + when(client1.getServices()).thenReturn(Collections.singletonList("serviceId1")); + when(client2.getServices()).thenReturn(Collections.singletonList("serviceId2")); + + List services = underTest.getServices(); + + then(services.size()).isEqualTo(2); + then(services).containsOnlyOnce("serviceId1", "serviceId2"); + } + + @Test + void shouldReturnAllDiscoveryClients() { + then(underTest.getDiscoveryClients()).containsOnlyOnce(client1, client2); + } + + @Test + void shouldCallProbeOnAllDiscoveryClients() { + underTest.probe(); + + // Every DiscoveryClient bean should invoke DiscoveryClient.probe() when + // CompositeDiscoveryClient.probe() is invoked. + verify(client1, times(1)).probe(); + verify(client1, times(0)).getServices(); + verify(client2, times(1)).probe(); + verify(client2, times(0)).getServices(); + } + +} diff --git a/today-service-discovery/src/test/java/infra/cloud/client/DiscoveryClientAutoConfigurationDefaultTests.java b/today-service-discovery/src/test/java/infra/cloud/client/DiscoveryClientAutoConfigurationDefaultTests.java new file mode 100644 index 0000000..b202dfd --- /dev/null +++ b/today-service-discovery/src/test/java/infra/cloud/client/DiscoveryClientAutoConfigurationDefaultTests.java @@ -0,0 +1,51 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client; + +import org.junit.jupiter.api.Test; + +import infra.app.test.context.InfraTest; +import infra.beans.factory.annotation.Autowired; +import infra.cloud.client.simple.SimpleDiscoveryClient; +import infra.context.annotation.Configuration; +import infra.context.annotation.config.EnableAutoConfiguration; + +import static org.assertj.core.api.BDDAssertions.then; + +/** + * DiscoveryClient implementation defaults to {@link CompositeDiscoveryClient}. + * + * @author Biju Kunjummen + */ +@InfraTest(classes = DiscoveryClientAutoConfigurationDefaultTests.Config.class) +class DiscoveryClientAutoConfigurationDefaultTests { + + @Autowired + private DiscoveryClient discoveryClient; + + @Test + void simpleDiscoveryClientShouldBeTheDefault() { + then(this.discoveryClient).isInstanceOf(SimpleDiscoveryClient.class); + } + + @EnableAutoConfiguration + @Configuration(proxyBeanMethods = false) + public static class Config { + + } + +} diff --git a/today-service-discovery/src/test/java/infra/cloud/client/SimpleDiscoveryClientPropertiesMappingTests.java b/today-service-discovery/src/test/java/infra/cloud/client/SimpleDiscoveryClientPropertiesMappingTests.java new file mode 100644 index 0000000..f145c3f --- /dev/null +++ b/today-service-discovery/src/test/java/infra/cloud/client/SimpleDiscoveryClientPropertiesMappingTests.java @@ -0,0 +1,90 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client; + +import org.junit.jupiter.api.Test; + +import infra.app.test.context.InfraTest; +import infra.beans.factory.annotation.Autowired; +import infra.cloud.client.simple.SimpleDiscoveryClient; +import infra.cloud.client.simple.SimpleDiscoveryProperties; +import infra.context.annotation.Configuration; +import infra.context.annotation.config.EnableAutoConfiguration; + +import static org.assertj.core.api.BDDAssertions.then; + +/** + * Tests for mapping properties to instances in {@link SimpleDiscoveryClient}. + * + * @author Biju Kunjummen + */ +@InfraTest(properties = { "app.name=service0", + "infra.cloud.discovery.simple.instances.service1[0].uri=http://s11:8080", + "infra.cloud.discovery.simple.instances.service1[1].uri=https://s12:8443", + "infra.cloud.discovery.simple.instances.service2[0].uri=https://s21:8080", + "infra.cloud.discovery.simple.instances.service2[1].uri=https://s22:443" }) +class SimpleDiscoveryClientPropertiesMappingTests { + + @Autowired + private SimpleDiscoveryProperties props; + + @Autowired + private SimpleDiscoveryClient discoveryClient; + + @Test + void propsShouldGetCleanlyMapped() { + then(this.props.getInstances().size()).isEqualTo(2); + then(this.props.getInstances().get("service1").size()).isEqualTo(2); + then(this.props.getInstances().get("service1").get(0).getHost()).isEqualTo("s11"); + then(this.props.getInstances().get("service1").get(0).getPort()).isEqualTo(8080); + then(this.props.getInstances().get("service1").get(0).isSecure()).isEqualTo(false); + + then(this.props.getInstances().get("service2").size()).isEqualTo(2); + then(this.props.getInstances().get("service2").get(0).getHost()).isEqualTo("s21"); + then(this.props.getInstances().get("service2").get(0).getPort()).isEqualTo(8080); + then(this.props.getInstances().get("service2").get(0).isSecure()).isEqualTo(true); + } + + @Test + void testDiscoveryClientShouldResolveSimpleValues() { + then(this.discoveryClient.getDescription()).isEqualTo("Simple Discovery Client"); + then(this.discoveryClient.getInstances("service1")).hasSize(2); + + ServiceInstance s1 = this.discoveryClient.getInstances("service1").get(0); + then(s1.getHost()).isEqualTo("s11"); + then(s1.getPort()).isEqualTo(8080); + then(s1.isSecure()).isEqualTo(false); + } + + @Test + void testGetServices() { + then(this.discoveryClient.getServices()).containsExactlyInAnyOrder("service1", "service2"); + } + + @Test + void testGetANonExistentServiceShouldReturnAnEmptyList() { + then(this.discoveryClient.getInstances("nonexistent")).isNotNull(); + then(this.discoveryClient.getInstances("nonexistent")).isEmpty(); + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration + public static class SampleConfig { + + } + +} diff --git a/today-service-discovery/src/test/java/infra/cloud/client/SimpleDiscoveryClientTests.java b/today-service-discovery/src/test/java/infra/cloud/client/SimpleDiscoveryClientTests.java new file mode 100644 index 0000000..cdcef57 --- /dev/null +++ b/today-service-discovery/src/test/java/infra/cloud/client/SimpleDiscoveryClientTests.java @@ -0,0 +1,77 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.client; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import infra.cloud.client.simple.SimpleDiscoveryClient; +import infra.cloud.client.simple.SimpleDiscoveryProperties; + +import static org.assertj.core.api.BDDAssertions.then; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/8 20:58 + */ +class SimpleDiscoveryClientTests { + + private SimpleDiscoveryClient simpleDiscoveryClient; + + @BeforeEach + void setUp() { + SimpleDiscoveryProperties simpleDiscoveryProperties = new SimpleDiscoveryProperties(); + + Map> map = new HashMap<>(); + DefaultServiceInstance service1Inst1 = new DefaultServiceInstance(null, null, "host1", 8080, false); + DefaultServiceInstance service1Inst2 = new DefaultServiceInstance(null, null, "host2", 0, true); + DefaultServiceInstance service1Inst3 = new DefaultServiceInstance(null, null, "host3", 0, false); + map.put("service1", Arrays.asList(service1Inst1, service1Inst2, service1Inst3)); + simpleDiscoveryProperties.setInstances(map); + simpleDiscoveryProperties.afterPropertiesSet(); + this.simpleDiscoveryClient = new SimpleDiscoveryClient(simpleDiscoveryProperties); + } + + @Test + void shouldBeAbleToRetrieveServiceDetailsByName() { + List instances = this.simpleDiscoveryClient.getInstances("service1"); + then(instances.size()).isEqualTo(3); + then(instances.get(0).getServiceId()).isEqualTo("service1"); + then(instances.get(0).getHost()).isEqualTo("host1"); + then(instances.get(0).getPort()).isEqualTo(8080); + then(instances.get(0).isSecure()).isEqualTo(false); + then(instances.get(0).getMetadata()).isNotNull(); + + then(instances.get(1).getServiceId()).isEqualTo("service1"); + then(instances.get(1).getHost()).isEqualTo("host2"); + then(instances.get(1).getPort()).isEqualTo(0); + then(instances.get(1).isSecure()).isEqualTo(true); + then(instances.get(1).getMetadata()).isNotNull(); + + then(instances.get(2).getServiceId()).isEqualTo("service1"); + then(instances.get(2).getHost()).isEqualTo("host3"); + then(instances.get(2).getPort()).isEqualTo(0); + then(instances.get(2).isSecure()).isEqualTo(false); + then(instances.get(2).getMetadata()).isNotNull(); + } + +} \ No newline at end of file diff --git a/today-service-provider/build.gradle b/today-service-provider/build.gradle index 68652e3..956e011 100644 --- a/today-service-provider/build.gradle +++ b/today-service-provider/build.gradle @@ -1,20 +1,3 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - description = "TODAY Service Provider" @@ -22,21 +5,12 @@ dependencies { api project(":today-remoting") api project(":today-cloud-core") + api project(":today-service-api") - implementation project(":today-service-client") - implementation project(":today-service-registry") - - optional 'cn.taketoday:today-web' - implementation 'cn.taketoday:today-framework' - - optional 'io.netty:netty-transport' - optional 'io.netty:netty-codec' - optional 'io.netty:netty-handler' - optional 'io.netty:netty-transport-classes-epoll' - - implementation 'io.protostuff:protostuff-core:1.7.4' - implementation 'io.protostuff:protostuff-runtime:1.7.4' + optional 'cn.taketoday:infra-context' + optional project(":today-remoting-transport-tcp") + annotationProcessor 'cn.taketoday:infra-configuration-processor' } // --add-opens=java.base/java.nio=ALL-UNNAMED diff --git a/today-service-provider/src/main/java/infra/cloud/provider/DefaultServiceInterfaceMetadataProvider.java b/today-service-provider/src/main/java/infra/cloud/provider/DefaultServiceInterfaceMetadataProvider.java new file mode 100644 index 0000000..2660cb3 --- /dev/null +++ b/today-service-provider/src/main/java/infra/cloud/provider/DefaultServiceInterfaceMetadataProvider.java @@ -0,0 +1,62 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.provider; + +import java.lang.reflect.Method; + +import infra.cloud.service.AbstractServiceInterfaceMetadataProvider; +import infra.cloud.service.ServiceMetadata; +import infra.cloud.service.ServiceMetadataProvider; +import infra.cloud.service.ServiceMethod; + +/** + * Default implementation of {@link AbstractServiceInterfaceMetadataProvider} that creates + * {@link ServiceMethod} instances for service interface methods. + * + *

    This provider is responsible for generating metadata representations of methods + * defined in service interfaces, using the underlying {@link ServiceMetadataProvider} + * to retrieve base service metadata. + * + * @author 海子 Yang + * @since 1.0 2025/8/20 22:12 + */ +public class DefaultServiceInterfaceMetadataProvider extends AbstractServiceInterfaceMetadataProvider { + + /** + * Constructs a new {@code DefaultServiceInterfaceMetadataProvider} with the specified + * service metadata provider. + * + * @param serviceMetadataProvider the provider used to retrieve base service metadata + */ + public DefaultServiceInterfaceMetadataProvider(ServiceMetadataProvider serviceMetadataProvider) { + super(serviceMetadataProvider); + } + + /** + * Creates a new {@link ServiceMethod} instance for the given method of a service interface. + * + * @param serviceMetadata the base metadata of the service + * @param serviceInterface the service interface class + * @param method the method for which to create metadata + * @return a new {@code ServiceMethod} instance + */ + @Override + protected ServiceMethod createServiceMethod(ServiceMetadata serviceMetadata, Class serviceInterface, Method method) { + return new ServiceMethod(serviceMetadata, serviceInterface, method); + } + +} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/EnableServiceProvider.java b/today-service-provider/src/main/java/infra/cloud/provider/EnableServiceProvider.java deleted file mode 100644 index 77a24a5..0000000 --- a/today-service-provider/src/main/java/infra/cloud/provider/EnableServiceProvider.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.provider; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import infra.beans.factory.ObjectProvider; -import infra.cloud.RpcResponse; -import infra.cloud.core.serialize.JdkSerialization; -import infra.cloud.core.serialize.Serialization; -import infra.cloud.protocol.http.HttpServiceRegistry; -import infra.cloud.registry.HttpRegistration; -import infra.cloud.registry.RegistryProperties; -import infra.cloud.registry.ServiceRegistry; -import infra.cloud.serialize.ReturnValueSerialization; -import infra.cloud.serialize.RpcArgumentSerialization; -import infra.cloud.serialize.RpcResponseSerialization; -import infra.cloud.serialize.ThrowableSerialization; -import infra.context.annotation.Configuration; -import infra.context.annotation.Import; -import infra.context.annotation.MissingBean; -import infra.context.properties.EnableConfigurationProperties; -import infra.lang.TodayStrategies; -import infra.stereotype.Component; -import infra.web.server.ServerProperties; - -/** - * @author Harry Yang - * @since 1.0 2023/11/28 20:45 - */ -@Import(TcpServiceProviderConfig.class) -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE, ElementType.METHOD }) -public @interface EnableServiceProvider { - -} - -@Import(ServicePublishConfig.class) -@Configuration(proxyBeanMethods = false) -@EnableConfigurationProperties({ ServerProperties.class, RegistryProperties.class }) -class TcpServiceProviderConfig { - - @MissingBean - static ServiceRegistry serviceRegistry(RegistryProperties properties, Serialization serialization) { - return HttpServiceRegistry.ofURL(properties.getHttpUrl(), serialization); - } - - @MissingBean - static Serialization requestSerialization() { - return new JdkSerialization<>(); - } - - @Component - @SuppressWarnings({ "rawtypes" }) - static RpcRequestDeserializer rpcRequestDeserializer(ObjectProvider serializations) { - var list = TodayStrategies.find(RpcArgumentSerialization.class); - serializations.addOrderedTo(list); - return new RpcRequestDeserializer(list); - } - - @Component - @SuppressWarnings({ "rawtypes" }) - static RpcResponseSerialization responseSerialization(ObjectProvider serializations) { - var list = TodayStrategies.find(ReturnValueSerialization.class); - serializations.addOrderedTo(list); - return new RpcResponseSerialization(list, new ThrowableSerialization()); - } - -} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/InvocableMethod.java b/today-service-provider/src/main/java/infra/cloud/provider/InvocableMethod.java new file mode 100644 index 0000000..c4162b1 --- /dev/null +++ b/today-service-provider/src/main/java/infra/cloud/provider/InvocableMethod.java @@ -0,0 +1,48 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.provider; + +import org.jspecify.annotations.Nullable; + +import java.lang.reflect.Method; + +import infra.cloud.service.ServiceInterfaceMetadata; +import infra.cloud.service.ServiceMethod; +import infra.reflect.MethodInvoker; + +/** + * @author 海子 Yang + * @since 1.0 2024/12/20 21:49 + */ +public class InvocableMethod extends ServiceMethod { + + private final Object instance; + + private final MethodInvoker invoker; + + public InvocableMethod(ServiceInterfaceMetadata metadata, ServiceObject service, Method method, MethodInvoker invoker) { + super(metadata.getServiceMetadata(), service.getInterface(), method); + this.invoker = invoker; + this.instance = service.getInstance(); + } + + @Nullable + public Object invoke(@Nullable Object @Nullable [] args) { + return invoker.invoke(instance, args); + } + +} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/InvocableRpcMethod.java b/today-service-provider/src/main/java/infra/cloud/provider/InvocableRpcMethod.java deleted file mode 100644 index e4e172b..0000000 --- a/today-service-provider/src/main/java/infra/cloud/provider/InvocableRpcMethod.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.provider; - -import java.lang.reflect.Method; - -import infra.cloud.RpcMethod; -import infra.reflect.MethodInvoker; - -/** - * @author 海子 Yang - * @since 1.0 2024/12/20 21:49 - */ -public class InvocableRpcMethod extends RpcMethod { - - private final MethodInvoker invoker; - - public InvocableRpcMethod(Method method, MethodInvoker invoker) { - super(method); - this.invoker = invoker; - } - - public Object invokeAndHandle(Object serviceInstance, Object[] args) throws Throwable { - return invoker.invoke(serviceInstance, args); - } - -} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/LocalServiceHolder.java b/today-service-provider/src/main/java/infra/cloud/provider/LocalServiceHolder.java index 8ec3fdf..a7214f6 100644 --- a/today-service-provider/src/main/java/infra/cloud/provider/LocalServiceHolder.java +++ b/today-service-provider/src/main/java/infra/cloud/provider/LocalServiceHolder.java @@ -1,107 +1,104 @@ /* - * Copyright 2021 - 2024 the original author or authors. + * Copyright 2021 - 2026 the TODAY authors * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * https://www.apache.org/licenses/LICENSE-2.0 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] + * 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 infra.cloud.provider; -import java.net.InetAddress; +import org.jspecify.annotations.Nullable; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Set; import infra.beans.factory.SmartInitializingSingleton; -import infra.cloud.registry.ServiceDefinition; -import infra.context.ApplicationContext; +import infra.cloud.service.ServiceMetadata; +import infra.cloud.service.ServiceMetadataProvider; import infra.context.support.ApplicationObjectSupport; -import infra.lang.Nullable; +import infra.lang.Assert; import infra.stereotype.Service; import infra.util.ClassUtils; -import infra.util.ExceptionUtils; -import infra.util.ObjectUtils; +import infra.util.MultiValueMap; /** + * Holder for local service instances, managing the mapping between service interfaces + * and their corresponding implementations. It collects beans annotated with {@link Service} + * during the application context initialization and registers them for retrieval. + * + *

    This class implements {@link SmartInitializingSingleton} to ensure all services are + * registered after all singleton beans have been instantiated. + * * @author Harry Yang * @since 1.0 2022/10/19 21:40 */ -public class LocalServiceHolder extends ApplicationObjectSupport implements SmartInitializingSingleton { - - private final InetAddress localHost = ExceptionUtils.sneakyThrow(InetAddress::getLocalHost); +public class LocalServiceHolder extends ApplicationObjectSupport implements SmartInitializingSingleton, ServicesProvider { - private String localHostName; + private final HashMap, Object> localServices = new HashMap<>(); - private final int port; + private final HashMap classNameMap = new HashMap<>(); - private final HashMap localServices = new HashMap<>(); + private final MultiValueMap> serviceMap = MultiValueMap.forLinkedHashMap(); - private final ArrayList definitions = new ArrayList<>(); + private final ServiceMetadataProvider serviceMetadataProvider; - public LocalServiceHolder(int port) { - this.port = port; + public LocalServiceHolder(ServiceMetadataProvider serviceMetadataProvider) { + Assert.notNull(serviceMetadataProvider, "serviceMetadataProvider is required"); + this.serviceMetadataProvider = serviceMetadataProvider; } - public void setLocalHostName(String localHostName) { - this.localHostName = localHostName; + @SuppressWarnings("unchecked") + public @Nullable T getService(Class serviceInterface) { + return (T) localServices.get(serviceInterface); } - public ArrayList getServices() { - return definitions; - } - - public int getPort() { - return port; + @Nullable + public ServiceObject getServiceObject(String serviceClass) { + return classNameMap.get(serviceClass); } - @Nullable - public Object getService(String serviceName) { - return localServices.get(serviceName); + @Override + public List getServices() { + return new ArrayList<>(serviceMap.keySet()); } @Override public void afterSingletonsInstantiated() { - ApplicationContext context = obtainApplicationContext(); - List services = context.getAnnotatedBeans(Service.class); + List services = applicationContext().getAnnotatedBeans(Service.class); + for (Object service : services) { Class serviceImpl = ClassUtils.getUserClass(service); - Class[] interfaces = serviceImpl.getInterfaces(); - if (ObjectUtils.isEmpty(interfaces)) { + Set> interfaces = ClassUtils.getAllInterfacesForClassAsSet(serviceImpl); + if (interfaces.isEmpty()) { continue; } - Class interfaceToUse = interfaces[0]; - if (interfaces.length > 1) { - for (final Class anInterface : interfaces) { - if (anInterface.isAnnotationPresent(Service.class)) { - interfaceToUse = anInterface; - break; + for (final Class anInterface : interfaces) { + if (anInterface.isAnnotationPresent(Service.class)) { + Object object = localServices.put(anInterface, service); + String interfaceName = anInterface.getName(); + if (object != null) { + throw new IllegalStateException("Service '%s' is already registered: [%s]".formatted(interfaceName, object)); } + + ServiceMetadata serviceMetadata = serviceMetadataProvider.getMetadata(anInterface); + serviceMap.add(serviceMetadata, anInterface); + classNameMap.put(interfaceName, new ServiceObject(anInterface, service)); + logger.info("Adding service: [{}] to interface: [{}]", service, interfaceName); } } - - ServiceDefinition definition = new ServiceDefinition(); - - definition.setHost(localHostName == null ? localHost.getHostAddress() : localHostName); - - definition.setPort(port); - definition.setName(interfaceToUse.getName()); - - logger.info("add service: [{}] to interface: [{}]", service, definition.getName()); - definitions.add(definition); - localServices.put(interfaceToUse.getName(), service); // register object } - } } diff --git a/today-service-provider/src/main/java/infra/cloud/provider/RemoteRequest.java b/today-service-provider/src/main/java/infra/cloud/provider/RemoteRequest.java new file mode 100644 index 0000000..1fc7acb --- /dev/null +++ b/today-service-provider/src/main/java/infra/cloud/provider/RemoteRequest.java @@ -0,0 +1,52 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.provider; + +import org.jspecify.annotations.Nullable; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/27 22:11 + */ +public class RemoteRequest { + + private final InvocableMethod method; + + private final @Nullable Object @Nullable [] args; + + private final ServiceObject serviceObject; + + public RemoteRequest(InvocableMethod method, @Nullable Object @Nullable [] args, ServiceObject serviceObject) { + this.method = method; + this.args = args; + this.serviceObject = serviceObject; + } + + @Nullable + public Object invoke() throws Throwable { + return method.invoke(args); + } + + public InvocableMethod getMethod() { + return method; + } + + public ServiceObject getServiceObject() { + return serviceObject; + } + +} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/RequestDeserializer.java b/today-service-provider/src/main/java/infra/cloud/provider/RequestDeserializer.java new file mode 100644 index 0000000..4192318 --- /dev/null +++ b/today-service-provider/src/main/java/infra/cloud/provider/RequestDeserializer.java @@ -0,0 +1,134 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.provider; + +import org.jspecify.annotations.Nullable; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Objects; + +import infra.cloud.serialize.ArgumentSerialization; +import infra.cloud.serialize.Readable; +import infra.cloud.serialize.SerializationException; +import infra.cloud.service.ServiceInterfaceMetadata; +import infra.cloud.service.ServiceInterfaceMetadataProvider; +import infra.core.MethodParameter; +import infra.lang.Assert; +import infra.reflect.MethodInvoker; +import infra.util.MapCache; + +/** + * Deserializes incoming remote requests + * + * @author 海子 Yang + * @since 1.0 2025/3/8 22:43 + */ +@SuppressWarnings({ "unchecked", "rawtypes" }) +public class RequestDeserializer { + + private final List argumentSerializations; + + /** fast method mapping cache */ + private final MethodMapCache methodMapCache = new MethodMapCache(); + + private final ServiceInterfaceMetadataProvider metadataProvider; + + private final LocalServiceHolder localServiceHolder; + + public RequestDeserializer(List argumentSerializations, + ServiceInterfaceMetadataProvider metadataProvider, LocalServiceHolder localServiceHolder) { + this.argumentSerializations = argumentSerializations; + this.metadataProvider = metadataProvider; + this.localServiceHolder = localServiceHolder; + } + + public RemoteRequest deserialize(Readable readable) throws SerializationException { + String serviceClass = readable.readString(); + String methodName = readable.readString(); + String[] paramTypes = readable.read(String.class, Readable::readString); + + var serviceInterface = localServiceHolder.getServiceObject(serviceClass); + Assert.state(serviceInterface != null, "service interface not found"); + InvocableMethod method = methodMapCache.get(new MethodKey(serviceClass, methodName, paramTypes), serviceInterface); + + MethodParameter[] parameters = method.getParameters(); + @Nullable Object[] args = new Object[parameters.length]; + + int idx = 0; + for (MethodParameter parameter : parameters) { + var serialization = findArgumentSerialization(parameter); + args[idx++] = serialization.deserialize(parameter, readable); + } + + return new RemoteRequest(method, args, serviceInterface); + } + + private ArgumentSerialization findArgumentSerialization(MethodParameter parameter) { + for (var argumentSerialization : argumentSerializations) { + if (argumentSerialization.supportsArgument(parameter)) { + return argumentSerialization; + } + } + throw new IllegalStateException("ArgumentSerialization for parameter %s not found".formatted(parameter)); + } + + private final class MethodMapCache extends MapCache { + + @Override + protected InvocableMethod createValue(MethodKey key, ServiceObject serviceObject) { + Method methodToUse = getMethod(key, serviceObject.getInterface()); + if (methodToUse == null) { + throw new IllegalStateException("No method found for method: " + key.method); + } + MethodInvoker methodInvoker = MethodInvoker.forMethod(methodToUse); + ServiceInterfaceMetadata metadata = metadataProvider.getMetadata(serviceObject.getInterface()); + return new InvocableMethod(metadata, serviceObject, methodToUse, methodInvoker); + } + + @Nullable + private static Method getMethod(MethodKey key, Class serviceInterface) { + String method = key.method; + String[] paramTypes = key.paramTypes; + int parameterLength = paramTypes.length; + + for (Method serviceMethod : serviceInterface.getMethods()) { + if (Objects.equals(serviceMethod.getName(), method) + && parameterLength == serviceMethod.getParameterCount()) { + int current = 0; + boolean equals = true; + for (Class parameterType : serviceMethod.getParameterTypes()) { + if (!parameterType.getName().equals(paramTypes[current++])) { + // not target method + equals = false; + break; + } + } + if (equals) { + return serviceMethod; + } + } + } + return null; + } + } + + private record MethodKey(String serviceClass, String method, String[] paramTypes) { + + } + +} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/ResponseSerializer.java b/today-service-provider/src/main/java/infra/cloud/provider/ResponseSerializer.java new file mode 100644 index 0000000..abc0241 --- /dev/null +++ b/today-service-provider/src/main/java/infra/cloud/provider/ResponseSerializer.java @@ -0,0 +1,77 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.provider; + +import org.jspecify.annotations.Nullable; + +import java.util.List; + +import infra.cloud.serialize.MessagePackWriter; +import infra.cloud.serialize.ReturnValueSerializer; +import infra.cloud.service.ServiceMethod; +import infra.remoting.Payload; +import infra.remoting.util.ByteBufPayload; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import reactor.core.publisher.Mono; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/31 16:21 + */ +@SuppressWarnings("rawtypes") +public class ResponseSerializer { + + private final List returnValueSerializers; + + private final ByteBufAllocator allocator = ByteBufAllocator.DEFAULT; + + public ResponseSerializer(List returnValueSerializers) { + this.returnValueSerializers = returnValueSerializers; + } + + @SuppressWarnings({ "unchecked" }) + public Mono serialize(RemoteRequest request, @Nullable Object result) { + return Mono.create(sink -> { + ByteBuf buffer = allocator.ioBuffer(); + if (new MessagePackWriter(buffer).writeNullable(result, (out, v) -> { + InvocableMethod invocableMethod = request.getMethod(); + findSerializer(invocableMethod) + .serialize(invocableMethod, v, out); + + sink.success(ByteBufPayload.create(buffer)); + })) { + sink.success(ByteBufPayload.create(buffer)); + } + }); + } + + public Mono serialize(RemoteRequest request, Throwable throwable) { + return Mono.error(throwable); + } + + @SuppressWarnings("rawtypes") + private ReturnValueSerializer findSerializer(ServiceMethod method) { + for (ReturnValueSerializer serializer : returnValueSerializers) { + if (serializer.supportsReturnValue(method)) { + return serializer; + } + } + throw new IllegalStateException("ReturnValueSerialization for method %s not found".formatted(method)); + } + +} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/RpcRequestDeserializer.java b/today-service-provider/src/main/java/infra/cloud/provider/RpcRequestDeserializer.java deleted file mode 100644 index c4c1c2b..0000000 --- a/today-service-provider/src/main/java/infra/cloud/provider/RpcRequestDeserializer.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.provider; - -import java.lang.reflect.Method; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; - -import infra.cloud.RpcRequest; -import infra.cloud.core.serialize.DeserializeFailedException; -import infra.cloud.serialize.RpcArgumentSerialization; -import infra.lang.Nullable; -import infra.reflect.MethodInvoker; -import infra.util.ClassUtils; -import infra.util.MapCache; -import io.netty.buffer.ByteBuf; - -/** - * @author 海子 Yang - * @since 1.0 2025/3/8 22:43 - */ -@SuppressWarnings({ "unchecked", "rawtypes" }) -public class RpcRequestDeserializer { - - private final List argumentSerializations; - - /** fast method mapping cache */ - private final MethodMapCache methodMapCache = new MethodMapCache(); - - public RpcRequestDeserializer(List argumentSerializations) { - this.argumentSerializations = argumentSerializations; - } - - public RpcRequest deserialize(ByteBuf payload) throws DeserializeFailedException { - RpcRequest rpcRequest = new RpcRequest(); -// ByteBufInput input = new ByteBufInput(payload); - - rpcRequest.setMethodName(payload.readCharSequence(payload.readInt(), StandardCharsets.UTF_8).toString()); - rpcRequest.setServiceName(payload.readCharSequence(payload.readInt(), StandardCharsets.UTF_8).toString()); - - InvocableRpcMethod rpcMethod = methodMapCache.get(new MethodCacheKey(rpcRequest), null); - -// body.readInt(); -// body.readCharSequence(); - - return rpcRequest; - } - - private static final class MethodMapCache extends MapCache { - - @Nullable - @Override - protected InvocableRpcMethod createValue(MethodCacheKey key, @Nullable Object service) { - Method methodToUse = getMethod(key, service); - if (methodToUse == null) { - return null; - } - MethodInvoker methodInvoker = MethodInvoker.forMethod(methodToUse); - return new InvocableRpcMethod(methodToUse, methodInvoker); - } - - private static Method getMethod(MethodCacheKey key, Object service) { - String method = key.method; - String[] paramTypes = key.paramTypes; - int parameterLength = paramTypes.length; - - Class serviceImpl = ClassUtils.getUserClass(service); - for (Method serviceMethod : serviceImpl.getMethods()) { - if (Objects.equals(serviceMethod.getName(), method) - && parameterLength == serviceMethod.getParameterCount()) { - int current = 0; - boolean equals = true; - for (Class parameterType : serviceMethod.getParameterTypes()) { - if (!parameterType.getName().equals(paramTypes[current++])) { - // not target method - equals = false; - break; - } - } - if (equals) { - return serviceMethod; - } - } - } - return null; - } - } - - private static class MethodCacheKey { - public final String method; - - public final String[] paramTypes; - - MethodCacheKey(RpcRequest request) { - this.method = request.getMethodName(); - this.paramTypes = request.getParamTypes(); - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (!(o instanceof MethodCacheKey that)) - return false; - return Objects.equals(method, that.method) - && Arrays.equals(paramTypes, that.paramTypes); - } - - @Override - public int hashCode() { - return 31 * Objects.hash(method) + Arrays.hashCode(paramTypes); - } - } - -} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/ServerTransportFactory.java b/today-service-provider/src/main/java/infra/cloud/provider/ServerTransportFactory.java new file mode 100644 index 0000000..b813450 --- /dev/null +++ b/today-service-provider/src/main/java/infra/cloud/provider/ServerTransportFactory.java @@ -0,0 +1,45 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.provider; + +import infra.remoting.Closeable; +import infra.remoting.transport.ServerTransport; + +/** + * Factory interface for creating {@link ServerTransport} instances. + *

    + * Implementations of this interface are responsible for instantiating and configuring + * server transport objects that handle incoming network connections and communication. + * + * @param the type of closeable resource associated with the transport + * @author 海子 Yang + * @see ServerTransport + * @since 1.0 2025/8/22 22:18 + */ +public interface ServerTransportFactory { + + /** + * Creates a new {@link ServerTransport} instance. + *

    + * The returned transport is ready to accept connections and handle communication + * according to its specific implementation details. + * + * @return a new {@code ServerTransport} instance + */ + ServerTransport createTransport(); + +} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/ServiceChannelHandler.java b/today-service-provider/src/main/java/infra/cloud/provider/ServiceChannelHandler.java new file mode 100644 index 0000000..7631985 --- /dev/null +++ b/today-service-provider/src/main/java/infra/cloud/provider/ServiceChannelHandler.java @@ -0,0 +1,82 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.provider; + +import org.reactivestreams.Publisher; + +import infra.cloud.serialize.MessagePackReader; +import infra.remoting.Channel; +import infra.remoting.Payload; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/21 22:58 + */ +public class ServiceChannelHandler implements Channel { + + private final LocalServiceHolder localServiceHolder; + + private final RequestDeserializer requestDeserializer; + + private final ResponseSerializer responseSerializer; + + public ServiceChannelHandler(LocalServiceHolder localServiceHolder, + RequestDeserializer requestDeserializer, ResponseSerializer responseSerializer) { + this.localServiceHolder = localServiceHolder; + this.requestDeserializer = requestDeserializer; + this.responseSerializer = responseSerializer; + } + + @Override + public Mono requestResponse(Payload payload) { + RemoteRequest request = null; + try { + request = requestDeserializer.deserialize(new MessagePackReader(payload.data())); + Object result = request.invoke(); + return responseSerializer.serialize(request, result); + } + catch (Throwable e) { + if (request == null) { + return Mono.error(e); + } + return responseSerializer.serialize(request, e); + } + } + + @Override + public Flux requestStream(Payload payload) { + return Channel.super.requestStream(payload); + } + + @Override + public Flux requestChannel(Publisher payloads) { + return Channel.super.requestChannel(payloads); + } + + @Override + public Mono fireAndForget(Payload payload) { + return Channel.super.fireAndForget(payload); + } + + @Override + public Mono metadataPush(Payload payload) { + return Channel.super.metadataPush(payload); + } + +} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/ServiceObject.java b/today-service-provider/src/main/java/infra/cloud/provider/ServiceObject.java new file mode 100644 index 0000000..26c9beb --- /dev/null +++ b/today-service-provider/src/main/java/infra/cloud/provider/ServiceObject.java @@ -0,0 +1,42 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.provider; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/26 22:07 + */ +public class ServiceObject { + + private final Class serviceInterface; + + private final Object instance; + + public ServiceObject(Class serviceInterface, Object instance) { + this.serviceInterface = serviceInterface; + this.instance = instance; + } + + public Class getInterface() { + return serviceInterface; + } + + public Object getInstance() { + return instance; + } + +} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/ServiceProviderProperties.java b/today-service-provider/src/main/java/infra/cloud/provider/ServiceProviderProperties.java deleted file mode 100644 index 5240910..0000000 --- a/today-service-provider/src/main/java/infra/cloud/provider/ServiceProviderProperties.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.provider; - -import infra.context.properties.ConfigurationProperties; - -/** - * @author Harry Yang - * @since 1.0 2023/9/4 16:56 - */ -@ConfigurationProperties("service.provider") -public class ServiceProviderProperties { - -} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/ServiceProviderServer.java b/today-service-provider/src/main/java/infra/cloud/provider/ServiceProviderServer.java new file mode 100644 index 0000000..6f44263 --- /dev/null +++ b/today-service-provider/src/main/java/infra/cloud/provider/ServiceProviderServer.java @@ -0,0 +1,104 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.provider; + +import org.jspecify.annotations.Nullable; + +import infra.context.SmartLifecycle; +import infra.logging.Logger; +import infra.logging.LoggerFactory; +import infra.remoting.Channel; +import infra.remoting.ChannelAcceptor; +import infra.remoting.Closeable; +import infra.remoting.ConnectionSetupPayload; +import infra.remoting.core.RemotingServer; +import infra.remoting.core.Resume; +import infra.remoting.frame.decoder.PayloadDecoder; +import reactor.core.publisher.Mono; + +/** + * The {@code ServiceProviderServer} is responsible for starting and managing the lifecycle of a service provider. + * It implements {@link SmartLifecycle} to handle startup and shutdown processes, and {@link ChannelAcceptor} + * to accept incoming connections and configure the communication channel with the appropriate handler. + *

    + * This server utilizes a {@link ServiceChannelHandler} to process channels and relies on a + * {@link ServerTransportFactory} to create the underlying transport mechanism. It also supports optional + * session resumption via the {@link Resume} component. + * + * @author 海子 Yang + * @since 1.0 2025/8/21 22:25 + */ +public class ServiceProviderServer implements SmartLifecycle, ChannelAcceptor { + + private static final Logger log = LoggerFactory.getLogger(ServiceProviderServer.class); + + private final ServiceServerProperties properties; + + private final ServiceChannelHandler channelHandler; + + private final ServerTransportFactory serverTransportFactory; + + @Nullable + private final Resume resume; + + @Nullable + private Closeable serverCloseable; + + public ServiceProviderServer(ServiceServerProperties properties, @Nullable Resume resume, + ServiceChannelHandler channelHandler, ServerTransportFactory serverTransportFactory) { + this.properties = properties; + this.resume = resume; + this.channelHandler = channelHandler; + this.serverTransportFactory = serverTransportFactory; + } + + @Override + public void start() { + serverCloseable = RemotingServer.create(this) + .resume(resume) + .payloadDecoder(PayloadDecoder.ZERO_COPY) + .fragment(properties.getMaxTransmissionUnit().toBytesInt()) + .maxTimeToFirstFrame(properties.getMaxTimeToFirstFrame()) + .maxInboundPayloadSize(properties.getMaxInboundPayloadSize().toBytesInt()) + .bindNow(serverTransportFactory.createTransport()); + + log.info("Service provider server started on port: {}", properties.getPort()); + } + + @Override + public void stop() { + if (serverCloseable != null) { + serverCloseable.dispose(); + } + } + + @Override + public boolean isRunning() { + return serverCloseable != null; + } + + @Override + public boolean isPausable() { + return false; + } + + @Override + public Mono accept(ConnectionSetupPayload setup, Channel channel) { + return Mono.just(channelHandler); + } + +} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/ServicePublishConfig.java b/today-service-provider/src/main/java/infra/cloud/provider/ServicePublishConfig.java deleted file mode 100644 index 0f18f8f..0000000 --- a/today-service-provider/src/main/java/infra/cloud/provider/ServicePublishConfig.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.provider; - -import java.util.concurrent.atomic.AtomicBoolean; - -import infra.cloud.registry.HttpRegistration; -import infra.cloud.registry.ServiceRegistry; -import infra.context.SmartLifecycle; -import infra.context.annotation.Configuration; -import infra.logging.Logger; -import infra.logging.LoggerFactory; -import infra.stereotype.Component; -import infra.stereotype.Singleton; - -/** - * @author Harry Yang - * @since 1.0 2023/11/28 20:51 - */ -@Configuration(proxyBeanMethods = false) -class ServicePublishConfig { - - private static final Logger log = LoggerFactory.getLogger(ServicePublishConfig.class); - - @Singleton - static LocalServiceHolder localServiceHolder() { - return new LocalServiceHolder(9001); - } - - @Component - static ServiceProviderLifecycle serviceProviderLifecycle(ServiceRegistry serviceRegistry, LocalServiceHolder serviceHolder) { - return new ServiceProviderLifecycle(serviceRegistry, serviceHolder); - } - - static class ServiceProviderLifecycle implements SmartLifecycle { - final LocalServiceHolder serviceHolder; - - final ServiceRegistry serviceRegistry; - - private final AtomicBoolean started = new AtomicBoolean(); - - ServiceProviderLifecycle(ServiceRegistry serviceRegistry, LocalServiceHolder serviceHolder) { - this.serviceRegistry = serviceRegistry; - this.serviceHolder = serviceHolder; - } - - @Override - public void start() { - if (started.compareAndSet(false, true)) { - log.info("Registering services to registry: [{}]", serviceRegistry); - serviceRegistry.register(new HttpRegistration(serviceHolder.getServices())); // register to registry - } - } - - @Override - public void stop() { - throw new UnsupportedOperationException("Stop must not be invoked directly"); - } - - /** - * Go offline to delete the service registered on the machine - */ - @Override - public void stop(Runnable callback) { - if (started.compareAndSet(true, false)) { - log.info("Un-Registering services: [{}]", serviceRegistry); - try { - HttpRegistration registration = new HttpRegistration(serviceHolder.getServices()); - serviceRegistry.unregister(registration); - } - finally { - callback.run(); - } - } - } - - @Override - public boolean isRunning() { - return started.get(); - } - - } - -} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/ServiceServerProperties.java b/today-service-provider/src/main/java/infra/cloud/provider/ServiceServerProperties.java new file mode 100644 index 0000000..2f5dcc8 --- /dev/null +++ b/today-service-provider/src/main/java/infra/cloud/provider/ServiceServerProperties.java @@ -0,0 +1,146 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.provider; + +import org.jspecify.annotations.Nullable; + +import java.time.Duration; + +import infra.cloud.service.config.ResumeProperties; +import infra.context.properties.ConfigurationProperties; +import infra.context.properties.NestedConfigurationProperty; +import infra.remoting.frame.FrameLengthCodec; +import infra.util.DataSize; + +/** + * Service provider server properties + * + * @author Harry Yang + * @since 1.0 2023/9/4 16:56 + */ +@ConfigurationProperties("today.service.server") +public class ServiceServerProperties { + + /** + * The address to bind + */ + @Nullable + private String bindAddress; + + /** + * The port to bind + */ + private int port = 9000; + + /** + * Configurations that exposes the maximum frame size that a Connection can bring up. + */ + private DataSize maxFrameLength = DataSize.ofBytes(FrameLengthCodec.FRAME_LENGTH_MASK); + + /** + * The threshold size for reassembly, must not be less than 64 bytes + */ + private DataSize maxInboundPayloadSize = DataSize.ofBytes(Integer.MAX_VALUE); + + /** + * Specify the max time to wait for the first frame (e.g. {@code SETUP}) + * on an accepted connection. + */ + private Duration maxTimeToFirstFrame = Duration.ofMinutes(1); + + /** + * Protocol frames larger than the given maximum transmission unit (mtu) size value are + * fragmented. the threshold size for fragmentation, must be no less than 64 + */ + private DataSize maxTransmissionUnit = DataSize.ofBytes(0); + + @NestedConfigurationProperty + public final ResumeProperties resume = new ResumeProperties(); + + public void setPort(int port) { + this.port = port; + } + + public int getPort() { + return port; + } + + /** + * The address to bind to + * + * @param bindAddress the address to bind to + */ + public void setBindAddress(@Nullable String bindAddress) { + this.bindAddress = bindAddress; + } + + @Nullable + public String getBindAddress() { + return bindAddress; + } + + /** + * When this is set, frames reassembler control maximum payload size which can be reassembled. + * + *

    By default, this is not set in which case maximum reassembled payloads size is not + * controlled. + * + * @param maxInboundPayloadSize the threshold size for reassembly, must not be less than 64 bytes. + * Please note, {@code maxInboundPayloadSize} must always be greater or equal to {@link + * infra.remoting.transport.Transport#getMaxFrameLength()}, otherwise inbound frame can exceed the + * {@code maxInboundPayloadSize} + */ + public void setMaxInboundPayloadSize(DataSize maxInboundPayloadSize) { + this.maxInboundPayloadSize = maxInboundPayloadSize; + } + + public DataSize getMaxInboundPayloadSize() { + return maxInboundPayloadSize; + } + + /** + * Configurations that exposes the maximum frame size that a {@link infra.remoting.Connection} can bring up. + * + *

    This number should not exist the 16,777,215 (maximum frame size specified by protocol spec) + */ + public void setMaxFrameLength(DataSize maxFrameLength) { + this.maxFrameLength = maxFrameLength; + } + + /** + * @return return maximum configured frame size limit + */ + public DataSize getMaxFrameLength() { + return maxFrameLength; + } + + public void setMaxTimeToFirstFrame(Duration maxTimeToFirstFrame) { + this.maxTimeToFirstFrame = maxTimeToFirstFrame; + } + + public Duration getMaxTimeToFirstFrame() { + return maxTimeToFirstFrame; + } + + public DataSize getMaxTransmissionUnit() { + return maxTransmissionUnit; + } + + public void setMaxTransmissionUnit(DataSize maxTransmissionUnit) { + this.maxTransmissionUnit = maxTransmissionUnit; + } +} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/ServicesProvider.java b/today-service-provider/src/main/java/infra/cloud/provider/ServicesProvider.java new file mode 100644 index 0000000..327b3d7 --- /dev/null +++ b/today-service-provider/src/main/java/infra/cloud/provider/ServicesProvider.java @@ -0,0 +1,42 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.provider; + +import java.util.List; + +import infra.cloud.service.ServiceMetadata; + +/** + * Provider interface for discovering and retrieving available service metadata. + *

    + * Implementations of this interface are responsible for providing a list of + * {@link ServiceMetadata} representing the services currently available in the cloud environment. + *

    + * + * @author Hai Zi Yang + * @since 1.0 2025/8/24 22:08 + */ +public interface ServicesProvider { + + /** + * Retrieves a list of metadata for all available services. + * + * @return a list of {@link ServiceMetadata} objects + */ + List getServices(); + +} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/TcpServerTransportFactory.java b/today-service-provider/src/main/java/infra/cloud/provider/TcpServerTransportFactory.java new file mode 100644 index 0000000..bf42c7c --- /dev/null +++ b/today-service-provider/src/main/java/infra/cloud/provider/TcpServerTransportFactory.java @@ -0,0 +1,49 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.provider; + +import infra.lang.Assert; +import infra.remoting.transport.netty.server.CloseableChannel; +import infra.remoting.transport.netty.server.TcpServerTransport; +import infra.util.DataSize; +import reactor.netty.tcp.TcpServer; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/22 22:20 + */ +public class TcpServerTransportFactory implements ServerTransportFactory { + + private final ServiceServerProperties properties; + + public TcpServerTransportFactory(ServiceServerProperties properties) { + Assert.notNull(properties, "properties is required"); + this.properties = properties; + } + + @Override + public TcpServerTransport createTransport() { + String bindAddress = properties.getBindAddress(); + DataSize maxFrameLength = properties.getMaxFrameLength(); + if (bindAddress != null) { + TcpServer server = TcpServer.create().host(bindAddress).port(properties.getPort()); + return TcpServerTransport.create(server, maxFrameLength.toBytesInt()); + } + return TcpServerTransport.create(TcpServer.create().port(properties.getPort()), maxFrameLength.toBytesInt()); + } + +} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/config/ServiceProviderAutoConfiguration.java b/today-service-provider/src/main/java/infra/cloud/provider/config/ServiceProviderAutoConfiguration.java new file mode 100644 index 0000000..e502b9f --- /dev/null +++ b/today-service-provider/src/main/java/infra/cloud/provider/config/ServiceProviderAutoConfiguration.java @@ -0,0 +1,147 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.provider.config; + +import org.jspecify.annotations.Nullable; + +import java.util.List; + +import infra.cloud.client.annotation.ConditionalOnDiscoveryEnabled; +import infra.cloud.net.InetProperties; +import infra.cloud.net.InetService; +import infra.cloud.provider.DefaultServiceInterfaceMetadataProvider; +import infra.cloud.provider.LocalServiceHolder; +import infra.cloud.provider.RequestDeserializer; +import infra.cloud.provider.ResponseSerializer; +import infra.cloud.provider.ServerTransportFactory; +import infra.cloud.provider.ServiceChannelHandler; +import infra.cloud.provider.ServiceProviderServer; +import infra.cloud.provider.ServiceServerProperties; +import infra.cloud.provider.TcpServerTransportFactory; +import infra.cloud.serialize.ArgumentSerialization; +import infra.cloud.serialize.ReturnValueSerializer; +import infra.cloud.service.DefaultServiceMetadataProvider; +import infra.cloud.service.ServiceInterfaceMetadataProvider; +import infra.cloud.service.ServiceMetadataProvider; +import infra.cloud.service.ServiceMethod; +import infra.cloud.service.config.ResumeProperties; +import infra.context.annotation.config.DisableDIAutoConfiguration; +import infra.context.condition.ConditionalOnBooleanProperty; +import infra.context.condition.ConditionalOnMissingBean; +import infra.context.properties.EnableConfigurationProperties; +import infra.core.io.ResourceLoader; +import infra.lang.TodayStrategies; +import infra.remoting.Closeable; +import infra.remoting.core.Resume; +import infra.remoting.resume.InMemoryResumableFramesStoreFactory; +import infra.remoting.resume.RandomUUIDResumeTokenGenerator; +import infra.remoting.resume.ResumableFramesStoreFactory; +import infra.remoting.resume.ResumeTokenGenerator; +import infra.stereotype.Component; + +/** + * Auto-configuration for the Service Provider. + * + * @author 海子 Yang + * @since 1.0 2025/8/10 22:31 + */ +@SuppressWarnings("rawtypes") +@DisableDIAutoConfiguration +@ConditionalOnDiscoveryEnabled +@EnableConfigurationProperties({ InetProperties.class, ServiceServerProperties.class }) +public final class ServiceProviderAutoConfiguration { + + @Component + public static LocalServiceHolder localServiceHolder(ServiceMetadataProvider metadataProvider) { + return new LocalServiceHolder(metadataProvider); + } + + @Component + @ConditionalOnMissingBean + public static ServiceMetadataProvider serviceMetadataProvider() { + return new DefaultServiceMetadataProvider(); + } + + @Component + @ConditionalOnMissingBean + public static ServiceInterfaceMetadataProvider serviceInterfaceMetadataProvider(ServiceMetadataProvider serviceMetadataProvider) { + return new DefaultServiceInterfaceMetadataProvider(serviceMetadataProvider); + } + + @Component + public static InetService inetService(InetProperties inetProperties) { + return new InetService(inetProperties); + } + + @Component + public static RequestDeserializer requestDeserializer(List argumentSerializations, + ServiceInterfaceMetadataProvider serviceInterfaceMetadataProvider, + ResourceLoader resourceLoader, LocalServiceHolder localServiceHolder) { + List serializations = TodayStrategies.find(ArgumentSerialization.class, resourceLoader.getClassLoader()); + argumentSerializations.addAll(serializations); // order after ArgumentSerialization beans + return new RequestDeserializer(argumentSerializations, serviceInterfaceMetadataProvider, localServiceHolder); + } + + @Component + public static ResponseSerializer responseSerializer(List returnValueSerializers, ResourceLoader resourceLoader) { + List serializations = TodayStrategies.find(ReturnValueSerializer.class, resourceLoader.getClassLoader()); + returnValueSerializers.addAll(serializations); // order after ReturnValueSerializer beans + return new ResponseSerializer(returnValueSerializers); + } + + @Component + public static ServiceChannelHandler serviceChannelHandler(LocalServiceHolder localServiceHolder, + RequestDeserializer requestDeserializer, ResponseSerializer responseSerializer) { + return new ServiceChannelHandler(localServiceHolder, requestDeserializer, responseSerializer); + } + + @Component + @ConditionalOnMissingBean + public static ResumeTokenGenerator resumeTokenGenerator() { + return new RandomUUIDResumeTokenGenerator(); + } + + @Component + @ConditionalOnMissingBean + @ConditionalOnBooleanProperty(name = "today.service.server.resume.enabled", matchIfMissing = true) + public static Resume remotingResume(@Nullable ResumableFramesStoreFactory storeFactory, + ResumeTokenGenerator resumeTokenGenerator, ServiceServerProperties properties) { + ResumeProperties resume = properties.resume; + if (storeFactory == null) { + storeFactory = new InMemoryResumableFramesStoreFactory("server", resume.getMemoryCacheLimit().toBytesInt()); + } + return new Resume() + .storeFactory(storeFactory) + .token(resumeTokenGenerator) + .streamTimeout(resume.getStreamTimeout()) + .sessionDuration(resume.getSessionDuration()) + .cleanupStoreOnKeepAlive(resume.isCleanupStoreOnKeepAlive()); + } + + @Component + @ConditionalOnMissingBean + public static ServerTransportFactory serverTransportFactory(ServiceServerProperties properties) { + return new TcpServerTransportFactory(properties); + } + + @Component + public static ServiceProviderServer serviceProviderServer(@Nullable Resume resume, ServiceServerProperties properties, + ServiceChannelHandler serviceChannelHandler, ServerTransportFactory serverTransportFactory) { + return new ServiceProviderServer(properties, resume, serviceChannelHandler, serverTransportFactory); + } + +} diff --git a/today-service-provider/src/main/java/infra/cloud/provider/package-info.java b/today-service-provider/src/main/java/infra/cloud/provider/package-info.java new file mode 100644 index 0000000..dc2acfb --- /dev/null +++ b/today-service-provider/src/main/java/infra/cloud/provider/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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. + */ + +@NullMarked +package infra.cloud.provider; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-service-provider/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports b/today-service-provider/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports new file mode 100644 index 0000000..d55bd92 --- /dev/null +++ b/today-service-provider/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports @@ -0,0 +1 @@ +infra.cloud.provider.config.ServiceProviderAutoConfiguration \ No newline at end of file diff --git a/today-service-provider/src/main/resources/META-INF/today.strategies b/today-service-provider/src/main/resources/META-INF/today.strategies new file mode 100644 index 0000000..cbc0d61 --- /dev/null +++ b/today-service-provider/src/main/resources/META-INF/today.strategies @@ -0,0 +1,5 @@ + +infra.cloud.serialize.ArgumentSerialization=\ + infra.cloud.serialize.support.SimpleValueArgumentSerialization + + diff --git a/today-service-provider/src/test/java/infra/cloud/provider/LocalServiceHolderTests.java b/today-service-provider/src/test/java/infra/cloud/provider/LocalServiceHolderTests.java new file mode 100644 index 0000000..c95526b --- /dev/null +++ b/today-service-provider/src/test/java/infra/cloud/provider/LocalServiceHolderTests.java @@ -0,0 +1,25 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.provider; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/24 22:07 + */ +class LocalServiceHolderTests { + +} \ No newline at end of file diff --git a/today-service-registration/build.gradle b/today-service-registration/build.gradle new file mode 100644 index 0000000..73f548d --- /dev/null +++ b/today-service-registration/build.gradle @@ -0,0 +1,13 @@ +description = "TODAY Service Registration API" + +dependencies { + api project(":today-cloud-core") + api project(":today-service-provider") + + api 'cn.taketoday:infra-core' + api 'cn.taketoday:infra-context' + + + annotationProcessor 'cn.taketoday:infra-configuration-processor' + +} \ No newline at end of file diff --git a/today-service-registration/src/main/java/infra/cloud/registry/AbstractAutoServiceRegistration.java b/today-service-registration/src/main/java/infra/cloud/registry/AbstractAutoServiceRegistration.java new file mode 100644 index 0000000..441fe6a --- /dev/null +++ b/today-service-registration/src/main/java/infra/cloud/registry/AbstractAutoServiceRegistration.java @@ -0,0 +1,169 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import infra.cloud.client.Registration; +import infra.cloud.provider.ServicesProvider; +import infra.cloud.registry.event.InstancePreRegisteredEvent; +import infra.cloud.registry.event.InstanceRegisteredEvent; +import infra.cloud.service.ServiceMetadata; +import infra.context.ApplicationContext; +import infra.context.SmartLifecycle; +import infra.context.support.ApplicationObjectSupport; + +/** + * Provides common lifecycle management methods for {@link ServiceRegistry} implementations. + *

    + * This abstract class handles the registration and un-registration processes of service + * instances, including event publishing and lifecycle callbacks via {@link RegistrationLifecycle}. + * It implements {@link SmartLifecycle} to integrate with the application context's lifecycle. + *

    + * + * @param The type of {@link Registration} used by the {@link ServiceRegistry}. + * @param The type of configuration or source object associated with the registry. + * @author Spencer Gibb + * @author Zen Huifer + * @author 海子 Yang + */ +public abstract class AbstractAutoServiceRegistration + extends ApplicationObjectSupport implements AutoServiceRegistration, SmartLifecycle { + + protected final ServiceRegistry serviceRegistry; + + private final AtomicBoolean running = new AtomicBoolean(false); + + private final List> registrationLifecycles; + + private final ArrayList registrations = new ArrayList<>(); + + private final ServicesProvider servicesProvider; + + protected AbstractAutoServiceRegistration(ServiceRegistry serviceRegistry, + List> registrationLifecycles, ServicesProvider servicesProvider) { + this.serviceRegistry = serviceRegistry; + this.registrationLifecycles = registrationLifecycles; + this.servicesProvider = servicesProvider; + } + + public void addRegistrationLifecycle(RegistrationLifecycle registrationLifecycle) { + this.registrationLifecycles.add(registrationLifecycle); + } + + @Override + public void start() { + if (!isEnabled()) { + if (logger.isDebugEnabled()) { + logger.debug("Discovery Lifecycle disabled. Not starting"); + } + return; + } + + logger.info("Registering services to registry: [{}]", serviceRegistry); + + if (!running.get()) { + ApplicationContext context = applicationContext(); + RegistrationFactory registrationFactory = getRegistrationFactory(); + for (ServiceMetadata serviceMetadata : servicesProvider.getServices()) { + R registration = registrationFactory.createRegistration(serviceMetadata); + context.publishEvent(new InstancePreRegisteredEvent(this, registration)); + + for (RegistrationLifecycle lifecycle : registrationLifecycles) { + lifecycle.postProcessBeforeStartRegister(registration); + } + register(registration); + for (RegistrationLifecycle lifecycle : registrationLifecycles) { + lifecycle.postProcessAfterStartRegister(registration); + } + + context.publishEvent(new InstanceRegisteredEvent<>(this, registration, getConfiguration())); + registrations.add(registration); + } + running.compareAndSet(false, true); + } + } + + @Override + public boolean isRunning() { + return running.get(); + } + + @Override + public int getPhase() { + return 0; + } + + /** + * Register the local service with the {@link ServiceRegistry}. + */ + protected void register(R registration) { + logger.debug("Registering registration: [{}]", registration); + serviceRegistry.register(registration); + } + + /** + * un-register the local service with the {@link ServiceRegistry}. + */ + protected void unregister(R registration) { + logger.debug("Unregistering registration: [{}]", registration); + serviceRegistry.unregister(registration); + } + + /** + * Go offline to delete the service registered on the machine + */ + @Override + public void stop() { + if (running.compareAndSet(true, false) && isEnabled()) { + logger.info("Un-Registering services: [{}]", serviceRegistry); + for (R registration : registrations) { + for (RegistrationLifecycle lifecycle : registrationLifecycles) { + lifecycle.postProcessBeforeStopRegister(registration); + } + + unregister(registration); + + for (RegistrationLifecycle lifecycle : registrationLifecycles) { + lifecycle.postProcessAfterStopRegister(registration); + } + + serviceRegistry.close(); + } + } + } + + /** + * @return The object used to configure the registration. + */ + protected abstract Object getConfiguration(); + + /** + * @return True, if this is enabled. + */ + protected abstract boolean isEnabled(); + + /** + * Returns the factory used to create {@link Registration} instances for the services. + * + * @return the registration factory + */ + protected abstract RegistrationFactory getRegistrationFactory(); + +} diff --git a/today-service-registration/src/main/java/infra/cloud/registry/AutoServiceRegistration.java b/today-service-registration/src/main/java/infra/cloud/registry/AutoServiceRegistration.java new file mode 100644 index 0000000..5d047cc --- /dev/null +++ b/today-service-registration/src/main/java/infra/cloud/registry/AutoServiceRegistration.java @@ -0,0 +1,30 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry; + +/** + * Interface for auto service registration. + *

    + * Implementations of this interface are responsible for automatically registering + * services with a service discovery system during application startup. + *

    + * + * @author 海子 Yang + */ +public interface AutoServiceRegistration { + +} diff --git a/today-service-registration/src/main/java/infra/cloud/registry/AutoServiceRegistrationProperties.java b/today-service-registration/src/main/java/infra/cloud/registry/AutoServiceRegistrationProperties.java new file mode 100644 index 0000000..1d58ac4 --- /dev/null +++ b/today-service-registration/src/main/java/infra/cloud/registry/AutoServiceRegistrationProperties.java @@ -0,0 +1,54 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry; + +import infra.context.properties.ConfigurationProperties; + +/** + * @author Spencer Gibb + * @author 海子 Yang + */ +@ConfigurationProperties("today.service.auto-registration") +public class AutoServiceRegistrationProperties { + + /** + * Whether service auto-registration is enabled. Defaults to true. + */ + private boolean enabled = true; + + /** + * Whether startup fails if there is no AutoServiceRegistration. Defaults to false. + */ + private boolean failFast = false; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isFailFast() { + return this.failFast; + } + + public void setFailFast(boolean failFast) { + this.failFast = failFast; + } + +} diff --git a/today-service-registration/src/main/java/infra/cloud/registry/RegistrationFactory.java b/today-service-registration/src/main/java/infra/cloud/registry/RegistrationFactory.java new file mode 100644 index 0000000..eb40b52 --- /dev/null +++ b/today-service-registration/src/main/java/infra/cloud/registry/RegistrationFactory.java @@ -0,0 +1,43 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry; + +import infra.cloud.client.Registration; +import infra.cloud.service.ServiceMetadata; + +/** + * A factory interface responsible for creating {@link Registration} instances. + *

    + * Implementations of this interface should generate a specific registration object + * based on the provided {@link ServiceMetadata}. This is typically used during the + * service discovery process to register a service instance with a registry center. + * + * @param the type of registration, which must extend {@link Registration} + * @author 海子 Yang + * @since 1.0 2025/8/24 22:12 + */ +public interface RegistrationFactory { + + /** + * Creates a new registration instance using the given service metadata. + * + * @param serviceMetadata the metadata containing information about the service to be registered + * @return a new instance of {@code R} representing the service registration + */ + R createRegistration(ServiceMetadata serviceMetadata); + +} diff --git a/today-service-registration/src/main/java/infra/cloud/registry/RegistrationLifecycle.java b/today-service-registration/src/main/java/infra/cloud/registry/RegistrationLifecycle.java new file mode 100644 index 0000000..1d813a7 --- /dev/null +++ b/today-service-registration/src/main/java/infra/cloud/registry/RegistrationLifecycle.java @@ -0,0 +1,62 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry; + +import infra.cloud.client.Registration; + +/** + * Service registration life cycle. This life cycle is only related to + * {@link Registration}. + * + * @author Zen Huifer + * @author 海子 Yang + */ +public interface RegistrationLifecycle { + + /** + * A method executed before registering the local service with the + * {@link ServiceRegistry}. + * + * @param registration registration + */ + void postProcessBeforeStartRegister(R registration); + + /** + * A method executed after registering the local service with the + * {@link ServiceRegistry}. + * + * @param registration registration + */ + void postProcessAfterStartRegister(R registration); + + /** + * A method executed before de-registering the local service with the + * {@link ServiceRegistry}. + * + * @param registration registration + */ + void postProcessBeforeStopRegister(R registration); + + /** + * A method executed after de-registering the local service with the + * {@link ServiceRegistry}. + * + * @param registration registration + */ + void postProcessAfterStopRegister(R registration); + +} diff --git a/today-service-registration/src/main/java/infra/cloud/registry/ServiceRegisterFailedException.java b/today-service-registration/src/main/java/infra/cloud/registry/ServiceRegisterFailedException.java new file mode 100644 index 0000000..b254676 --- /dev/null +++ b/today-service-registration/src/main/java/infra/cloud/registry/ServiceRegisterFailedException.java @@ -0,0 +1,46 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry; + +import org.jspecify.annotations.Nullable; + +import java.io.Serial; + +import infra.cloud.RemotingException; +import infra.cloud.client.Registration; + +/** + * @author TODAY 2021/7/11 17:19 + */ +public class ServiceRegisterFailedException extends RemotingException { + + @Serial + private static final long serialVersionUID = 1L; + + private final Registration registration; + + public ServiceRegisterFailedException(Registration registration, @Nullable Throwable cause) { + super("Service register failed", cause); + this.registration = registration; + } + + public Registration getRegistration() { + return registration; + } + +} + diff --git a/today-service-registration/src/main/java/infra/cloud/registry/ServiceRegistry.java b/today-service-registration/src/main/java/infra/cloud/registry/ServiceRegistry.java new file mode 100644 index 0000000..4f2e427 --- /dev/null +++ b/today-service-registration/src/main/java/infra/cloud/registry/ServiceRegistry.java @@ -0,0 +1,70 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry; + +import infra.cloud.client.Registration; + +/** + * Contract to register and deregister instances with a Service Registry. + * + * @param registration meta data + * @param The type of the status. + * @author Spencer Gibb + * @author 海子 Yang + * @since 2021/7/4 23:12 + */ +public interface ServiceRegistry { + + /** + * Registers the registration. A registration typically has information about an + * instance, such as its hostname and port. + * + * @param registration registration meta data + * @throws ServiceRegisterFailedException If register failed + */ + void register(R registration); + + /** + * unregister the registration. + * + * @param registration registration meta data + */ + void unregister(R registration); + + /** + * Sets the status of the registration. The status values are determined by the + * individual implementations. + * + * @param registration The registration to update. + * @param status The status to set. + */ + void setStatus(R registration, S status); + + /** + * Gets the status of a particular registration. + * + * @param registration The registration to query. + * @return The status of the registration. + */ + S getStatus(R registration); + + /** + * Closes the ServiceRegistry. This is a lifecycle method. + */ + void close(); + +} diff --git a/today-service-registration/src/main/java/infra/cloud/registry/annotation/config/AutoServiceRegistrationAutoConfiguration.java b/today-service-registration/src/main/java/infra/cloud/registry/annotation/config/AutoServiceRegistrationAutoConfiguration.java new file mode 100644 index 0000000..ce87aef --- /dev/null +++ b/today-service-registration/src/main/java/infra/cloud/registry/annotation/config/AutoServiceRegistrationAutoConfiguration.java @@ -0,0 +1,45 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.annotation.config; + +import org.jspecify.annotations.Nullable; + +import infra.beans.factory.annotation.DisableDependencyInjection; +import infra.cloud.registry.AutoServiceRegistration; +import infra.cloud.registry.AutoServiceRegistrationProperties; +import infra.context.annotation.Configuration; +import infra.context.annotation.Import; +import infra.context.condition.ConditionalOnProperty; + +/** + * @author Spencer Gibb + * @author 海子 Yang + */ +@DisableDependencyInjection +@Configuration(proxyBeanMethods = false) +@Import(AutoServiceRegistrationConfiguration.class) +@ConditionalOnProperty(value = "today.service.auto-registration.enabled", matchIfMissing = true) +public class AutoServiceRegistrationAutoConfiguration { + + public AutoServiceRegistrationAutoConfiguration(@Nullable AutoServiceRegistration autoRegistration, AutoServiceRegistrationProperties properties) { + if (autoRegistration == null && properties.isFailFast()) { + throw new IllegalStateException( + "Auto Service Registration has been requested, but there is no AutoServiceRegistration bean"); + } + } + +} diff --git a/today-service-registration/src/main/java/infra/cloud/registry/annotation/config/AutoServiceRegistrationConfiguration.java b/today-service-registration/src/main/java/infra/cloud/registry/annotation/config/AutoServiceRegistrationConfiguration.java new file mode 100644 index 0000000..cca037c --- /dev/null +++ b/today-service-registration/src/main/java/infra/cloud/registry/annotation/config/AutoServiceRegistrationConfiguration.java @@ -0,0 +1,33 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.annotation.config; + +import infra.cloud.registry.AutoServiceRegistrationProperties; +import infra.context.annotation.Configuration; +import infra.context.condition.ConditionalOnProperty; +import infra.context.properties.EnableConfigurationProperties; + +/** + * @author Spencer Gibb + * @author 海子 Yang + */ +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(AutoServiceRegistrationProperties.class) +@ConditionalOnProperty(value = "today.service.auto-registration.enabled", matchIfMissing = true) +public class AutoServiceRegistrationConfiguration { + +} diff --git a/today-service-registration/src/main/java/infra/cloud/registry/event/InstancePreRegisteredEvent.java b/today-service-registration/src/main/java/infra/cloud/registry/event/InstancePreRegisteredEvent.java new file mode 100644 index 0000000..e41b367 --- /dev/null +++ b/today-service-registration/src/main/java/infra/cloud/registry/event/InstancePreRegisteredEvent.java @@ -0,0 +1,52 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.event; + +import infra.cloud.client.Registration; +import infra.context.ApplicationEvent; + +/** + * An event to fire before a service is registered. + * + * @author Ryan Baxter + * @author 海子 Yang + */ +public class InstancePreRegisteredEvent extends ApplicationEvent { + + private final Registration registration; + + /** + * Create a new pre-registration event. + * + * @param source the object on which the event initially occurred (never {@code null}) + * @param registration the registration meta data + */ + public InstancePreRegisteredEvent(Object source, Registration registration) { + super(source); + this.registration = registration; + } + + /** + * Get the registration data. + * + * @return the registration data + */ + public Registration getRegistration() { + return this.registration; + } + +} diff --git a/today-service-registration/src/main/java/infra/cloud/registry/event/InstanceRegisteredEvent.java b/today-service-registration/src/main/java/infra/cloud/registry/event/InstanceRegisteredEvent.java new file mode 100644 index 0000000..c59889a --- /dev/null +++ b/today-service-registration/src/main/java/infra/cloud/registry/event/InstanceRegisteredEvent.java @@ -0,0 +1,58 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.registry.event; + +import infra.cloud.client.Registration; +import infra.context.ApplicationEvent; + +/** + * Event to be published after the local service instance registers itself with a + * discovery service. + * + * @param - type of configuration + * @author Spencer Gibb + * @author 海子 Yang + */ +@SuppressWarnings("serial") +public class InstanceRegisteredEvent extends ApplicationEvent { + + private final T config; + + private final Registration registration; + + /** + * Creates a new {@link InstanceRegisteredEvent} instance. + * + * @param source The component that published the event (never {@code null}). + * @param config The configuration of the instance. + * @param registration registration + */ + public InstanceRegisteredEvent(Object source, Registration registration, T config) { + super(source); + this.registration = registration; + this.config = config; + } + + public T getConfig() { + return this.config; + } + + public Registration getRegistration() { + return registration; + } + +} diff --git a/today-service-registration/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports b/today-service-registration/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports new file mode 100644 index 0000000..5a47b54 --- /dev/null +++ b/today-service-registration/src/main/resources/META-INF/config/infra.context.annotation.config.AutoConfiguration.imports @@ -0,0 +1 @@ +infra.cloud.registry.annotation.config.AutoServiceRegistrationAutoConfiguration \ No newline at end of file diff --git a/today-service-registry-etcd/build.gradle b/today-service-registry-etcd/build.gradle deleted file mode 100644 index 0802435..0000000 --- a/today-service-registry-etcd/build.gradle +++ /dev/null @@ -1,15 +0,0 @@ -description = "TODAY Cloud Etcd Service Registry" - -dependencies { - - implementation project(":today-cloud-core") - implementation project(":today-service-registry") - // implementation project(":today-cloud-config-server") - implementation 'cn.taketoday:today-framework' - implementation "io.etcd:jetcd-core:0.6.1" - - annotationProcessor 'cn.taketoday:infra-configuration-processor' - - testImplementation "cn.taketoday:today-test" - -} diff --git a/today-service-registry-etcd/src/main/java/infra/cloud/registry/EtcdDiscoveryClient.java b/today-service-registry-etcd/src/main/java/infra/cloud/registry/EtcdDiscoveryClient.java deleted file mode 100644 index 89c78e3..0000000 --- a/today-service-registry-etcd/src/main/java/infra/cloud/registry/EtcdDiscoveryClient.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.registry; - -import java.util.List; - -import infra.cloud.DiscoveryClient; -import infra.cloud.ServiceInstance; - -/** - * @author Harry Yang - * @since 1.0 2023/11/19 21:19 - */ -public class EtcdDiscoveryClient implements DiscoveryClient { - - @Override - public List getInstances(String serviceId) { - return null; - } - - @Override - public List getServices() { - return null; - } - -} diff --git a/today-service-registry/build.gradle b/today-service-registry/build.gradle deleted file mode 100644 index d814b15..0000000 --- a/today-service-registry/build.gradle +++ /dev/null @@ -1,9 +0,0 @@ -description = "TODAY Service Registry" - -dependencies { - implementation project(":today-cloud-core") - implementation 'cn.taketoday:today-starter-web' - - annotationProcessor 'cn.taketoday:infra-configuration-processor' - -} \ No newline at end of file diff --git a/today-service-registry/src/main/java/infra/cloud/registry/EnableHttpRegistry.java b/today-service-registry/src/main/java/infra/cloud/registry/EnableHttpRegistry.java deleted file mode 100644 index 058b91b..0000000 --- a/today-service-registry/src/main/java/infra/cloud/registry/EnableHttpRegistry.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.registry; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import infra.context.annotation.Import; -import infra.stereotype.Component; - -/** - * Enable Http registry - * - * @author TODAY 2021/7/11 16:14 - */ -@Import(HttpRegistryConfig.class) -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE, ElementType.METHOD }) -public @interface EnableHttpRegistry { - -} - -final class HttpRegistryConfig { - - @Component - public HttpServiceRegistryEndpoint serviceRegistryEndpoint() { - return new HttpServiceRegistryEndpoint(); - } - -} diff --git a/today-service-registry/src/main/java/infra/cloud/registry/HttpRegistration.java b/today-service-registry/src/main/java/infra/cloud/registry/HttpRegistration.java deleted file mode 100644 index 45bbe12..0000000 --- a/today-service-registry/src/main/java/infra/cloud/registry/HttpRegistration.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.registry; - -import java.util.List; - -import infra.cloud.Registration; - -/** - * @author Harry Yang - * @since 1.0 2023/11/20 21:59 - */ -public class HttpRegistration implements Registration { - - private List serviceDefinitions; - - public HttpRegistration() { } - - public HttpRegistration(List serviceDefinitions) { - this.serviceDefinitions = serviceDefinitions; - } - - public void setServiceDefinitions(List serviceDefinitions) { - this.serviceDefinitions = serviceDefinitions; - } - - public List getServiceDefinitions() { - return serviceDefinitions; - } - -} diff --git a/today-service-registry/src/main/java/infra/cloud/registry/HttpServiceRegistryEndpoint.java b/today-service-registry/src/main/java/infra/cloud/registry/HttpServiceRegistryEndpoint.java deleted file mode 100644 index 6085b86..0000000 --- a/today-service-registry/src/main/java/infra/cloud/registry/HttpServiceRegistryEndpoint.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.registry; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -import infra.cloud.DefaultServiceInstance; -import infra.cloud.ServiceInstance; -import infra.http.HttpStatus; -import infra.http.MediaType; -import infra.logging.Logger; -import infra.logging.LoggerFactory; -import infra.util.CollectionUtils; -import infra.util.MultiValueMap; -import infra.web.annotation.DELETE; -import infra.web.annotation.ExceptionHandler; -import infra.web.annotation.GET; -import infra.web.annotation.POST; -import infra.web.annotation.PathVariable; -import infra.web.annotation.RequestBody; -import infra.web.annotation.RequestMapping; -import infra.web.annotation.ResponseStatus; -import infra.web.annotation.RestController; - -/** - * @author TODAY 2021/7/9 23:08 - */ -@RestController -@RequestMapping("${registry.services.uri}") -public class HttpServiceRegistryEndpoint implements ServiceRegistry { - - private static final Logger log = LoggerFactory.getLogger(HttpServiceRegistryEndpoint.class); - - private final MultiValueMap serviceMapping - = MultiValueMap.forAdaption(new ConcurrentHashMap<>()); - - @GET(produces = MediaType.APPLICATION_JSON_VALUE) - public MultiValueMap services() { - return serviceMapping; - } - - @GET("/{name}") - public List lookup(@PathVariable String name) { - List serviceDefinitions = serviceMapping.get(name); - if (CollectionUtils.isEmpty(serviceDefinitions)) { - throw new ServiceNotFoundException(name); - } - - ArrayList instances = new ArrayList<>(); - for (ServiceDefinition definition : serviceDefinitions) { - var instance = new DefaultServiceInstance(definition.getHost() + ":" + definition.getPort(), - definition.getName(), definition.getHost(), definition.getPort()); - instances.add(instance); - } - return instances; - } - - @POST - @Override - public void register(@RequestBody HttpRegistration registration) { - for (ServiceDefinition definition : registration.getServiceDefinitions()) { - log.info("Registering service: [{}] ", definition); - serviceMapping.add(definition.getName(), definition); - } - } - - @DELETE - @Override - public void unregister(@RequestBody HttpRegistration registration) { - for (ServiceDefinition definition : registration.getServiceDefinitions()) { - List serviceDefinitions = serviceMapping.get(definition.getName()); - if (CollectionUtils.isNotEmpty(serviceDefinitions) - && serviceDefinitions.removeIf(def -> Objects.equals(def, definition))) { - log.info("un-register service: [{}] ", definition); - } - } - } - - @ResponseStatus(HttpStatus.NOT_FOUND) - @ExceptionHandler(ServiceNotFoundException.class) - void handleServiceNotFound(ServiceNotFoundException serviceNotFound) { - - } - -} diff --git a/today-service-registry/src/main/java/infra/cloud/registry/InstanceSelector.java b/today-service-registry/src/main/java/infra/cloud/registry/InstanceSelector.java deleted file mode 100644 index 9e20bcb..0000000 --- a/today-service-registry/src/main/java/infra/cloud/registry/InstanceSelector.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.registry; - -import java.util.List; - -import infra.cloud.ServiceInstance; - -/** - * @author TODAY 2021/7/9 23:18 - */ -public interface InstanceSelector { - - ServiceInstance select(List instances); -} diff --git a/today-service-registry/src/main/java/infra/cloud/registry/RandomInstanceSelector.java b/today-service-registry/src/main/java/infra/cloud/registry/RandomInstanceSelector.java deleted file mode 100644 index 4472e49..0000000 --- a/today-service-registry/src/main/java/infra/cloud/registry/RandomInstanceSelector.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.registry; - -import java.util.List; -import java.util.Random; - -import infra.cloud.ServiceInstance; -import infra.lang.Assert; - -/** - * @author TODAY 2021/7/9 23:20 - */ -public class RandomInstanceSelector implements InstanceSelector { - private final Random random; - - public RandomInstanceSelector() { - this(new Random()); - } - - public RandomInstanceSelector(Random random) { - Assert.notNull(random, "Random is required"); - this.random = random; - } - - @Override - public ServiceInstance select(List instances) { - final int size = instances.size(); - final int idx = random.nextInt(size); - return instances.get(idx); - } - -} diff --git a/today-service-registry/src/main/java/infra/cloud/registry/RegistryProperties.java b/today-service-registry/src/main/java/infra/cloud/registry/RegistryProperties.java deleted file mode 100644 index 3a28759..0000000 --- a/today-service-registry/src/main/java/infra/cloud/registry/RegistryProperties.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.registry; - -import infra.context.properties.ConfigurationProperties; - -/** - * @author Harry Yang - * @since 1.0 2023/9/4 16:28 - */ -@ConfigurationProperties("registry") -public class RegistryProperties { - - /** - * Service Registry HTTP URL - */ - private String httpUrl; - - /** - * services - */ - private final Services services = new Services(); - - public void setHttpUrl(String httpUrl) { - this.httpUrl = httpUrl; - } - - public String getHttpUrl() { - return httpUrl; - } - - public Services getServices() { - return services; - } - - /** - * Services - */ - public static class Services { - - /** - * Service Registry HTTP URI. - */ - private String uri; - - public void setUri(String uri) { - this.uri = uri; - } - - public String getUri() { - return uri; - } - } - -} diff --git a/today-service-registry/src/main/java/infra/cloud/registry/ServiceDefinition.java b/today-service-registry/src/main/java/infra/cloud/registry/ServiceDefinition.java deleted file mode 100644 index d5123af..0000000 --- a/today-service-registry/src/main/java/infra/cloud/registry/ServiceDefinition.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.registry; - -import java.io.Serial; -import java.io.Serializable; -import java.util.Objects; - -import infra.core.style.ToStringBuilder; - -/** - * @author TODAY 2021/7/4 00:36 - */ -public class ServiceDefinition implements Serializable { - - @Serial - private static final long serialVersionUID = 1L; - - private int port; - - private String host; - - private String name; // service name - - private String version; - - public void setVersion(String version) { - this.version = version; - } - - public String getVersion() { - return version; - } - - public void setName(String name) { - this.name = name; - } - - public void setHost(String host) { - this.host = host; - } - - public void setPort(int port) { - this.port = port; - } - - public String getName() { - return name; - } - - public int getPort() { - return port; - } - - public String getHost() { - return host; - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (!(o instanceof ServiceDefinition that)) - return false; - return port == that.port - && Objects.equals(host, that.host) - && Objects.equals(name, that.name) - && Objects.equals(version, that.version); - } - - @Override - public int hashCode() { - return Objects.hash(port, host, name, version); - } - - @Override - public String toString() { - return ToStringBuilder.forInstance(this) - .append("port", port) - .append("host", host) - .append("name", name) - .append("version", version) - .toString(); - } - -} diff --git a/today-service-registry/src/main/java/infra/cloud/registry/ServiceRegisterFailedException.java b/today-service-registry/src/main/java/infra/cloud/registry/ServiceRegisterFailedException.java deleted file mode 100644 index e92c37c..0000000 --- a/today-service-registry/src/main/java/infra/cloud/registry/ServiceRegisterFailedException.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.registry; - -import java.io.Serial; - -import infra.cloud.Registration; -import infra.cloud.RemotingException; -import infra.lang.Nullable; - -/** - * @author TODAY 2021/7/11 17:19 - */ -public class ServiceRegisterFailedException extends RemotingException { - - @Serial - private static final long serialVersionUID = 1L; - - private final Registration registration; - - public ServiceRegisterFailedException(Registration registration, @Nullable Throwable cause) { - super("Service register failed", cause); - this.registration = registration; - } - - public Registration getRegistration() { - return registration; - } - -} - diff --git a/today-service-registry/src/main/java/infra/cloud/registry/ServiceRegistry.java b/today-service-registry/src/main/java/infra/cloud/registry/ServiceRegistry.java deleted file mode 100644 index 8cd5e2f..0000000 --- a/today-service-registry/src/main/java/infra/cloud/registry/ServiceRegistry.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2021 - 2024 the original author or authors. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see [http://www.gnu.org/licenses/] - */ - -package infra.cloud.registry; - -import infra.cloud.Registration; - -/** - * @author TODAY 2021/7/4 23:12 - */ -public interface ServiceRegistry { - - /** - * Registers the registration. A registration typically has information about an - * instance, such as its hostname and port. - * - * @param registration registration meta data - * @throws ServiceRegisterFailedException If register failed - */ - void register(R registration); - - /** - * unregister the registration. - * - * @param registration registration meta data - */ - void unregister(R registration); - -} diff --git a/today-service-registry/src/main/resources/application.properties b/today-service-registry/src/main/resources/application.properties deleted file mode 100644 index 78b5f34..0000000 --- a/today-service-registry/src/main/resources/application.properties +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright 2021 - 2023 the original author or authors. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see [http://www.gnu.org/licenses/] -# - -registry.services.uri=/services diff --git a/today-service-serialization/build.gradle b/today-service-serialization/build.gradle new file mode 100644 index 0000000..5ccb830 --- /dev/null +++ b/today-service-serialization/build.gradle @@ -0,0 +1,6 @@ +description = "TODAY Service serialization API and default format based on msgpack specification" + +dependencies { + api "cn.taketoday:infra-core" + +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/Deserializer.java b/today-service-serialization/src/main/java/infra/cloud/serialize/Deserializer.java new file mode 100644 index 0000000..f180cba --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/Deserializer.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize; + +import java.io.IOException; + +/** + * A strategy interface for converting from data in an Input to an Object. + * + * @author 海子 Yang + * @since 1.0 2025/8/16 16:59 + */ +public interface Deserializer { + + /** + * @return the deserialized object + * @throws IOException in case of errors reading from the stream + */ + Object read(Readable readable) throws IOException; + +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/Message.java b/today-service-serialization/src/main/java/infra/cloud/serialize/Message.java new file mode 100644 index 0000000..9a25043 --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/Message.java @@ -0,0 +1,82 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize; + +/** + * Only the identity of the class of a Message instance is + * written in the serialization stream, and it is the responsibility + * of the class to save and restore the contents of its instances. + *

    + * The writeTo and readFrom methods of the Message + * interface are implemented by a class to give the class complete + * control over the format and contents of the stream for an object + * and its supertypes. + * + * @author 海子 Yang + * @see java.io.Serializable + * @see java.io.Externalizable + * @since 1.0 2025/8/16 23:42 + */ +public interface Message { + + /** + * The object implements the writeTo method to save its contents + * by calling the methods of {@link Writable} for its primitive values or + * calling the write method of Output for objects, strings, + * and arrays. + * + * @param writable the stream to write the object to + * @throws SerializationException Serialization occur + */ + void writeTo(Writable writable); + + /** + * The object implements the readFrom method to restore its + * contents by calling the methods of {@link Readable} for primitive + * types and read for objects, strings and arrays. The + * readFrom method must read the values in the same sequence + * and with the same types as were written by writeTo. + * + * @param readable the source to read data from in order to restore the object + * @throws SerializationException Serialization occur + */ + void readFrom(Readable readable); + + interface Factory { + + /** + * Create a new instance of the Message class, instantiating it + * from the given Input whose data had previously been written by + * {@link Message#writeTo Message.writeTo()}. + * + * @param source The Parcel to read the object's data from. + * @return Returns a new instance of the Message class. + * @throws SerializationException Serialization occur + */ + M create(Readable source); + + /** + * Create a new array of the Parcelable class. + * + * @param size Size of the array. + * @return Returns an array of the Parcelable class, with every entry + * initialized to null. + */ + M[] newArray(int size); + } + +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/Readable.java b/today-service-serialization/src/main/java/infra/cloud/serialize/Readable.java new file mode 100644 index 0000000..b9e2396 --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/Readable.java @@ -0,0 +1,241 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize; + +import org.jspecify.annotations.Nullable; + +import java.time.Instant; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; + +import infra.lang.Enumerable; + +/** + * A readable interface that allows an application to read + * primitive data types and objects from a data source. + * + * @author 海子 Yang + * @see java.io.DataInput + * @since 1.0 2025/8/16 16:59 + */ +public interface Readable { + + /** + * Reads some bytes from an input + * stream and stores them into the buffer + * array {@code b}. The number of bytes + * read is equal + * to the length of {@code b}. + * + * @param b the buffer into which the data is read. + * @throws NullPointerException if {@code b} is {@code null}. + */ + void read(byte[] b); + + /** + * Reads {@code len} bytes from an input. + * + * @param b the buffer into which the data is read. + * @param off an int specifying the offset in the data array {@code b}. + * @param len an int specifying the number of bytes to read. + * @throws NullPointerException if {@code b} is {@code null}. + * @throws IndexOutOfBoundsException if {@code off} is negative, + * {@code len} is negative, or {@code len} is greater than + * {@code b.length - off}. + */ + void read(byte[] b, int off, int len); + + /** + * Reads byte array + */ + byte[] read(); + + /** + * Reads bytes with given length + */ + byte[] read(int len); + + /** + * Reads all left bytes + */ + byte[] readFully(); + + /** + * Skip bytes + * + * @param n the number of bytes to be skipped. + * @return the number of bytes actually skipped. + * @throws SerializationException if a serialization error occurs. + */ + int skipBytes(int n); + + /** + * Reads a {@code boolean} value. + * + * @return the {@code boolean} value read. + * @throws SerializationException if a serialization error occurs. + */ + boolean readBoolean(); + + /** + * Reads a {@code byte} value. + * + * @return the 8-bit value read. + * @throws SerializationException if a serialization error occurs. + */ + byte readByte(); + + /** + * Reads a unsigned {@code byte} value. + * + * @return the unsigned 8-bit value read. + * @throws SerializationException if a serialization error occurs. + */ + int readUnsignedByte(); + + /** + * Reads a {@code short} value. + * + * @return the 16-bit value read. + * @throws SerializationException if a serialization error occurs. + */ + short readShort(); + + /** + * Reads a unsigned {@code short} value. + * + * @return the unsigned 16-bit value read. + * @throws SerializationException if a serialization error occurs. + */ + int readUnsignedShort(); + + /** + * Reads a {@code int} value. + * + * @return the {@code int} value read. + * @throws SerializationException if a serialization error occurs. + */ + int readInt(); + + /** + * Reads a {@code long} value. + * + * @return the {@code long} value read. + * @throws SerializationException if a serialization error occurs. + */ + long readLong(); + + /** + * Reads a {@code float} value. + * + * @return the {@code float} value read. + * @throws SerializationException if a serialization error occurs. + */ + float readFloat(); + + /** + * Reads a {@code double} value. + * + * @return the {@code double} value read. + * @throws SerializationException if a serialization error occurs. + */ + double readDouble(); + + /** + * Reads a {@link String} value. + * + * @return a string. + * @throws SerializationException if a serialization error occurs. + */ + String readString(); + + /** + * Reads a {@link Instant} value. + * + * @return an Instant object. + * @throws SerializationException if a serialization error occurs. + */ + Instant readTimestamp(); + + /** + * Reads a {@link Message} value. + * + * @throws SerializationException if a serialization error occurs. + */ + void read(Message message); + + /** + * Reads an enum value. + * + * @param type the enum class type. + * @return an enum value. + * @throws SerializationException if a serialization error occurs. + */ + > V readEnum(Class type); + + /** + * Reads a nullable value. + * + * @param valueMapper function to map the value from readable. + * @return a nullable value. + * @throws SerializationException if a serialization error occurs. + */ + V readNullable(Function valueMapper); + + /** + * Reads a {@code array} value. + * + * @return a array object. + * @throws SerializationException if a serialization error occurs. + */ + T[] read(Class type, Function mapper); + + /** + * Reads a {@code array} value. + * + * @return a array object. + * @throws SerializationException if a serialization error occurs. + */ + T[] read(Class type, Supplier supplier); + + /** + * Reads a {@link List} value. + * + * @return a List object. + * @throws SerializationException if a serialization error occurs. + */ + List read(Function mapper); + + /** + * Reads a {@link List} value. + * + * @return a List object. + * @throws SerializationException if a serialization error occurs. + */ + List read(Supplier supplier); + + /** + * Reads a {@link Map} value. + * + * @return a Map object. + * @throws SerializationException if a serialization error occurs. + */ + Map read(Function keyMapper, Function valueMapper); + +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/Serialization.java b/today-service-serialization/src/main/java/infra/cloud/serialize/Serialization.java new file mode 100644 index 0000000..2b0ee90 --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/Serialization.java @@ -0,0 +1,25 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/16 16:57 + */ +public interface Serialization extends Deserializer, Serializer { + +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/SerializationException.java b/today-service-serialization/src/main/java/infra/cloud/serialize/SerializationException.java new file mode 100644 index 0000000..9e02605 --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/SerializationException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize; + +import org.jspecify.annotations.Nullable; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/16 17:04 + */ +public class SerializationException extends RuntimeException { + + public SerializationException(@Nullable String message, @Nullable Throwable cause) { + super(message, cause); + } + +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/Serializer.java b/today-service-serialization/src/main/java/infra/cloud/serialize/Serializer.java new file mode 100644 index 0000000..fec3514 --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/Serializer.java @@ -0,0 +1,36 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize; + +import java.io.IOException; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/16 16:57 + */ +public interface Serializer { + + /** + * Write an object to the given Output. + * + * @param object the object to serialize + * @param writable the output stream + * @throws IOException in case of errors writing to the stream + */ + void write(Object object, Writable writable) throws IOException; + +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/Writable.java b/today-service-serialization/src/main/java/infra/cloud/serialize/Writable.java new file mode 100644 index 0000000..86606bc --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/Writable.java @@ -0,0 +1,228 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize; + +import org.jspecify.annotations.Nullable; + +import java.time.Instant; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import infra.lang.Enumerable; + +/** + * Interface for writing data to an output stream. + * Provides methods for writing various data types including primitives, arrays, collections, and objects. + * + * @author 海子 Yang + * @see java.io.DataOutput + * @since 1.0 2025/8/16 16:58 + */ +public interface Writable { + + /** + * Writes a byte array object to the output. + * + * @param b the data to add + * @throws SerializationException if a serialization error occurs. + * @see #writeFully(byte[]) + */ + void write(byte @Nullable [] b); + + /** + * Writes a byte array object to the output. + * + * @param b the data to add + * @throws SerializationException if a serialization error occurs. + * @see #writeFully(byte[]) + */ + void write(byte[] b, int off, int len); + + /** + * Append byte array to this Output + */ + void writeFully(byte[] b); + + /** + * Append byte array to this Output + */ + void writeFully(byte[] b, int off, int len); + + /** + * Writes a {@code boolean} value. + * + * @param v the boolean to be written. + * @throws SerializationException if a serialization error occurs. + */ + void write(boolean v); + + /** + * Writes a {@code byte} value. + * + * @param b the byte to be written. + * @throws SerializationException if a serialization error occurs. + */ + void write(byte b); + + /** + * Writes a {@code short} value. + * + * @param v the {@code short} value to be written. + * @throws SerializationException if a serialization error occurs. + */ + void write(short v); + + /** + * Writes an {@code int} value. + * + * @param v the {@code int} value to be written. + * @throws SerializationException if a serialization error occurs. + */ + void write(int v); + + /** + * Writes a {@code long} value. + * + * @param v the integer to be written + * @throws SerializationException if write failed. + */ + void write(long v); + + /** + * Writes a {@code float} value. + * + * @param v the {@code float} value to be written. + * @throws SerializationException if a serialization error occurs. + */ + void write(float v); + + /** + * Writes a {@code double} value. + * + * @param v the {@code double} value to be written. + * @throws SerializationException if a serialization error occurs. + */ + void write(double v); + + /** + * Writes a {@code String} value. + * + * @param v the string value to be written. + * @throws SerializationException if a serialization error occurs. + */ + void write(@Nullable String v); + + /** + * Writes an {@code Enumerable} value. + * + * @param v the enumerable integer value to be written. + * @throws SerializationException if a serialization error occurs. + */ + void write(Enumerable v); + + /** + * Writes a Timestamp value. + * + * @param v the timestamp to be written + * @throws SerializationException if a serialization error occurs. + */ + void write(Instant v); + + /** + * Writes a Timestamp value. + * + * @param epochSecond the number of seconds from 1970-01-01T00:00:00Z + * @param nanoAdjustment the nanosecond adjustment to the number of seconds, positive or negative + * @throws SerializationException if a serialization error occurs. + */ + void writeTimestamp(long epochSecond, int nanoAdjustment); + + /** + * Writes a Timestamp value using a millisecond value (e.g., System.currentTimeMillis()) + * + * @param millis the millisecond value + * @throws SerializationException if a serialization error occurs. + */ + void writeTimestamp(long millis); + + /** + * Writes a {@code Message} value. + * + * @param v the Message value to be written. + * @throws SerializationException if a serialization error occurs. + */ + void write(Message v); + + /** + * Writes a nullable {@code V} value. + * + * @param v the value to be written. + * @throws SerializationException if a serialization error occurs. + */ + boolean writeNullable(@Nullable V v, BiConsumer valueMapper); + + /** + * Writes a {@code array} value. + * + * @param v the array value to be written. + * @throws SerializationException if a serialization error occurs. + */ + void write(T[] v, Consumer mapper); + + /** + * Writes a {@code array} value. + * + * @param v the array value to be written. + * @throws SerializationException if a serialization error occurs. + */ + void write(T[] v, BiConsumer mapper); + + /** + * Writes a {@code List} value. + * + * @param v the List value to be written. + * @throws SerializationException if a serialization error occurs. + */ + void write(List v, Consumer mapper); + + /** + * Writes a {@code List} value. + * + * @param v the List value to be written. + * @throws SerializationException if a serialization error occurs. + */ + void write(List v, BiConsumer mapper); + + /** + * Writes a {@code Map} value. + * + * @param v the Map value to be written. + * @throws SerializationException if a serialization error occurs. + */ + void write(Map v, Consumer keyMapper, Consumer valueMapper); + + /** + * Writes a {@code Map} value. + * + * @param v the Map value to be written. + * @throws SerializationException if a serialization error occurs. + */ + void write(Map v, BiConsumer keyMapper, BiConsumer valueMapper); + +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/format/ExtensionTypeHeader.java b/today-service-serialization/src/main/java/infra/cloud/serialize/format/ExtensionTypeHeader.java new file mode 100644 index 0000000..41155e3 --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/format/ExtensionTypeHeader.java @@ -0,0 +1,81 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize.format; + +import infra.lang.Assert; + +public class ExtensionTypeHeader { + + private final byte type; + + private final int length; + + /** + * Create an extension type header + * Example: + *

    +   * {@code
    +   * ...
    +   * ExtensionTypeHeader header = new ExtensionTypeHeader(checkedCastToByte(0x01), 32);
    +   * ...
    +   * }
    +   * 
    + * + * @param type extension type (byte). You can check the valid byte range with {@link #checkedCastToByte(int)} method. + * @param length extension type data length + */ + public ExtensionTypeHeader(byte type, int length) { + Assert.isTrue(length >= 0, "length must be >= 0"); + this.type = type; + this.length = length; + } + + public static byte checkedCastToByte(int code) { + Assert.isTrue(Byte.MIN_VALUE <= code && code <= Byte.MAX_VALUE, "Extension type code must be within the range of byte"); + return (byte) code; + } + + public byte getType() { + return type; + } + + public boolean isTimestampType() { + return type == MessagePackCode.EXT_TIMESTAMP; + } + + public int getLength() { + return length; + } + + @Override + public int hashCode() { + return (type + 31) * 31 + length; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ExtensionTypeHeader other) { + return this.type == other.type && this.length == other.length; + } + return false; + } + + @Override + public String toString() { + return String.format("ExtensionTypeHeader(type:%d, length:%,d)", type, length); + } +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageFormat.java b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageFormat.java new file mode 100644 index 0000000..a1f6163 --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageFormat.java @@ -0,0 +1,161 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize.format; + +import infra.lang.VisibleForTesting; + +/** + * Describes the list of the message format types defined in the MessagePack specification. + */ +public enum MessageFormat { + // INT7 + POS_FIX_INT(ValueType.INTEGER), + // MAP4 + FIX_MAP(ValueType.MAP), + // ARRAY4 + FIX_ARRAY(ValueType.ARRAY), + // STR5 + FIX_STR(ValueType.STRING), + NIL(ValueType.NIL), + NEVER_USED(null), + BOOLEAN(ValueType.BOOLEAN), + BIN8(ValueType.BINARY), + BIN16(ValueType.BINARY), + BIN32(ValueType.BINARY), + EXT8(ValueType.EXTENSION), + EXT16(ValueType.EXTENSION), + EXT32(ValueType.EXTENSION), + FLOAT32(ValueType.FLOAT), + FLOAT64(ValueType.FLOAT), + UINT8(ValueType.INTEGER), + UINT16(ValueType.INTEGER), + UINT32(ValueType.INTEGER), + UINT64(ValueType.INTEGER), + + INT8(ValueType.INTEGER), + INT16(ValueType.INTEGER), + INT32(ValueType.INTEGER), + INT64(ValueType.INTEGER), + FIX_EXT1(ValueType.EXTENSION), + FIX_EXT2(ValueType.EXTENSION), + FIX_EXT4(ValueType.EXTENSION), + FIX_EXT8(ValueType.EXTENSION), + FIX_EXT16(ValueType.EXTENSION), + STR8(ValueType.STRING), + STR16(ValueType.STRING), + STR32(ValueType.STRING), + ARRAY16(ValueType.ARRAY), + ARRAY32(ValueType.ARRAY), + MAP16(ValueType.MAP), + MAP32(ValueType.MAP), + NEG_FIX_INT(ValueType.INTEGER); + + private static final MessageFormat[] formatTable = new MessageFormat[256]; + + private final ValueType valueType; + + MessageFormat(ValueType valueType) { + this.valueType = valueType; + } + + /** + * Retruns the ValueType corresponding to this MessageFormat + * + * @return value type + * @throws MessageFormatException if this == NEVER_USED type + */ + public ValueType getValueType() throws MessageFormatException { + if (this == NEVER_USED) { + throw new MessageFormatException("Cannot convert NEVER_USED to ValueType"); + } + return valueType; + } + + static { + // Preparing a look up table for converting byte values into MessageFormat types + for (int b = 0; b <= 0xFF; ++b) { + MessageFormat mf = toMessageFormat((byte) b); + formatTable[b] = mf; + } + } + + /** + * Returns a MessageFormat type of the specified byte value + * + * @param b MessageFormat of the given byte + */ + public static MessageFormat valueOf(final byte b) { + return formatTable[b & 0xFF]; + } + + /** + * Converting a byte value into MessageFormat. For faster performance, use {@link #valueOf} + * + * @param b MessageFormat of the given byte + */ + @VisibleForTesting + static MessageFormat toMessageFormat(final byte b) { + if (MessagePackCode.isPosFixInt(b)) { + return POS_FIX_INT; + } + if (MessagePackCode.isNegFixInt(b)) { + return NEG_FIX_INT; + } + if (MessagePackCode.isFixStr(b)) { + return FIX_STR; + } + if (MessagePackCode.isFixedArray(b)) { + return FIX_ARRAY; + } + if (MessagePackCode.isFixedMap(b)) { + return FIX_MAP; + } + return switch (b) { + case MessagePackCode.NIL -> NIL; + case MessagePackCode.FALSE, MessagePackCode.TRUE -> BOOLEAN; + case MessagePackCode.BIN8 -> BIN8; + case MessagePackCode.BIN16 -> BIN16; + case MessagePackCode.BIN32 -> BIN32; + case MessagePackCode.EXT8 -> EXT8; + case MessagePackCode.EXT16 -> EXT16; + case MessagePackCode.EXT32 -> EXT32; + case MessagePackCode.FLOAT32 -> FLOAT32; + case MessagePackCode.FLOAT64 -> FLOAT64; + case MessagePackCode.UINT8 -> UINT8; + case MessagePackCode.UINT16 -> UINT16; + case MessagePackCode.UINT32 -> UINT32; + case MessagePackCode.UINT64 -> UINT64; + case MessagePackCode.INT8 -> INT8; + case MessagePackCode.INT16 -> INT16; + case MessagePackCode.INT32 -> INT32; + case MessagePackCode.INT64 -> INT64; + case MessagePackCode.FIXEXT1 -> FIX_EXT1; + case MessagePackCode.FIXEXT2 -> FIX_EXT2; + case MessagePackCode.FIXEXT4 -> FIX_EXT4; + case MessagePackCode.FIXEXT8 -> FIX_EXT8; + case MessagePackCode.FIXEXT16 -> FIX_EXT16; + case MessagePackCode.STR8 -> STR8; + case MessagePackCode.STR16 -> STR16; + case MessagePackCode.STR32 -> STR32; + case MessagePackCode.ARRAY16 -> ARRAY16; + case MessagePackCode.ARRAY32 -> ARRAY32; + case MessagePackCode.MAP16 -> MAP16; + case MessagePackCode.MAP32 -> MAP32; + default -> NEVER_USED; + }; + } +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageFormatException.java b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageFormatException.java new file mode 100644 index 0000000..6f4bb86 --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageFormatException.java @@ -0,0 +1,33 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize.format; + +import org.jspecify.annotations.Nullable; + +/** + * Thrown when the input message pack format is invalid + */ +public class MessageFormatException extends MessagePackException { + + public MessageFormatException(@Nullable String message) { + this(message, null); + } + + public MessageFormatException(@Nullable String message, @Nullable Throwable cause) { + super(message, cause); + } +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageInsufficientBufferException.java b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageInsufficientBufferException.java new file mode 100644 index 0000000..a3b05bb --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageInsufficientBufferException.java @@ -0,0 +1,28 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize.format; + +/** + * Exception that indicates end of input. + */ +public class MessageInsufficientBufferException extends MessagePackException { + + public MessageInsufficientBufferException() { + super(null, null); + } + +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageIntegerOverflowException.java b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageIntegerOverflowException.java new file mode 100644 index 0000000..7ca917b --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageIntegerOverflowException.java @@ -0,0 +1,47 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize.format; + +import java.math.BigInteger; + +/** + * This error is thrown when the user tries to read an integer value + * using a smaller types. For example, calling MessageUnpacker.unpackInt() for an integer value + * that is larger than Integer.MAX_VALUE will cause this exception. + */ +public class MessageIntegerOverflowException extends MessageTypeException { + + private final BigInteger bigInteger; + + public MessageIntegerOverflowException(BigInteger bigInteger) { + super(null, null); + this.bigInteger = bigInteger; + } + + public MessageIntegerOverflowException(long value) { + this(BigInteger.valueOf(value)); + } + + public BigInteger getBigInteger() { + return bigInteger; + } + + @Override + public String getMessage() { + return bigInteger.toString(); + } +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageNeverUsedFormatException.java b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageNeverUsedFormatException.java new file mode 100644 index 0000000..cb3251e --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageNeverUsedFormatException.java @@ -0,0 +1,30 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize.format; + +import org.jspecify.annotations.Nullable; + +/** + * Thrown when the input message pack format is invalid + */ +public class MessageNeverUsedFormatException extends MessageFormatException { + + public MessageNeverUsedFormatException(@Nullable String message) { + super(message, null); + } + +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessagePackCode.java b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessagePackCode.java new file mode 100644 index 0000000..f21119e --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessagePackCode.java @@ -0,0 +1,103 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize.format; + +/** + * The prefix code set of MessagePack format. See also https://github.com/msgpack/msgpack/blob/master/spec.md for details. + * + * @author 海子 Yang + * @since 1.0 2026/1/30 21:08 + */ +public class MessagePackCode { + + public static boolean isFixInt(byte b) { + int v = b & 0xFF; + return v <= 0x7f || v >= 0xe0; + } + + public static boolean isPosFixInt(byte b) { + return (b & POSFIXINT_MASK) == 0; + } + + public static boolean isNegFixInt(byte b) { + return (b & NEGFIXINT_PREFIX) == NEGFIXINT_PREFIX; + } + + public static boolean isFixStr(byte b) { + return (b & (byte) 0xe0) == MessagePackCode.FIXSTR_PREFIX; + } + + public static boolean isFixedArray(byte b) { + return (b & (byte) 0xf0) == MessagePackCode.FIXARRAY_PREFIX; + } + + public static boolean isFixedMap(byte b) { + return (b & (byte) 0xf0) == MessagePackCode.FIXMAP_PREFIX; + } + + public static boolean isFixedRaw(byte b) { + return (b & (byte) 0xe0) == MessagePackCode.FIXSTR_PREFIX; + } + + public static final byte POSFIXINT_MASK = (byte) 0x80; + + public static final byte FIXMAP_PREFIX = (byte) 0x80; + public static final byte FIXARRAY_PREFIX = (byte) 0x90; + public static final byte FIXSTR_PREFIX = (byte) 0xa0; + + public static final byte NIL = (byte) 0xc0; + public static final byte NEVER_USED = (byte) 0xc1; + public static final byte FALSE = (byte) 0xc2; + public static final byte TRUE = (byte) 0xc3; + public static final byte BIN8 = (byte) 0xc4; + public static final byte BIN16 = (byte) 0xc5; + public static final byte BIN32 = (byte) 0xc6; + public static final byte EXT8 = (byte) 0xc7; + public static final byte EXT16 = (byte) 0xc8; + public static final byte EXT32 = (byte) 0xc9; + public static final byte FLOAT32 = (byte) 0xca; + public static final byte FLOAT64 = (byte) 0xcb; + public static final byte UINT8 = (byte) 0xcc; + public static final byte UINT16 = (byte) 0xcd; + public static final byte UINT32 = (byte) 0xce; + public static final byte UINT64 = (byte) 0xcf; + + public static final byte INT8 = (byte) 0xd0; + public static final byte INT16 = (byte) 0xd1; + public static final byte INT32 = (byte) 0xd2; + public static final byte INT64 = (byte) 0xd3; + + public static final byte FIXEXT1 = (byte) 0xd4; + public static final byte FIXEXT2 = (byte) 0xd5; + public static final byte FIXEXT4 = (byte) 0xd6; + public static final byte FIXEXT8 = (byte) 0xd7; + public static final byte FIXEXT16 = (byte) 0xd8; + + public static final byte STR8 = (byte) 0xd9; + public static final byte STR16 = (byte) 0xda; + public static final byte STR32 = (byte) 0xdb; + + public static final byte ARRAY16 = (byte) 0xdc; + public static final byte ARRAY32 = (byte) 0xdd; + + public static final byte MAP16 = (byte) 0xde; + public static final byte MAP32 = (byte) 0xdf; + + public static final byte NEGFIXINT_PREFIX = (byte) 0xe0; + + public static final byte EXT_TIMESTAMP = (byte) -1; +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessagePackException.java b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessagePackException.java new file mode 100644 index 0000000..3cd235e --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessagePackException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize.format; + +import org.jspecify.annotations.Nullable; + +import infra.cloud.serialize.SerializationException; + +/** + * A base class of all the message pack exceptions. + */ +public class MessagePackException extends SerializationException { + + public MessagePackException(@Nullable String message, @Nullable Throwable cause) { + super(message, cause); + } + +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageSizeException.java b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageSizeException.java new file mode 100644 index 0000000..5ac9d92 --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageSizeException.java @@ -0,0 +1,39 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize.format; + +/** + * Thrown to indicate too large message size (e.g, larger than 2^31-1). + */ +public class MessageSizeException extends MessagePackException { + + private final long size; + + public MessageSizeException(long size) { + super(null, null); + this.size = size; + } + + public MessageSizeException(String message, long size) { + super(message, null); + this.size = size; + } + + public long getSize() { + return size; + } +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageTypeException.java b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageTypeException.java new file mode 100644 index 0000000..b1fa3fc --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/format/MessageTypeException.java @@ -0,0 +1,34 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize.format; + +import org.jspecify.annotations.Nullable; + +/** + * Thrown when a type mismatch error occurs + */ +public class MessageTypeException extends MessagePackException { + + public MessageTypeException(@Nullable String message) { + super(message, null); + } + + public MessageTypeException(@Nullable String message, @Nullable Throwable cause) { + super(message, cause); + } + +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/format/ValueType.java b/today-service-serialization/src/main/java/infra/cloud/serialize/format/ValueType.java new file mode 100644 index 0000000..83b429a --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/format/ValueType.java @@ -0,0 +1,61 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize.format; + +/** + * Representation of MessagePack types. + *

    + * MessagePack uses hierarchical type system. Integer and Float are subypte of Number, Thus {@link #isNumberType()} + * returns true if type is Integer or Float. String and Binary are subtype of Raw. Thus {@link #isRawType()} returns + * true if type is String or Binary. + * + * @see MessageFormat + */ +public enum ValueType { + NIL(false, false), + BOOLEAN(false, false), + INTEGER(true, false), + FLOAT(true, false), + STRING(false, true), + BINARY(false, true), + ARRAY(false, false), + MAP(false, false), + EXTENSION(false, false); + + /** + * Design note: We do not add Timestamp as a ValueType here because + * detecting Timestamp values requires reading 1-3 bytes ahead while the other + * value types can be determined just by reading the first one byte. + */ + + private final boolean numberType; + + private final boolean rawType; + + ValueType(boolean numberType, boolean rawType) { + this.numberType = numberType; + this.rawType = rawType; + } + + public boolean isNumberType() { + return numberType; + } + + public boolean isRawType() { + return rawType; + } +} diff --git a/today-service-serialization/src/main/java/infra/cloud/serialize/format/package-info.java b/today-service-serialization/src/main/java/infra/cloud/serialize/format/package-info.java new file mode 100644 index 0000000..c66fad4 --- /dev/null +++ b/today-service-serialization/src/main/java/infra/cloud/serialize/format/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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. + */ + +/** + * msgpack specification implementation + */ +@NullMarked +package infra.cloud.serialize.format; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/today-service-serialization/src/test/java/infra/cloud/serialize/MessageTests.java b/today-service-serialization/src/test/java/infra/cloud/serialize/MessageTests.java new file mode 100644 index 0000000..3149d3e --- /dev/null +++ b/today-service-serialization/src/test/java/infra/cloud/serialize/MessageTests.java @@ -0,0 +1,80 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize; + +import org.junit.jupiter.api.Test; + +import java.io.Serializable; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/16 23:46 + */ +class MessageTests { + + @Test + void test() { + User user = new User("1", 2); + +// user.write(); + + } + + static class User implements Message, Serializable { + private String name; + + private int age; + + public User() { + } + + public User(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public String getName() { + return name; + } + + public void setAge(int age) { + this.age = age; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public void writeTo(Writable writable) { + writable.write(name); + writable.write(age); + } + + @Override + public void readFrom(Readable readable) { + this.name = readable.readString(); + this.age = readable.readInt(); + } + + } + +} \ No newline at end of file diff --git a/today-service-serialization/src/test/java/infra/cloud/serialize/ReadableTests.java b/today-service-serialization/src/test/java/infra/cloud/serialize/ReadableTests.java new file mode 100644 index 0000000..f400d89 --- /dev/null +++ b/today-service-serialization/src/test/java/infra/cloud/serialize/ReadableTests.java @@ -0,0 +1,56 @@ +/* + * Copyright 2021 - 2026 the TODAY authors + * + * 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 + * + * https://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 infra.cloud.serialize; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.stream.Stream; + +/** + * @author 海子 Yang + * @since 1.0 2025/8/18 17:12 + */ +class ReadableTests { + + @ParameterizedTest + @MethodSource("args") + void list(Readable readable) { + List list = readable.read(Readable::readInt); + + Random random = new Random(); + List read = readable.read(() -> random.nextInt()); + + } + + @ParameterizedTest + @MethodSource("args") + void map(Readable readable) { + Map map = readable.read(Readable::readString, Readable::readString); + + } + + static Stream args() { +// DefaultByteBufInput input = new DefaultByteBufInput(); + return Stream.of(Arguments.arguments()); + } + +} \ No newline at end of file