├── .editorconfig ├── .github ├── maven-cd-settings.xml ├── maven-ci-settings.xml └── workflows │ ├── ci-4.x.yml │ ├── ci-5.x-stable.yml │ ├── ci-5.x.yml │ ├── ci-matrix-5.x.yml │ ├── ci.yml │ └── deploy.yml ├── .gitignore ├── LICENSE.txt ├── README.adoc ├── pom.xml └── src ├── license └── license.txt ├── main ├── asciidoc │ └── index.adoc ├── generated │ └── io │ │ └── vertx │ │ └── amqp │ │ ├── AmqpClientOptionsConverter.java │ │ ├── AmqpReceiverOptionsConverter.java │ │ └── AmqpSenderOptionsConverter.java └── java │ ├── examples │ ├── AmqpClientExamples.java │ └── package-info.java │ ├── io │ └── vertx │ │ └── amqp │ │ ├── AmqpClient.java │ │ ├── AmqpClientOptions.java │ │ ├── AmqpConnection.java │ │ ├── AmqpMessage.java │ │ ├── AmqpMessageBuilder.java │ │ ├── AmqpReceiver.java │ │ ├── AmqpReceiverOptions.java │ │ ├── AmqpSender.java │ │ ├── AmqpSenderOptions.java │ │ ├── impl │ │ ├── AmqpClientImpl.java │ │ ├── AmqpConnectionImpl.java │ │ ├── AmqpMessageBuilderImpl.java │ │ ├── AmqpMessageImpl.java │ │ ├── AmqpReceiverImpl.java │ │ └── AmqpSenderImpl.java │ │ └── package-info.java │ └── module-info.java └── test ├── java ├── io │ └── vertx │ │ └── amqp │ │ └── tests │ │ ├── BareTestBase.java │ │ ├── CloseTest.java │ │ ├── ConnectionMetadataTest.java │ │ ├── ConnectionTest.java │ │ ├── DisabledAnonymousLinkTest.java │ │ ├── DisconnectTest.java │ │ ├── FutureHandler.java │ │ ├── MockServer.java │ │ ├── ReceiverCreditTest.java │ │ ├── ReceiverTest.java │ │ ├── ReceptionTypeTest.java │ │ ├── RequestReplyTest.java │ │ ├── SSLTest.java │ │ ├── SenderTest.java │ │ ├── SenderTypeTest.java │ │ ├── SenderUnknownAckStateTest.java │ │ └── impl │ │ └── AmqpMessageImplTest.java └── module-info.java └── resources ├── README.txt ├── broker-pkcs12.keystore ├── broker-pkcs12.truststore ├── broker-wrong-host-pkcs12.keystore ├── broker-wrong-host.crt ├── broker-wrong-host.csr ├── broker.crt ├── broker.csr ├── ca-pkcs12.keystore ├── ca.crt ├── client-pkcs12.keystore ├── client-pkcs12.truststore ├── client.crt ├── client.csr ├── other-ca-pkcs12.truststore └── other-ca.crt /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | trim_trailing_whitespace = true 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | [Makefile] 12 | indent_style = tab 13 | -------------------------------------------------------------------------------- /.github/maven-cd-settings.xml: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | false 21 | 22 | 23 | 24 | vertx-snapshots-repository 25 | ${env.VERTX_NEXUS_USERNAME} 26 | ${env.VERTX_NEXUS_PASSWORD} 27 | 28 | 29 | 30 | 31 | 32 | google-mirror 33 | 34 | true 35 | 36 | 37 | 38 | google-maven-central 39 | GCS Maven Central mirror EU 40 | https://maven-central.storage-download.googleapis.com/maven2/ 41 | 42 | true 43 | 44 | 45 | false 46 | 47 | 48 | 49 | 50 | 51 | google-maven-central 52 | GCS Maven Central mirror 53 | https://maven-central.storage-download.googleapis.com/maven2/ 54 | 55 | true 56 | 57 | 58 | false 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /.github/maven-ci-settings.xml: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | false 21 | 22 | 23 | 24 | google-mirror 25 | 26 | true 27 | 28 | 29 | 30 | google-maven-central 31 | GCS Maven Central mirror EU 32 | https://maven-central.storage-download.googleapis.com/maven2/ 33 | 34 | true 35 | 36 | 37 | false 38 | 39 | 40 | 41 | 42 | 43 | google-maven-central 44 | GCS Maven Central mirror 45 | https://maven-central.storage-download.googleapis.com/maven2/ 46 | 47 | true 48 | 49 | 50 | false 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /.github/workflows/ci-4.x.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2018-2019 The original author or authors 3 | # 4 | # All rights reserved. This program and the accompanying materials 5 | # are made available under the terms of the Eclipse Public License v1.0 6 | # and Apache License v2.0 which accompanies this distribution. 7 | # 8 | # The Eclipse Public License is available at 9 | # http://www.eclipse.org/legal/epl-v10.html 10 | # 11 | # The Apache License v2.0 is available at 12 | # http://www.opensource.org/licenses/apache2.0.php 13 | # 14 | # You may elect to redistribute this code under either of these licenses. 15 | # 16 | 17 | name: vertx-amqp-client (4.x) 18 | on: 19 | schedule: 20 | - cron: '0 4 * * *' 21 | jobs: 22 | CI: 23 | strategy: 24 | matrix: 25 | include: 26 | - os: ubuntu-latest 27 | jdk: 8 28 | - os: ubuntu-latest 29 | jdk: 17 30 | uses: ./.github/workflows/ci.yml 31 | with: 32 | branch: 4.x 33 | jdk: ${{ matrix.jdk }} 34 | os: ${{ matrix.os }} 35 | secrets: inherit 36 | Deploy: 37 | if: ${{ github.repository_owner == 'vert-x3' && (github.event_name == 'push' || github.event_name == 'schedule') }} 38 | needs: CI 39 | uses: ./.github/workflows/deploy.yml 40 | with: 41 | branch: 4.x 42 | jdk: 8 43 | secrets: inherit 44 | -------------------------------------------------------------------------------- /.github/workflows/ci-5.x-stable.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2018-2019 The original author or authors 3 | # 4 | # All rights reserved. This program and the accompanying materials 5 | # are made available under the terms of the Eclipse Public License v1.0 6 | # and Apache License v2.0 which accompanies this distribution. 7 | # 8 | # The Eclipse Public License is available at 9 | # http://www.eclipse.org/legal/epl-v10.html 10 | # 11 | # The Apache License v2.0 is available at 12 | # http://www.opensource.org/licenses/apache2.0.php 13 | # 14 | # You may elect to redistribute this code under either of these licenses. 15 | # 16 | 17 | name: vertx-amqp-client (5.x-stable) 18 | on: 19 | push: 20 | branches: 21 | - '5.[0-9]+' 22 | pull_request: 23 | branches: 24 | - '5.[0-9]+' 25 | schedule: 26 | - cron: '0 6 * * *' 27 | jobs: 28 | CI-CD: 29 | uses: ./.github/workflows/ci-matrix-5.x.yml 30 | secrets: inherit 31 | with: 32 | branch: ${{ github.event_name == 'schedule' && vars.VERTX_5_STABLE_BRANCH || github.event.pull_request.head.sha || github.ref_name }} 33 | -------------------------------------------------------------------------------- /.github/workflows/ci-5.x.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2018-2019 The original author or authors 3 | # 4 | # All rights reserved. This program and the accompanying materials 5 | # are made available under the terms of the Eclipse Public License v1.0 6 | # and Apache License v2.0 which accompanies this distribution. 7 | # 8 | # The Eclipse Public License is available at 9 | # http://www.eclipse.org/legal/epl-v10.html 10 | # 11 | # The Apache License v2.0 is available at 12 | # http://www.opensource.org/licenses/apache2.0.php 13 | # 14 | # You may elect to redistribute this code under either of these licenses. 15 | # 16 | 17 | name: vertx-amqp-client (5.x) 18 | on: 19 | push: 20 | branches: 21 | - master 22 | pull_request: 23 | branches: 24 | - master 25 | schedule: 26 | - cron: '0 5 * * *' 27 | jobs: 28 | CI-CD: 29 | uses: ./.github/workflows/ci-matrix-5.x.yml 30 | secrets: inherit 31 | with: 32 | branch: ${{ github.event.pull_request.head.sha || github.ref_name }} 33 | -------------------------------------------------------------------------------- /.github/workflows/ci-matrix-5.x.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2018-2019 The original author or authors 3 | # 4 | # All rights reserved. This program and the accompanying materials 5 | # are made available under the terms of the Eclipse Public License v1.0 6 | # and Apache License v2.0 which accompanies this distribution. 7 | # 8 | # The Eclipse Public License is available at 9 | # http://www.eclipse.org/legal/epl-v10.html 10 | # 11 | # The Apache License v2.0 is available at 12 | # http://www.opensource.org/licenses/apache2.0.php 13 | # 14 | # You may elect to redistribute this code under either of these licenses. 15 | # 16 | 17 | name: CI matrix (5.x) 18 | on: 19 | workflow_call: 20 | inputs: 21 | branch: 22 | required: true 23 | type: string 24 | jobs: 25 | CI: 26 | strategy: 27 | matrix: 28 | include: 29 | - os: ubuntu-latest 30 | jdk: 11 31 | - os: ubuntu-latest 32 | jdk: 21 33 | uses: ./.github/workflows/ci.yml 34 | with: 35 | branch: ${{ inputs.branch }} 36 | jdk: ${{ matrix.jdk }} 37 | os: ${{ matrix.os }} 38 | secrets: inherit 39 | Deploy: 40 | if: ${{ github.repository_owner == 'vert-x3' && (github.event_name == 'push' || github.event_name == 'schedule') }} 41 | needs: CI 42 | uses: ./.github/workflows/deploy.yml 43 | with: 44 | branch: ${{ inputs.branch }} 45 | jdk: 11 46 | secrets: inherit 47 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2018-2019 The original author or authors 3 | # 4 | # All rights reserved. This program and the accompanying materials 5 | # are made available under the terms of the Eclipse Public License v1.0 6 | # and Apache License v2.0 which accompanies this distribution. 7 | # 8 | # The Eclipse Public License is available at 9 | # http://www.eclipse.org/legal/epl-v10.html 10 | # 11 | # The Apache License v2.0 is available at 12 | # http://www.opensource.org/licenses/apache2.0.php 13 | # 14 | # You may elect to redistribute this code under either of these licenses. 15 | # 16 | 17 | name: CI 18 | on: 19 | workflow_call: 20 | inputs: 21 | branch: 22 | required: true 23 | type: string 24 | jdk: 25 | default: 8 26 | type: string 27 | os: 28 | default: ubuntu-latest 29 | type: string 30 | jobs: 31 | Test: 32 | name: Run tests 33 | runs-on: ${{ inputs.os }} 34 | steps: 35 | - name: Checkout 36 | uses: actions/checkout@v2 37 | with: 38 | ref: ${{ inputs.branch }} 39 | - name: Install JDK 40 | uses: actions/setup-java@v2 41 | with: 42 | java-version: ${{ inputs.jdk }} 43 | distribution: temurin 44 | - name: Run tests 45 | run: mvn -s .github/maven-ci-settings.xml -q clean verify -B 46 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2018-2019 The original author or authors 3 | # 4 | # All rights reserved. This program and the accompanying materials 5 | # are made available under the terms of the Eclipse Public License v1.0 6 | # and Apache License v2.0 which accompanies this distribution. 7 | # 8 | # The Eclipse Public License is available at 9 | # http://www.eclipse.org/legal/epl-v10.html 10 | # 11 | # The Apache License v2.0 is available at 12 | # http://www.opensource.org/licenses/apache2.0.php 13 | # 14 | # You may elect to redistribute this code under either of these licenses. 15 | # 16 | 17 | name: Deploy 18 | on: 19 | workflow_call: 20 | inputs: 21 | branch: 22 | required: true 23 | type: string 24 | jdk: 25 | default: 8 26 | type: string 27 | jobs: 28 | Deploy: 29 | name: Deploy to OSSRH 30 | runs-on: ubuntu-latest 31 | env: 32 | VERTX_NEXUS_USERNAME: ${{ secrets.VERTX_NEXUS_USERNAME }} 33 | VERTX_NEXUS_PASSWORD: ${{ secrets.VERTX_NEXUS_PASSWORD }} 34 | steps: 35 | - name: Checkout 36 | uses: actions/checkout@v2 37 | with: 38 | ref: ${{ inputs.branch }} 39 | - name: Install JDK 40 | uses: actions/setup-java@v2 41 | with: 42 | java-version: ${{ inputs.jdk }} 43 | distribution: temurin 44 | - name: Get project version 45 | run: echo "PROJECT_VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:evaluate -Dexpression=project.version -q -DforceStdout | grep -v '\[')" >> $GITHUB_ENV 46 | - name: Maven deploy 47 | if: ${{ endsWith(env.PROJECT_VERSION, '-SNAPSHOT') }} 48 | run: mvn deploy -s .github/maven-cd-settings.xml -DskipTests -B 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # CMake 7 | cmake-build-debug/ 8 | cmake-build-release/ 9 | 10 | # IntelliJ 11 | .idea 12 | *.iws 13 | *.ipr 14 | *.iml 15 | out/ 16 | 17 | # JIRA plugin 18 | atlassian-ide-plugin.xml 19 | 20 | # Crashlytics plugin (for Android Studio and IntelliJ) 21 | com_crashlytics_export_strings.xml 22 | crashlytics.properties 23 | crashlytics-build.properties 24 | fabric.properties 25 | 26 | ### Eclipse template 27 | 28 | .metadata 29 | bin/ 30 | tmp/ 31 | *.tmp 32 | *.bak 33 | *.swp 34 | *~.nib 35 | local.properties 36 | .classpath 37 | .project 38 | .settings/ 39 | .loadpath 40 | .recommenders 41 | 42 | # External tool builders 43 | .externalToolBuilders/ 44 | 45 | # Locally stored "Eclipse launch configurations" 46 | *.launch 47 | 48 | # PyDev specific (Python IDE for Eclipse) 49 | *.pydevproject 50 | 51 | # CDT-specific (C/C++ Development Tooling) 52 | .cproject 53 | 54 | # CDT- autotools 55 | .autotools 56 | 57 | # Java annotation processor (APT) 58 | .factorypath 59 | 60 | # PDT-specific (PHP Development Tools) 61 | .buildpath 62 | 63 | # sbteclipse plugin 64 | .target 65 | 66 | # Tern plugin 67 | .tern-project 68 | 69 | # TeXlipse plugin 70 | .texlipse 71 | 72 | # STS (Spring Tool Suite) 73 | .springBeans 74 | 75 | # Code Recommenders 76 | .recommenders/ 77 | 78 | # Scala IDE specific (Scala & Java development for Eclipse) 79 | .cache-main 80 | .scala_dependencies 81 | .worksheet 82 | ### Maven template 83 | target/ 84 | pom.xml.tag 85 | pom.xml.releaseBackup 86 | pom.xml.versionsBackup 87 | pom.xml.next 88 | release.properties 89 | dependency-reduced-pom.xml 90 | buildNumber.properties 91 | .mvn/timing.properties 92 | 93 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) 94 | !/.mvn/wrapper/maven-wrapper.jar 95 | ### VisualStudioCode template 96 | .vscode/* 97 | !.vscode/settings.json 98 | !.vscode/tasks.json 99 | !.vscode/launch.json 100 | !.vscode/extensions.json 101 | ### VisualStudio template 102 | ## Ignore Visual Studio temporary files, build results, and 103 | ## files generated by popular Visual Studio add-ons. 104 | ## 105 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 106 | 107 | # User-specific files 108 | *.suo 109 | *.user 110 | *.userosscache 111 | *.sln.docstates 112 | 113 | # User-specific files (MonoDevelop/Xamarin Studio) 114 | *.userprefs 115 | 116 | # Build results 117 | [Dd]ebug/ 118 | [Dd]ebugPublic/ 119 | [Rr]elease/ 120 | [Rr]eleases/ 121 | x64/ 122 | x86/ 123 | bld/ 124 | [Bb]in/ 125 | [Oo]bj/ 126 | [Ll]og/ 127 | 128 | # Visual Studio 2015/2017 cache/options directory 129 | .vs/ 130 | # Uncomment if you have tasks that create the project's static files in wwwroot 131 | #wwwroot/ 132 | 133 | # Visual Studio 2017 auto generated files 134 | Generated\ Files/ 135 | 136 | # MSTest test Results 137 | [Tt]est[Rr]esult*/ 138 | [Bb]uild[Ll]og.* 139 | 140 | # NUNIT 141 | *.VisualState.xml 142 | TestResult.xml 143 | 144 | # Build Results of an ATL Project 145 | [Dd]ebugPS/ 146 | [Rr]eleasePS/ 147 | dlldata.c 148 | 149 | # Benchmark Results 150 | BenchmarkDotNet.Artifacts/ 151 | 152 | # .NET Core 153 | project.lock.json 154 | project.fragment.lock.json 155 | artifacts/ 156 | 157 | # StyleCop 158 | StyleCopReport.xml 159 | 160 | # Files built by Visual Studio 161 | *_i.c 162 | *_p.c 163 | *_i.h 164 | *.ilk 165 | *.meta 166 | *.obj 167 | *.iobj 168 | *.pch 169 | *.pdb 170 | *.ipdb 171 | *.pgc 172 | *.pgd 173 | *.rsp 174 | *.sbr 175 | *.tlb 176 | *.tli 177 | *.tlh 178 | *.tmp 179 | *.tmp_proj 180 | *.log 181 | *.vspscc 182 | *.vssscc 183 | .builds 184 | *.pidb 185 | *.svclog 186 | *.scc 187 | 188 | # Chutzpah Test files 189 | _Chutzpah* 190 | 191 | # Visual C++ cache files 192 | ipch/ 193 | *.aps 194 | *.ncb 195 | *.opendb 196 | *.opensdf 197 | *.sdf 198 | *.cachefile 199 | *.VC.db 200 | *.VC.VC.opendb 201 | 202 | # Visual Studio profiler 203 | *.psess 204 | *.vsp 205 | *.vspx 206 | *.sap 207 | 208 | # Visual Studio Trace Files 209 | *.e2e 210 | 211 | # TFS 2012 Local Workspace 212 | $tf/ 213 | 214 | # Guidance Automation Toolkit 215 | *.gpState 216 | 217 | # ReSharper is a .NET coding add-in 218 | _ReSharper*/ 219 | *.[Rr]e[Ss]harper 220 | *.DotSettings.user 221 | 222 | # JustCode is a .NET coding add-in 223 | .JustCode 224 | 225 | # TeamCity is a build add-in 226 | _TeamCity* 227 | 228 | # DotCover is a Code Coverage Tool 229 | *.dotCover 230 | 231 | # AxoCover is a Code Coverage Tool 232 | .axoCover/* 233 | !.axoCover/settings.json 234 | 235 | # Visual Studio code coverage results 236 | *.coverage 237 | *.coveragexml 238 | 239 | # NCrunch 240 | _NCrunch_* 241 | .*crunch*.local.xml 242 | nCrunchTemp_* 243 | 244 | # MightyMoose 245 | *.mm.* 246 | AutoTest.Net/ 247 | 248 | # Web workbench (sass) 249 | .sass-cache/ 250 | 251 | # Installshield output folder 252 | [Ee]xpress/ 253 | 254 | # DocProject is a documentation generator add-in 255 | DocProject/buildhelp/ 256 | DocProject/Help/*.HxT 257 | DocProject/Help/*.HxC 258 | DocProject/Help/*.hhc 259 | DocProject/Help/*.hhk 260 | DocProject/Help/*.hhp 261 | DocProject/Help/Html2 262 | DocProject/Help/html 263 | 264 | # Click-Once directory 265 | publish/ 266 | 267 | # Publish Web Output 268 | *.[Pp]ublish.xml 269 | *.azurePubxml 270 | # Note: Comment the next line if you want to checkin your web deploy settings, 271 | # but database connection strings (with potential passwords) will be unencrypted 272 | *.pubxml 273 | *.publishproj 274 | 275 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 276 | # checkin your Azure Web App publish settings, but sensitive information contained 277 | # in these scripts will be unencrypted 278 | PublishScripts/ 279 | 280 | # NuGet Packages 281 | *.nupkg 282 | # The packages folder can be ignored because of Package Restore 283 | **/[Pp]ackages/* 284 | # except build/, which is used as an MSBuild target. 285 | !**/[Pp]ackages/build/ 286 | # Uncomment if necessary however generally it will be regenerated when needed 287 | #!**/[Pp]ackages/repositories.config 288 | # NuGet v3's project.json files produces more ignorable files 289 | *.nuget.props 290 | *.nuget.targets 291 | 292 | # Microsoft Azure Build Output 293 | csx/ 294 | *.build.csdef 295 | 296 | # Microsoft Azure Emulator 297 | ecf/ 298 | rcf/ 299 | 300 | # Windows Store app package directories and files 301 | AppPackages/ 302 | BundleArtifacts/ 303 | Package.StoreAssociation.xml 304 | _pkginfo.txt 305 | *.appx 306 | 307 | # Visual Studio cache files 308 | # files ending in .cache can be ignored 309 | *.[Cc]ache 310 | # but keep track of directories ending in .cache 311 | !*.[Cc]ache/ 312 | 313 | # Others 314 | ClientBin/ 315 | ~$* 316 | *~ 317 | *.dbmdl 318 | *.dbproj.schemaview 319 | *.jfm 320 | *.pfx 321 | *.publishsettings 322 | orleans.codegen.cs 323 | 324 | # Including strong name files can present a security risk 325 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 326 | #*.snk 327 | 328 | # Since there are multiple workflows, uncomment next line to ignore bower_components 329 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 330 | #bower_components/ 331 | 332 | # RIA/Silverlight projects 333 | Generated_Code/ 334 | 335 | # Backup & report files from converting an old project file 336 | # to a newer Visual Studio version. Backup files are not needed, 337 | # because we have git ;-) 338 | _UpgradeReport_Files/ 339 | Backup*/ 340 | UpgradeLog*.XML 341 | UpgradeLog*.htm 342 | ServiceFabricBackup/ 343 | *.rptproj.bak 344 | 345 | # SQL Server files 346 | *.mdf 347 | *.ldf 348 | *.ndf 349 | 350 | # Business Intelligence projects 351 | *.rdl.data 352 | *.bim.layout 353 | *.bim_*.settings 354 | *.rptproj.rsuser 355 | 356 | # Microsoft Fakes 357 | FakesAssemblies/ 358 | 359 | # GhostDoc plugin setting file 360 | *.GhostDoc.xml 361 | 362 | # Node.js Tools for Visual Studio 363 | .ntvs_analysis.dat 364 | node_modules/ 365 | 366 | # Visual Studio 6 build log 367 | *.plg 368 | 369 | # Visual Studio 6 workspace options file 370 | *.opt 371 | 372 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 373 | *.vbw 374 | 375 | # Visual Studio LightSwitch build output 376 | **/*.HTMLClient/GeneratedArtifacts 377 | **/*.DesktopClient/GeneratedArtifacts 378 | **/*.DesktopClient/ModelManifest.xml 379 | **/*.Server/GeneratedArtifacts 380 | **/*.Server/ModelManifest.xml 381 | _Pvt_Extensions 382 | 383 | # Paket dependency manager 384 | .paket/paket.exe 385 | paket-files/ 386 | 387 | # FAKE - F# Make 388 | .fake/ 389 | 390 | # CodeRush 391 | .cr/ 392 | 393 | # Python Tools for Visual Studio (PTVS) 394 | __pycache__/ 395 | *.pyc 396 | 397 | # Cake - Uncomment if you are using it 398 | # tools/** 399 | # !tools/packages.config 400 | 401 | # Tabs Studio 402 | *.tss 403 | 404 | # Telerik's JustMock configuration file 405 | *.jmconfig 406 | 407 | # BizTalk build output 408 | *.btp.cs 409 | *.btm.cs 410 | *.odx.cs 411 | *.xsd.cs 412 | 413 | # OpenCover UI analysis results 414 | OpenCover/ 415 | 416 | # Azure Stream Analytics local run output 417 | ASALocalRun/ 418 | 419 | # MSBuild Binary and Structured Log 420 | *.binlog 421 | 422 | # NVidia Nsight GPU debugger configuration file 423 | *.nvuser 424 | 425 | # MFractors (Xamarin productivity tool) working folder 426 | .mfractor/ 427 | ### Java template 428 | # Compiled class file 429 | *.class 430 | 431 | # Log file 432 | *.log 433 | 434 | # BlueJ files 435 | *.ctxt 436 | 437 | # Mobile Tools for Java (J2ME) 438 | .mtj.tmp/ 439 | 440 | # Package Files # 441 | *.jar 442 | *.war 443 | *.nar 444 | *.ear 445 | *.zip 446 | *.tar.gz 447 | *.rar 448 | 449 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 450 | hs_err_pid* 451 | 452 | .vertx 453 | 454 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Vert.x AMQP Client 2 | 3 | image:https://github.com/vert-x3/vertx-amqp-client/actions/workflows/ci-5.x.yml/badge.svg["Build Status (5.x)",link="https://github.com/vert-x3/vertx-amqp-client/actions/workflows/ci-5.x.yml"] 4 | image:https://github.com/vert-x3/vertx-amqp-client/actions/workflows/ci-4.x.yml/badge.svg["Build Status (4.x)",link="https://github.com/vert-x3/vertx-amqp-client/actions/workflows/ci-4.x.yml"] 5 | 6 | == Build 7 | 8 | === Requirements 9 | 10 | * Docker (used with Test Container) 11 | * Java 8 12 | * Maven 13 | 14 | 15 | === Instructions 16 | 17 | `mvn clean install` 18 | 19 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 4.0.0 20 | 21 | 22 | io.vertx 23 | vertx5-parent 24 | 12 25 | 26 | 27 | vertx-amqp-client 28 | 5.1.0-SNAPSHOT 29 | 30 | Eclipse Vert.x AMQP 1.0 Client 31 | 32 | 33 | scm:git:git@github.com:vert-x3/vertx-amqp-client.git 34 | scm:git:git@github.com:vert-x3/vertx-amqp-client.git 35 | git@github.com:vert-x3/vertx-amqp-client.git 36 | 37 | 38 | 39 | 40 | 41 | io.vertx 42 | vertx-dependencies 43 | ${project.version} 44 | pom 45 | import 46 | 47 | 48 | 49 | 50 | 51 | 52 | io.vertx 53 | vertx-codegen-api 54 | true 55 | 56 | 57 | io.vertx 58 | vertx-codegen-json 59 | true 60 | 61 | 62 | io.vertx 63 | vertx-docgen-api 64 | true 65 | 66 | 67 | io.vertx 68 | vertx-core 69 | 70 | 71 | io.vertx 72 | vertx-proton 73 | 74 | 75 | 76 | org.reactivestreams 77 | reactive-streams 78 | 1.0.3 79 | provided 80 | 81 | 82 | 83 | junit 84 | junit 85 | 4.13.2 86 | test 87 | 88 | 89 | org.hamcrest 90 | hamcrest-core 91 | 92 | 93 | 94 | 95 | org.assertj 96 | assertj-core 97 | 3.21.0 98 | test 99 | 100 | 101 | io.vertx 102 | vertx-unit 103 | test 104 | 105 | 106 | org.awaitility 107 | awaitility 108 | 4.2.2 109 | test 110 | 111 | 112 | org.slf4j 113 | slf4j-api 114 | test 115 | 116 | 117 | ch.qos.logback 118 | logback-classic 119 | 1.2.7 120 | test 121 | 122 | 123 | org.apache.logging.log4j 124 | log4j-core 125 | test 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | maven-compiler-plugin 134 | 135 | 136 | default-compile 137 | compile 138 | 139 | 140 | 141 | io.vertx 142 | vertx-codegen 143 | processor 144 | 145 | 146 | io.vertx 147 | vertx-docgen-processor 148 | processor 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | maven-assembly-plugin 160 | 161 | 162 | package-docs 163 | 164 | single 165 | 166 | 167 | 168 | 169 | 170 | maven-failsafe-plugin 171 | 172 | 173 | 174 | integration-test 175 | verify 176 | 177 | 178 | 179 | true 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | com.mycila 188 | license-maven-plugin 189 | 3.0 190 | 191 |
src/license/license.txt
192 | 193 | DOUBLESLASH_STYLE 194 | 195 | SLASHSTAR_STYLE 196 | 197 | 198 | LICENSE.txt 199 | .travis* 200 | .editorconfig 201 | pom.xml 202 | **/*.txt 203 | 204 |
205 | 206 | 207 | 208 | format 209 | 210 | process-sources 211 | 212 | 213 |
214 |
215 |
216 |
-------------------------------------------------------------------------------- /src/license/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2019 The original author or authors 2 | 3 | All rights reserved. This program and the accompanying materials 4 | are made available under the terms of the Eclipse Public License v1.0 5 | and Apache License v2.0 which accompanies this distribution. 6 | 7 | The Eclipse Public License is available at 8 | http://www.eclipse.org/legal/epl-v10.html 9 | 10 | The Apache License v2.0 is available at 11 | http://www.opensource.org/licenses/apache2.0.php 12 | 13 | You may elect to redistribute this code under either of these licenses. 14 | 15 | -------------------------------------------------------------------------------- /src/main/asciidoc/index.adoc: -------------------------------------------------------------------------------- 1 | = Vert.x AMQP Client 2 | :toc: left 3 | 4 | The Vert.x AMQP Client allows interacting with https://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol[AMQP 1.0] 5 | brokers and routers. It allows: 6 | 7 | * Connecting to an AMQP broker or router - SASL and TLS connections are supported 8 | * Consuming message from a queue or a topic 9 | * Sending messages to a queue or a topic 10 | * Checking acknowledgement for sent messages 11 | 12 | The AMQP 1.0 protocol support durable subscriptions, persistence, security, conversations, sophisticated routing... More 13 | details on the protocol can be found on the https://www.amqp.org/[AMQP homepage]. 14 | 15 | The Vert.x AMQP client is based on Vert.x Proton. If you need fine-grain control, we recommend using 16 | https://github.com/vert-x3/vertx-proton[Vert.x Proton] directly. 17 | 18 | == Using Vert.x AMQP Client 19 | 20 | To use the Vert.x AMQP Client, add the following dependency to the _dependencies_ section of your build 21 | descriptor: 22 | 23 | * Maven (in your `pom.xml`): 24 | 25 | [source,xml,subs="+attributes"] 26 | ---- 27 | 28 | io.vertx 29 | vertx-amqp-client 30 | ${maven.version} 31 | 32 | ---- 33 | 34 | * Gradle (in your `build.gradle` file): 35 | 36 | [source,groovy,subs="+attributes"] 37 | ---- 38 | compile 'io.vertx:vertx-amqp-client:${maven.version}' 39 | ---- 40 | 41 | == Creating an AMQP client 42 | 43 | Once you have added the client to your _CLASSPATH_, you can instantiate an {@link io.vertx.amqp.AmqpClient} as 44 | follows: 45 | 46 | [source,$lang] 47 | ---- 48 | {@link examples.AmqpClientExamples#creation(io.vertx.core.Vertx)} 49 | ---- 50 | 51 | There are two methods to instantiate an {@link io.vertx.amqp.AmqpClient}. You can pass an explicit Vert.x instance. 52 | Use this approach if you are in a Vert.x application, or a Vert.x verticle. Otherwise you can omit passing the Vert.x 53 | instance, an internal instance is created and closed when the client is closed. 54 | 55 | To instantiate an {@link io.vertx.amqp.AmqpClient}, you need to pass {@link io.vertx.amqp.AmqpClientOptions}. 56 | These options contains the location of the broker or router, credentials... Many aspect of the AMQP client can be 57 | configured using these options. Note that you can also use these options to configure the underlying Proton client. 58 | 59 | Host, port, username and password can also be configured from system properties or environment variables: 60 | 61 | * Host: system property: `amqp-client-host`, environment variable: AMQP_CLIENT_HOST` (mandatory) 62 | * Port: system property: `amqp-client-port`, environment variable: AMQP_CLIENT_PORT` (defaults to 5672) 63 | * Username: system property: `amqp-client-username`, environment variable: AMQP_CLIENT_USERNAME` 64 | * Password: system property: `amqp-client-password`, environment variable: AMQP_CLIENT_PASSWORD` 65 | 66 | == Establishing a connection 67 | 68 | Once you have created a client, you need to explicitly connect to the remote server. This is done using the `connect` 69 | method: 70 | 71 | [source,$lang] 72 | ---- 73 | {@link examples.AmqpClientExamples#connect(io.vertx.amqp.AmqpClient)} 74 | ---- 75 | 76 | Once established or failed, the handler is called. Note that the connection is used to create receivers and senders. 77 | 78 | == Creating a receiver 79 | 80 | A receiver is used to receive messages. The AMQP receiver can be retrieved using one of the two following methods: 81 | 82 | [source,$lang] 83 | ---- 84 | {@link examples.AmqpClientExamples#receiver1(io.vertx.amqp.AmqpConnection)} 85 | 86 | {@link examples.AmqpClientExamples#receiver2(io.vertx.amqp.AmqpConnection)} 87 | ---- 88 | 89 | The main difference between these 2 approaches is _when_ the message handler is attached to the receiver. In the first 90 | approach, the handler is immediately passed and will start receiving messages immediately. In the second approach, the 91 | handler is attached manually after the completion. This give you more control and let you attach other handlers. 92 | 93 | The receiver passed in the completion handler can be used as a stream. So, you can pause and resume the reception of 94 | messages. The back-pressure protocol is implemented using 95 | http://docs.oasis-open.org/amqp/core/v1.0/csprd02/amqp-core-transport-v1.0-csprd02.html#doc-flow-control[AMQP credits]. 96 | 97 | The received messages are instances of {@link io.vertx.amqp.AmqpMessage}. Instances are immutable, and provide 98 | access to most of the metadata supported by AMQP. See the list of 99 | http://docs.oasis-open.org/amqp/core/v1.0/amqp-core-messaging-v1.0.html#type-properties[properties] as references. Note 100 | that retrieving a JSON object or a JSON array from the body required the value to be passed as AMQP _Data_. 101 | 102 | You can also create a receiver directly from the client: 103 | 104 | [source, $lang] 105 | ---- 106 | {@link examples.AmqpClientExamples#receiverFromClient(io.vertx.amqp.AmqpClient)} 107 | ---- 108 | 109 | In this case, a connection is established automatically. You can retrieve it using 110 | {@link io.vertx.amqp.AmqpReceiver#connection()} 111 | 112 | By default the messages are automatically acknowledged. You can disable this behavior using 113 | {@link io.vertx.amqp.AmqpReceiverOptions#setAutoAcknowledgement(boolean)}. Then, you need to explicitly acknowledge 114 | the incoming messages using: 115 | * {@link io.vertx.amqp.AmqpMessage#accepted()} 116 | * {@link io.vertx.amqp.AmqpMessage#rejected()} 117 | * {@link io.vertx.amqp.AmqpMessage#released()} 118 | 119 | == Creating a sender 120 | 121 | Senders allows publishing messages to queues and topics. You retrieve a sender as follows: 122 | 123 | [source,$lang] 124 | ---- 125 | {@link examples.AmqpClientExamples#sender(io.vertx.amqp.AmqpConnection)} 126 | ---- 127 | 128 | Once you have retrieved an AMQP sender, you can create messages. Because {@link io.vertx.amqp.AmqpMessage} are 129 | immutable, the creation uses the {@link io.vertx.amqp.AmqpMessageBuilder} builder class. The following snippet 130 | provides a few examples: 131 | 132 | [source,$lang] 133 | ---- 134 | {@link examples.AmqpClientExamples#messages()} 135 | ---- 136 | 137 | Once you have the sender and created the message, you can send it using: 138 | 139 | * {@link io.vertx.amqp.AmqpSender#send(AmqpMessage)} - send the message 140 | * {@link io.vertx.amqp.AmqpSender#sendWithAck(AmqpMessage)} - send the message and monitor its acknowledgment 141 | 142 | The simplest way to send a message is the following: 143 | 144 | [source,$lang] 145 | ---- 146 | {@link examples.AmqpClientExamples#send(io.vertx.amqp.AmqpSender)} 147 | ---- 148 | 149 | When sending a message, you can monitor the acknowledgment: 150 | 151 | [source,$lang] 152 | ---- 153 | {@link examples.AmqpClientExamples#sendWithAck(io.vertx.amqp.AmqpSender)} 154 | ---- 155 | 156 | Note that message is considered as acknowledged if the delivery is set fo `ACCEPTED`. Other delivery values are considered 157 | as non-acknowledged (details can be found in the passed cause). 158 | 159 | The {@link io.vertx.amqp.AmqpSender} can be used as a write stream. The flow control is implemented using AMQP credits. 160 | 161 | You can also create a sender directly from the client: 162 | 163 | [source, $lang] 164 | ---- 165 | {@link examples.AmqpClientExamples#senderFromClient(io.vertx.amqp.AmqpClient)} 166 | ---- 167 | 168 | In this case, a connection is established automatically. You can retrieve it using 169 | {@link io.vertx.amqp.AmqpSender#connection()}. 170 | 171 | == Implementing request-reply 172 | 173 | To implement a request-reply behavior, you could use a dynamic receiver and an anonymous sender. A dynamic receiver is not 174 | associated with an address by the user, but the address it provided by the broker. Anonymous senders are also not associated to 175 | a specific address, requiring all messages to contain an address. 176 | 177 | The following snippet shows how request-reply can be implemented: 178 | 179 | [source, $lang] 180 | ---- 181 | {@link examples.AmqpClientExamples#requestReply(io.vertx.amqp.AmqpConnection)} 182 | ---- 183 | 184 | To reply to a message, send it to the address specified into the `reply-to`. Also, it's a good practice to indicate the 185 | `correlation id` using the `message id`, so the reply receiver can associate the response to the request. 186 | 187 | == Closing the client 188 | 189 | Once you are done with a connection receiver or sender, you should close them using the `close` method. Closing a 190 | connection, closes all created receivers and senders. 191 | 192 | Once the client is not used anymore, you must also close it. It would close all opened connections, and as a consequences 193 | receivers and senders. 194 | 195 | -------------------------------------------------------------------------------- /src/main/generated/io/vertx/amqp/AmqpClientOptionsConverter.java: -------------------------------------------------------------------------------- 1 | package io.vertx.amqp; 2 | 3 | import io.vertx.core.json.JsonObject; 4 | import io.vertx.core.json.JsonArray; 5 | import java.time.Instant; 6 | import java.time.format.DateTimeFormatter; 7 | 8 | /** 9 | * Converter and mapper for {@link io.vertx.amqp.AmqpClientOptions}. 10 | * NOTE: This class has been automatically generated from the {@link io.vertx.amqp.AmqpClientOptions} original class using Vert.x codegen. 11 | */ 12 | public class AmqpClientOptionsConverter { 13 | 14 | static void fromJson(Iterable> json, AmqpClientOptions obj) { 15 | for (java.util.Map.Entry member : json) { 16 | switch (member.getKey()) { 17 | case "host": 18 | if (member.getValue() instanceof String) { 19 | obj.setHost((String)member.getValue()); 20 | } 21 | break; 22 | case "port": 23 | if (member.getValue() instanceof Number) { 24 | obj.setPort(((Number)member.getValue()).intValue()); 25 | } 26 | break; 27 | case "username": 28 | if (member.getValue() instanceof String) { 29 | obj.setUsername((String)member.getValue()); 30 | } 31 | break; 32 | case "password": 33 | if (member.getValue() instanceof String) { 34 | obj.setPassword((String)member.getValue()); 35 | } 36 | break; 37 | case "containerId": 38 | if (member.getValue() instanceof String) { 39 | obj.setContainerId((String)member.getValue()); 40 | } 41 | break; 42 | case "connectionHostname": 43 | if (member.getValue() instanceof String) { 44 | obj.setConnectionHostname((String)member.getValue()); 45 | } 46 | break; 47 | } 48 | } 49 | } 50 | 51 | static void toJson(AmqpClientOptions obj, JsonObject json) { 52 | toJson(obj, json.getMap()); 53 | } 54 | 55 | static void toJson(AmqpClientOptions obj, java.util.Map json) { 56 | if (obj.getHost() != null) { 57 | json.put("host", obj.getHost()); 58 | } 59 | json.put("port", obj.getPort()); 60 | if (obj.getUsername() != null) { 61 | json.put("username", obj.getUsername()); 62 | } 63 | if (obj.getPassword() != null) { 64 | json.put("password", obj.getPassword()); 65 | } 66 | if (obj.getContainerId() != null) { 67 | json.put("containerId", obj.getContainerId()); 68 | } 69 | if (obj.getConnectionHostname() != null) { 70 | json.put("connectionHostname", obj.getConnectionHostname()); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/generated/io/vertx/amqp/AmqpReceiverOptionsConverter.java: -------------------------------------------------------------------------------- 1 | package io.vertx.amqp; 2 | 3 | import io.vertx.core.json.JsonObject; 4 | import io.vertx.core.json.JsonArray; 5 | import java.time.Instant; 6 | import java.time.format.DateTimeFormatter; 7 | 8 | /** 9 | * Converter and mapper for {@link io.vertx.amqp.AmqpReceiverOptions}. 10 | * NOTE: This class has been automatically generated from the {@link io.vertx.amqp.AmqpReceiverOptions} original class using Vert.x codegen. 11 | */ 12 | public class AmqpReceiverOptionsConverter { 13 | 14 | static void fromJson(Iterable> json, AmqpReceiverOptions obj) { 15 | for (java.util.Map.Entry member : json) { 16 | switch (member.getKey()) { 17 | case "linkName": 18 | if (member.getValue() instanceof String) { 19 | obj.setLinkName((String)member.getValue()); 20 | } 21 | break; 22 | case "dynamic": 23 | if (member.getValue() instanceof Boolean) { 24 | obj.setDynamic((Boolean)member.getValue()); 25 | } 26 | break; 27 | case "qos": 28 | if (member.getValue() instanceof String) { 29 | obj.setQos((String)member.getValue()); 30 | } 31 | break; 32 | case "capabilities": 33 | if (member.getValue() instanceof JsonArray) { 34 | java.util.ArrayList list = new java.util.ArrayList<>(); 35 | ((Iterable)member.getValue()).forEach( item -> { 36 | if (item instanceof String) 37 | list.add((String)item); 38 | }); 39 | obj.setCapabilities(list); 40 | } 41 | break; 42 | case "capabilitys": 43 | if (member.getValue() instanceof JsonArray) { 44 | ((Iterable)member.getValue()).forEach( item -> { 45 | if (item instanceof String) 46 | obj.addCapability((String)item); 47 | }); 48 | } 49 | break; 50 | case "durable": 51 | if (member.getValue() instanceof Boolean) { 52 | obj.setDurable((Boolean)member.getValue()); 53 | } 54 | break; 55 | case "maxBufferedMessages": 56 | if (member.getValue() instanceof Number) { 57 | obj.setMaxBufferedMessages(((Number)member.getValue()).intValue()); 58 | } 59 | break; 60 | case "autoAcknowledgement": 61 | if (member.getValue() instanceof Boolean) { 62 | obj.setAutoAcknowledgement((Boolean)member.getValue()); 63 | } 64 | break; 65 | case "selector": 66 | if (member.getValue() instanceof String) { 67 | obj.setSelector((String)member.getValue()); 68 | } 69 | break; 70 | case "noLocal": 71 | if (member.getValue() instanceof Boolean) { 72 | obj.setNoLocal((Boolean)member.getValue()); 73 | } 74 | break; 75 | } 76 | } 77 | } 78 | 79 | static void toJson(AmqpReceiverOptions obj, JsonObject json) { 80 | toJson(obj, json.getMap()); 81 | } 82 | 83 | static void toJson(AmqpReceiverOptions obj, java.util.Map json) { 84 | if (obj.getLinkName() != null) { 85 | json.put("linkName", obj.getLinkName()); 86 | } 87 | json.put("dynamic", obj.isDynamic()); 88 | if (obj.getQos() != null) { 89 | json.put("qos", obj.getQos()); 90 | } 91 | if (obj.getCapabilities() != null) { 92 | JsonArray array = new JsonArray(); 93 | obj.getCapabilities().forEach(item -> array.add(item)); 94 | json.put("capabilities", array); 95 | } 96 | json.put("durable", obj.isDurable()); 97 | json.put("maxBufferedMessages", obj.getMaxBufferedMessages()); 98 | json.put("autoAcknowledgement", obj.isAutoAcknowledgement()); 99 | if (obj.getSelector() != null) { 100 | json.put("selector", obj.getSelector()); 101 | } 102 | json.put("noLocal", obj.isNoLocal()); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/generated/io/vertx/amqp/AmqpSenderOptionsConverter.java: -------------------------------------------------------------------------------- 1 | package io.vertx.amqp; 2 | 3 | import io.vertx.core.json.JsonObject; 4 | import io.vertx.core.json.JsonArray; 5 | import java.time.Instant; 6 | import java.time.format.DateTimeFormatter; 7 | 8 | /** 9 | * Converter and mapper for {@link io.vertx.amqp.AmqpSenderOptions}. 10 | * NOTE: This class has been automatically generated from the {@link io.vertx.amqp.AmqpSenderOptions} original class using Vert.x codegen. 11 | */ 12 | public class AmqpSenderOptionsConverter { 13 | 14 | static void fromJson(Iterable> json, AmqpSenderOptions obj) { 15 | for (java.util.Map.Entry member : json) { 16 | switch (member.getKey()) { 17 | case "linkName": 18 | if (member.getValue() instanceof String) { 19 | obj.setLinkName((String)member.getValue()); 20 | } 21 | break; 22 | case "dynamic": 23 | if (member.getValue() instanceof Boolean) { 24 | obj.setDynamic((Boolean)member.getValue()); 25 | } 26 | break; 27 | case "autoDrained": 28 | if (member.getValue() instanceof Boolean) { 29 | obj.setAutoDrained((Boolean)member.getValue()); 30 | } 31 | break; 32 | case "capabilities": 33 | if (member.getValue() instanceof JsonArray) { 34 | java.util.ArrayList list = new java.util.ArrayList<>(); 35 | ((Iterable)member.getValue()).forEach( item -> { 36 | if (item instanceof String) 37 | list.add((String)item); 38 | }); 39 | obj.setCapabilities(list); 40 | } 41 | break; 42 | case "capabilitys": 43 | if (member.getValue() instanceof JsonArray) { 44 | ((Iterable)member.getValue()).forEach( item -> { 45 | if (item instanceof String) 46 | obj.addCapability((String)item); 47 | }); 48 | } 49 | break; 50 | } 51 | } 52 | } 53 | 54 | static void toJson(AmqpSenderOptions obj, JsonObject json) { 55 | toJson(obj, json.getMap()); 56 | } 57 | 58 | static void toJson(AmqpSenderOptions obj, java.util.Map json) { 59 | if (obj.getLinkName() != null) { 60 | json.put("linkName", obj.getLinkName()); 61 | } 62 | json.put("dynamic", obj.isDynamic()); 63 | json.put("autoDrained", obj.isAutoDrained()); 64 | if (obj.getCapabilities() != null) { 65 | JsonArray array = new JsonArray(); 66 | obj.getCapabilities().forEach(item -> array.add(item)); 67 | json.put("capabilities", array); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/examples/AmqpClientExamples.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package examples; 17 | 18 | import io.vertx.amqp.*; 19 | import io.vertx.core.Vertx; 20 | import io.vertx.core.json.JsonObject; 21 | 22 | public class AmqpClientExamples { 23 | 24 | public void creation(Vertx vertx) { 25 | AmqpClientOptions options = new AmqpClientOptions() 26 | .setHost("localhost") 27 | .setPort(5672) 28 | .setUsername("user") 29 | .setPassword("secret"); 30 | // Create a client using its own internal Vert.x instance. 31 | AmqpClient client1 = AmqpClient.create(options); 32 | 33 | // USe an explicit Vert.x instance. 34 | AmqpClient client2 = AmqpClient.create(vertx, options); 35 | } 36 | 37 | public void connect(AmqpClient client) { 38 | client 39 | .connect() 40 | .onComplete(ar -> { 41 | if (ar.failed()) { 42 | System.out.println("Unable to connect to the broker"); 43 | } else { 44 | System.out.println("Connection succeeded"); 45 | AmqpConnection connection = ar.result(); 46 | } 47 | }); 48 | } 49 | 50 | public void receiver1(AmqpConnection connection) { 51 | connection 52 | .createReceiver("my-queue") 53 | .onComplete( 54 | done -> { 55 | if (done.failed()) { 56 | System.out.println("Unable to create receiver"); 57 | } else { 58 | AmqpReceiver receiver = done.result(); 59 | receiver.handler(msg -> { 60 | // called on every received messages 61 | System.out.println("Received " + msg.bodyAsString()); 62 | }); 63 | } 64 | } 65 | ); 66 | } 67 | 68 | public void receiverFromClient(AmqpClient client) { 69 | client 70 | .createReceiver("my-queue") 71 | .onComplete( 72 | done -> { 73 | if (done.failed()) { 74 | System.out.println("Unable to create receiver"); 75 | } else { 76 | AmqpReceiver receiver = done.result(); 77 | receiver.handler(msg -> { 78 | // called on every received messages 79 | System.out.println("Received " + msg.bodyAsString()); 80 | }); 81 | } 82 | } 83 | ); 84 | } 85 | 86 | public void senderFromClient(AmqpClient client) { 87 | client 88 | .createSender("my-queue") 89 | .onComplete(maybeSender -> { 90 | //... 91 | }); 92 | } 93 | 94 | public void receiver2(AmqpConnection connection) { 95 | connection 96 | .createReceiver("my-queue") 97 | .onComplete( 98 | done -> { 99 | if (done.failed()) { 100 | System.out.println("Unable to create receiver"); 101 | } else { 102 | AmqpReceiver receiver = done.result(); 103 | receiver 104 | .exceptionHandler(t -> { 105 | // Error thrown. 106 | }) 107 | .handler(msg -> { 108 | // Attach the message handler 109 | }); 110 | } 111 | } 112 | ); 113 | } 114 | 115 | public void sender(AmqpConnection connection) { 116 | connection 117 | .createSender("my-queue") 118 | .onComplete(done -> { 119 | if (done.failed()) { 120 | System.out.println("Unable to create a sender"); 121 | } else { 122 | AmqpSender result = done.result(); 123 | } 124 | }); 125 | } 126 | 127 | public void messages() { 128 | // Retrieve a builder 129 | AmqpMessageBuilder builder = AmqpMessage.create(); 130 | 131 | // Very simple message 132 | AmqpMessage m1 = builder.withBody("hello").build(); 133 | 134 | // Message overriding the destination 135 | AmqpMessage m2 = builder.withBody("hello").address("another-queue").build(); 136 | 137 | // Message with a JSON object as body, metadata and TTL 138 | AmqpMessage m3 = builder 139 | .withJsonObjectAsBody(new JsonObject().put("message", "hello")) 140 | .subject("subject") 141 | .ttl(10000) 142 | .applicationProperties(new JsonObject().put("prop1", "value1")) 143 | .build(); 144 | } 145 | 146 | public void send(AmqpSender sender) { 147 | sender.send(AmqpMessage.create().withBody("hello").build()); 148 | } 149 | 150 | public void sendWithAck(AmqpSender sender) { 151 | sender 152 | .sendWithAck(AmqpMessage.create().withBody("hello").build()) 153 | .onComplete(acked -> { 154 | if (acked.succeeded()) { 155 | System.out.println("Message accepted"); 156 | } else { 157 | System.out.println("Message not accepted"); 158 | } 159 | }); 160 | } 161 | 162 | public void requestReply(AmqpConnection connection) { 163 | // On the receiver side (receiving the initial request and replying) 164 | connection 165 | .createAnonymousSender() 166 | .onComplete(responseSender -> { 167 | // You got an anonymous sender, used to send the reply 168 | // Now register the main receiver: 169 | connection 170 | .createReceiver("my-queue") 171 | .onComplete(done -> { 172 | if (done.failed()) { 173 | System.out.println("Unable to create receiver"); 174 | } else { 175 | AmqpReceiver receiver = done.result(); 176 | receiver.handler(msg -> { 177 | // You got the message, let's reply. 178 | responseSender.result().send(AmqpMessage.create() 179 | .address(msg.replyTo()) 180 | .correlationId(msg.id()) // send the message id as correlation id 181 | .withBody("my response to your request") 182 | .build() 183 | ); 184 | }); 185 | } 186 | }); 187 | }); 188 | 189 | // On the sender side (sending the initial request and expecting a reply) 190 | connection 191 | .createDynamicReceiver() 192 | .onComplete(replyReceiver -> { 193 | // We got a receiver, the address is provided by the broker 194 | String replyToAddress = replyReceiver.result().address(); 195 | 196 | // Attach the handler receiving the reply 197 | replyReceiver.result().handler(msg -> { 198 | System.out.println("Got the reply! " + msg.bodyAsString()); 199 | }); 200 | 201 | // Create a sender and send the message: 202 | connection 203 | .createSender("my-queue") 204 | .onComplete(sender -> { 205 | sender.result().send(AmqpMessage.create() 206 | .replyTo(replyToAddress) 207 | .id("my-message-id") 208 | .withBody("This is my request").build()); 209 | }); 210 | }); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/main/java/examples/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | @Source 17 | package examples; 18 | 19 | import io.vertx.docgen.Source; 20 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/amqp/AmqpClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp; 17 | 18 | import io.vertx.amqp.impl.AmqpClientImpl; 19 | import io.vertx.codegen.annotations.VertxGen; 20 | import io.vertx.core.Future; 21 | import io.vertx.core.Vertx; 22 | 23 | import java.util.Objects; 24 | 25 | /** 26 | * AMQP Client entry point. 27 | * Use this interface to create an instance of {@link AmqpClient} and connect to a broker and server. 28 | */ 29 | @VertxGen 30 | public interface AmqpClient { 31 | 32 | /** 33 | * Creates a new instance of {@link AmqpClient} using an internal Vert.x instance (with default configuration) and 34 | * the given AMQP client configuration. Note that the created Vert.x instance will be closed when the client is 35 | * closed. 36 | * 37 | * @param options the AMQP client options, may be {@code null} falling back to the default configuration 38 | * @return the created instances. 39 | */ 40 | static AmqpClient create(AmqpClientOptions options) { 41 | return new AmqpClientImpl(Vertx.vertx(), options, true); 42 | } 43 | 44 | /** 45 | * Creates a new instance of {@link AmqpClient} with the given Vert.x instance and the given options. 46 | * 47 | * @param vertx the vert.x instance, must not be {@code null} 48 | * @param options the AMQP options, may be @{code null} falling back to the default configuration 49 | * @return the AMQP client instance 50 | */ 51 | static AmqpClient create(Vertx vertx, AmqpClientOptions options) { 52 | return new AmqpClientImpl(Objects.requireNonNull(vertx), options, false); 53 | } 54 | 55 | /** 56 | * Connects to the AMQP broker or router. The location is specified in the {@link AmqpClientOptions} as well as the 57 | * potential credential required. 58 | * 59 | * @return a future notified with the result, giving either the connection or failure cause. Must 60 | * not be {@code null}. 61 | */ 62 | Future connect(); 63 | 64 | /** 65 | * Closes the client. 66 | * The client must always be closed once not needed anymore. 67 | * 68 | * @return a future notified when the operation completes 69 | */ 70 | Future close(); 71 | 72 | /** 73 | * Creates a receiver used to consume messages from the given address. The receiver has no handler and won't 74 | * start receiving messages until a handler is explicitly configured. This method avoids having to connect explicitly. 75 | * You can retrieve the connection using {@link AmqpReceiver#connection()}. 76 | * 77 | * @param address The source address to attach the consumer to, must not be {@code null} 78 | * @return a future notified with the receiver. The receiver has been opened. 79 | */ 80 | Future createReceiver(String address); 81 | 82 | /** 83 | * Creates a receiver used to consumer messages from the given address. This method avoids having to connect 84 | * explicitly. You can retrieve the connection using {@link AmqpReceiver#connection()}. 85 | * 86 | * @param address The source address to attach the consumer to. 87 | * @param receiverOptions The options for this receiver. 88 | * @return a future notified with the receiver, once opened. Note that the {@code messageHandler} 89 | * can be called before the {@code completionHandler} if messages are awaiting delivery. 90 | */ 91 | Future createReceiver(String address, AmqpReceiverOptions receiverOptions); 92 | 93 | /** 94 | * Creates a sender used to send messages to the given address. The address must be set. 95 | * 96 | * @param address The target address to attach to, must not be {@code null} 97 | * @return a future notified with the sender, once opened 98 | */ 99 | Future createSender(String address); 100 | 101 | /** 102 | * Creates a sender used to send messages to the given address. The address must be set. 103 | * 104 | * @param address The target address to attach to, must not be {@code null} 105 | * @param options The options for this sender. 106 | * @return a future notified with the sender, once opened 107 | */ 108 | Future createSender(String address, AmqpSenderOptions options); 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/amqp/AmqpConnection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp; 17 | 18 | import io.vertx.codegen.annotations.Fluent; 19 | import io.vertx.codegen.annotations.GenIgnore; 20 | import io.vertx.codegen.annotations.VertxGen; 21 | import io.vertx.core.Future; 22 | import io.vertx.core.Handler; 23 | import io.vertx.proton.ProtonConnection; 24 | 25 | import static io.vertx.codegen.annotations.GenIgnore.PERMITTED_TYPE; 26 | 27 | /** 28 | * Once connected to the broker or router, you get a connection. This connection is automatically opened. 29 | */ 30 | @VertxGen 31 | public interface AmqpConnection { 32 | 33 | /** 34 | * Registers a handler called on disconnection. 35 | * 36 | * @param handler the exception handler. 37 | * @return the connection 38 | */ 39 | @Fluent 40 | AmqpConnection exceptionHandler(Handler handler); 41 | 42 | /** 43 | * Closes the AMQP connection, i.e. allows the Close frame to be emitted. 44 | * 45 | * @return a future notified when the connection is closed 46 | * @return the connection 47 | */ 48 | Future close(); 49 | 50 | /** 51 | * Creates a receiver used to consume messages from the given address. The receiver has no handler and won't 52 | * start receiving messages until a handler is explicitly configured. 53 | * 54 | * @param address The source address to attach the consumer to, must not be {@code null} 55 | * @return a future notified with the receiver. The receiver has been opened. 56 | */ 57 | Future createReceiver(String address); 58 | 59 | /** 60 | * Creates a receiver used to consumer messages from the given address. 61 | * 62 | * @param address The source address to attach the consumer to. 63 | * @param receiverOptions The options for this receiver. 64 | * @return a future notified with the receiver, once opened. Note that the {@code messageHandler} 65 | * can be called before the {@code completionHandler} if messages are awaiting delivery. 66 | */ 67 | Future createReceiver(String address, AmqpReceiverOptions receiverOptions); 68 | 69 | /** 70 | * Creates a dynamic receiver. The address is provided by the broker and is available in the {@code completionHandler}, 71 | * using the {@link AmqpReceiver#address()} method. this method is useful for request-reply to generate a unique 72 | * reply address. 73 | * 74 | * @return a future notified when the receiver has been created and opened. 75 | */ 76 | Future createDynamicReceiver(); 77 | 78 | /** 79 | * Creates a sender used to send messages to the given address. The address must be set. For anonymous sender, check 80 | * {@link #createAnonymousSender()}. 81 | * 82 | * @param address The target address to attach to, must not be {@code null} 83 | * @return a future notified with the sender, once opened 84 | * @see #createAnonymousSender() 85 | */ 86 | Future createSender(String address); 87 | 88 | /** 89 | * Creates a sender used to send messages to the given address. The address must be set. For anonymous sender, check 90 | * {@link #createAnonymousSender()}. 91 | * 92 | * @param address The target address to attach to, allowed to be {@code null} if the {@code options} 93 | * configures the sender to be attached to a dynamic address (provided by the broker). 94 | * @param options The AMQP sender options 95 | * @return a future notified with the sender, once opened 96 | * @see #createAnonymousSender() 97 | */ 98 | Future createSender(String address, AmqpSenderOptions options); 99 | 100 | /** 101 | * Creates an anonymous sender. 102 | *

103 | * Unlike "regular" sender, this sender is not associated to a specific address, and each message sent must provide 104 | * an address. This method can be used in request-reply scenarios where you create a sender to send the reply, 105 | * but you don't know the address, as the reply address is passed into the message you are going to receive. 106 | * 107 | * @return a future notifid with the created sender, once opened 108 | */ 109 | Future createAnonymousSender(); 110 | 111 | /** 112 | * @return whether the connection has been disconnected. 113 | */ 114 | boolean isDisconnected(); 115 | 116 | /** 117 | * @return a future completed when the connection is closed 118 | */ 119 | Future closeFuture(); 120 | 121 | /** 122 | * @return the underlying ProtonConnection. 123 | */ 124 | @GenIgnore(PERMITTED_TYPE) 125 | ProtonConnection unwrap(); 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/amqp/AmqpMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp; 17 | 18 | import io.vertx.amqp.impl.AmqpMessageBuilderImpl; 19 | import io.vertx.codegen.annotations.Fluent; 20 | import io.vertx.codegen.annotations.GenIgnore; 21 | import io.vertx.codegen.annotations.VertxGen; 22 | import io.vertx.core.buffer.Buffer; 23 | import io.vertx.core.json.JsonArray; 24 | import io.vertx.core.json.JsonObject; 25 | import org.apache.qpid.proton.message.Message; 26 | 27 | import java.time.Instant; 28 | import java.util.List; 29 | import java.util.Map; 30 | import java.util.UUID; 31 | 32 | /** 33 | * Represents an AMQP message. 34 | *

35 | * Reference about the different metadata can be found on 36 | * AMQP message properties. 37 | *

38 | * Note that the body is retrieved using {@code body*} method depending on the expected type. 39 | */ 40 | @VertxGen 41 | public interface AmqpMessage { 42 | 43 | /** 44 | * @return a builder to create an {@link AmqpMessage}. 45 | */ 46 | static AmqpMessageBuilder create() { 47 | return new AmqpMessageBuilderImpl(); 48 | } 49 | 50 | /** 51 | * Creates a builder to create a new {@link AmqpMessage} copying the metadata from the passed message. 52 | * 53 | * @param existing an existing message, must not be {@code null}. 54 | * @return a builder to create an {@link AmqpMessage}. 55 | */ 56 | static AmqpMessageBuilder create(AmqpMessage existing) { 57 | return new AmqpMessageBuilderImpl(existing); 58 | } 59 | 60 | /** 61 | * Creates a builder to create a new {@link AmqpMessage} copying the metadata from the passed (Proton) message. 62 | * 63 | * @param existing an existing (Proton) message, must not be {@code null}. 64 | * @return a builder to create an {@link AmqpMessage}. 65 | */ 66 | @GenIgnore 67 | static AmqpMessageBuilder create(Message existing) { 68 | return new AmqpMessageBuilderImpl(existing); 69 | } 70 | 71 | /** 72 | * @return whether or not the message is durable. 73 | * @see AMQP specification 74 | */ 75 | boolean isDurable(); 76 | 77 | /** 78 | * @return if {@code true}, then this message has not been acquired by any other link. If {@code false}, then this 79 | * message MAY have previously been acquired by another link or links. 80 | * @see AMQP specification 81 | */ 82 | boolean isFirstAcquirer(); 83 | 84 | /** 85 | * @return the relative message priority. Higher numbers indicate higher priority messages. Messages with higher 86 | * priorities MAY be delivered before those with lower priorities. 87 | * @see AMQP specification 88 | */ 89 | int priority(); 90 | 91 | /** 92 | * @return the number of unsuccessful previous attempts to deliver this message. If this value is non-zero it can be 93 | * taken as an indication that the delivery might be a duplicate. On first delivery, the value is zero. It is 94 | * incremented upon an outcome being settled at the sender, according to rules defined for each outcome. 95 | * @see AMQP specification 96 | */ 97 | int deliveryCount(); 98 | 99 | /** 100 | * @return the duration in milliseconds for which the message is to be considered "live". 101 | * @see AMQP specification 102 | */ 103 | long ttl(); 104 | 105 | /** 106 | * @return the message id 107 | * @see AMQP specification 108 | */ 109 | String id(); 110 | 111 | /** 112 | * @return the message address, also named {@code to} field 113 | * @see AMQP specification 114 | */ 115 | String address(); 116 | 117 | /** 118 | * @return The address of the node to send replies to, if any. 119 | * @see AMQP specification 120 | */ 121 | String replyTo(); 122 | 123 | /** 124 | * @return The client-specific id that can be used to mark or identify messages between clients. 125 | * @see AMQP specification 126 | */ 127 | String correlationId(); 128 | 129 | /** 130 | * @return whether the body is {@code null}. This method returns {@code true} is the message does not contain a body or 131 | * if the message contain a {@code null} AMQP value as body. 132 | */ 133 | boolean isBodyNull(); 134 | 135 | /** 136 | * @return the boolean value contained in the body. The value must be passed as AMQP value. 137 | */ 138 | boolean bodyAsBoolean(); 139 | 140 | /** 141 | * @return the byte value contained in the body. The value must be passed as AMQP value. 142 | */ 143 | byte bodyAsByte(); 144 | 145 | /** 146 | * @return the short value contained in the body. The value must be passed as AMQP value. 147 | */ 148 | short bodyAsShort(); 149 | 150 | /** 151 | * @return the integer value contained in the body. The value must be passed as AMQP value. 152 | */ 153 | int bodyAsInteger(); 154 | 155 | /** 156 | * @return the long value contained in the body. The value must be passed as AMQP value. 157 | */ 158 | long bodyAsLong(); 159 | 160 | /** 161 | * @return the float value contained in the body. The value must be passed as AMQP value. 162 | */ 163 | float bodyAsFloat(); 164 | 165 | /** 166 | * @return the double value contained in the body. The value must be passed as AMQP value. 167 | */ 168 | double bodyAsDouble(); 169 | 170 | /** 171 | * @return the character value contained in the body. The value must be passed as AMQP value. 172 | */ 173 | char bodyAsChar(); 174 | 175 | /** 176 | * @return the timestamp value contained in the body. The value must be passed as AMQP value. 177 | */ 178 | @GenIgnore(GenIgnore.PERMITTED_TYPE) 179 | Instant bodyAsTimestamp(); 180 | 181 | /** 182 | * @return the UUID value contained in the body. The value must be passed as AMQP value. 183 | */ 184 | @GenIgnore(GenIgnore.PERMITTED_TYPE) 185 | UUID bodyAsUUID(); 186 | 187 | /** 188 | * @return the bytes contained in the body. The value must be passed as AMQP data. 189 | */ 190 | Buffer bodyAsBinary(); 191 | 192 | /** 193 | * @return the string value contained in the body. The value must be passed as AMQP value. 194 | */ 195 | String bodyAsString(); 196 | 197 | /** 198 | * @return the symbol value contained in the body. The value must be passed as AMQP value. 199 | */ 200 | String bodyAsSymbol(); 201 | 202 | /** 203 | * @return the list of values contained in the body. The value must be passed as AMQP value. 204 | */ 205 | List bodyAsList(); 206 | 207 | /** 208 | * @return the map contained in the body. The value must be passed as AMQP value. 209 | */ 210 | @GenIgnore 211 | Map bodyAsMap(); 212 | 213 | /** 214 | * @return the JSON object contained in the body. The value must be passed as AMQP data. 215 | */ 216 | JsonObject bodyAsJsonObject(); 217 | 218 | /** 219 | * @return the JSON array contained in the body. The value must be passed as AMQP data. 220 | */ 221 | JsonArray bodyAsJsonArray(); 222 | 223 | String subject(); 224 | 225 | String contentType(); 226 | 227 | String contentEncoding(); 228 | 229 | long expiryTime(); 230 | 231 | long creationTime(); 232 | 233 | String groupId(); 234 | 235 | String replyToGroupId(); 236 | 237 | long groupSequence(); 238 | 239 | /** 240 | * @return the message properties as JSON object. 241 | */ 242 | JsonObject applicationProperties(); 243 | 244 | @GenIgnore 245 | Message unwrap(); 246 | 247 | /** 248 | * When receiving a message, and when auto-acknowledgement is disabled, this method is used to acknowledge 249 | * the incoming message. It marks the message as delivered with the {@code accepted} status. 250 | * 251 | * @return the current {@link AmqpMessage} object 252 | * @throws IllegalStateException is the current message is not a received message. 253 | */ 254 | @Fluent 255 | AmqpMessage accepted(); 256 | 257 | /** 258 | * When receiving a message, and when auto-acknowledgement is disabled, this method is used to acknowledge 259 | * the incoming message as {@code rejected}. 260 | * 261 | * @return the current {@link AmqpMessage} object 262 | * @throws IllegalStateException is the current message is not a received message. 263 | */ 264 | @Fluent 265 | AmqpMessage rejected(); 266 | 267 | /** 268 | * When receiving a message, and when auto-acknowledgement is disabled, this method is used to acknowledge 269 | * the incoming message as {@code released}. 270 | * 271 | * @return the current {@link AmqpMessage} object 272 | * @throws IllegalStateException is the current message is not a received message. 273 | */ 274 | @Fluent 275 | AmqpMessage released(); 276 | 277 | /** 278 | * When receiving a message, and when auto-acknowledgement is disabled, this method is used to acknowledge 279 | * the incoming message as {@code modified}. 280 | * 281 | * @param deliveryFailed pass {@code true} to increase the failed delivery count 282 | * @param undeliverableHere pass {@code true} to prevent re-delivery of this message to the same consumer 283 | * @return the current {@link AmqpMessage} object 284 | * @throws IllegalStateException is the current message is not a received message. 285 | */ 286 | @Fluent 287 | AmqpMessage modified(boolean deliveryFailed, boolean undeliverableHere); 288 | 289 | 290 | //TODO What type should we use for delivery annotations and message annotations 291 | 292 | } 293 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/amqp/AmqpMessageBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp; 17 | 18 | import io.vertx.amqp.impl.AmqpMessageBuilderImpl; 19 | import io.vertx.codegen.annotations.GenIgnore; 20 | import io.vertx.codegen.annotations.VertxGen; 21 | import io.vertx.core.buffer.Buffer; 22 | import io.vertx.core.json.JsonArray; 23 | import io.vertx.core.json.JsonObject; 24 | 25 | import java.time.Instant; 26 | import java.util.List; 27 | import java.util.Map; 28 | import java.util.UUID; 29 | 30 | /** 31 | * Builder to create a new {@link AmqpMessage}. 32 | *

33 | * Reference about the different metadata can be found on 34 | * AMQP message properties. 35 | *

36 | * Note that the body is set using {@code withBodyAs*} method depending on the passed type. 37 | */ 38 | @VertxGen 39 | public interface AmqpMessageBuilder { 40 | 41 | /** 42 | * @return a new instance of {@link AmqpMessageBuilder} 43 | */ 44 | static AmqpMessageBuilder create() { 45 | return new AmqpMessageBuilderImpl(); 46 | } 47 | 48 | /** 49 | * @return the message. 50 | */ 51 | AmqpMessage build(); 52 | 53 | // Headers 54 | 55 | AmqpMessageBuilder priority(short priority); 56 | 57 | AmqpMessageBuilder durable(boolean durable); 58 | 59 | AmqpMessageBuilder ttl(long ttl); 60 | 61 | AmqpMessageBuilder firstAcquirer(boolean first); 62 | 63 | AmqpMessageBuilder deliveryCount(int count); 64 | 65 | // Properties 66 | 67 | AmqpMessageBuilder id(String id); 68 | 69 | AmqpMessageBuilder address(String address); 70 | 71 | AmqpMessageBuilder replyTo(String replyTo); 72 | 73 | AmqpMessageBuilder correlationId(String correlationId); 74 | 75 | AmqpMessageBuilder withBody(String value); 76 | 77 | AmqpMessageBuilder withSymbolAsBody(String value); 78 | 79 | AmqpMessageBuilder subject(String subject); 80 | 81 | AmqpMessageBuilder contentType(String ct); 82 | 83 | AmqpMessageBuilder contentEncoding(String ct); 84 | 85 | AmqpMessageBuilder expiryTime(long expiry); 86 | 87 | AmqpMessageBuilder creationTime(long ct); 88 | 89 | AmqpMessageBuilder groupId(String gi); 90 | 91 | AmqpMessageBuilder replyToGroupId(String rt); 92 | 93 | AmqpMessageBuilder applicationProperties(JsonObject props); 94 | 95 | AmqpMessageBuilder withBooleanAsBody(boolean v); 96 | 97 | AmqpMessageBuilder withByteAsBody(byte v); 98 | 99 | AmqpMessageBuilder withShortAsBody(short v); 100 | 101 | AmqpMessageBuilder withIntegerAsBody(int v); 102 | 103 | AmqpMessageBuilder withLongAsBody(long v); 104 | 105 | AmqpMessageBuilder withFloatAsBody(float v); 106 | 107 | AmqpMessageBuilder withDoubleAsBody(double v); 108 | 109 | AmqpMessageBuilder withCharAsBody(char c); 110 | 111 | @GenIgnore(GenIgnore.PERMITTED_TYPE) 112 | AmqpMessageBuilder withInstantAsBody(Instant v); 113 | 114 | @GenIgnore(GenIgnore.PERMITTED_TYPE) 115 | AmqpMessageBuilder withUuidAsBody(UUID v); 116 | 117 | @GenIgnore 118 | AmqpMessageBuilder withListAsBody(List list); 119 | 120 | @GenIgnore 121 | AmqpMessageBuilder withMapAsBody(Map map); 122 | 123 | AmqpMessageBuilder withBufferAsBody(Buffer buffer); 124 | 125 | AmqpMessageBuilder withJsonObjectAsBody(JsonObject json); 126 | 127 | AmqpMessageBuilder withJsonArrayAsBody(JsonArray json); 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/amqp/AmqpReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp; 17 | 18 | import io.vertx.codegen.annotations.CacheReturn; 19 | import io.vertx.codegen.annotations.GenIgnore; 20 | import io.vertx.codegen.annotations.Nullable; 21 | import io.vertx.codegen.annotations.VertxGen; 22 | import io.vertx.core.Future; 23 | import io.vertx.core.Handler; 24 | import io.vertx.core.streams.ReadStream; 25 | import io.vertx.proton.ProtonReceiver; 26 | 27 | import static io.vertx.codegen.annotations.GenIgnore.PERMITTED_TYPE; 28 | 29 | /** 30 | * Interface used to consume AMQP message as a stream of message. 31 | * Back pressure is implemented using AMQP credits. 32 | */ 33 | @VertxGen 34 | public interface AmqpReceiver extends ReadStream { 35 | 36 | @Override 37 | AmqpReceiver exceptionHandler(Handler handler); 38 | 39 | @Override 40 | AmqpReceiver handler(@Nullable Handler handler); 41 | 42 | @Override 43 | AmqpReceiver pause(); 44 | 45 | @Override 46 | AmqpReceiver resume(); 47 | 48 | @Override 49 | AmqpReceiver fetch(long amount); 50 | 51 | @Override 52 | AmqpReceiver endHandler(@Nullable Handler endHandler); 53 | 54 | /** 55 | * The listened address. 56 | * 57 | * @return the address, not {@code null} 58 | */ 59 | @CacheReturn 60 | String address(); 61 | 62 | /** 63 | * Closes the receiver. 64 | * 65 | * @return a future notified when the receiver has been closed 66 | */ 67 | Future close(); 68 | 69 | /** 70 | * Gets the connection having created the receiver. Cannot be {@code null} 71 | * 72 | * @return the connection having created the receiver. 73 | */ 74 | AmqpConnection connection(); 75 | 76 | /** 77 | * @return the underlying ProtonReceiver. 78 | */ 79 | @GenIgnore(PERMITTED_TYPE) 80 | ProtonReceiver unwrap(); 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/amqp/AmqpReceiverOptions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp; 17 | 18 | import io.vertx.codegen.annotations.DataObject; 19 | import io.vertx.codegen.json.annotations.JsonGen; 20 | import io.vertx.core.json.JsonObject; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.Objects; 25 | 26 | /** 27 | * Configures the AMQP Receiver. 28 | */ 29 | @DataObject 30 | @JsonGen(publicConverter = false) 31 | public class AmqpReceiverOptions { 32 | 33 | private String linkName; 34 | private boolean dynamic; 35 | private String qos; 36 | private List capabilities = new ArrayList<>(); 37 | private boolean durable; 38 | private int maxBufferedMessages; 39 | private boolean autoAcknowledgement = true; 40 | private boolean noLocal; 41 | private String selector; 42 | 43 | public AmqpReceiverOptions() { 44 | 45 | } 46 | 47 | public AmqpReceiverOptions(AmqpReceiverOptions other) { 48 | this(); 49 | setDynamic(other.isDynamic()); 50 | setLinkName(other.getLinkName()); 51 | setCapabilities(other.getCapabilities()); 52 | setDurable(other.isDurable()); 53 | setMaxBufferedMessages(other.maxBufferedMessages); 54 | setNoLocal(other.noLocal); 55 | setSelector(other.selector); 56 | } 57 | 58 | public AmqpReceiverOptions(JsonObject json) { 59 | super(); 60 | AmqpReceiverOptionsConverter.fromJson(json, this); 61 | } 62 | 63 | public JsonObject toJson() { 64 | JsonObject json = new JsonObject(); 65 | AmqpReceiverOptionsConverter.toJson(this, json); 66 | return json; 67 | } 68 | 69 | public String getLinkName() { 70 | return linkName; 71 | } 72 | 73 | public AmqpReceiverOptions setLinkName(String linkName) { 74 | this.linkName = linkName; 75 | return this; 76 | } 77 | 78 | /** 79 | * @return whether the receiver is using a dynamic address. 80 | */ 81 | public boolean isDynamic() { 82 | return dynamic; 83 | } 84 | 85 | /** 86 | * Sets whether the Source terminus to be used should specify it is 'dynamic', 87 | * requesting the peer creates a node and names it with a generated address. 88 | *

89 | * The address provided by the peer can then be inspected using the 90 | * {@link AmqpReceiver#address()} method on the {@link AmqpReceiver} received once opened. 91 | * 92 | * @param dynamic true if the receiver should request dynamic creation of a node and address to consume from 93 | * @return the options 94 | */ 95 | public AmqpReceiverOptions setDynamic(boolean dynamic) { 96 | this.dynamic = dynamic; 97 | return this; 98 | } 99 | 100 | /** 101 | * Gets the local QOS config, values can be {@code null}, {@code AT_MOST_ONCE} or {@code AT_LEAST_ONCE}. 102 | * 103 | * @return the local QOS config. 104 | */ 105 | public String getQos() { 106 | return qos; 107 | } 108 | 109 | /** 110 | * Sets the local QOS config. 111 | * 112 | * @param qos the local QOS config. Accepted values are: {@code null}, {@code AT_MOST_ONCE} or {@code AT_LEAST_ONCE}. 113 | * @return the options. 114 | */ 115 | public AmqpReceiverOptions setQos(String qos) { 116 | this.qos = qos; 117 | return this; 118 | } 119 | 120 | /** 121 | * Gets the list of capabilities to be set on the receiver source terminus. 122 | * 123 | * @return the list of capabilities, empty if none. 124 | */ 125 | public List getCapabilities() { 126 | if (capabilities == null) { 127 | return new ArrayList<>(); 128 | } 129 | return capabilities; 130 | } 131 | 132 | /** 133 | * Sets the list of capabilities to be set on the receiver source terminus. 134 | * 135 | * @param capabilities the set of source capabilities. 136 | * @return the options 137 | */ 138 | public AmqpReceiverOptions setCapabilities(List capabilities) { 139 | this.capabilities = capabilities; 140 | return this; 141 | } 142 | 143 | /** 144 | * Adds a capability to be set on the receiver source terminus. 145 | * 146 | * @param capability the source capability to add, must not be {@code null} 147 | * @return the options 148 | */ 149 | public AmqpReceiverOptions addCapability(String capability) { 150 | Objects.requireNonNull(capability, "The capability must not be null"); 151 | if (this.capabilities == null) { 152 | this.capabilities = new ArrayList<>(); 153 | } 154 | this.capabilities.add(capability); 155 | return this; 156 | } 157 | 158 | /** 159 | * @return if the receiver is durable. 160 | */ 161 | public boolean isDurable() { 162 | return durable; 163 | } 164 | 165 | /** 166 | * Sets the durability. 167 | *

168 | * Passing {@code true} sets the expiry policy of the source to {@code NEVER} and the durability of the source 169 | * to {@code UNSETTLED_STATE}. 170 | * 171 | * @param durable whether or not the receiver must indicate it's durable 172 | * @return the options. 173 | */ 174 | public AmqpReceiverOptions setDurable(boolean durable) { 175 | this.durable = durable; 176 | return this; 177 | } 178 | 179 | /** 180 | * @return the max buffered messages 181 | */ 182 | public int getMaxBufferedMessages() { 183 | return this.maxBufferedMessages; 184 | } 185 | 186 | /** 187 | * Sets the max buffered messages. This message can be used to configure the initial credit of a receiver. 188 | * 189 | * @param maxBufferSize the max buffered size, must be positive. If not set, default credit is used. 190 | * @return the current {@link AmqpReceiverOptions} instance. 191 | */ 192 | public AmqpReceiverOptions setMaxBufferedMessages(int maxBufferSize) { 193 | this.maxBufferedMessages = maxBufferSize; 194 | return this; 195 | } 196 | 197 | /** 198 | * @return {@code true} if the auto-acknowledgement is enabled, {@code false} otherwise. 199 | */ 200 | public boolean isAutoAcknowledgement() { 201 | return autoAcknowledgement; 202 | } 203 | 204 | /** 205 | * Sets the auto-acknowledgement. 206 | * When enabled (default), the messages are automatically acknowledged. If set to {@code false}, the messages must 207 | * be acknowledged explicitly using {@link AmqpMessage#accepted()}, {@link AmqpMessage#released()} and 208 | * {@link AmqpMessage#rejected()}. 209 | * 210 | * @param auto whether or not the auto-acknowledgement should be enabled. 211 | * @return the current {@link AmqpReceiverOptions} instance. 212 | */ 213 | public AmqpReceiverOptions setAutoAcknowledgement(boolean auto) { 214 | this.autoAcknowledgement = auto; 215 | return this; 216 | } 217 | 218 | /** 219 | * @return the message selector String 220 | */ 221 | public String getSelector() { 222 | return selector; 223 | } 224 | 225 | /** 226 | * Sets a message selector string. 227 | * 228 | * Used to define an "apache.org:selector-filter:string" filter on the source terminus, using SQL-based syntax to request 229 | * the server filters which messages are delivered to the receiver (if supported by the server in question). Precise 230 | * functionality supported and syntax needed can vary depending on the server. 231 | * 232 | * @param selector the selector string to set. 233 | */ 234 | public AmqpReceiverOptions setSelector(final String selector) { 235 | this.selector = selector; 236 | return this; 237 | } 238 | 239 | /** 240 | * @return whether this receiver should not receive messages that were sent on the same underlying connection 241 | */ 242 | public boolean isNoLocal() { 243 | return noLocal; 244 | } 245 | 246 | /** 247 | * Sets whether this receiver should not receive messages that were sent using the same underlying connection. 248 | * 249 | * Used to determine whether to define an "apache.org:no-local-filter:list" filter on the source terminus, requesting 250 | * that the server filters which messages are delivered to the receiver so that they do not include messages sent on 251 | * the same underlying connection (if supported by the server in question). 252 | * 253 | * @param noLocal true if this receiver shall not receive messages that were sent on the same underlying connection 254 | */ 255 | public AmqpReceiverOptions setNoLocal(final boolean noLocal) { 256 | this.noLocal = noLocal; 257 | return this; 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/amqp/AmqpSender.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp; 17 | 18 | import io.vertx.codegen.annotations.Fluent; 19 | import io.vertx.codegen.annotations.GenIgnore; 20 | import io.vertx.codegen.annotations.VertxGen; 21 | import io.vertx.core.Future; 22 | import io.vertx.core.Handler; 23 | import io.vertx.core.streams.WriteStream; 24 | import io.vertx.proton.ProtonSender; 25 | 26 | import static io.vertx.codegen.annotations.GenIgnore.PERMITTED_TYPE; 27 | 28 | /** 29 | * AMQP Sender interface used to send messages. 30 | */ 31 | @VertxGen 32 | public interface AmqpSender extends WriteStream { 33 | 34 | @Override 35 | AmqpSender exceptionHandler(Handler handler); 36 | 37 | @Override 38 | AmqpSender setWriteQueueMaxSize(int maxSize); 39 | 40 | /** 41 | * Sends an AMQP message. The destination the configured sender address or the address configured in the message. 42 | * 43 | * @param message the message, must not be {@code null} 44 | * @return the current sender 45 | */ 46 | @Fluent 47 | AmqpSender send(AmqpMessage message); 48 | 49 | /** 50 | * Sends an AMQP message and waits for an acknowledgement. It returns a future marked as failed if the message 51 | * has been rejected or re-routed. If the message has been accepted, the handler is called with a success. 52 | * 53 | * @param message the message, must not be {@code null} 54 | * @return a future notified with the acknowledgement 55 | * @return the current sender 56 | */ 57 | Future sendWithAck(AmqpMessage message); 58 | 59 | /** 60 | * Closes the sender. 61 | * 62 | * @return a future notified when the sender has been closed 63 | */ 64 | Future close(); 65 | 66 | /** 67 | * @return the configured address. 68 | */ 69 | String address(); 70 | 71 | /** 72 | * Gets the connection having created the sender. Cannot be {@code null} 73 | * 74 | * @return the connection having created the sender. 75 | */ 76 | AmqpConnection connection(); 77 | 78 | /** 79 | * @return the remaining credit, 0 is none. 80 | */ 81 | long remainingCredits(); 82 | 83 | /** 84 | * @return the underlying ProtonSender. 85 | */ 86 | @GenIgnore(PERMITTED_TYPE) 87 | ProtonSender unwrap(); 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/amqp/AmqpSenderOptions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.Objects; 21 | 22 | import io.vertx.codegen.annotations.DataObject; 23 | import io.vertx.codegen.json.annotations.JsonGen; 24 | import io.vertx.core.json.JsonObject; 25 | 26 | /** 27 | * Configures the AMQP Sender. 28 | */ 29 | @DataObject 30 | @JsonGen(publicConverter = false) 31 | public class AmqpSenderOptions { 32 | 33 | private String linkName; 34 | private boolean dynamic; 35 | private boolean autoDrained = true; 36 | private List capabilities = new ArrayList<>(); 37 | 38 | public AmqpSenderOptions() { 39 | 40 | } 41 | 42 | public AmqpSenderOptions(AmqpSenderOptions other) { 43 | this(); 44 | setDynamic(other.isDynamic()); 45 | setLinkName(other.getLinkName()); 46 | setAutoDrained(other.isAutoDrained()); 47 | } 48 | 49 | public AmqpSenderOptions(JsonObject json) { 50 | super(); 51 | AmqpSenderOptionsConverter.fromJson(json, this); 52 | } 53 | 54 | public JsonObject toJson() { 55 | JsonObject json = new JsonObject(); 56 | AmqpSenderOptionsConverter.toJson(this, json); 57 | return json; 58 | } 59 | 60 | public String getLinkName() { 61 | return linkName; 62 | } 63 | 64 | public AmqpSenderOptions setLinkName(String linkName) { 65 | this.linkName = linkName; 66 | return this; 67 | } 68 | 69 | /** 70 | * @return whether the sender is using a dynamic address. 71 | */ 72 | public boolean isDynamic() { 73 | return dynamic; 74 | } 75 | 76 | /** 77 | * Sets whether the Target terminus to be used should specify it is 'dynamic', 78 | * requesting the peer creates a node and names it with a generated address. 79 | *

80 | * The address provided by the peer can then be inspected using the 81 | * {@link AmqpSender#address()} method on the {@link AmqpSender} received once opened. 82 | * 83 | * @param dynamic true if the sender should request dynamic creation of a node and address to send to 84 | * @return the options 85 | */ 86 | public AmqpSenderOptions setDynamic(boolean dynamic) { 87 | this.dynamic = dynamic; 88 | return this; 89 | } 90 | 91 | /** 92 | * Get whether the link will automatically be marked drained after the send queue drain handler fires in drain mode. 93 | * 94 | * @return whether the link will automatically be drained after the send queue drain handler fires in drain mode 95 | * @see #setAutoDrained(boolean) 96 | */ 97 | public boolean isAutoDrained() { 98 | return autoDrained; 99 | } 100 | 101 | /** 102 | * Sets whether the link is automatically marked drained after the send queue drain handler callback 103 | * returns if the receiving peer requested that credit be drained. 104 | *

105 | * {@code true} by default. 106 | * 107 | * @param autoDrained whether the link will automatically be drained after the send queue drain handler fires in drain mode 108 | * @return the options 109 | */ 110 | public AmqpSenderOptions setAutoDrained(boolean autoDrained) { 111 | this.autoDrained = autoDrained; 112 | return this; 113 | } 114 | 115 | /** 116 | * Gets the list of capabilities to be set on the sender target terminus. 117 | * 118 | * @return the list of capabilities, empty if none. 119 | */ 120 | public List getCapabilities() { 121 | if (capabilities == null) { 122 | return new ArrayList<>(); 123 | } 124 | return capabilities; 125 | } 126 | 127 | /** 128 | * Sets the list of capabilities to be set on the sender target terminus. 129 | * 130 | * @param capabilities the set of target capabilities. 131 | * @return the options 132 | */ 133 | public AmqpSenderOptions setCapabilities(List capabilities) { 134 | this.capabilities = capabilities; 135 | return this; 136 | } 137 | 138 | /** 139 | * Adds a capability to be set on the sender target terminus. 140 | * 141 | * @param capability the target capability to add, must not be {@code null} 142 | * @return the options 143 | */ 144 | public AmqpSenderOptions addCapability(String capability) { 145 | Objects.requireNonNull(capability, "The capability must not be null"); 146 | if (this.capabilities == null) { 147 | this.capabilities = new ArrayList<>(); 148 | } 149 | this.capabilities.add(capability); 150 | return this; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/amqp/impl/AmqpClientImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp.impl; 17 | 18 | import io.vertx.amqp.*; 19 | import io.vertx.core.*; 20 | import io.vertx.core.internal.ContextInternal; 21 | import io.vertx.core.internal.PromiseInternal; 22 | import io.vertx.proton.ProtonClient; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | import java.util.Objects; 27 | import java.util.concurrent.CopyOnWriteArrayList; 28 | 29 | public class AmqpClientImpl implements AmqpClient { 30 | 31 | private final Vertx vertx; 32 | private final ProtonClient proton; 33 | private final AmqpClientOptions options; 34 | 35 | private final List connections = new CopyOnWriteArrayList<>(); 36 | private final boolean mustCloseVertxOnClose; 37 | 38 | public AmqpClientImpl(Vertx vertx, AmqpClientOptions options, boolean mustCloseVertxOnClose) { 39 | this.vertx = vertx; 40 | if (options == null) { 41 | this.options = new AmqpClientOptions(); 42 | } else { 43 | this.options = options; 44 | } 45 | this.proton = ProtonClient.create(vertx); 46 | this.mustCloseVertxOnClose = mustCloseVertxOnClose; 47 | } 48 | 49 | public AmqpClient connect(Handler> connectionHandler) { 50 | Objects.requireNonNull(connectionHandler, "Handler must not be null"); 51 | connect().onComplete(connectionHandler); 52 | return this; 53 | } 54 | 55 | @Override 56 | public Future connect() { 57 | Objects.requireNonNull(options.getHost(), "Host must be set"); 58 | ContextInternal ctx = (ContextInternal) vertx.getOrCreateContext(); 59 | PromiseInternal promise = ctx.promise(); 60 | new AmqpConnectionImpl(ctx, options, proton, promise); 61 | Future future = promise.future(); 62 | future.onSuccess(conn -> { 63 | connections.add(conn); 64 | conn.closeFuture().onComplete(ar -> { 65 | connections.remove(conn); 66 | }); 67 | }); 68 | return future; 69 | } 70 | 71 | public void close(Completable handler) { 72 | List> actions = new ArrayList<>(); 73 | for (AmqpConnection connection : connections) { 74 | actions.add(connection.close()); 75 | } 76 | 77 | Future.join(actions).onComplete(done -> { 78 | connections.clear(); 79 | if (mustCloseVertxOnClose) { 80 | vertx 81 | .close() 82 | .onComplete(x -> { 83 | if (done.succeeded() && x.succeeded()) { 84 | if (handler != null) { 85 | handler.succeed(); 86 | } 87 | } else { 88 | if (handler != null) { 89 | handler.fail(done.failed() ? done.cause() : x.cause()); 90 | } 91 | } 92 | }); 93 | } else if (handler != null) { 94 | handler.complete(null, done.cause()); 95 | } 96 | }); 97 | } 98 | 99 | @Override 100 | public Future close() { 101 | Promise promise = Promise.promise(); 102 | close(promise); 103 | return promise.future(); 104 | } 105 | 106 | public AmqpClient createReceiver(String address, 107 | Completable completionHandler) { 108 | return connect(res -> { 109 | if (res.failed()) { 110 | completionHandler.complete(null, res.cause()); 111 | } else { 112 | res.result().createReceiver(address).onComplete(completionHandler); 113 | } 114 | }); 115 | } 116 | 117 | @Override 118 | public Future createReceiver(String address) { 119 | Promise promise = Promise.promise(); 120 | createReceiver(address, promise); 121 | return promise.future(); 122 | } 123 | 124 | public AmqpClient createReceiver(String address, AmqpReceiverOptions receiverOptions, Completable completionHandler) { 125 | return connect(res -> { 126 | if (res.failed()) { 127 | completionHandler.complete(null, res.cause()); 128 | } else { 129 | res.result().createReceiver(address, receiverOptions).onComplete(completionHandler); 130 | } 131 | }); 132 | } 133 | 134 | @Override 135 | public Future createReceiver(String address, AmqpReceiverOptions receiverOptions) { 136 | Promise promise = Promise.promise(); 137 | createReceiver(address, receiverOptions, promise); 138 | return promise.future(); 139 | } 140 | 141 | public AmqpClient createSender(String address, Completable completionHandler) { 142 | return connect(res -> { 143 | if (res.failed()) { 144 | completionHandler.complete(null, res.cause()); 145 | } else { 146 | res.result().createSender(address).onComplete(completionHandler); 147 | } 148 | }); 149 | } 150 | 151 | @Override 152 | public Future createSender(String address) { 153 | Promise promise = Promise.promise(); 154 | createSender(address, promise); 155 | return promise.future(); 156 | } 157 | 158 | public AmqpClient createSender(String address, AmqpSenderOptions options, 159 | Completable completionHandler) { 160 | return connect(res -> { 161 | if (res.failed()) { 162 | completionHandler.complete(null, res.cause()); 163 | } else { 164 | res.result().createSender(address, options).onComplete(completionHandler); 165 | } 166 | }); 167 | } 168 | 169 | @Override 170 | public Future createSender(String address, AmqpSenderOptions options) { 171 | Promise promise = Promise.promise(); 172 | createSender(address, options, promise); 173 | return promise.future(); 174 | } 175 | 176 | public int numConnections() { 177 | return connections.size(); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/amqp/impl/AmqpMessageBuilderImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp.impl; 17 | 18 | import io.vertx.amqp.AmqpMessage; 19 | import io.vertx.amqp.AmqpMessageBuilder; 20 | import io.vertx.codegen.annotations.GenIgnore; 21 | import io.vertx.core.buffer.Buffer; 22 | import io.vertx.core.json.JsonArray; 23 | import io.vertx.core.json.JsonObject; 24 | import org.apache.qpid.proton.amqp.Binary; 25 | import org.apache.qpid.proton.amqp.Symbol; 26 | import org.apache.qpid.proton.amqp.messaging.AmqpValue; 27 | import org.apache.qpid.proton.amqp.messaging.ApplicationProperties; 28 | import org.apache.qpid.proton.amqp.messaging.Data; 29 | import org.apache.qpid.proton.message.Message; 30 | 31 | import java.sql.Date; 32 | import java.time.Instant; 33 | import java.util.List; 34 | import java.util.Map; 35 | import java.util.UUID; 36 | 37 | public class AmqpMessageBuilderImpl implements AmqpMessageBuilder { 38 | 39 | private Message message; 40 | 41 | public AmqpMessageBuilderImpl() { 42 | message = Message.Factory.create(); 43 | } 44 | 45 | public AmqpMessageBuilderImpl(AmqpMessage existing) { 46 | message = existing.unwrap(); 47 | } 48 | 49 | public AmqpMessageBuilderImpl(Message existing) { 50 | message = existing; 51 | } 52 | 53 | @Override 54 | public AmqpMessage build() { 55 | return new AmqpMessageImpl(message); 56 | } 57 | 58 | @Override 59 | public AmqpMessageBuilderImpl priority(short priority) { 60 | message.setPriority(priority); 61 | return this; 62 | } 63 | 64 | @Override public AmqpMessageBuilder durable(boolean durable) { 65 | message.setDurable(durable); 66 | return this; 67 | } 68 | 69 | @Override 70 | public AmqpMessageBuilderImpl id(String id) { 71 | message.setMessageId(id); 72 | return this; 73 | } 74 | 75 | @Override 76 | public AmqpMessageBuilderImpl address(String address) { 77 | message.setAddress(address); 78 | return this; 79 | } 80 | 81 | @Override 82 | public AmqpMessageBuilderImpl replyTo(String replyTo) { 83 | message.setReplyTo(replyTo); 84 | return this; 85 | } 86 | 87 | @Override 88 | public AmqpMessageBuilderImpl correlationId(String correlationId) { 89 | message.setCorrelationId(correlationId); 90 | return this; 91 | } 92 | 93 | @Override 94 | public AmqpMessageBuilderImpl withBody(String value) { 95 | message.setBody(new AmqpValue(value)); 96 | return this; 97 | } 98 | 99 | @Override 100 | public AmqpMessageBuilderImpl withSymbolAsBody(String value) { 101 | message.setBody(new AmqpValue(Symbol.valueOf(value))); 102 | return this; 103 | } 104 | 105 | @Override 106 | public AmqpMessageBuilderImpl subject(String subject) { 107 | message.setSubject(subject); 108 | return this; 109 | } 110 | 111 | @Override 112 | public AmqpMessageBuilderImpl contentType(String ct) { 113 | message.setContentType(ct); 114 | return this; 115 | } 116 | 117 | @Override 118 | public AmqpMessageBuilderImpl contentEncoding(String ct) { 119 | message.setContentEncoding(ct); 120 | return this; 121 | } 122 | 123 | @Override 124 | public AmqpMessageBuilderImpl expiryTime(long expiry) { 125 | message.setExpiryTime(expiry); 126 | return this; 127 | } 128 | 129 | @Override 130 | public AmqpMessageBuilderImpl creationTime(long ct) { 131 | message.setCreationTime(ct); 132 | return this; 133 | } 134 | 135 | @Override 136 | public AmqpMessageBuilderImpl ttl(long ttl) { 137 | message.setTtl(ttl); 138 | return this; 139 | } 140 | 141 | @Override public AmqpMessageBuilder firstAcquirer(boolean first) { 142 | message.setFirstAcquirer(first); 143 | return this; 144 | } 145 | 146 | @Override public AmqpMessageBuilder deliveryCount(int count) { 147 | message.setDeliveryCount(count); 148 | return this; 149 | } 150 | 151 | @Override 152 | public AmqpMessageBuilderImpl groupId(String gi) { 153 | message.setGroupId(gi); 154 | return this; 155 | } 156 | 157 | @Override 158 | public AmqpMessageBuilderImpl replyToGroupId(String rt) { 159 | message.setReplyToGroupId(rt); 160 | return this; 161 | } 162 | 163 | @Override 164 | public AmqpMessageBuilderImpl applicationProperties(JsonObject props) { 165 | ApplicationProperties properties = new ApplicationProperties(props.getMap()); 166 | message.setApplicationProperties(properties); 167 | return this; 168 | } 169 | 170 | @Override 171 | public AmqpMessageBuilderImpl withBooleanAsBody(boolean v) { 172 | message.setBody(new AmqpValue(v)); 173 | return this; 174 | } 175 | 176 | @Override 177 | public AmqpMessageBuilderImpl withByteAsBody(byte v) { 178 | message.setBody(new AmqpValue(v)); 179 | return this; 180 | } 181 | 182 | @Override 183 | public AmqpMessageBuilderImpl withShortAsBody(short v) { 184 | message.setBody(new AmqpValue(v)); 185 | return this; 186 | } 187 | 188 | @Override 189 | public AmqpMessageBuilderImpl withIntegerAsBody(int v) { 190 | message.setBody(new AmqpValue(v)); 191 | return this; 192 | } 193 | 194 | @Override 195 | public AmqpMessageBuilderImpl withLongAsBody(long v) { 196 | message.setBody(new AmqpValue(v)); 197 | return this; 198 | } 199 | 200 | @Override 201 | public AmqpMessageBuilderImpl withFloatAsBody(float v) { 202 | message.setBody(new AmqpValue(v)); 203 | return this; 204 | } 205 | 206 | @Override 207 | public AmqpMessageBuilderImpl withDoubleAsBody(double v) { 208 | message.setBody(new AmqpValue(v)); 209 | return this; 210 | } 211 | 212 | @Override 213 | public AmqpMessageBuilderImpl withCharAsBody(char c) { 214 | message.setBody(new AmqpValue(c)); 215 | return this; 216 | } 217 | 218 | @Override 219 | @GenIgnore(GenIgnore.PERMITTED_TYPE) 220 | public AmqpMessageBuilderImpl withInstantAsBody(Instant v) { 221 | message.setBody(new AmqpValue(Date.from(v))); 222 | return this; 223 | } 224 | 225 | @Override 226 | @GenIgnore(GenIgnore.PERMITTED_TYPE) 227 | public AmqpMessageBuilderImpl withUuidAsBody(UUID v) { 228 | message.setBody(new AmqpValue(v)); 229 | return this; 230 | } 231 | 232 | @Override 233 | public AmqpMessageBuilderImpl withListAsBody(List list) { 234 | message.setBody(new AmqpValue(list)); 235 | return this; 236 | } 237 | 238 | @Override 239 | @GenIgnore(GenIgnore.PERMITTED_TYPE) 240 | public AmqpMessageBuilderImpl withMapAsBody(Map map) { 241 | message.setBody(new AmqpValue(map)); 242 | return this; 243 | } 244 | 245 | @Override 246 | public AmqpMessageBuilderImpl withBufferAsBody(Buffer buffer) { 247 | message.setBody(new Data(new Binary(buffer.getBytes()))); 248 | return this; 249 | } 250 | 251 | @Override 252 | public AmqpMessageBuilderImpl withJsonObjectAsBody(JsonObject json) { 253 | return contentType("application/json") 254 | .withBufferAsBody(json.toBuffer()); 255 | } 256 | 257 | @Override 258 | public AmqpMessageBuilderImpl withJsonArrayAsBody(JsonArray json) { 259 | return contentType("application/json") 260 | .withBufferAsBody(json.toBuffer()); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/amqp/impl/AmqpMessageImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp.impl; 17 | 18 | import io.vertx.amqp.AmqpMessage; 19 | import io.vertx.core.buffer.Buffer; 20 | import io.vertx.core.json.JsonArray; 21 | import io.vertx.core.json.JsonObject; 22 | import io.vertx.proton.ProtonDelivery; 23 | import io.vertx.proton.ProtonHelper; 24 | import org.apache.qpid.proton.amqp.Symbol; 25 | import org.apache.qpid.proton.amqp.messaging.*; 26 | import org.apache.qpid.proton.message.Message; 27 | 28 | import java.time.Instant; 29 | import java.util.Date; 30 | import java.util.List; 31 | import java.util.Map; 32 | import java.util.UUID; 33 | 34 | public class AmqpMessageImpl implements AmqpMessage { 35 | private final Message message; 36 | private final ProtonDelivery delivery; 37 | private AmqpConnectionImpl connection; 38 | 39 | public AmqpMessageImpl(Message message, ProtonDelivery delivery, AmqpConnectionImpl connection) { 40 | this.message = message; 41 | this.delivery = delivery; 42 | this.connection = connection; 43 | } 44 | 45 | public AmqpMessageImpl(Message message) { 46 | this.message = message; 47 | this.delivery = null; 48 | } 49 | 50 | @Override 51 | public boolean isDurable() { 52 | return message.isDurable(); 53 | } 54 | 55 | @Override 56 | public boolean isFirstAcquirer() { 57 | return message.isFirstAcquirer(); 58 | } 59 | 60 | @Override 61 | public int priority() { 62 | return message.getPriority(); 63 | } 64 | 65 | @Override 66 | public String id() { 67 | Object id = message.getMessageId(); 68 | if (id != null) { 69 | return id.toString(); 70 | } 71 | return null; 72 | } 73 | 74 | @Override 75 | public String address() { 76 | return message.getAddress(); 77 | } 78 | 79 | @Override 80 | public String replyTo() { 81 | return message.getReplyTo(); 82 | } 83 | 84 | @Override 85 | public String correlationId() { 86 | Object id = message.getCorrelationId(); 87 | if (id != null) { 88 | return id.toString(); 89 | } 90 | return null; 91 | } 92 | 93 | private boolean isBodyAmqpValue() { 94 | final Section body = message.getBody(); 95 | return body != null && body.getType() == Section.SectionType.AmqpValue; 96 | } 97 | 98 | @Override 99 | public boolean isBodyNull() { 100 | final Section body = message.getBody(); 101 | return body == null || isBodyAmqpValue() && ((AmqpValue) body).getValue() == null; 102 | } 103 | 104 | private Object getAmqpValue() { 105 | if (!isBodyAmqpValue()) { 106 | throw new IllegalStateException("The body is not an AMQP Value"); 107 | } 108 | return ((AmqpValue) message.getBody()).getValue(); 109 | } 110 | 111 | @Override 112 | public boolean bodyAsBoolean() { 113 | return (boolean) getAmqpValue(); 114 | } 115 | 116 | @Override 117 | public byte bodyAsByte() { 118 | return (byte) getAmqpValue(); 119 | } 120 | 121 | @Override 122 | public short bodyAsShort() { 123 | return (short) getAmqpValue(); 124 | } 125 | 126 | @Override 127 | public int bodyAsInteger() { 128 | return (int) getAmqpValue(); 129 | } 130 | 131 | @Override 132 | public long bodyAsLong() { 133 | return (long) getAmqpValue(); 134 | } 135 | 136 | @Override 137 | public float bodyAsFloat() { 138 | return (float) getAmqpValue(); 139 | } 140 | 141 | @Override 142 | public double bodyAsDouble() { 143 | return (double) getAmqpValue(); 144 | } 145 | 146 | @Override 147 | public char bodyAsChar() { 148 | return (char) getAmqpValue(); 149 | } 150 | 151 | @Override 152 | public Instant bodyAsTimestamp() { 153 | Object value = getAmqpValue(); 154 | if (!(value instanceof Date)) { 155 | throw new IllegalStateException("Expecting a Date object, got a " + value); 156 | } 157 | return ((Date) value).toInstant(); 158 | } 159 | 160 | @Override 161 | public UUID bodyAsUUID() { 162 | return (UUID) getAmqpValue(); 163 | } 164 | 165 | @Override 166 | public Buffer bodyAsBinary() { 167 | Section body = message.getBody(); 168 | if (body.getType() != Section.SectionType.Data) { 169 | throw new IllegalStateException("The body is not of type 'data'"); 170 | } 171 | byte[] bytes = ((Data) message.getBody()).getValue().getArray(); 172 | return Buffer.buffer(bytes); 173 | } 174 | 175 | @Override 176 | public String bodyAsString() { 177 | return (String) getAmqpValue(); 178 | } 179 | 180 | @Override 181 | public String bodyAsSymbol() { 182 | Object value = getAmqpValue(); 183 | if (value instanceof Symbol) { 184 | return ((Symbol) value).toString(); 185 | } 186 | throw new IllegalStateException("Expected a Symbol, got a " + value.getClass()); 187 | } 188 | 189 | @Override 190 | @SuppressWarnings("unchecked") 191 | public List bodyAsList() { 192 | Section body = message.getBody(); 193 | if (body.getType() == Section.SectionType.AmqpSequence) { 194 | return (List) ((AmqpSequence) message.getBody()).getValue(); 195 | } else { 196 | Object value = getAmqpValue(); 197 | if (value instanceof List) { 198 | return (List) value; 199 | } 200 | throw new IllegalStateException("Cannot extract a list from the message body"); 201 | } 202 | } 203 | 204 | @Override 205 | @SuppressWarnings("unchecked") 206 | public Map bodyAsMap() { 207 | Object value = getAmqpValue(); 208 | if (value instanceof Map) { 209 | return (Map) value; 210 | } 211 | throw new IllegalStateException("Cannot extract a map from the message body"); 212 | } 213 | 214 | @Override 215 | public JsonObject bodyAsJsonObject() { 216 | return bodyAsBinary().toJsonObject(); 217 | } 218 | 219 | @Override 220 | public JsonArray bodyAsJsonArray() { 221 | return bodyAsBinary().toJsonArray(); 222 | } 223 | 224 | @Override 225 | public String subject() { 226 | return message.getSubject(); 227 | } 228 | 229 | @Override 230 | public String contentType() { 231 | return message.getContentType(); 232 | } 233 | 234 | @Override 235 | public String contentEncoding() { 236 | return message.getContentEncoding(); 237 | } 238 | 239 | @Override 240 | public long expiryTime() { 241 | return message.getExpiryTime(); 242 | } 243 | 244 | @Override 245 | public long creationTime() { 246 | return message.getCreationTime(); 247 | } 248 | 249 | @Override 250 | public long ttl() { 251 | return message.getTtl(); 252 | } 253 | 254 | @Override 255 | public int deliveryCount() { 256 | return (int) message.getDeliveryCount(); 257 | } 258 | 259 | @Override 260 | public String groupId() { 261 | return message.getGroupId(); 262 | } 263 | 264 | @Override 265 | public String replyToGroupId() { 266 | return message.getReplyToGroupId(); 267 | } 268 | 269 | @Override 270 | public long groupSequence() { 271 | return message.getGroupSequence(); 272 | } 273 | 274 | @Override 275 | public JsonObject applicationProperties() { 276 | ApplicationProperties properties = message.getApplicationProperties(); 277 | if (properties == null) { 278 | return null; 279 | } 280 | return new JsonObject(properties.getValue()); 281 | } 282 | 283 | @Override 284 | public Message unwrap() { 285 | return message; 286 | } 287 | 288 | @Override 289 | public AmqpMessage accepted() { 290 | if (delivery != null) { 291 | connection.runWithTrampoline(v -> ProtonHelper.accepted(delivery, true)); 292 | } else { 293 | throw new IllegalStateException("The message is not a received message"); 294 | } 295 | return this; 296 | } 297 | 298 | @Override 299 | public AmqpMessage rejected() { 300 | if (delivery != null) { 301 | connection.runWithTrampoline(v -> ProtonHelper.rejected(delivery, true)); 302 | } else { 303 | throw new IllegalStateException("The message is not a received message"); 304 | } 305 | return this; 306 | } 307 | 308 | @Override 309 | public AmqpMessage released() { 310 | if (delivery != null) { 311 | connection.runWithTrampoline(v -> ProtonHelper.released(delivery, true)); 312 | } else { 313 | throw new IllegalStateException("The message is not a received message"); 314 | } 315 | return this; 316 | } 317 | 318 | @Override 319 | public AmqpMessage modified(boolean deliveryFailed, boolean undeliverableHere) { 320 | if (delivery != null) { 321 | connection.runWithTrampoline(v -> ProtonHelper.modified(delivery, true, deliveryFailed, undeliverableHere)); 322 | } else { 323 | throw new IllegalStateException("The message is not a received message"); 324 | } 325 | return this; 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/amqp/impl/AmqpReceiverImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp.impl; 17 | 18 | import io.vertx.amqp.AmqpConnection; 19 | import io.vertx.amqp.AmqpMessage; 20 | import io.vertx.amqp.AmqpReceiver; 21 | import io.vertx.amqp.AmqpReceiverOptions; 22 | import io.vertx.codegen.annotations.Nullable; 23 | import io.vertx.core.*; 24 | import io.vertx.core.internal.logging.Logger; 25 | import io.vertx.core.internal.logging.LoggerFactory; 26 | import io.vertx.proton.ProtonReceiver; 27 | 28 | import java.util.ArrayDeque; 29 | import java.util.Queue; 30 | 31 | public class AmqpReceiverImpl implements AmqpReceiver { 32 | 33 | private static final Logger LOGGER = LoggerFactory.getLogger(AmqpReceiverImpl.class); 34 | private final ProtonReceiver receiver; 35 | private final AmqpConnectionImpl connection; 36 | private final Queue buffered = new ArrayDeque<>(); 37 | private final boolean durable; 38 | private final boolean autoAck; 39 | /** 40 | * The address. 41 | * Not final because for dynamic link the address is set when the createReceiver is opened. 42 | */ 43 | private String address; 44 | private Handler handler; 45 | private long demand = Long.MAX_VALUE; 46 | private boolean closed; 47 | private Handler endHandler; 48 | private Handler exceptionHandler; 49 | private boolean initialCreditGiven; 50 | private int initialCredit = 1000; 51 | 52 | /** 53 | * Creates a new instance of {@link AmqpReceiverImpl}. 54 | * This method must be called on the connection context. 55 | * 56 | * @param address the address, may be {@code null} for dynamic links 57 | * @param connection the connection 58 | * @param options the receiver options, must not be {@code null} 59 | * @param receiver the underlying proton createReceiver 60 | * @param completionHandler called when the createReceiver is opened 61 | */ 62 | AmqpReceiverImpl( 63 | String address, 64 | AmqpConnectionImpl connection, 65 | AmqpReceiverOptions options, 66 | ProtonReceiver receiver, 67 | Completable completionHandler) { 68 | this.address = address; 69 | this.receiver = receiver; 70 | this.connection = connection; 71 | this.durable = options.isDurable(); 72 | this.autoAck = options.isAutoAcknowledgement(); 73 | int maxBufferedMessages = options.getMaxBufferedMessages(); 74 | if (maxBufferedMessages > 0) { 75 | this.initialCredit = maxBufferedMessages; 76 | } 77 | 78 | // Disable auto-accept and automated prefetch, we manage disposition and credit 79 | // manually to allow for delayed handler registration and pause/resume functionality. 80 | this.receiver 81 | .setAutoAccept(false) 82 | .setPrefetch(0); 83 | 84 | this.receiver.handler((delivery, message) -> handleMessage(new AmqpMessageImpl(message, delivery, connection))); 85 | 86 | this.receiver.closeHandler(res -> { 87 | onClose(address, receiver, res, false); 88 | }) 89 | .detachHandler(res -> { 90 | onClose(address, receiver, res, true); 91 | }); 92 | 93 | this.receiver 94 | .openHandler(res -> { 95 | if (res.failed()) { 96 | completionHandler.complete(null, res.cause()); 97 | } else { 98 | this.connection.register(this); 99 | synchronized (this) { 100 | if (this.address == null) { 101 | this.address = res.result().getRemoteAddress(); 102 | } 103 | } 104 | completionHandler.succeed(this); 105 | } 106 | }); 107 | 108 | this.receiver.open(); 109 | } 110 | 111 | private void onClose(String address, ProtonReceiver receiver, AsyncResult res, boolean detach) { 112 | Handler endh = null; 113 | Handler exh = null; 114 | boolean closeReceiver = false; 115 | 116 | synchronized (AmqpReceiverImpl.this) { 117 | if (!closed && endHandler != null) { 118 | endh = endHandler; 119 | } else if (!closed && exceptionHandler != null) { 120 | exh = exceptionHandler; 121 | } 122 | 123 | if (!closed) { 124 | closed = true; 125 | closeReceiver = true; 126 | } 127 | } 128 | 129 | if (endh != null) { 130 | endh.handle(null); 131 | } else if (exh != null) { 132 | if (res.succeeded()) { 133 | exh.handle(new VertxException("Consumer closed remotely")); 134 | } else { 135 | exh.handle(new VertxException("Consumer closed remotely with error", res.cause())); 136 | } 137 | } else { 138 | if (res.succeeded()) { 139 | LOGGER.warn("Consumer for address " + address + " unexpectedly closed remotely"); 140 | } else { 141 | LOGGER.warn("Consumer for address " + address + " unexpectedly closed remotely with error", res.cause()); 142 | } 143 | } 144 | 145 | if (closeReceiver) { 146 | if (detach) { 147 | receiver.detach(); 148 | } else { 149 | receiver.close(); 150 | } 151 | } 152 | } 153 | 154 | private void handleMessage(AmqpMessageImpl message) { 155 | boolean schedule = false; 156 | 157 | Handler h; 158 | synchronized (this) { 159 | h = handler; 160 | if(h == null || demand == 0L) { 161 | // Buffer message until we aren't paused 162 | buffered.add(message); 163 | return; 164 | } 165 | 166 | if(!buffered.isEmpty()) { 167 | // Buffered messages present, deliver the oldest of those instead 168 | buffered.add(message); 169 | message = buffered.poll(); 170 | // Schedule a delivery for the next buffered message 171 | schedule = true; 172 | } 173 | if (demand != Long.MAX_VALUE) { 174 | demand--; 175 | } 176 | } 177 | deliverMessageToHandler(h, message); 178 | 179 | // schedule next delivery if appropriate, after earlier delivery to allow chance to pause etc. 180 | if(schedule) { 181 | scheduleBufferedMessageDelivery(); 182 | } 183 | } 184 | 185 | @Override 186 | public synchronized AmqpReceiver exceptionHandler(Handler handler) { 187 | exceptionHandler = handler; 188 | return this; 189 | } 190 | 191 | @Override 192 | public AmqpReceiver handler(@Nullable Handler handler) { 193 | int creditToFlow = 0; 194 | boolean schedule = false; 195 | 196 | synchronized (this) { 197 | this.handler = handler; 198 | if (handler != null) { 199 | schedule = true; 200 | 201 | // Flow initial credit if needed 202 | if (!initialCreditGiven) { 203 | initialCreditGiven = true; 204 | creditToFlow = initialCredit; 205 | } 206 | } 207 | } 208 | 209 | if (creditToFlow > 0) { 210 | final int c = creditToFlow; 211 | connection.runWithTrampoline(v -> receiver.flow(c)); 212 | } 213 | 214 | if (schedule) { 215 | scheduleBufferedMessageDelivery(); 216 | } 217 | 218 | return this; 219 | } 220 | 221 | @Override 222 | public synchronized AmqpReceiverImpl pause() { 223 | demand = 0L; 224 | return this; 225 | } 226 | 227 | @Override 228 | public synchronized AmqpReceiverImpl fetch(long amount) { 229 | if (amount > 0) { 230 | demand += amount; 231 | if (demand < 0L) { 232 | demand = Long.MAX_VALUE; 233 | } 234 | scheduleBufferedMessageDelivery(); 235 | } 236 | return this; 237 | } 238 | 239 | @Override 240 | public synchronized AmqpReceiverImpl resume() { 241 | return fetch(Long.MAX_VALUE); 242 | } 243 | 244 | @Override 245 | public synchronized AmqpReceiverImpl endHandler(Handler endHandler) { 246 | this.endHandler = endHandler; 247 | return this; 248 | } 249 | 250 | private void deliverMessageToHandler(Handler h, AmqpMessageImpl message) { 251 | try { 252 | h.handle(message); 253 | if (autoAck) { 254 | message.accepted(); 255 | } 256 | } catch (Exception e) { 257 | LOGGER.error("Unable to dispatch the AMQP message", e); 258 | if (autoAck) { 259 | message.rejected(); 260 | } 261 | } 262 | 263 | this.receiver.flow(1); 264 | } 265 | 266 | private void scheduleBufferedMessageDelivery() { 267 | boolean schedule; 268 | 269 | synchronized (this) { 270 | schedule = !buffered.isEmpty() && demand > 0L; 271 | } 272 | 273 | if (schedule) { 274 | connection.runOnContext(v -> { 275 | Handler h; 276 | AmqpMessageImpl message = null; 277 | 278 | synchronized (this) { 279 | h = handler; 280 | if (h != null && demand > 0L) { 281 | message = buffered.poll(); 282 | if (demand != Long.MAX_VALUE && message != null) { 283 | demand--; 284 | } 285 | } 286 | } 287 | 288 | if (message != null) { 289 | // Delivering outside the synchronized block 290 | deliverMessageToHandler(h, message); 291 | 292 | // Schedule a delivery for a further buffered message if any 293 | scheduleBufferedMessageDelivery(); 294 | } 295 | }); 296 | } 297 | } 298 | 299 | @Override 300 | public synchronized String address() { 301 | return address; 302 | } 303 | 304 | @Override 305 | public AmqpConnection connection() { 306 | return connection; 307 | } 308 | 309 | public void close(Completable handler) { 310 | Completable actualHandler; 311 | if (handler == null) { 312 | actualHandler = (res, err) -> { /* NOOP */ }; 313 | } else { 314 | actualHandler = handler; 315 | } 316 | 317 | synchronized (this) { 318 | if (closed) { 319 | actualHandler.succeed(); 320 | return; 321 | } 322 | closed = true; 323 | } 324 | 325 | connection.unregister(this); 326 | connection.runWithTrampoline(x -> { 327 | if (this.receiver.isOpen()) { 328 | try { 329 | if (isDurable()) { 330 | receiver.detachHandler(done -> actualHandler.complete(null, done.cause())) 331 | .detach(); 332 | } else { 333 | receiver 334 | .closeHandler(done -> actualHandler.complete(null, done.cause())) 335 | .close(); 336 | } 337 | } catch (Exception e) { 338 | // Somehow closed remotely 339 | actualHandler.fail(e); 340 | } 341 | } else { 342 | actualHandler.succeed(); 343 | } 344 | }); 345 | 346 | } 347 | 348 | @Override 349 | public Future close() { 350 | Promise promise = Promise.promise(); 351 | close(promise); 352 | return promise.future(); 353 | } 354 | 355 | @Override 356 | public ProtonReceiver unwrap() { 357 | return receiver; 358 | } 359 | 360 | private synchronized boolean isDurable() { 361 | return durable; 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/amqp/impl/AmqpSenderImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp.impl; 17 | 18 | import io.vertx.amqp.AmqpConnection; 19 | import io.vertx.amqp.AmqpMessage; 20 | import io.vertx.amqp.AmqpSender; 21 | import io.vertx.core.*; 22 | import io.vertx.core.internal.logging.Logger; 23 | import io.vertx.core.internal.logging.LoggerFactory; 24 | import io.vertx.proton.ProtonDelivery; 25 | import io.vertx.proton.ProtonSender; 26 | import org.apache.qpid.proton.amqp.messaging.Rejected; 27 | import org.apache.qpid.proton.amqp.transport.DeliveryState; 28 | 29 | public class AmqpSenderImpl implements AmqpSender { 30 | private static final Logger LOGGER = LoggerFactory.getLogger(AmqpSender.class); 31 | private final ProtonSender sender; 32 | private final AmqpConnectionImpl connection; 33 | private boolean closed; 34 | private Handler exceptionHandler; 35 | private Handler drainHandler; 36 | private long remoteCredit = 0; 37 | 38 | private AmqpSenderImpl(ProtonSender sender, AmqpConnectionImpl connection, 39 | Completable completionHandler) { 40 | this.sender = sender; 41 | this.connection = connection; 42 | 43 | sender 44 | .closeHandler(res -> onClose(sender, res, false)) 45 | .detachHandler(res -> onClose(sender, res, true)); 46 | 47 | sender.sendQueueDrainHandler(s -> { 48 | Handler dh = null; 49 | synchronized (AmqpSenderImpl.this) { 50 | // Update current state of remote credit 51 | remoteCredit = sender.getRemoteCredit(); 52 | 53 | // check the user drain handler, fire it outside synchronized block if not null 54 | if (drainHandler != null) { 55 | dh = drainHandler; 56 | } 57 | } 58 | 59 | if (dh != null) { 60 | dh.handle(null); 61 | } 62 | }); 63 | 64 | sender.openHandler(done -> { 65 | if (done.failed()) { 66 | completionHandler.complete(null, done.cause()); 67 | } else { 68 | connection.register(this); 69 | completionHandler.succeed(this); 70 | } 71 | }); 72 | 73 | sender.open(); 74 | } 75 | 76 | /** 77 | * Creates a new instance of {@link AmqpSenderImpl}. The created sender is passed into the {@code completionHandler} 78 | * once opened. This method must be called on the connection context. 79 | * 80 | * @param sender the underlying proton sender 81 | * @param connection the connection 82 | * @param completionHandler the completion handler 83 | */ 84 | static void create(ProtonSender sender, AmqpConnectionImpl connection, 85 | Completable completionHandler) { 86 | new AmqpSenderImpl(sender, connection, completionHandler); 87 | } 88 | 89 | private void onClose(ProtonSender sender, AsyncResult res, boolean detach) { 90 | Handler eh = null; 91 | boolean closeSender = false; 92 | 93 | synchronized (AmqpSenderImpl.this) { 94 | if (!closed && exceptionHandler != null) { 95 | eh = exceptionHandler; 96 | } 97 | 98 | if (!closed) { 99 | closed = true; 100 | closeSender = true; 101 | } 102 | } 103 | 104 | if (eh != null) { 105 | if (res.succeeded()) { 106 | eh.handle(new Exception("Sender closed remotely")); 107 | } else { 108 | eh.handle(new Exception("Sender closed remotely with error", res.cause())); 109 | } 110 | } 111 | 112 | if (closeSender) { 113 | if (detach) { 114 | sender.detach(); 115 | } else { 116 | sender.close(); 117 | } 118 | } 119 | } 120 | 121 | @Override 122 | public synchronized boolean writeQueueFull() { 123 | return remoteCredit <= 0; 124 | } 125 | 126 | @Override 127 | public AmqpConnection connection() { 128 | return connection; 129 | } 130 | 131 | @Override 132 | public AmqpSender send(AmqpMessage message) { 133 | return doSend(message, null); 134 | } 135 | 136 | private AmqpSender doSend(AmqpMessage message, Completable acknowledgmentHandler) { 137 | Handler ack = delivery -> { 138 | DeliveryState remoteState = delivery.getRemoteState(); 139 | 140 | Completable handler = acknowledgmentHandler; 141 | if (acknowledgmentHandler == null) { 142 | handler = (res, err) -> { 143 | if (err != null) { 144 | LOGGER.warn("Message rejected by remote peer", err); 145 | } 146 | }; 147 | } 148 | 149 | if (remoteState == null) { 150 | handler.fail("Unknown message state"); 151 | return; 152 | } 153 | 154 | switch (remoteState.getType()) { 155 | case Rejected: 156 | handler.fail("message rejected (REJECTED): " + ((Rejected) remoteState).getError()); 157 | break; 158 | case Modified: 159 | handler.fail("message rejected (MODIFIED)"); 160 | break; 161 | case Released: 162 | handler.fail("message rejected (RELEASED)"); 163 | break; 164 | case Accepted: 165 | handler.succeed(); 166 | break; 167 | default: 168 | handler.fail("Unsupported delivery type: " + remoteState.getType()); 169 | } 170 | }; 171 | 172 | synchronized (AmqpSenderImpl.this) { 173 | // Update the credit tracking. We only need to adjust this here because the sends etc may not be on the context 174 | // thread and if that is the case we can't use the ProtonSender sendQueueFull method to check that credit has been 175 | // exhausted following this doSend call since we will have only scheduled the actual send for later. 176 | remoteCredit--; 177 | } 178 | 179 | connection.runWithTrampoline(x -> { 180 | AmqpMessage updated; 181 | if (message.address() == null) { 182 | updated = AmqpMessage.create(message).address(address()).build(); 183 | } else { 184 | updated = message; 185 | } 186 | 187 | sender.send(updated.unwrap(), ack); 188 | 189 | synchronized (AmqpSenderImpl.this) { 190 | // Update the credit tracking *again*. We need to reinitialise it here in case the doSend call was performed on 191 | // a thread other than the client context, to ensure we didn't fall foul of a race between the above pre-send 192 | // update on that thread, the above send on the context thread, and the sendQueueDrainHandler based updates on 193 | // the context thread. 194 | remoteCredit = sender.getRemoteCredit(); 195 | } 196 | }); 197 | return this; 198 | } 199 | 200 | @Override 201 | public synchronized AmqpSender exceptionHandler(Handler handler) { 202 | exceptionHandler = handler; 203 | return this; 204 | } 205 | 206 | @Override 207 | public Future write(AmqpMessage data) { 208 | Promise promise = Promise.promise(); 209 | doSend(data, promise); 210 | return promise.future(); 211 | } 212 | 213 | @Override 214 | public AmqpSender setWriteQueueMaxSize(int maxSize) { 215 | // No-op, available sending credit is controlled by recipient peer in AMQP 1.0. 216 | return this; 217 | } 218 | 219 | @Override 220 | public Future end() { 221 | Promise promise = Promise.promise(); 222 | close(promise); 223 | return promise.future(); 224 | } 225 | 226 | @Override 227 | public synchronized AmqpSender drainHandler(Handler handler) { 228 | drainHandler = handler; 229 | return this; 230 | } 231 | 232 | public AmqpSender sendWithAck(AmqpMessage message, Promise acknowledgementHandler) { 233 | return doSend(message, acknowledgementHandler); 234 | } 235 | 236 | @Override 237 | public Future sendWithAck(AmqpMessage message) { 238 | Promise promise = Promise.promise(); 239 | sendWithAck(message, promise); 240 | return promise.future(); 241 | } 242 | 243 | public void close(Completable handler) { 244 | Completable actualHandler; 245 | if (handler == null) { 246 | actualHandler = (res, err) -> { /* NOOP */ }; 247 | } else { 248 | actualHandler = handler; 249 | } 250 | 251 | synchronized (this) { 252 | if (closed) { 253 | actualHandler.succeed(); 254 | return; 255 | } 256 | closed = true; 257 | } 258 | 259 | connection.unregister(this); 260 | connection.runWithTrampoline(x -> { 261 | if (sender.isOpen()) { 262 | try { 263 | sender 264 | .closeHandler(v -> actualHandler.complete(null, v.cause())) 265 | .close(); 266 | } catch (Exception e) { 267 | // Somehow closed remotely 268 | actualHandler.fail(e); 269 | } 270 | } else { 271 | actualHandler.succeed(); 272 | } 273 | }); 274 | } 275 | 276 | @Override 277 | public Future close() { 278 | Promise promise = Promise.promise(); 279 | close(promise); 280 | return promise.future(); 281 | } 282 | 283 | @Override 284 | public String address() { 285 | return sender.getRemoteAddress(); 286 | } 287 | 288 | @Override 289 | public long remainingCredits() { 290 | return sender.getRemoteCredit(); 291 | } 292 | 293 | @Override 294 | public ProtonSender unwrap() { 295 | return sender; 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/amqp/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | @ModuleGen(name = "vertx-amqp-client", groupPackage = "io.vertx") 17 | package io.vertx.amqp; 18 | 19 | import io.vertx.codegen.annotations.ModuleGen; 20 | -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | module io.vertx.amqp.client { 17 | 18 | requires static io.vertx.codegen.api; 19 | requires static io.vertx.codegen.json; 20 | requires static io.vertx.docgen; 21 | 22 | requires io.vertx.core; 23 | requires io.vertx.core.logging; 24 | requires io.vertx.proton; 25 | requires java.sql; 26 | requires org.apache.qpid.proton.j; 27 | requires static org.reactivestreams; 28 | 29 | exports io.vertx.amqp; 30 | exports io.vertx.amqp.impl to io.vertx.amqp.client.tests; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/amqp/tests/BareTestBase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp.tests; 17 | 18 | import io.vertx.amqp.AmqpClient; 19 | import io.vertx.core.Vertx; 20 | import io.vertx.ext.unit.junit.VertxUnitRunner; 21 | import org.junit.After; 22 | import org.junit.Before; 23 | import org.junit.Rule; 24 | import org.junit.Test; 25 | import org.junit.rules.TestName; 26 | import org.junit.runner.RunWith; 27 | 28 | import java.util.concurrent.CountDownLatch; 29 | import java.util.concurrent.TimeUnit; 30 | 31 | @RunWith(VertxUnitRunner.class) 32 | public class BareTestBase { 33 | 34 | @Rule 35 | public TestName name = new TestName(); 36 | 37 | protected AmqpClient client; 38 | 39 | protected Vertx vertx; 40 | 41 | @Before 42 | public void setUp() { 43 | vertx = Vertx.vertx(); 44 | } 45 | 46 | @After 47 | public void tearDown() throws InterruptedException { 48 | CountDownLatch latchForClient = new CountDownLatch(1); 49 | CountDownLatch latchForVertx = new CountDownLatch(1); 50 | if (client != null) { 51 | client.close().onComplete(x -> latchForClient.countDown()); 52 | latchForClient.await(10, TimeUnit.SECONDS); 53 | } 54 | vertx.close().onComplete(x -> latchForVertx.countDown()); 55 | latchForVertx.await(10, TimeUnit.SECONDS); 56 | } 57 | 58 | @Test 59 | public void justToAvoidTheIdeToFail() { 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/amqp/tests/ConnectionMetadataTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp.tests; 17 | 18 | import io.vertx.amqp.AmqpClient; 19 | import io.vertx.amqp.AmqpClientOptions; 20 | import io.vertx.amqp.AmqpConnection; 21 | import io.vertx.amqp.impl.AmqpConnectionImpl; 22 | import io.vertx.core.Vertx; 23 | import io.vertx.ext.unit.Async; 24 | import io.vertx.ext.unit.TestContext; 25 | import io.vertx.ext.unit.junit.VertxUnitRunner; 26 | import org.apache.qpid.proton.amqp.Symbol; 27 | import org.junit.After; 28 | import org.junit.Before; 29 | import org.junit.Test; 30 | import org.junit.runner.RunWith; 31 | 32 | import java.util.Map; 33 | import java.util.concurrent.ExecutionException; 34 | import java.util.concurrent.atomic.AtomicBoolean; 35 | 36 | @RunWith(VertxUnitRunner.class) 37 | public class ConnectionMetadataTest { 38 | 39 | private Vertx vertx; 40 | private MockServer server; 41 | 42 | @Before 43 | public void setUp() { 44 | vertx = Vertx.vertx(); 45 | } 46 | 47 | @After 48 | public void tearDown() { 49 | if (server != null) { 50 | server.close(); 51 | } 52 | if (vertx != null) { 53 | vertx.close(); 54 | } 55 | } 56 | 57 | @Test(timeout = 20000) 58 | public void testMetadata(TestContext context) throws ExecutionException, InterruptedException { 59 | Async asyncMetaData = context.async(); 60 | Async asyncShutdown = context.async(); 61 | 62 | server = new MockServer(vertx, serverConnection -> { 63 | serverConnection.closeHandler(x -> serverConnection.close()); 64 | 65 | serverConnection.openHandler(x -> { 66 | // Open the connection. 67 | serverConnection.open(); 68 | 69 | // Validate the properties separately. 70 | Map properties = serverConnection.getRemoteProperties(); 71 | 72 | context.assertNotNull(properties, "connection properties not present"); 73 | 74 | context.assertTrue(properties.containsKey(AmqpConnectionImpl.PRODUCT_KEY), 75 | "product property key not present"); 76 | context.assertEquals(AmqpConnectionImpl.PRODUCT, properties.get(AmqpConnectionImpl.PRODUCT_KEY), 77 | "unexpected product property value"); 78 | 79 | asyncMetaData.complete(); 80 | }); 81 | }); 82 | 83 | AmqpClient.create(new AmqpClientOptions().setHost("localhost").setPort(server.actualPort())) 84 | .connect() 85 | .compose(AmqpConnection::close) 86 | .onComplete(context.asyncAssertSuccess(x -> { 87 | asyncShutdown.complete(); 88 | })); 89 | } 90 | 91 | @Test(timeout = 20000) 92 | public void testConnectionHostnameAndContainerID(TestContext context) throws Exception { 93 | doConnectionHostnameAndContainerIDTestImpl(context, true); 94 | doConnectionHostnameAndContainerIDTestImpl(context, false); 95 | } 96 | 97 | private void doConnectionHostnameAndContainerIDTestImpl(TestContext context, boolean customValues) throws Exception { 98 | String tcpConnectionHostname = "localhost"; 99 | String containerId = "myCustomContainer"; 100 | String vhost = "myCustomVhost"; 101 | 102 | Async asyncShutdown = context.async(); 103 | AtomicBoolean linkOpened = new AtomicBoolean(); 104 | 105 | MockServer server = new MockServer(vertx, serverConnection -> { 106 | serverConnection.openHandler(x -> { 107 | if (customValues) { 108 | context.assertEquals(vhost, serverConnection.getRemoteHostname()); 109 | context.assertFalse(tcpConnectionHostname.equals(serverConnection.getRemoteHostname())); 110 | 111 | context.assertEquals(containerId, serverConnection.getRemoteContainer()); 112 | } else { 113 | context.assertEquals(tcpConnectionHostname, serverConnection.getRemoteHostname()); 114 | context.assertNotNull(containerId, serverConnection.getRemoteContainer()); 115 | } 116 | serverConnection.open(); 117 | }); 118 | serverConnection.closeHandler(x -> { 119 | serverConnection.close(); 120 | }); 121 | }); 122 | 123 | AmqpClientOptions opts = new AmqpClientOptions() 124 | .setHost(tcpConnectionHostname).setPort(server.actualPort()); 125 | if (customValues) { 126 | opts.setContainerId(containerId).setVirtualHost(vhost); 127 | } 128 | 129 | AmqpClient.create(opts) 130 | .connect().onComplete(context.asyncAssertSuccess(conn -> { 131 | conn.close().onComplete(context.asyncAssertSuccess(shutdownRes -> { 132 | asyncShutdown.complete(); 133 | })); 134 | })); 135 | 136 | try { 137 | asyncShutdown.awaitSuccess(); 138 | } finally { 139 | server.close(); 140 | } 141 | 142 | context.assertFalse(linkOpened.get()); 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/amqp/tests/ConnectionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp.tests; 17 | 18 | import io.vertx.amqp.AmqpClient; 19 | import io.vertx.amqp.AmqpClientOptions; 20 | import org.apache.qpid.proton.engine.Sasl; 21 | import org.apache.qpid.proton.engine.Transport; 22 | import org.apache.qpid.proton.engine.Sasl.SaslOutcome; 23 | import org.junit.Test; 24 | 25 | import io.vertx.core.Handler; 26 | import io.vertx.core.net.NetSocket; 27 | import io.vertx.proton.ProtonConnection; 28 | import io.vertx.proton.sasl.ProtonSaslAuthenticator; 29 | 30 | import java.nio.charset.StandardCharsets; 31 | import java.util.Objects; 32 | import java.util.concurrent.CountDownLatch; 33 | import java.util.concurrent.TimeUnit; 34 | import java.util.concurrent.atomic.AtomicBoolean; 35 | import java.util.concurrent.atomic.AtomicReference; 36 | 37 | import static org.assertj.core.api.Assertions.assertThat; 38 | 39 | public class ConnectionTest extends BareTestBase { 40 | 41 | private static final String USER = "MY_USER"; 42 | private static final String PASSWD = "PASSWD_VALUE"; 43 | private static final String BAD_PASSWD = "WRONG_VALUE"; 44 | 45 | private static final String PLAIN = "PLAIN"; 46 | 47 | @Test(timeout = 10000) 48 | public void testConnectionSuccessWithDetailsPassedInOptions() throws Exception { 49 | doConnectionWithDetailsPassedInOptionsTestImpl(true); 50 | } 51 | 52 | @Test(timeout = 10000) 53 | public void testConnectionFailureWithDetailsPassedInOptions() throws Exception { 54 | doConnectionWithDetailsPassedInOptionsTestImpl(false); 55 | } 56 | 57 | private void doConnectionWithDetailsPassedInOptionsTestImpl(boolean succeed) throws Exception { 58 | CountDownLatch done = new CountDownLatch(1); 59 | String password = succeed ? PASSWD : BAD_PASSWD; 60 | AtomicBoolean serverConnectionOpen = new AtomicBoolean(); 61 | 62 | MockServer server = new MockServer(vertx, serverConnection -> { 63 | serverConnection.openHandler(serverSender -> { 64 | serverConnectionOpen.set(true); 65 | serverConnection.closeHandler(x -> serverConnection.close()); 66 | serverConnection.open(); 67 | }); 68 | }); 69 | 70 | TestAuthenticator authenticator = new TestAuthenticator(PLAIN, succeed); 71 | server.getProtonServer().saslAuthenticatorFactory(() -> authenticator); 72 | 73 | try { 74 | client = AmqpClient.create(vertx, new AmqpClientOptions() 75 | .setHost("localhost") 76 | .setPort(server.actualPort()) 77 | .setUsername(USER) 78 | .setPassword(password)); 79 | 80 | client.connect().onComplete(ar -> { 81 | if (ar.failed() && succeed) { 82 | ar.cause().printStackTrace(); 83 | } else { 84 | done.countDown(); 85 | } 86 | }); 87 | 88 | assertThat(done.await(6, TimeUnit.SECONDS)).isTrue(); 89 | assertThat(serverConnectionOpen.get()).isEqualTo(succeed); 90 | } finally { 91 | server.close(); 92 | } 93 | 94 | assertThat(authenticator.getChosenMech()).isEqualTo(PLAIN); 95 | assertThat(authenticator.getInitialResponse()).isEqualTo(getPlainInitialResponse(USER, password)); 96 | } 97 | 98 | @Test(timeout = 10000) 99 | public void testConnectionSuccessWithDetailsPassedAsSystemVariables() throws Exception { 100 | doConnectionWithDetailsPassedAsSystemVariablesTestImpl(true); 101 | } 102 | 103 | @Test(timeout = 10000) 104 | public void testConnectionFailureWithDetailsPassedAsSystemVariables() throws Exception { 105 | doConnectionWithDetailsPassedAsSystemVariablesTestImpl(false); 106 | } 107 | 108 | private void doConnectionWithDetailsPassedAsSystemVariablesTestImpl(boolean succeed) throws Exception { 109 | CountDownLatch done = new CountDownLatch(1); 110 | AtomicBoolean serverConnectionOpen = new AtomicBoolean(); 111 | String password = succeed ? PASSWD : BAD_PASSWD; 112 | 113 | MockServer server = new MockServer(vertx, serverConnection -> { 114 | // Expect a connection 115 | serverConnection.openHandler(serverSender -> { 116 | serverConnectionOpen.set(true); 117 | serverConnection.closeHandler(x -> serverConnection.close()); 118 | serverConnection.open(); 119 | }); 120 | }); 121 | 122 | TestAuthenticator authenticator = new TestAuthenticator(PLAIN, succeed); 123 | server.getProtonServer().saslAuthenticatorFactory(() -> authenticator); 124 | 125 | System.setProperty("amqp-client-host", "localhost"); 126 | System.setProperty("amqp-client-port", Integer.toString(server.actualPort())); 127 | System.setProperty("amqp-client-username", USER); 128 | System.setProperty("amqp-client-password", password); 129 | try { 130 | client = AmqpClient.create(vertx, new AmqpClientOptions()); 131 | 132 | client.connect().onComplete(ar -> { 133 | if (ar.failed() && succeed) { 134 | ar.cause().printStackTrace(); 135 | } else { 136 | done.countDown(); 137 | } 138 | }); 139 | 140 | assertThat(done.await(6, TimeUnit.SECONDS)).isTrue(); 141 | assertThat(serverConnectionOpen.get()).isEqualTo(succeed); 142 | } 143 | finally { 144 | System.clearProperty("amqp-client-host"); 145 | System.clearProperty("amqp-client-port"); 146 | System.clearProperty("amqp-client-username"); 147 | System.clearProperty("amqp-client-password"); 148 | server.close(); 149 | } 150 | } 151 | 152 | @Test(timeout = 10000) 153 | public void testConnectionFailedBecauseOfBadHost() throws Exception { 154 | CountDownLatch done = new CountDownLatch(1); 155 | AtomicBoolean serverConnectionOpen = new AtomicBoolean(); 156 | 157 | MockServer server = new MockServer(vertx, serverConnection -> { 158 | // [Dont] expect a connection 159 | serverConnection.openHandler(serverSender -> { 160 | serverConnectionOpen.set(true); 161 | serverConnection.closeHandler(x -> serverConnection.close()); 162 | serverConnection.open(); 163 | }); 164 | }); 165 | 166 | try { 167 | AtomicReference failure = new AtomicReference<>(); 168 | client = AmqpClient.create(vertx, new AmqpClientOptions() 169 | .setHost("org.acme") 170 | .setPort(server.actualPort())); 171 | 172 | client.connect().onComplete(ar -> { 173 | failure.set(ar.cause()); 174 | done.countDown(); 175 | }); 176 | 177 | assertThat(done.await(6, TimeUnit.SECONDS)).isTrue(); 178 | assertThat(failure.get()).isNotNull(); 179 | assertThat(serverConnectionOpen.get()).isFalse(); 180 | } finally { 181 | server.close(); 182 | } 183 | } 184 | 185 | @Test(timeout = 10000) 186 | public void testConnectionWithAmqpOpenHostnameOverride() throws Exception { 187 | CountDownLatch done = new CountDownLatch(1); 188 | AtomicReference serverConnectionRef = new AtomicReference<>(); 189 | 190 | MockServer server = new MockServer(vertx, serverConnection -> { 191 | serverConnection.openHandler(x -> { 192 | serverConnectionRef.set(serverConnection); 193 | serverConnection.closeHandler(y -> serverConnection.close()); 194 | serverConnection.open(); 195 | }); 196 | }); 197 | 198 | String serverDnsHost = "localhost"; 199 | String connectionHostname = "some-other-hostname"; 200 | 201 | assertThat(serverDnsHost).isNotEqualTo(connectionHostname); 202 | 203 | try { 204 | AmqpClientOptions options = new AmqpClientOptions() 205 | .setHost(serverDnsHost) 206 | .setPort(server.actualPort()) 207 | .setConnectionHostname(connectionHostname); 208 | 209 | client = AmqpClient.create(vertx, options); 210 | 211 | client.connect().onComplete(ar -> { 212 | if (ar.failed()) { 213 | ar.cause().printStackTrace(); 214 | } else { 215 | done.countDown(); 216 | } 217 | }); 218 | 219 | assertThat(done.await(5, TimeUnit.SECONDS)).isTrue(); 220 | ProtonConnection serverConnection = serverConnectionRef.get(); 221 | 222 | assertThat(serverConnection).isNotNull(); 223 | assertThat(serverConnection.getRemoteHostname()).isEqualTo(connectionHostname); 224 | } finally { 225 | server.close(); 226 | } 227 | } 228 | 229 | private static byte[] getPlainInitialResponse(String username, String password) { 230 | Objects.requireNonNull(username); 231 | Objects.requireNonNull(password); 232 | 233 | byte[] usernameBytes = username.getBytes(StandardCharsets.UTF_8); 234 | byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8); 235 | 236 | byte[] data = new byte[usernameBytes.length + passwordBytes.length + 2]; 237 | System.arraycopy(usernameBytes, 0, data, 1, usernameBytes.length); 238 | System.arraycopy(passwordBytes, 0, data, 2 + usernameBytes.length, passwordBytes.length); 239 | 240 | return data; 241 | } 242 | 243 | private static final class TestAuthenticator implements ProtonSaslAuthenticator { 244 | private Sasl sasl; 245 | private boolean succeed; 246 | private String offeredMech; 247 | String chosenMech = null; 248 | byte[] initialResponse = null; 249 | 250 | public TestAuthenticator(String offeredMech, boolean succeed){ 251 | this.offeredMech = offeredMech; 252 | this.succeed = succeed; 253 | } 254 | 255 | @Override 256 | public void init(NetSocket socket, ProtonConnection protonConnection, Transport transport) { 257 | this.sasl = transport.sasl(); 258 | sasl.server(); 259 | sasl.allowSkip(false); 260 | sasl.setMechanisms(offeredMech); 261 | } 262 | 263 | @Override 264 | public void process(Handler processComplete) { 265 | boolean done = false; 266 | String[] remoteMechanisms = sasl.getRemoteMechanisms(); 267 | if (remoteMechanisms.length > 0) { 268 | chosenMech = remoteMechanisms[0]; 269 | 270 | initialResponse = new byte[sasl.pending()]; 271 | sasl.recv(initialResponse, 0, initialResponse.length); 272 | 273 | if (succeed) { 274 | sasl.done(SaslOutcome.PN_SASL_OK); 275 | } else { 276 | sasl.done(SaslOutcome.PN_SASL_AUTH); 277 | } 278 | 279 | done = true; 280 | } 281 | 282 | processComplete.handle(done); 283 | } 284 | 285 | @Override 286 | public boolean succeeded() { 287 | return succeed; 288 | } 289 | 290 | public String getChosenMech() { 291 | return chosenMech; 292 | } 293 | 294 | public byte[] getInitialResponse() { 295 | return initialResponse; 296 | } 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/amqp/tests/DisabledAnonymousLinkTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp.tests; 17 | 18 | import io.vertx.amqp.AmqpClient; 19 | import io.vertx.amqp.AmqpClientOptions; 20 | import io.vertx.ext.unit.Async; 21 | import io.vertx.ext.unit.TestContext; 22 | import io.vertx.proton.ProtonHelper; 23 | import io.vertx.proton.ProtonSession; 24 | import org.apache.qpid.proton.amqp.transport.AmqpError; 25 | import org.junit.Test; 26 | 27 | import java.util.concurrent.atomic.AtomicBoolean; 28 | 29 | public class DisabledAnonymousLinkTest extends BareTestBase { 30 | 31 | @Test(timeout = 20000) 32 | public void testConnectionToServerWithoutAnonymousSenderLinkSupport(TestContext context) throws Exception { 33 | Async asyncShutdown = context.async(); 34 | AtomicBoolean linkOpened = new AtomicBoolean(); 35 | 36 | MockServer server = new MockServer(vertx, serverConnection -> { 37 | serverConnection.openHandler(x -> serverConnection.open()); 38 | serverConnection.closeHandler(x -> serverConnection.close()); 39 | serverConnection.sessionOpenHandler(ProtonSession::open); 40 | serverConnection.receiverOpenHandler(serverReceiver -> { 41 | linkOpened.set(true); 42 | serverReceiver.setCondition(ProtonHelper.condition(AmqpError.PRECONDITION_FAILED, "Expected no links")); 43 | serverReceiver.close(); 44 | }); 45 | serverConnection.senderOpenHandler(serverSender -> { 46 | linkOpened.set(true); 47 | serverSender.setCondition(ProtonHelper.condition(AmqpError.PRECONDITION_FAILED, "Expected no links")); 48 | serverSender.close(); 49 | }); 50 | }); 51 | server.getProtonServer().setAdvertiseAnonymousRelayCapability(false); 52 | 53 | AmqpClientOptions options = new AmqpClientOptions() 54 | .setHost("localhost") 55 | .setPort(server.actualPort()); 56 | 57 | this.client = AmqpClient.create(vertx, options); 58 | client.connect().onComplete(context.asyncAssertSuccess(res -> { 59 | res.close().onComplete(context.asyncAssertSuccess(shutdownRes -> { 60 | asyncShutdown.complete(); 61 | })); 62 | })); 63 | 64 | try { 65 | asyncShutdown.awaitSuccess(); 66 | } finally { 67 | server.close(); 68 | } 69 | 70 | context.assertFalse(linkOpened.get()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/amqp/tests/DisconnectTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp.tests; 17 | 18 | import java.util.UUID; 19 | 20 | import io.vertx.amqp.AmqpClient; 21 | import io.vertx.amqp.AmqpClientOptions; 22 | import io.vertx.amqp.AmqpReceiverOptions; 23 | import io.vertx.amqp.impl.AmqpClientImpl; 24 | import org.junit.Test; 25 | import org.junit.runner.RunWith; 26 | 27 | import io.vertx.ext.unit.Async; 28 | import io.vertx.ext.unit.TestContext; 29 | import io.vertx.ext.unit.junit.VertxUnitRunner; 30 | 31 | @RunWith(VertxUnitRunner.class) 32 | public class DisconnectTest extends BareTestBase { 33 | 34 | @Test(timeout = 20000) 35 | public void testUseConnectionAfterDisconnect(TestContext ctx) throws Exception { 36 | MockServer server = new MockServer(vertx, serverConnection -> { 37 | // Expect a connection 38 | serverConnection.openHandler(serverSender -> { 39 | serverConnection.closeHandler(x -> serverConnection.close()); 40 | serverConnection.open(); 41 | }); 42 | }); 43 | 44 | String queue = UUID.randomUUID().toString(); 45 | client = AmqpClient.create(vertx, new AmqpClientOptions() 46 | .setHost("localhost") 47 | .setPort(server.actualPort())); 48 | 49 | Async handlerFired = ctx.async(); 50 | client.connect().onComplete(ctx.asyncAssertSuccess(conn -> { 51 | conn.exceptionHandler(err -> { 52 | conn.createSender(queue).onComplete(ctx.asyncAssertFailure(sender -> { 53 | })); 54 | conn.createAnonymousSender().onComplete(ctx.asyncAssertFailure(sender -> { 55 | })); 56 | conn.createReceiver("some-address").onComplete(ctx.asyncAssertFailure(sender -> { 57 | })); 58 | conn.createReceiver("some-address", new AmqpReceiverOptions()).onComplete(ctx.asyncAssertFailure(sender -> { 59 | })); 60 | conn.createDynamicReceiver().onComplete(ctx.asyncAssertFailure(sender -> { 61 | })); 62 | 63 | handlerFired.complete(); 64 | }); 65 | 66 | server.close(); 67 | })); 68 | 69 | handlerFired.awaitSuccess(); 70 | } 71 | 72 | @Test(timeout = 20000) 73 | public void testConnectionsCleanupOnDisconnect(TestContext ctx) throws Exception { 74 | MockServer server = new MockServer(vertx, serverConnection -> { 75 | // Expect a connection 76 | serverConnection.openHandler(serverSender -> { 77 | serverConnection.open(); 78 | }); 79 | }); 80 | 81 | client = AmqpClient.create(vertx, new AmqpClientOptions() 82 | .setHost("localhost") 83 | .setPort(server.actualPort())); 84 | 85 | Async handlerFired = ctx.async(); 86 | client.connect().onComplete(ctx.asyncAssertSuccess(conn -> { 87 | conn.closeFuture().onComplete(ar -> { 88 | ctx.assertEquals(0, ((AmqpClientImpl)client).numConnections()); 89 | handlerFired.complete(); 90 | }); 91 | server.close(); 92 | })); 93 | 94 | handlerFired.awaitSuccess(); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/amqp/tests/FutureHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp.tests; 17 | 18 | import io.vertx.core.AsyncResult; 19 | import io.vertx.core.Handler; 20 | 21 | import java.util.concurrent.*; 22 | 23 | abstract public class FutureHandler implements Future, Handler { 24 | 25 | protected ExecutionException exception; 26 | protected T result; 27 | protected CountDownLatch latch = new CountDownLatch(1); 28 | 29 | public static FutureHandler simple() { 30 | return new FutureHandler() { 31 | @Override 32 | synchronized public void handle(T t) { 33 | result = t; 34 | latch.countDown(); 35 | } 36 | }; 37 | } 38 | 39 | public static FutureHandler> asyncResult() { 40 | return new FutureHandler>() { 41 | @Override 42 | synchronized public void handle(AsyncResult t) { 43 | if (t.succeeded()) { 44 | result = t.result(); 45 | } else { 46 | exception = new ExecutionException(t.cause()); 47 | } 48 | latch.countDown(); 49 | } 50 | }; 51 | } 52 | 53 | @Override 54 | abstract public void handle(X t); 55 | 56 | public T get() throws InterruptedException, ExecutionException { 57 | latch.await(); 58 | return result(); 59 | } 60 | 61 | private T result() throws ExecutionException { 62 | synchronized (this) { 63 | if (exception != null) { 64 | throw exception; 65 | } 66 | return result; 67 | } 68 | } 69 | 70 | public T get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException, ExecutionException { 71 | if (latch.await(timeout, unit)) { 72 | return result(); 73 | } else { 74 | throw new TimeoutException(); 75 | } 76 | } 77 | 78 | @Override 79 | public boolean cancel(boolean mayInterruptIfRunning) { 80 | return false; 81 | } 82 | 83 | @Override 84 | public boolean isCancelled() { 85 | return false; 86 | } 87 | 88 | @Override 89 | public boolean isDone() { 90 | return false; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/amqp/tests/MockServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp.tests; 17 | 18 | import io.vertx.core.AsyncResult; 19 | import io.vertx.core.Handler; 20 | import io.vertx.core.Vertx; 21 | import io.vertx.proton.ProtonConnection; 22 | import io.vertx.proton.ProtonServer; 23 | import io.vertx.proton.ProtonServerOptions; 24 | 25 | import java.util.concurrent.ExecutionException; 26 | 27 | public class MockServer { 28 | private ProtonServer server; 29 | 30 | // Toggle to (re)use a fixed port, e.g for capture. 31 | private int bindPort = 0; 32 | private boolean reuseAddress = false; 33 | 34 | public MockServer(Vertx vertx, Handler connectionHandler) 35 | throws ExecutionException, InterruptedException { 36 | this(vertx, connectionHandler, null); 37 | } 38 | 39 | public MockServer(Vertx vertx, Handler connectionHandler, ProtonServerOptions protonServerOptions) 40 | throws ExecutionException, InterruptedException { 41 | if (protonServerOptions == null) { 42 | protonServerOptions = new ProtonServerOptions(); 43 | } 44 | 45 | protonServerOptions.setReuseAddress(reuseAddress); 46 | server = ProtonServer.create(vertx, protonServerOptions); 47 | server.connectHandler(connectionHandler); 48 | 49 | FutureHandler> handler = FutureHandler.asyncResult(); 50 | server.listen(bindPort, handler); 51 | handler.get(); 52 | } 53 | 54 | public int actualPort() { 55 | return server.actualPort(); 56 | } 57 | 58 | public void close() { 59 | server.close(); 60 | } 61 | 62 | ProtonServer getProtonServer() { 63 | return server; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/amqp/tests/ReceiverCreditTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp.tests; 17 | 18 | import io.vertx.amqp.AmqpClient; 19 | import io.vertx.amqp.AmqpClientOptions; 20 | import io.vertx.amqp.AmqpReceiverOptions; 21 | import io.vertx.ext.unit.Async; 22 | import io.vertx.ext.unit.TestContext; 23 | import io.vertx.proton.ProtonSession; 24 | import org.apache.qpid.proton.Proton; 25 | import org.apache.qpid.proton.amqp.messaging.AmqpValue; 26 | import org.apache.qpid.proton.amqp.messaging.Source; 27 | import org.junit.Test; 28 | 29 | import java.util.UUID; 30 | import java.util.concurrent.ExecutionException; 31 | import java.util.concurrent.atomic.AtomicBoolean; 32 | 33 | public class ReceiverCreditTest extends BareTestBase { 34 | 35 | private MockServer server; 36 | 37 | @Override 38 | public void tearDown() throws InterruptedException { 39 | super.tearDown(); 40 | if (server != null) { 41 | server.close(); 42 | } 43 | } 44 | 45 | @Test(timeout = 20000) 46 | public void testInitialCredit(TestContext context) throws Exception { 47 | doConsumerInitialCreditTestImpl(context, false, 1000); 48 | } 49 | 50 | @Test(timeout = 20000) 51 | public void testInitialCreditInfluencedByConsumerBufferSize(TestContext context) throws Exception { 52 | doConsumerInitialCreditTestImpl(context, true, 42); 53 | } 54 | 55 | private void doConsumerInitialCreditTestImpl(TestContext context, boolean setMaxBuffered, 56 | int initialCredit) throws Exception { 57 | final String testName = name.getMethodName(); 58 | final String sentContent = "myMessageContent-" + testName; 59 | 60 | final AtomicBoolean firstSendQDrainHandlerCall = new AtomicBoolean(); 61 | final Async asyncInitialCredit = context.async(); 62 | final Async asyncCompletion = context.async(); 63 | 64 | // === Server handling ==== 65 | 66 | server = new MockServer(vertx, serverConnection -> { 67 | // Expect a connection 68 | serverConnection.openHandler(serverSender -> { 69 | // Add a close handler 70 | serverConnection.closeHandler(x -> serverConnection.close()); 71 | serverConnection.open(); 72 | }); 73 | 74 | // Expect a session to open, when the receiver is created 75 | serverConnection.sessionOpenHandler(ProtonSession::open); 76 | 77 | // Expect a sender link open for the receiver 78 | serverConnection.senderOpenHandler(serverSender -> { 79 | Source remoteSource = (Source) serverSender.getRemoteSource(); 80 | context.assertNotNull(remoteSource, "source should not be null"); 81 | context.assertEquals(testName, remoteSource.getAddress(), "expected given address"); 82 | // Naive test-only handling 83 | serverSender.setSource(remoteSource.copy()); 84 | 85 | serverSender.sendQueueDrainHandler(s -> { 86 | // Verify the initial credit when the handler is first called and send a message 87 | if (firstSendQDrainHandlerCall.compareAndSet(false, true)) { 88 | context.assertEquals(initialCredit, s.getCredit(), "unexpected initial credit"); 89 | context.assertFalse(s.sendQueueFull(), "expected send queue not to be full"); 90 | 91 | asyncInitialCredit.complete(); 92 | 93 | // send message 94 | org.apache.qpid.proton.message.Message protonMsg = Proton.message(); 95 | protonMsg.setBody(new AmqpValue(sentContent)); 96 | 97 | serverSender.send(protonMsg); 98 | } 99 | }); 100 | 101 | serverSender.open(); 102 | }); 103 | }); 104 | 105 | // === Client consumer handling ==== 106 | 107 | AmqpClientOptions options = new AmqpClientOptions().setHost("localhost") 108 | .setPort(server.actualPort()); 109 | 110 | client = AmqpClient.create(vertx, options); 111 | client.connect().onComplete(context.asyncAssertSuccess(res -> { 112 | AmqpReceiverOptions recOpts = new AmqpReceiverOptions(); 113 | if (setMaxBuffered) { 114 | recOpts.setMaxBufferedMessages(initialCredit); 115 | } 116 | res.createReceiver(testName, recOpts).onComplete(context.asyncAssertSuccess(consumer -> { 117 | consumer.handler(msg -> { 118 | context.assertNotNull(msg.bodyAsString(), "amqp message body content was null"); 119 | context.assertEquals(sentContent, msg.bodyAsString(), "amqp message body not as expected"); 120 | asyncCompletion.complete(); 121 | }); 122 | })); 123 | })); 124 | 125 | asyncInitialCredit.awaitSuccess(); 126 | asyncCompletion.awaitSuccess(); 127 | } 128 | 129 | @Test(timeout = 20000) 130 | public void testDynamicReceiver(TestContext context) throws ExecutionException, InterruptedException { 131 | String address = UUID.randomUUID().toString(); 132 | Async serverLinkOpenAsync = context.async(); 133 | 134 | server = new MockServer(vertx, serverConnection -> { 135 | serverConnection.openHandler(result -> serverConnection.open()); 136 | 137 | serverConnection.sessionOpenHandler(ProtonSession::open); 138 | serverConnection.closeHandler(conn -> serverConnection.close()); 139 | serverConnection.senderOpenHandler(serverReceiver -> { 140 | serverReceiver.closeHandler(res -> serverReceiver.close()); 141 | 142 | // Verify the remote terminus details used were as expected 143 | context.assertNotNull(serverReceiver.getRemoteSource(), "source should not be null"); 144 | org.apache.qpid.proton.amqp.messaging.Source remoteSource = 145 | (org.apache.qpid.proton.amqp.messaging.Source) serverReceiver.getRemoteSource(); 146 | context.assertTrue(remoteSource.getDynamic(), "expected dynamic source to be requested"); 147 | context.assertNull(remoteSource.getAddress(), "expected no source address to be set"); 148 | 149 | // Set the local terminus details 150 | org.apache.qpid.proton.amqp.messaging.Source target = 151 | (org.apache.qpid.proton.amqp.messaging.Source) remoteSource.copy(); 152 | target.setAddress(address); 153 | serverReceiver.setSource(target); 154 | 155 | serverReceiver.open(); 156 | 157 | serverLinkOpenAsync.complete(); 158 | }); 159 | }); 160 | 161 | client = AmqpClient.create(vertx, 162 | new AmqpClientOptions().setHost("localhost").setPort(server.actualPort())); 163 | 164 | client.connect().onComplete(context.asyncAssertSuccess(res -> { 165 | res.createDynamicReceiver().onComplete(context.asyncAssertSuccess(rec -> { 166 | context.assertEquals(rec.address(), address); 167 | })); 168 | })); 169 | 170 | serverLinkOpenAsync.awaitSuccess(); 171 | 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/amqp/tests/ReceptionTypeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp.tests; 17 | 18 | import io.vertx.amqp.AmqpClient; 19 | import io.vertx.amqp.AmqpClientOptions; 20 | import io.vertx.amqp.AmqpConnection; 21 | import io.vertx.amqp.AmqpMessage; 22 | import io.vertx.core.buffer.Buffer; 23 | import io.vertx.core.json.JsonArray; 24 | import io.vertx.core.json.JsonObject; 25 | import org.apache.qpid.proton.amqp.Binary; 26 | import org.apache.qpid.proton.amqp.Symbol; 27 | import org.apache.qpid.proton.amqp.messaging.AmqpSequence; 28 | import org.apache.qpid.proton.amqp.messaging.AmqpValue; 29 | import org.apache.qpid.proton.amqp.messaging.Data; 30 | import org.apache.qpid.proton.amqp.messaging.Section; 31 | import org.apache.qpid.proton.message.Message; 32 | import org.junit.After; 33 | import org.junit.Before; 34 | import org.junit.Test; 35 | 36 | import java.time.Instant; 37 | import java.util.*; 38 | import java.util.concurrent.CopyOnWriteArrayList; 39 | import java.util.concurrent.CountDownLatch; 40 | import java.util.concurrent.TimeUnit; 41 | import java.util.concurrent.atomic.AtomicReference; 42 | import java.util.function.Function; 43 | 44 | import static org.assertj.core.api.Assertions.assertThat; 45 | 46 | public class ReceptionTypeTest extends BareTestBase { 47 | 48 | private AmqpConnection connection; 49 | private AtomicReference msgPayloadRef; 50 | private MockServer server; 51 | 52 | @Before 53 | public void init() throws Exception { 54 | msgPayloadRef = new AtomicReference<>(); 55 | server = setupMockServer(msgPayloadRef); 56 | 57 | CountDownLatch latch = new CountDownLatch(1); 58 | AtomicReference reference = new AtomicReference<>(); 59 | client = AmqpClient.create(vertx, new AmqpClientOptions() 60 | .setHost("localhost") 61 | .setPort(server.actualPort())); 62 | client 63 | .connect() 64 | .onComplete(connection -> { 65 | reference.set(connection.result()); 66 | if (connection.failed()) { 67 | connection.cause().printStackTrace(); 68 | } 69 | latch.countDown(); 70 | }); 71 | 72 | assertThat(latch.await(6, TimeUnit.SECONDS)).isTrue(); 73 | this.connection = reference.get(); 74 | assertThat(connection).isNotNull(); 75 | } 76 | 77 | @After 78 | public void tearDown() throws InterruptedException { 79 | super.tearDown(); 80 | if(server != null) { 81 | server.close(); 82 | } 83 | } 84 | 85 | private void testType(Object payload, Function extractor, T expected) throws Exception { 86 | assertThat(msgPayloadRef.compareAndSet(null, payload)).isTrue(); 87 | CountDownLatch latch = new CountDownLatch(1); 88 | List list = new CopyOnWriteArrayList<>(); 89 | 90 | connection.createReceiver(UUID.randomUUID().toString()).onComplete(done -> { 91 | if (done.failed()) { 92 | done.cause().printStackTrace(); 93 | } 94 | done.result().handler(message -> { 95 | list.add(extractor.apply(message)); 96 | latch.countDown(); 97 | }); 98 | }); 99 | 100 | assertThat(latch.await(6, TimeUnit.SECONDS)).isTrue(); 101 | assertThat(list).containsExactly(expected); 102 | } 103 | 104 | @Test(timeout = 10000) 105 | public void testNoBody() throws Exception { 106 | testType(null, AmqpMessage::isBodyNull, true); 107 | } 108 | 109 | @Test(timeout = 10000) 110 | public void testNull() throws Exception { 111 | testType(new AmqpValue(null), AmqpMessage::isBodyNull, true); 112 | } 113 | 114 | @Test(timeout = 10000) 115 | public void testBooleanTrue() throws Exception { 116 | boolean b = Boolean.TRUE; 117 | testType(b, AmqpMessage::bodyAsBoolean, b); 118 | } 119 | 120 | @Test(timeout = 10000) 121 | public void testBooleanFalse() throws Exception { 122 | boolean b = Boolean.FALSE; 123 | testType(b, AmqpMessage::bodyAsBoolean, b); 124 | } 125 | 126 | @Test(timeout = 10000) 127 | public void testByte() throws Exception { 128 | byte b = 1; 129 | testType(b, AmqpMessage::bodyAsByte, b); 130 | } 131 | 132 | @Test(timeout = 10000) 133 | public void testShort() throws Exception { 134 | short s = 2; 135 | testType(s, AmqpMessage::bodyAsShort, s); 136 | } 137 | 138 | @Test(timeout = 10000) 139 | public void testInteger() throws Exception { 140 | int i = 3; 141 | testType(i, AmqpMessage::bodyAsInteger, i); 142 | } 143 | 144 | @Test(timeout = 10000) 145 | public void testLong() throws Exception { 146 | long l = Long.MAX_VALUE - 1; 147 | testType(l, AmqpMessage::bodyAsLong, l); 148 | } 149 | 150 | @Test(timeout = 10000) 151 | public void testFloat() throws Exception { 152 | float f = 12.34f; 153 | testType(f, AmqpMessage::bodyAsFloat, f); 154 | } 155 | 156 | @Test(timeout = 10000) 157 | public void testDouble() throws Exception { 158 | double d = 56.78; 159 | testType(d, AmqpMessage::bodyAsDouble, d); 160 | } 161 | 162 | @Test(timeout = 10000) 163 | public void testCharacter() throws Exception { 164 | char c = 'c'; 165 | testType(c, AmqpMessage::bodyAsChar, c); 166 | } 167 | 168 | @Test(timeout = 10000) 169 | public void testTimestamp() throws Exception { 170 | // We avoid using Instant.now() in the test since its precision is 171 | // variable and JDK + platform dependent, while timestamp is always 172 | // millisecond precision. A mismatch throws off the equality comparison. 173 | long currentTimeMillis = System.currentTimeMillis(); 174 | Instant instant = Instant.ofEpochMilli(currentTimeMillis); 175 | testType(new Date(currentTimeMillis), AmqpMessage::bodyAsTimestamp, instant); 176 | } 177 | 178 | @Test(timeout = 10000) 179 | public void testUUID() throws Exception { 180 | UUID uuid = UUID.randomUUID(); 181 | testType(uuid, AmqpMessage::bodyAsUUID, uuid); 182 | } 183 | 184 | @Test(timeout = 10000) 185 | public void testBinary() throws Exception { 186 | Buffer buffer = Buffer.buffer("hello !!!"); 187 | testType(new Data(new Binary(buffer.getBytes())), AmqpMessage::bodyAsBinary, buffer); 188 | } 189 | 190 | @Test(timeout = 10000) 191 | public void testString() throws Exception { 192 | String string = "hello !"; 193 | testType(string, AmqpMessage::bodyAsString, string); 194 | } 195 | 196 | @Test(timeout = 10000) 197 | public void testSymbol() throws Exception { 198 | String string = "my-symbol"; 199 | testType(Symbol.valueOf(string), AmqpMessage::bodyAsSymbol, string); 200 | } 201 | 202 | @Test(timeout = 10000) 203 | public void testList() throws Exception { 204 | List l = new ArrayList<>(); 205 | l.add("foo"); 206 | l.add(1); 207 | l.add(true); 208 | testType(l, AmqpMessage::bodyAsList, l); 209 | } 210 | 211 | @Test(timeout = 10000) 212 | public void testListPassedAsAmqpSequence() throws Exception { 213 | List l = new ArrayList<>(); 214 | l.add("sequence"); 215 | l.add(2); 216 | l.add(true); 217 | testType(new AmqpSequence(l), AmqpMessage::bodyAsList, l); 218 | } 219 | 220 | @Test(timeout = 10000) 221 | public void testMap() throws Exception { 222 | Map map = new HashMap<>(); 223 | map.put("1", "hello"); 224 | map.put("2", "bonjour"); 225 | testType(map, AmqpMessage::bodyAsMap, map); 226 | } 227 | 228 | @Test(timeout = 10000) 229 | public void testJsonObject() throws Exception { 230 | JsonObject json = new JsonObject().put("data", "message").put("number", 1) 231 | .put("array", new JsonArray().add(1).add(2).add(3)); 232 | Data data = new Data(new Binary(json.toBuffer().getBytes())); 233 | testType(data, AmqpMessage::bodyAsJsonObject, json); 234 | } 235 | 236 | @Test(timeout = 10000) 237 | public void testJsonArray() throws Exception { 238 | JsonArray array = new JsonArray().add(1).add(2).add(3); 239 | Data data = new Data(new Binary(array.toBuffer().getBytes())); 240 | testType(data, AmqpMessage::bodyAsJsonArray, array); 241 | } 242 | 243 | private MockServer setupMockServer(AtomicReference msgPayloadSupplier) throws Exception { 244 | return new MockServer(vertx, serverConnection -> { 245 | serverConnection.openHandler(serverSender -> { 246 | serverConnection.closeHandler(x -> serverConnection.close()); 247 | serverConnection.open(); 248 | }); 249 | 250 | serverConnection.sessionOpenHandler(serverSession -> { 251 | serverSession.closeHandler(x -> serverSession.close()); 252 | serverSession.open(); 253 | }); 254 | 255 | serverConnection.senderOpenHandler(serverSender-> { 256 | serverSender.open(); 257 | 258 | Message msg = Message.Factory.create(); 259 | 260 | Object payload = msgPayloadSupplier.get(); 261 | if (payload instanceof Section) { 262 | msg.setBody((Section) payload); 263 | } else if (payload != null) { 264 | msg.setBody(new AmqpValue(payload)); 265 | } else { 266 | // Don't set a body. 267 | } 268 | 269 | serverSender.send(msg); 270 | }); 271 | }); 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/amqp/tests/SenderUnknownAckStateTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp.tests; 17 | 18 | import io.vertx.amqp.AmqpClient; 19 | import io.vertx.amqp.AmqpClientOptions; 20 | import io.vertx.amqp.AmqpConnection; 21 | import io.vertx.amqp.AmqpMessage; 22 | import io.vertx.ext.unit.TestContext; 23 | import org.junit.After; 24 | import org.junit.Before; 25 | import org.junit.Test; 26 | 27 | import java.util.*; 28 | import java.util.concurrent.CountDownLatch; 29 | import java.util.concurrent.TimeUnit; 30 | import java.util.concurrent.atomic.AtomicReference; 31 | 32 | import static org.assertj.core.api.Assertions.assertThat; 33 | 34 | public class SenderUnknownAckStateTest extends BareTestBase { 35 | 36 | private AmqpConnection connection; 37 | private String address; 38 | private MockServer server; 39 | 40 | @Before 41 | public void init() throws Exception { 42 | server = setupMockServer(); 43 | 44 | CountDownLatch latch = new CountDownLatch(1); 45 | AtomicReference reference = new AtomicReference<>(); 46 | client = AmqpClient.create(vertx, new AmqpClientOptions() 47 | .setHost("localhost") 48 | .setPort(server.actualPort())); 49 | client.connect() 50 | .onComplete(connection -> { 51 | reference.set(connection.result()); 52 | if (connection.failed()) { 53 | connection.cause().printStackTrace(); 54 | } 55 | latch.countDown(); 56 | }); 57 | 58 | assertThat(latch.await(6, TimeUnit.SECONDS)).isTrue(); 59 | this.connection = reference.get(); 60 | assertThat(connection).isNotNull(); 61 | this.address = UUID.randomUUID().toString(); 62 | } 63 | 64 | @After 65 | public void tearDown() throws InterruptedException { 66 | super.tearDown(); 67 | server.close(); 68 | } 69 | 70 | @Test(timeout = 10000) 71 | public void test(TestContext context) throws Exception { 72 | connection.createSender(address) 73 | .compose(sender -> { 74 | AmqpMessage msg = AmqpMessage.create().withBooleanAsBody(true).build(); 75 | return sender 76 | .write(msg); 77 | }).onComplete(context.asyncAssertFailure(expected -> { 78 | // Expected 79 | })); 80 | } 81 | 82 | private MockServer setupMockServer() throws Exception { 83 | return new MockServer(vertx, serverConnection -> { 84 | serverConnection.openHandler(serverSender -> { 85 | serverConnection.closeHandler(x -> serverConnection.close()); 86 | serverConnection.open(); 87 | }); 88 | 89 | serverConnection.sessionOpenHandler(serverSession -> { 90 | serverSession.closeHandler(x -> serverSession.close()); 91 | serverSession.open(); 92 | }); 93 | 94 | serverConnection.receiverOpenHandler(serverReceiver-> { 95 | serverReceiver.handler((delivery, message) -> { 96 | // Triggers message state to be null 97 | delivery.settle(); 98 | }); 99 | 100 | serverReceiver.open(); 101 | }); 102 | }); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/amqp/tests/impl/AmqpMessageImplTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.amqp.tests.impl; 17 | 18 | import static org.junit.Assert.*; 19 | 20 | import io.vertx.amqp.impl.AmqpMessageImpl; 21 | import org.apache.qpid.proton.amqp.Binary; 22 | import org.apache.qpid.proton.amqp.messaging.AmqpValue; 23 | import org.apache.qpid.proton.amqp.messaging.Data; 24 | import org.apache.qpid.proton.message.Message; 25 | import org.junit.Test; 26 | 27 | public class AmqpMessageImplTest { 28 | 29 | @Test 30 | public void testContentEncoding() { 31 | String contentEncoding = "some-encoding"; 32 | 33 | Message protonMsg = Message.Factory.create(); 34 | protonMsg.setContentEncoding(contentEncoding); 35 | 36 | AmqpMessageImpl message = new AmqpMessageImpl(protonMsg); 37 | assertEquals(contentEncoding, message.contentEncoding()); 38 | } 39 | 40 | @Test 41 | public void testIsBodyNull() { 42 | 43 | Message protonMsg = Message.Factory.create(); 44 | AmqpMessageImpl message = new AmqpMessageImpl(protonMsg); 45 | 46 | protonMsg.setBody(null); 47 | assertTrue(message.isBodyNull()); 48 | 49 | protonMsg.setBody(new AmqpValue(null)); 50 | assertTrue(message.isBodyNull()); 51 | 52 | protonMsg.setBody(new Data(new Binary(new byte[0]))); 53 | assertFalse(message.isBodyNull()); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 The original author or authors 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | open module io.vertx.amqp.client.tests { 17 | requires io.netty.handler; 18 | requires io.vertx.core; 19 | requires io.vertx.amqp.client; 20 | requires io.vertx.proton; 21 | requires io.vertx.testing.unit; 22 | requires org.assertj.core; 23 | requires junit; 24 | requires org.apache.qpid.proton.j; 25 | } 26 | -------------------------------------------------------------------------------- /src/test/resources/README.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2016 the original author or authors. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | # The various SSL stores and certificates were created with the following commands: 18 | 19 | # Clean up existing files 20 | # ----------------------- 21 | rm -f *.crt *.csr *.keystore *.truststore 22 | 23 | # Create a key and self-signed certificate for the CA, to sign certificate requests and use for trust: 24 | # ---------------------------------------------------------------------------------------------------- 25 | keytool -storetype pkcs12 -keystore ca-pkcs12.keystore -storepass password -keypass password -alias ca -genkey -keyalg "RSA" -keysize 2048 -dname "O=My Trusted Inc.,CN=my-vertx-ca.org" -validity 9999 -ext bc:c=ca:true 26 | keytool -storetype pkcs12 -keystore ca-pkcs12.keystore -storepass password -alias ca -exportcert -rfc > ca.crt 27 | 28 | # Create a key pair for the broker, and sign it with the CA: 29 | # ---------------------------------------------------------- 30 | keytool -storetype pkcs12 -keystore broker-pkcs12.keystore -storepass password -keypass password -alias broker -genkey -keyalg "RSA" -keysize 2048 -dname "O=Server,CN=localhost" -validity 9999 -ext bc=ca:false -ext eku=sA 31 | 32 | keytool -storetype pkcs12 -keystore broker-pkcs12.keystore -storepass password -alias broker -certreq -file broker.csr 33 | keytool -storetype pkcs12 -keystore ca-pkcs12.keystore -storepass password -alias ca -gencert -rfc -infile broker.csr -outfile broker.crt -validity 9999 -ext bc=ca:false -ext eku=sA 34 | 35 | keytool -storetype pkcs12 -keystore broker-pkcs12.keystore -storepass password -keypass password -importcert -alias ca -file ca.crt -noprompt 36 | keytool -storetype pkcs12 -keystore broker-pkcs12.keystore -storepass password -keypass password -importcert -alias broker -file broker.crt 37 | 38 | # Create trust store for the broker, import the CA cert: 39 | # ------------------------------------------------------- 40 | keytool -storetype pkcs12 -keystore broker-pkcs12.truststore -storepass password -keypass password -importcert -alias ca -file ca.crt -noprompt 41 | 42 | # Create a key pair for the client, and sign it with the CA: 43 | # ---------------------------------------------------------- 44 | keytool -storetype pkcs12 -keystore client-pkcs12.keystore -storepass password -keypass password -alias client -genkey -keyalg "RSA" -keysize 2048 -dname "O=Client,CN=client" -validity 9999 -ext bc=ca:false -ext eku=cA 45 | 46 | keytool -storetype pkcs12 -keystore client-pkcs12.keystore -storepass password -alias client -certreq -file client.csr 47 | keytool -storetype pkcs12 -keystore ca-pkcs12.keystore -storepass password -alias ca -gencert -rfc -infile client.csr -outfile client.crt -validity 9999 -ext bc=ca:false -ext eku=cA 48 | 49 | keytool -storetype pkcs12 -keystore client-pkcs12.keystore -storepass password -keypass password -importcert -alias ca -file ca.crt -noprompt 50 | keytool -storetype pkcs12 -keystore client-pkcs12.keystore -storepass password -keypass password -importcert -alias client -file client.crt 51 | 52 | # Create trust store for the client, import the CA cert: 53 | # ------------------------------------------------------- 54 | keytool -storetype pkcs12 -keystore client-pkcs12.truststore -storepass password -keypass password -importcert -alias ca -file ca.crt -noprompt 55 | 56 | # Create a truststore with self-signed certificate for an alternative CA, to 57 | # allow 'failure to trust' of certs signed by the original CA above: 58 | # ------------------------------------------------------------------ 59 | keytool -storetype pkcs12 -keystore other-ca-pkcs12.truststore -storepass password -keypass password -alias other-ca -genkey -keyalg "RSA" -keysize 2048 -dname "O=Other Trusted Inc.,CN=other-vertx-ca.org" -validity 9999 -ext bc:c=ca:true 60 | keytool -storetype pkcs12 -keystore other-ca-pkcs12.truststore -storepass password -alias other-ca -exportcert -rfc > other-ca.crt 61 | keytool -storetype pkcs12 -keystore other-ca-pkcs12.truststore -storepass password -alias other-ca -delete 62 | keytool -storetype pkcs12 -keystore other-ca-pkcs12.truststore -storepass password -keypass password -importcert -alias other-ca -file other-ca.crt -noprompt 63 | 64 | # Create a key pair for the broker with an unexpected hostname, and sign it with the CA: 65 | # -------------------------------------------------------------------------------------- 66 | keytool -storetype pkcs12 -keystore broker-wrong-host-pkcs12.keystore -storepass password -keypass password -alias broker-wrong-host -genkey -keyalg "RSA" -keysize 2048 -dname "O=Server,CN=wronghost" -validity 9999 -ext bc=ca:false -ext eku=sA 67 | 68 | keytool -storetype pkcs12 -keystore broker-wrong-host-pkcs12.keystore -storepass password -alias broker-wrong-host -certreq -file broker-wrong-host.csr 69 | keytool -storetype pkcs12 -keystore ca-pkcs12.keystore -storepass password -alias ca -gencert -rfc -infile broker-wrong-host.csr -outfile broker-wrong-host.crt -validity 9999 -ext bc=ca:false -ext eku=sA 70 | 71 | keytool -storetype pkcs12 -keystore broker-wrong-host-pkcs12.keystore -storepass password -keypass password -importcert -alias ca -file ca.crt -noprompt 72 | keytool -storetype pkcs12 -keystore broker-wrong-host-pkcs12.keystore -storepass password -keypass password -importcert -alias broker-wrong-host -file broker-wrong-host.crt 73 | -------------------------------------------------------------------------------- /src/test/resources/broker-pkcs12.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vert-x3/vertx-amqp-client/92e8039a968d1882d5361581372d5ce687596da9/src/test/resources/broker-pkcs12.keystore -------------------------------------------------------------------------------- /src/test/resources/broker-pkcs12.truststore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vert-x3/vertx-amqp-client/92e8039a968d1882d5361581372d5ce687596da9/src/test/resources/broker-pkcs12.truststore -------------------------------------------------------------------------------- /src/test/resources/broker-wrong-host-pkcs12.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vert-x3/vertx-amqp-client/92e8039a968d1882d5361581372d5ce687596da9/src/test/resources/broker-wrong-host-pkcs12.keystore -------------------------------------------------------------------------------- /src/test/resources/broker-wrong-host.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDOTCCAiGgAwIBAgIEXAs2eDANBgkqhkiG9w0BAQsFADA0MRgwFgYDVQQDEw9t 3 | eS12ZXJ0eC1jYS5vcmcxGDAWBgNVBAoTD015IFRydXN0ZWQgSW5jLjAeFw0yMDA4 4 | MjcxMDMxNDlaFw00ODAxMTIxMDMxNDlaMCUxEjAQBgNVBAMTCXdyb25naG9zdDEP 5 | MA0GA1UEChMGU2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA 6 | wTnsePEuTlCf5yEJZjp5kIlG5cX17pXPuZ/gBIhF95PkLIb7iy1qotXwBcDSM2wn 7 | R1Jy6c4DA5QjcrCbEeLDPW+6lwIIhmJp5hkUQAcmu/hVgkb/dbrz5MD9LA97OACN 8 | OcNRZ343suTBjMCbEk8Tdo+AeanL76Y/GfJwYkUQEK+ZjZ6w2sB267jEzZrr3h19 9 | bJd29W7NtBnJfr0/ZVxY15WVNLD27q9Z+LrwVvIgsP9xYjqY6iaQYvWqUSVz/qC9 10 | wRJr9d0MKjGNFpgp6WDmvKqGFIOgLKLOpQHaqH9ImsYwtziFcnxozn1JhS31n+xh 11 | 1kVix3jxkuoNzsXgc4eZmwIDAQABo2IwYDAdBgNVHQ4EFgQUMinhy2hdxnHgi/ky 12 | BH4kZtpmZkUwCQYDVR0TBAIwADAfBgNVHSMEGDAWgBSHhaibFRAHyQVowTKrl/Lv 13 | lPIVGTATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAQEAROkO 14 | aKbH4mGw304VCY1IPUnyzhOj8JfZEL4xPXhNhgzD9mwj+aQwBhP/TCULLt9FIW/x 15 | uYr+tJOpVBDyPnEyUOPCieLPek3U+J1vmH24F/2XcJ6uhHIm+yPul8VA/LsPLW3o 16 | b5BtwjGdnQHjttciS4WciPFmR3e9IG7m1Jh6neUmlSeySBFPqIm5Kjnw3E1Yhl8j 17 | yant40qcDwdBVCXYD0Neun/sYo9Yp+8A5HBUEjFqWa4+MUD4MksXpqKqZqZVEmQ5 18 | vTQJXir7K56CTlOIFhIe4LaVgVDnaYXCwFylUnDMV6WiSRJKdKgVBeYi8QCQPNhc 19 | AhFbxHRB9xOGoYEb0Q== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /src/test/resources/broker-wrong-host.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN NEW CERTIFICATE REQUEST----- 2 | MIICmjCCAYICAQAwJTESMBAGA1UEAxMJd3Jvbmdob3N0MQ8wDQYDVQQKEwZTZXJ2 3 | ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBOex48S5OUJ/nIQlm 4 | OnmQiUblxfXulc+5n+AEiEX3k+QshvuLLWqi1fAFwNIzbCdHUnLpzgMDlCNysJsR 5 | 4sM9b7qXAgiGYmnmGRRABya7+FWCRv91uvPkwP0sD3s4AI05w1Fnfjey5MGMwJsS 6 | TxN2j4B5qcvvpj8Z8nBiRRAQr5mNnrDawHbruMTNmuveHX1sl3b1bs20Gcl+vT9l 7 | XFjXlZU0sPbur1n4uvBW8iCw/3FiOpjqJpBi9apRJXP+oL3BEmv13QwqMY0WmCnp 8 | YOa8qoYUg6Asos6lAdqof0iaxjC3OIVyfGjOfUmFLfWf7GHWRWLHePGS6g3OxeBz 9 | h5mbAgMBAAGgMDAuBgkqhkiG9w0BCQ4xITAfMB0GA1UdDgQWBBQyKeHLaF3GceCL 10 | +TIEfiRm2mZmRTANBgkqhkiG9w0BAQsFAAOCAQEAQx3CUBSA8tkSs2lu2PSM53/A 11 | qm6ZVvOZQW8tmHIW+sAmcxbGKp/DYlMJTFD5udeEFb4ADI/SxUgolGeMCoEbc2Fw 12 | btvQmKEkB9YHkhyIzXuyaAP7lfgNsUQM1nDyc1pfpvIM6lTozOYdHkp+vZFxP3dL 13 | C5pmoU1M1ix5nAoHdFoFUrxjLUHReavUCmjkroHl54VRKOcIeky4t0adcejg9s0Z 14 | O7ILutU7SOpQuuCbLkpMwQE7pfPsEaaCh+dpbipB+ak2B7kWSVL85XLg2oNNuSWB 15 | WDtj63BNxRFTPXeiOa/wqfCq3S9ljYYzf8hB4j0YPoy10Lst8thDVBizDKMOig== 16 | -----END NEW CERTIFICATE REQUEST----- 17 | -------------------------------------------------------------------------------- /src/test/resources/broker.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDOTCCAiGgAwIBAgIEDixaNzANBgkqhkiG9w0BAQsFADA0MRgwFgYDVQQDEw9t 3 | eS12ZXJ0eC1jYS5vcmcxGDAWBgNVBAoTD015IFRydXN0ZWQgSW5jLjAeFw0yMDA4 4 | MjcxMDMxNDJaFw00ODAxMTIxMDMxNDJaMCUxEjAQBgNVBAMTCWxvY2FsaG9zdDEP 5 | MA0GA1UEChMGU2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA 6 | sKvJz9twI3PdiKAW9r1sHOObyu8p7rsWu2Pd4JLlJM8JqJ0BdjmmijGrN6CJ1etM 7 | vJn0aJO9NqnWGyl3akzUlaB0BvsiXtOW6O6tM1ElnduYZag4SBJde3FjBOGxfstF 8 | fldk+B7reTrUOFES0F2yekhRNLlqVKrz1dd2PINlxJ3KVSJiOQ3ZoNKLuXfBfyUs 9 | 9oiifGCkSGadCnCzoVddUKBPOFXEfwrW+ZUB4UHIetT/z31FPEIdxwvLTAdDfXtm 10 | qtGPs+rcWx0VHwanrxShvPES+1dUzm9btoIJbJiD2Ed7vFcRL3+5OCnA3xkR58vD 11 | jetWt7FqE+ER6kZEqFifxQIDAQABo2IwYDAdBgNVHQ4EFgQUL37KDml9Scfe9OWG 12 | vT0IlkBsT2UwCQYDVR0TBAIwADAfBgNVHSMEGDAWgBSHhaibFRAHyQVowTKrl/Lv 13 | lPIVGTATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAQEABbj2 14 | OcsFOtAkosKWnqI6ldLyd+s2hiye/L3dATHefk6KUHxC2i6LfVYhOFgSnBckYGol 15 | ngBwnluBxzJzuP0UsjZv9UKWT6LxXE+GsD4WIRe82FK1jSl8cDhZhY0Z9/XsnEmE 16 | JE9BGixohETTPtXY97e0aP4c296YD59C6/qtiZ/ouid2g55Y45Il2ZFq2/BA+w6U 17 | HYTT5HdNBfrT1b91gRLUtWJgN0YZm0BNrEHET+lf6TaqZYq61yLkvUgnjhfCTX5N 18 | lVZjQbpKFBkJY/Zna7ZtCo3CCS689f6GKoBDxGWnqXGRH5yc1hAxQzdj+C6+f3wC 19 | sfqxxIr1Z0PSBO4K7A== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /src/test/resources/broker.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN NEW CERTIFICATE REQUEST----- 2 | MIICmjCCAYICAQAwJTESMBAGA1UEAxMJbG9jYWxob3N0MQ8wDQYDVQQKEwZTZXJ2 3 | ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwq8nP23Ajc92IoBb2 4 | vWwc45vK7ynuuxa7Y93gkuUkzwmonQF2OaaKMas3oInV60y8mfRok702qdYbKXdq 5 | TNSVoHQG+yJe05bo7q0zUSWd25hlqDhIEl17cWME4bF+y0V+V2T4Hut5OtQ4URLQ 6 | XbJ6SFE0uWpUqvPV13Y8g2XEncpVImI5Ddmg0ou5d8F/JSz2iKJ8YKRIZp0KcLOh 7 | V11QoE84VcR/Ctb5lQHhQch61P/PfUU8Qh3HC8tMB0N9e2aq0Y+z6txbHRUfBqev 8 | FKG88RL7V1TOb1u2gglsmIPYR3u8VxEvf7k4KcDfGRHny8ON61a3sWoT4RHqRkSo 9 | WJ/FAgMBAAGgMDAuBgkqhkiG9w0BCQ4xITAfMB0GA1UdDgQWBBQvfsoOaX1Jx970 10 | 5Ya9PQiWQGxPZTANBgkqhkiG9w0BAQsFAAOCAQEABwPsUWYdrqiSopW4S+39yR/S 11 | ZoN4CMnJ8hpRJ+SWLt5wk2is0J6B0bIHtjHPmFrPKnGVKXjdu0JEf+//besRqU3d 12 | cHgFHv8r+ltd3TwNdKVsYwTMSvyxa/USI8Bj6XHZpsyuG2/+YpNXrySyhBx1s+BZ 13 | PMvWlWZ8sLG0GPVoWP+Y4NouwL8d7xwzK1FlK35guTD5/wa5jBE1HnrWGwlLGOQ4 14 | 0nESxqrZjV/vVX3aVeW/W71lNYEmCvLMk2YD/vVA9bKRdzgkPxWJ1OfLrulepMbm 15 | GveM2kBpT8XkRjnYZMKD7HWwhRHEMBDnTKdf+ILwWegT0kOWKE6LuZeNg6jTZw== 16 | -----END NEW CERTIFICATE REQUEST----- 17 | -------------------------------------------------------------------------------- /src/test/resources/ca-pkcs12.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vert-x3/vertx-amqp-client/92e8039a968d1882d5361581372d5ce687596da9/src/test/resources/ca-pkcs12.keystore -------------------------------------------------------------------------------- /src/test/resources/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDGDCCAgCgAwIBAgIEBoI1VTANBgkqhkiG9w0BAQsFADA0MRgwFgYDVQQDEw9t 3 | eS12ZXJ0eC1jYS5vcmcxGDAWBgNVBAoTD015IFRydXN0ZWQgSW5jLjAeFw0yMDA4 4 | MjcxMDMxNDBaFw00ODAxMTIxMDMxNDBaMDQxGDAWBgNVBAMTD215LXZlcnR4LWNh 5 | Lm9yZzEYMBYGA1UEChMPTXkgVHJ1c3RlZCBJbmMuMIIBIjANBgkqhkiG9w0BAQEF 6 | AAOCAQ8AMIIBCgKCAQEAoTPVpwtZQOUAUN5yf9MzwI6k3MJ+m/e2B/TsOhy+DaJs 7 | 9s6twYGOQ94RLtF5OrfewlVTZvjj1kC3Hs5BT/GGOyWpxLC5Uz54ErnJDFzqnNdr 8 | omHCTY+ZOGpNhvtcVn0ruvht8GjK2ueCN9HyYIjTR8VC6nFTP3kTX7M6UCMIx4nx 9 | aFbvPsfFkYlGlNaXTurzC0pngQDiln3FCNdunUJDpXt9Zszg0t2bmgyuDJ8+2TjR 10 | WoLJjvl0fZeObo+Uqm5NsZMwoNPU3JfvjZPTvBeJT/qHA7wqgBOG8PtuVc7bX6kw 11 | iNAWCFVpEakkYE+LARJ6A2PqW/x+iVwiCzPEiAUaqwIDAQABozIwMDAdBgNVHQ4E 12 | FgQUh4WomxUQB8kFaMEyq5fy75TyFRkwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG 13 | 9w0BAQsFAAOCAQEASkSA7p6YuYcfuLFj+3GynZUL9O1+QsJ7Hyoi1/sQH+4nE/id 14 | 6WCDNl2UAzD6FvcBN/qAtXIsUhAJK7vT2BBFGjKOWWuNytbLFw3lOaGywMUA28s/ 15 | ippLB9CnSW7cAZ3Pxy9bgXIR5Mqn+kkftEcCJAz7DG3+GLnNMnHX2h7FfAZn5a7K 16 | 8UPNiTVzO1KpUiaulGIdSqinJzZNUl4zXmaG8Q7Uu0zzMZ2+AsoIRnI8djYl9vh7 17 | MUiwNUeOk4dMpzvhpJUPBrDqX+OxYRFJCxpFLd2dEBZvMTbFwrgd9J2aO1CkOBBF 18 | MK/0qsKSCsLpMvuQl0gi9jzhnmvVIfQGgZDOcQ== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /src/test/resources/client-pkcs12.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vert-x3/vertx-amqp-client/92e8039a968d1882d5361581372d5ce687596da9/src/test/resources/client-pkcs12.keystore -------------------------------------------------------------------------------- /src/test/resources/client-pkcs12.truststore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vert-x3/vertx-amqp-client/92e8039a968d1882d5361581372d5ce687596da9/src/test/resources/client-pkcs12.truststore -------------------------------------------------------------------------------- /src/test/resources/client.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDNjCCAh6gAwIBAgIEDWaJ8jANBgkqhkiG9w0BAQsFADA0MRgwFgYDVQQDEw9t 3 | eS12ZXJ0eC1jYS5vcmcxGDAWBgNVBAoTD015IFRydXN0ZWQgSW5jLjAeFw0yMDA4 4 | MjcxMDMxNDRaFw00ODAxMTIxMDMxNDRaMCIxDzANBgNVBAMTBmNsaWVudDEPMA0G 5 | A1UEChMGQ2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqVUe 6 | uAYMUslbFzcbw4g7ARJ/9y1WmXVg+3jTMhMgLPySHdtqraiX1TOtqWFiO5PjiKkH 7 | 5TkEUyfsEWz4DNo2OYMnyxd8LIbs55hfQHFca013UJvt730y8q4m7GrE9H4FxT4R 8 | ikKbaQm/u9aZC5TAlp/vdfmovitsoL/wBpLPYwF+qrXwnEjZhR8f28OyEyA22my1 9 | ZherKa7IcHhrBapnFx3qfKKsonAYkpbNdwAFPEY+C0yc6WujaVxRTxhsOmbGljbZ 10 | X0x7EQVYYeroDvJgMgADgyYhfXC2JmjG8vhjfEReWywGKC0ziIRk70HNb4p21rtz 11 | gV+ZAqItTnfvnl1N6wIDAQABo2IwYDAdBgNVHQ4EFgQUFw5/+2NBnQ1irqMv/lp2 12 | fjknZ54wCQYDVR0TBAIwADAfBgNVHSMEGDAWgBSHhaibFRAHyQVowTKrl/LvlPIV 13 | GTATBgNVHSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAFj/fIktb 14 | gAzQs35Sw51vqo/f3xWjJPmMY52hqGeC7iFObd9OWlhJ5j1/XV6bIyA86vSKQjEU 15 | Qw8va/2EYmEnexMqBQBhAkeXoIe5jnbAYeAjcK7E87UGyQsKQpfYxmZlKFEdUP2O 16 | pph6qw18qQMhcMgpdWFnfnFQaVrnRtLGaceYiQ2qV/6seU4HpaINfjQlU8LB9yHk 17 | Ub1SfCn0blxQPFBZ/9mfomn3VG9Nc98UZKugeI7eFXKKEUv+3T6RsRzJ45JO5AQR 18 | xla8XIaHEhOzqF9RC1E5VZIfHJjbMHCmC+FIlKH6MLmEFXToUZS211EeI1Icy6e0 19 | dkC287YkY4YUhg== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /src/test/resources/client.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN NEW CERTIFICATE REQUEST----- 2 | MIIClzCCAX8CAQAwIjEPMA0GA1UEAxMGY2xpZW50MQ8wDQYDVQQKEwZDbGllbnQw 3 | ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpVR64BgxSyVsXNxvDiDsB 4 | En/3LVaZdWD7eNMyEyAs/JId22qtqJfVM62pYWI7k+OIqQflOQRTJ+wRbPgM2jY5 5 | gyfLF3wshuznmF9AcVxrTXdQm+3vfTLyribsasT0fgXFPhGKQptpCb+71pkLlMCW 6 | n+91+ai+K2ygv/AGks9jAX6qtfCcSNmFHx/bw7ITIDbabLVmF6sprshweGsFqmcX 7 | Hep8oqyicBiSls13AAU8Rj4LTJzpa6NpXFFPGGw6ZsaWNtlfTHsRBVhh6ugO8mAy 8 | AAODJiF9cLYmaMby+GN8RF5bLAYoLTOIhGTvQc1vinbWu3OBX5kCoi1Od++eXU3r 9 | AgMBAAGgMDAuBgkqhkiG9w0BCQ4xITAfMB0GA1UdDgQWBBQXDn/7Y0GdDWKuoy/+ 10 | WnZ+OSdnnjANBgkqhkiG9w0BAQsFAAOCAQEAeTaFJlYOGXuy4jr7hS07qaG3+mgB 11 | UmpeoYpFzVjI76U44OHq2N5E6q5Rj3sIS1XDtP69j5qps4q2aEtgFiM9ftNjXTHB 12 | ThGzd7rzvq51jTrk85ur6pD10i/npp6a9EaxQcTSDtdoi76i1ZqrPDTuAMeZH4X8 13 | MPUWqhwusJRDNsQar81lvwhZReZ70rbv0bsGjsQ0WumEJNqd9G54bdc8ntQvhyV8 14 | qXPVTshvb/vlCPkb1k87m0uVrbsl+LY4Go4fACKUzwhh4n6jApZJG6DiC+JgRv2K 15 | 7NTC6gvIkdelDwjkmxbidtJFadYkjqrU5/LXPXhntst/wnNnwxRPkYNMaQ== 16 | -----END NEW CERTIFICATE REQUEST----- 17 | -------------------------------------------------------------------------------- /src/test/resources/other-ca-pkcs12.truststore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vert-x3/vertx-amqp-client/92e8039a968d1882d5361581372d5ce687596da9/src/test/resources/other-ca-pkcs12.truststore -------------------------------------------------------------------------------- /src/test/resources/other-ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDJDCCAgygAwIBAgIEdZHQGzANBgkqhkiG9w0BAQsFADA6MRswGQYDVQQDExJv 3 | dGhlci12ZXJ0eC1jYS5vcmcxGzAZBgNVBAoTEk90aGVyIFRydXN0ZWQgSW5jLjAe 4 | Fw0yMDA4MjcxMDMxNDZaFw00ODAxMTIxMDMxNDZaMDoxGzAZBgNVBAMTEm90aGVy 5 | LXZlcnR4LWNhLm9yZzEbMBkGA1UEChMST3RoZXIgVHJ1c3RlZCBJbmMuMIIBIjAN 6 | BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsoKVlTfkv/3ph4W7lv0Mb8vOqdYJ 7 | Indgjt2ofgVRTSBYtipDHomzs05L4tddOYuXUpodme+CG1xNnmkOnSZid1RH6at7 8 | lYAAWNzKsb4Yn5HjH9+NzksBIuJuQH+eJYdHC6h4/YTIAFh8tL3+nvqi10G9kNMj 9 | B5pONX+zewMN4WNscysBm9Teh9BlnUJynSBPCNRTSmo9SEe6i2phkE8sFSRMqzV8 10 | vOw6IvgmiHQLxgFbI5UBApUEcU2kB17ccbEq3RgPiwE5RR9/v3+UjrAvt1wGpJKH 11 | 3IkMiTbLmn1q93MghAx8bZveNoSdSFcurNoZtV52yxHVi8OAl0qgG7VvCwIDAQAB 12 | ozIwMDAdBgNVHQ4EFgQUTY5WrUkOkmnhW6wzYDsjrgAF5z0wDwYDVR0TAQH/BAUw 13 | AwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAEuDefCWogHTjKM+NdlFTOaNi1hEOZ8It 14 | Dbe26v9nE0jpvvHQr4xFyL8eIxRaogVen5LvZAGmsBZe5V9mb1Ng8Y0hyDKTbVxh 15 | yFodsPfhL4NMxlXhRTYoEkVZUsANfW61a2uCReWtqeDCqx03GyX6SRhCNxaYTqMT 16 | w3aCeREBmmW+S+/lvK5AB+YZmiPpfScu0uRu2WtXYCRmhqNA+ikCPk/jGYw2hK2b 17 | O2Y7OcsAMbkvHwLwwMTIkHuo0bxauL4F37Go36gJ6SOUXlKA8GmfZ9/ZLLhQJpZU 18 | Tx4wQCNbiDLE60WS4wVUCeT/egppT5rmv7zuh7BVxH5aweNTmPr0ug== 19 | -----END CERTIFICATE----- 20 | --------------------------------------------------------------------------------