├── .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 ├── main ├── asciidoc │ └── index.adoc ├── generated │ └── io │ │ └── vertx │ │ └── ext │ │ └── stomp │ │ ├── FrameConverter.java │ │ ├── StompClientOptionsConverter.java │ │ └── StompServerOptionsConverter.java ├── java │ ├── examples │ │ ├── StompClientExamples.java │ │ ├── StompServerExamples.java │ │ └── package-info.java │ ├── io │ │ └── vertx │ │ │ └── ext │ │ │ └── stomp │ │ │ ├── Acknowledgement.java │ │ │ ├── BridgeOptions.java │ │ │ ├── Command.java │ │ │ ├── DefaultAbortHandler.java │ │ │ ├── DefaultAckHandler.java │ │ │ ├── DefaultBeginHandler.java │ │ │ ├── DefaultCommitHandler.java │ │ │ ├── DefaultConnectHandler.java │ │ │ ├── DefaultNackHandler.java │ │ │ ├── DefaultSendHandler.java │ │ │ ├── DefaultSubscribeHandler.java │ │ │ ├── DefaultUnsubscribeHandler.java │ │ │ ├── Destination.java │ │ │ ├── DestinationFactory.java │ │ │ ├── Frame.java │ │ │ ├── Frames.java │ │ │ ├── ServerFrame.java │ │ │ ├── StompClient.java │ │ │ ├── StompClientConnection.java │ │ │ ├── StompClientOptions.java │ │ │ ├── StompOptions.java │ │ │ ├── StompServer.java │ │ │ ├── StompServerConnection.java │ │ │ ├── StompServerHandler.java │ │ │ ├── StompServerOptions.java │ │ │ ├── impl │ │ │ ├── AcknowledgementImpl.java │ │ │ ├── DefaultStompHandler.java │ │ │ ├── EventBusBridge.java │ │ │ ├── FrameException.java │ │ │ ├── FrameParser.java │ │ │ ├── HeaderCodec.java │ │ │ ├── Queue.java │ │ │ ├── ServerFrameImpl.java │ │ │ ├── StompClientConnectionImpl.java │ │ │ ├── StompClientImpl.java │ │ │ ├── StompServerImpl.java │ │ │ ├── StompServerTCPConnectionImpl.java │ │ │ ├── StompServerWebSocketConnectionImpl.java │ │ │ ├── Topic.java │ │ │ ├── Transaction.java │ │ │ └── Transactions.java │ │ │ ├── package-info.java │ │ │ └── utils │ │ │ ├── Headers.java │ │ │ └── Server.java │ └── module-info.java └── resources │ └── vertx-stomp-version.txt └── test ├── java └── io │ └── vertx │ └── stomp │ └── tests │ ├── FrameTest.java │ ├── HeadersTest.java │ ├── StompClientOptionsTest.java │ ├── StompServerTest.java │ ├── impl │ ├── AckTest.java │ ├── AsyncLock.java │ ├── EventBusBridgeTest.java │ ├── FrameHandlerTest.java │ ├── FrameParserTest.java │ ├── FrameTest.java │ ├── HeaderCodecTest.java │ ├── Helper.java │ ├── LoadTest.java │ ├── QueueManagingAcknowledgments.java │ ├── QueueManagingAcknowledgmentsFactory.java │ ├── ReceiptTest.java │ ├── SecuredServerConnectionTest.java │ ├── ServerConnectionTest.java │ ├── StompClientImplTest.java │ ├── StompServerImplTest.java │ ├── SubscriptionsUsingQueueTest.java │ ├── SubscriptionsUsingTopicTest.java │ ├── TestSendFailure.java │ ├── TransactionsTest.java │ └── WebSocketBridgeTest.java │ ├── integration │ ├── AbstractClientIT.java │ ├── ActivemqDockerIT.java │ ├── ArtemisHornetQDockerIT.java │ ├── ArtemisStompDockerIT.java │ ├── RabbitMQDockerIT.java │ ├── StiltsClientIT.java │ ├── StompConsumer.java │ ├── StompIntegrationTests.java │ └── StompPublisher.java │ └── verticles │ ├── MultiInstanceSubscriptionTest.java │ ├── MultiInstanceTransactionTest.java │ ├── ReceiverStompClient.java │ ├── StompServerVerticle.java │ └── TxSenderStompClient.java └── resources ├── integration └── rabbitmq │ └── enabled_plugins └── test-auth.properties /.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 | [**/examples/**.java] 12 | # 84 looks like a odd number, however 13 | # it accounts for 4 spaces (class and example method indentation) 14 | max_line_length = 84 15 | -------------------------------------------------------------------------------- /.github/maven-cd-settings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | false 20 | 21 | 22 | 23 | vertx-snapshots-repository 24 | ${env.VERTX_NEXUS_USERNAME} 25 | ${env.VERTX_NEXUS_PASSWORD} 26 | 27 | 28 | 29 | 30 | 31 | google-mirror 32 | 33 | true 34 | 35 | 36 | 37 | google-maven-central 38 | GCS Maven Central mirror EU 39 | https://maven-central.storage-download.googleapis.com/maven2/ 40 | 41 | true 42 | 43 | 44 | false 45 | 46 | 47 | 48 | 49 | 50 | google-maven-central 51 | GCS Maven Central mirror 52 | https://maven-central.storage-download.googleapis.com/maven2/ 53 | 54 | true 55 | 56 | 57 | false 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /.github/maven-ci-settings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | false 20 | 21 | 22 | 23 | google-mirror 24 | 25 | true 26 | 27 | 28 | 29 | google-maven-central 30 | GCS Maven Central mirror EU 31 | https://maven-central.storage-download.googleapis.com/maven2/ 32 | 33 | true 34 | 35 | 36 | false 37 | 38 | 39 | 40 | 41 | 42 | google-maven-central 43 | GCS Maven Central mirror 44 | https://maven-central.storage-download.googleapis.com/maven2/ 45 | 46 | true 47 | 48 | 49 | false 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /.github/workflows/ci-4.x.yml: -------------------------------------------------------------------------------- 1 | name: vertx-stomp (4.x) 2 | on: 3 | schedule: 4 | - cron: '0 4 * * *' 5 | jobs: 6 | CI: 7 | strategy: 8 | matrix: 9 | include: 10 | - os: ubuntu-latest 11 | jdk: 8 12 | - os: ubuntu-latest 13 | jdk: 17 14 | uses: ./.github/workflows/ci.yml 15 | with: 16 | branch: 4.x 17 | jdk: ${{ matrix.jdk }} 18 | os: ${{ matrix.os }} 19 | secrets: inherit 20 | Deploy: 21 | if: ${{ github.repository_owner == 'vert-x3' && (github.event_name == 'push' || github.event_name == 'schedule') }} 22 | needs: CI 23 | uses: ./.github/workflows/deploy.yml 24 | with: 25 | branch: 4.x 26 | jdk: 8 27 | secrets: inherit 28 | -------------------------------------------------------------------------------- /.github/workflows/ci-5.x-stable.yml: -------------------------------------------------------------------------------- 1 | name: vertx-stomp (5.x-stable) 2 | on: 3 | push: 4 | branches: 5 | - '5.[0-9]+' 6 | pull_request: 7 | branches: 8 | - '5.[0-9]+' 9 | schedule: 10 | - cron: '0 6 * * *' 11 | jobs: 12 | CI-CD: 13 | uses: ./.github/workflows/ci-matrix-5.x.yml 14 | secrets: inherit 15 | with: 16 | branch: ${{ github.event_name == 'schedule' && vars.VERTX_5_STABLE_BRANCH || github.event.pull_request.head.sha || github.ref_name }} 17 | -------------------------------------------------------------------------------- /.github/workflows/ci-5.x.yml: -------------------------------------------------------------------------------- 1 | name: vertx-stomp (5.x) 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | schedule: 10 | - cron: '0 5 * * *' 11 | jobs: 12 | CI-CD: 13 | uses: ./.github/workflows/ci-matrix-5.x.yml 14 | secrets: inherit 15 | with: 16 | branch: ${{ github.event.pull_request.head.sha || github.ref_name }} 17 | -------------------------------------------------------------------------------- /.github/workflows/ci-matrix-5.x.yml: -------------------------------------------------------------------------------- 1 | name: CI matrix (5.x) 2 | on: 3 | workflow_call: 4 | inputs: 5 | branch: 6 | required: true 7 | type: string 8 | jobs: 9 | CI: 10 | strategy: 11 | matrix: 12 | include: 13 | - os: ubuntu-latest 14 | jdk: 11 15 | - os: ubuntu-latest 16 | jdk: 21 17 | uses: ./.github/workflows/ci.yml 18 | with: 19 | branch: ${{ inputs.branch }} 20 | jdk: ${{ matrix.jdk }} 21 | os: ${{ matrix.os }} 22 | secrets: inherit 23 | Deploy: 24 | if: ${{ github.repository_owner == 'vert-x3' && (github.event_name == 'push' || github.event_name == 'schedule') }} 25 | needs: CI 26 | uses: ./.github/workflows/deploy.yml 27 | with: 28 | branch: ${{ inputs.branch }} 29 | jdk: 11 30 | secrets: inherit 31 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | workflow_call: 4 | inputs: 5 | branch: 6 | required: true 7 | type: string 8 | jdk: 9 | default: 8 10 | type: string 11 | os: 12 | default: ubuntu-latest 13 | type: string 14 | jobs: 15 | Test: 16 | name: Run tests 17 | runs-on: ${{ inputs.os }} 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | with: 22 | ref: ${{ inputs.branch }} 23 | - name: Install JDK 24 | uses: actions/setup-java@v2 25 | with: 26 | java-version: ${{ inputs.jdk }} 27 | distribution: temurin 28 | - name: Run tests 29 | run: mvn -s .github/maven-ci-settings.xml -q clean verify -B 30 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | workflow_call: 4 | inputs: 5 | branch: 6 | required: true 7 | type: string 8 | jdk: 9 | default: 8 10 | type: string 11 | jobs: 12 | Deploy: 13 | name: Deploy to OSSRH 14 | runs-on: ubuntu-latest 15 | env: 16 | VERTX_NEXUS_USERNAME: ${{ secrets.VERTX_NEXUS_USERNAME }} 17 | VERTX_NEXUS_PASSWORD: ${{ secrets.VERTX_NEXUS_PASSWORD }} 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | with: 22 | ref: ${{ inputs.branch }} 23 | - name: Install JDK 24 | uses: actions/setup-java@v2 25 | with: 26 | java-version: ${{ inputs.jdk }} 27 | distribution: temurin 28 | - name: Get project version 29 | run: echo "PROJECT_VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:evaluate -Dexpression=project.version -q -DforceStdout | grep -v '\[')" >> $GITHUB_ENV 30 | - name: Maven deploy 31 | if: ${{ endsWith(env.PROJECT_VERSION, '-SNAPSHOT') }} 32 | run: mvn deploy -s .github/maven-cd-settings.xml -DskipTests -B 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Java template 3 | *.class 4 | 5 | # Mobile Tools for Java (J2ME) 6 | .mtj.tmp/ 7 | 8 | # Package Files # 9 | *.jar 10 | *.war 11 | *.ear 12 | 13 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 14 | hs_err_pid* 15 | 16 | 17 | ### Maven template 18 | target/ 19 | pom.xml.tag 20 | pom.xml.releaseBackup 21 | pom.xml.versionsBackup 22 | pom.xml.next 23 | release.properties 24 | dependency-reduced-pom.xml 25 | buildNumber.properties 26 | .mvn/timing.properties 27 | 28 | ### Vert.x build 29 | .yardoc 30 | .idea 31 | *.iml 32 | 33 | .project 34 | .classpath 35 | .settings 36 | .vertx 37 | .vscode 38 | 39 | .DS_Store 40 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = vert.x-stomp 2 | 3 | image:https://github.com/vert-x3/vertx-stomp/actions/workflows/ci-5.x.yml/badge.svg["Build Status (5.x)",link="https://github.com/vert-x3/vertx-stomp/actions/workflows/ci-5.x.yml"] 4 | image:https://github.com/vert-x3/vertx-stomp/actions/workflows/ci-4.x.yml/badge.svg["Build Status (4.x)",link="https://github.com/vert-x3/vertx-stomp/actions/workflows/ci-4.x.yml"] 5 | 6 | A vert.x implementation of the STOMP specification. It provides a STOMP server and client. 7 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | io.vertx 7 | vertx5-parent 8 | 12 9 | 10 | 11 | vertx-stomp 12 | 5.1.0-SNAPSHOT 13 | 14 | Vert.x Stomp 15 | Stomp support for Vert.x 3 16 | 17 | 18 | scm:git:git@github.com:vert-x3/vertx-stomp.git 19 | scm:git:git@github.com:vert-x3/vertx-stomp.git 20 | git@github.com:vert-x3/vertx-stomp.git 21 | 22 | 23 | 24 | 25 | 26 | io.vertx 27 | vertx-dependencies 28 | ${project.version} 29 | pom 30 | import 31 | 32 | 33 | 34 | 35 | 36 | 37 | io.vertx 38 | vertx-core 39 | 40 | 41 | io.vertx 42 | vertx-codegen-api 43 | true 44 | 45 | 46 | io.vertx 47 | vertx-codegen-json 48 | true 49 | 50 | 51 | io.vertx 52 | vertx-docgen-api 53 | true 54 | 55 | 56 | io.vertx 57 | vertx-auth-common 58 | 59 | 60 | io.vertx 61 | vertx-bridge-common 62 | 63 | 64 | 65 | junit 66 | junit 67 | 4.13.1 68 | test 69 | 70 | 71 | org.assertj 72 | assertj-core 73 | 3.8.0 74 | test 75 | 76 | 77 | io.vertx 78 | vertx-unit 79 | test 80 | 81 | 82 | com.jayway.awaitility 83 | awaitility 84 | 1.7.0 85 | test 86 | 87 | 88 | org.slf4j 89 | slf4j-api 90 | test 91 | 92 | 93 | io.vertx 94 | vertx-auth-properties 95 | test 96 | 97 | 98 | org.projectodd.stilts 99 | stilts-stomp-client 100 | 0.1.40 101 | test 102 | 103 | 104 | 105 | org.testcontainers 106 | testcontainers 107 | 1.19.8 108 | test 109 | 110 | 111 | org.slf4j 112 | slf4j-simple 113 | test 114 | 115 | 116 | 117 | 118 | 119 | 120 | ${project.basedir}/src/main/resources 121 | true 122 | 123 | *.txt 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | maven-compiler-plugin 132 | 133 | 134 | default-compile 135 | 136 | 137 | 138 | io.vertx 139 | vertx-codegen 140 | processor 141 | 142 | 143 | io.vertx 144 | vertx-docgen-processor 145 | processor 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | maven-assembly-plugin 158 | 159 | 160 | package-docs 161 | 162 | single 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /src/main/generated/io/vertx/ext/stomp/FrameConverter.java: -------------------------------------------------------------------------------- 1 | package io.vertx.ext.stomp; 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.ext.stomp.Frame}. 10 | * NOTE: This class has been automatically generated from the {@link io.vertx.ext.stomp.Frame} original class using Vert.x codegen. 11 | */ 12 | public class FrameConverter { 13 | 14 | static void fromJson(Iterable> json, Frame obj) { 15 | for (java.util.Map.Entry member : json) { 16 | switch (member.getKey()) { 17 | case "headers": 18 | if (member.getValue() instanceof JsonObject) { 19 | ((Iterable>)member.getValue()).forEach(entry -> { 20 | if (entry.getValue() instanceof String) 21 | obj.addHeader(entry.getKey(), (String)entry.getValue()); 22 | }); 23 | } 24 | break; 25 | case "ack": 26 | break; 27 | case "command": 28 | if (member.getValue() instanceof String) { 29 | obj.setCommand(io.vertx.ext.stomp.Command.valueOf((String)member.getValue())); 30 | } 31 | break; 32 | case "body": 33 | if (member.getValue() instanceof String) { 34 | obj.setBody(io.vertx.core.buffer.Buffer.fromJson((String)member.getValue())); 35 | } 36 | break; 37 | case "bodyAsString": 38 | break; 39 | case "destination": 40 | if (member.getValue() instanceof String) { 41 | obj.setDestination((String)member.getValue()); 42 | } 43 | break; 44 | case "transaction": 45 | if (member.getValue() instanceof String) { 46 | obj.setTransaction((String)member.getValue()); 47 | } 48 | break; 49 | case "id": 50 | if (member.getValue() instanceof String) { 51 | obj.setId((String)member.getValue()); 52 | } 53 | break; 54 | case "receipt": 55 | break; 56 | } 57 | } 58 | } 59 | 60 | static void toJson(Frame obj, JsonObject json) { 61 | toJson(obj, json.getMap()); 62 | } 63 | 64 | static void toJson(Frame obj, java.util.Map json) { 65 | if (obj.getHeaders() != null) { 66 | JsonObject map = new JsonObject(); 67 | obj.getHeaders().forEach((key, value) -> map.put(key, value)); 68 | json.put("headers", map); 69 | } 70 | if (obj.getAck() != null) { 71 | json.put("ack", obj.getAck()); 72 | } 73 | if (obj.getCommand() != null) { 74 | json.put("command", obj.getCommand().name()); 75 | } 76 | if (obj.getBody() != null) { 77 | json.put("body", obj.getBody().toJson()); 78 | } 79 | if (obj.getBodyAsString() != null) { 80 | json.put("bodyAsString", obj.getBodyAsString()); 81 | } 82 | if (obj.getDestination() != null) { 83 | json.put("destination", obj.getDestination()); 84 | } 85 | if (obj.getTransaction() != null) { 86 | json.put("transaction", obj.getTransaction()); 87 | } 88 | if (obj.getId() != null) { 89 | json.put("id", obj.getId()); 90 | } 91 | if (obj.getReceipt() != null) { 92 | json.put("receipt", obj.getReceipt()); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/generated/io/vertx/ext/stomp/StompClientOptionsConverter.java: -------------------------------------------------------------------------------- 1 | package io.vertx.ext.stomp; 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.ext.stomp.StompClientOptions}. 10 | * NOTE: This class has been automatically generated from the {@link io.vertx.ext.stomp.StompClientOptions} original class using Vert.x codegen. 11 | */ 12 | public class StompClientOptionsConverter { 13 | 14 | static void fromJson(Iterable> json, StompClientOptions 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 "login": 23 | if (member.getValue() instanceof String) { 24 | obj.setLogin((String)member.getValue()); 25 | } 26 | break; 27 | case "passcode": 28 | if (member.getValue() instanceof String) { 29 | obj.setPasscode((String)member.getValue()); 30 | } 31 | break; 32 | case "port": 33 | if (member.getValue() instanceof Number) { 34 | obj.setPort(((Number)member.getValue()).intValue()); 35 | } 36 | break; 37 | case "acceptedVersions": 38 | if (member.getValue() instanceof JsonArray) { 39 | java.util.ArrayList list = new java.util.ArrayList<>(); 40 | ((Iterable)member.getValue()).forEach( item -> { 41 | if (item instanceof String) 42 | list.add((String)item); 43 | }); 44 | obj.setAcceptedVersions(list); 45 | } 46 | break; 47 | case "autoComputeContentLength": 48 | if (member.getValue() instanceof Boolean) { 49 | obj.setAutoComputeContentLength((Boolean)member.getValue()); 50 | } 51 | break; 52 | case "useStompFrame": 53 | if (member.getValue() instanceof Boolean) { 54 | obj.setUseStompFrame((Boolean)member.getValue()); 55 | } 56 | break; 57 | case "bypassHostHeader": 58 | if (member.getValue() instanceof Boolean) { 59 | obj.setBypassHostHeader((Boolean)member.getValue()); 60 | } 61 | break; 62 | case "heartbeat": 63 | if (member.getValue() instanceof JsonObject) { 64 | obj.setHeartbeat(((JsonObject)member.getValue()).copy()); 65 | } 66 | break; 67 | case "virtualHost": 68 | if (member.getValue() instanceof String) { 69 | obj.setVirtualHost((String)member.getValue()); 70 | } 71 | break; 72 | case "trailingLine": 73 | if (member.getValue() instanceof Boolean) { 74 | obj.setTrailingLine((Boolean)member.getValue()); 75 | } 76 | break; 77 | } 78 | } 79 | } 80 | 81 | static void toJson(StompClientOptions obj, JsonObject json) { 82 | toJson(obj, json.getMap()); 83 | } 84 | 85 | static void toJson(StompClientOptions obj, java.util.Map json) { 86 | if (obj.getHost() != null) { 87 | json.put("host", obj.getHost()); 88 | } 89 | if (obj.getLogin() != null) { 90 | json.put("login", obj.getLogin()); 91 | } 92 | if (obj.getPasscode() != null) { 93 | json.put("passcode", obj.getPasscode()); 94 | } 95 | json.put("port", obj.getPort()); 96 | if (obj.getAcceptedVersions() != null) { 97 | JsonArray array = new JsonArray(); 98 | obj.getAcceptedVersions().forEach(item -> array.add(item)); 99 | json.put("acceptedVersions", array); 100 | } 101 | json.put("autoComputeContentLength", obj.isAutoComputeContentLength()); 102 | json.put("useStompFrame", obj.isUseStompFrame()); 103 | json.put("bypassHostHeader", obj.isBypassHostHeader()); 104 | if (obj.getHeartbeat() != null) { 105 | json.put("heartbeat", obj.getHeartbeat()); 106 | } 107 | if (obj.getVirtualHost() != null) { 108 | json.put("virtualHost", obj.getVirtualHost()); 109 | } 110 | json.put("trailingLine", obj.isTrailingLine()); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/generated/io/vertx/ext/stomp/StompServerOptionsConverter.java: -------------------------------------------------------------------------------- 1 | package io.vertx.ext.stomp; 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.ext.stomp.StompServerOptions}. 10 | * NOTE: This class has been automatically generated from the {@link io.vertx.ext.stomp.StompServerOptions} original class using Vert.x codegen. 11 | */ 12 | public class StompServerOptionsConverter { 13 | 14 | static void fromJson(Iterable> json, StompServerOptions obj) { 15 | for (java.util.Map.Entry member : json) { 16 | switch (member.getKey()) { 17 | case "maxBodyLength": 18 | if (member.getValue() instanceof Number) { 19 | obj.setMaxBodyLength(((Number)member.getValue()).intValue()); 20 | } 21 | break; 22 | case "maxHeaderLength": 23 | if (member.getValue() instanceof Number) { 24 | obj.setMaxHeaderLength(((Number)member.getValue()).intValue()); 25 | } 26 | break; 27 | case "maxHeaders": 28 | if (member.getValue() instanceof Number) { 29 | obj.setMaxHeaders(((Number)member.getValue()).intValue()); 30 | } 31 | break; 32 | case "supportedVersions": 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.setSupportedVersions(list); 40 | } 41 | break; 42 | case "secured": 43 | if (member.getValue() instanceof Boolean) { 44 | obj.setSecured((Boolean)member.getValue()); 45 | } 46 | break; 47 | case "sendErrorOnNoSubscriptions": 48 | if (member.getValue() instanceof Boolean) { 49 | obj.setSendErrorOnNoSubscriptions((Boolean)member.getValue()); 50 | } 51 | break; 52 | case "timeFactor": 53 | if (member.getValue() instanceof Number) { 54 | obj.setTimeFactor(((Number)member.getValue()).intValue()); 55 | } 56 | break; 57 | case "heartbeat": 58 | if (member.getValue() instanceof JsonObject) { 59 | obj.setHeartbeat(((JsonObject)member.getValue()).copy()); 60 | } 61 | break; 62 | case "maxFrameInTransaction": 63 | if (member.getValue() instanceof Number) { 64 | obj.setMaxFrameInTransaction(((Number)member.getValue()).intValue()); 65 | } 66 | break; 67 | case "transactionChunkSize": 68 | if (member.getValue() instanceof Number) { 69 | obj.setTransactionChunkSize(((Number)member.getValue()).intValue()); 70 | } 71 | break; 72 | case "maxSubscriptionsByClient": 73 | if (member.getValue() instanceof Number) { 74 | obj.setMaxSubscriptionsByClient(((Number)member.getValue()).intValue()); 75 | } 76 | break; 77 | case "websocketBridge": 78 | if (member.getValue() instanceof Boolean) { 79 | obj.setWebsocketBridge((Boolean)member.getValue()); 80 | } 81 | break; 82 | case "websocketPath": 83 | if (member.getValue() instanceof String) { 84 | obj.setWebsocketPath((String)member.getValue()); 85 | } 86 | break; 87 | case "trailingLine": 88 | if (member.getValue() instanceof Boolean) { 89 | obj.setTrailingLine((Boolean)member.getValue()); 90 | } 91 | break; 92 | } 93 | } 94 | } 95 | 96 | static void toJson(StompServerOptions obj, JsonObject json) { 97 | toJson(obj, json.getMap()); 98 | } 99 | 100 | static void toJson(StompServerOptions obj, java.util.Map json) { 101 | json.put("maxBodyLength", obj.getMaxBodyLength()); 102 | json.put("maxHeaderLength", obj.getMaxHeaderLength()); 103 | json.put("maxHeaders", obj.getMaxHeaders()); 104 | if (obj.getSupportedVersions() != null) { 105 | JsonArray array = new JsonArray(); 106 | obj.getSupportedVersions().forEach(item -> array.add(item)); 107 | json.put("supportedVersions", array); 108 | } 109 | json.put("secured", obj.isSecured()); 110 | json.put("sendErrorOnNoSubscriptions", obj.isSendErrorOnNoSubscriptions()); 111 | json.put("timeFactor", obj.getTimeFactor()); 112 | if (obj.getHeartbeat() != null) { 113 | json.put("heartbeat", obj.getHeartbeat()); 114 | } 115 | json.put("maxFrameInTransaction", obj.getMaxFrameInTransaction()); 116 | json.put("transactionChunkSize", obj.getTransactionChunkSize()); 117 | json.put("maxSubscriptionsByClient", obj.getMaxSubscriptionsByClient()); 118 | json.put("websocketBridge", obj.isWebsocketBridge()); 119 | if (obj.getWebsocketPath() != null) { 120 | json.put("websocketPath", obj.getWebsocketPath()); 121 | } 122 | json.put("trailingLine", obj.isTrailingLine()); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/examples/StompClientExamples.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package examples; 18 | 19 | import io.vertx.core.Vertx; 20 | import io.vertx.core.buffer.Buffer; 21 | import io.vertx.core.net.NetClient; 22 | import io.vertx.ext.stomp.StompClient; 23 | import io.vertx.ext.stomp.StompClientOptions; 24 | 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | 28 | /** 29 | * @author Clement Escoffier 30 | */ 31 | public class StompClientExamples { 32 | 33 | public void example1(Vertx vertx) { 34 | StompClient.create(vertx) 35 | .connect() 36 | .onSuccess(connection -> { 37 | // use the connection 38 | }) 39 | .onFailure(err -> 40 | System.out.println( 41 | "Failed to connect to the STOMP server: " + err.toString())); 42 | } 43 | 44 | public void example2(Vertx vertx) { 45 | StompClient.create(vertx) 46 | .connect(61613, "0.0.0.0") 47 | .onSuccess(connection -> { 48 | // use the connection 49 | }) 50 | .onFailure(err -> 51 | System.out.println( 52 | "Failed to connect to the STOMP server: " + err.toString())); 53 | } 54 | 55 | public void example21(Vertx vertx) { 56 | StompClient.create(vertx) 57 | .errorFrameHandler(frame -> { 58 | // Received the ERROR frame 59 | }) 60 | .connect(61613, "0.0.0.0") 61 | .onSuccess(connection -> { 62 | // use the connection 63 | }) 64 | .onFailure(err -> 65 | System.out.println( 66 | "Failed to connect to the STOMP server: " + err.toString())); 67 | } 68 | 69 | public void example3(Vertx vertx) { 70 | StompClient 71 | .create(vertx, new StompClientOptions().setHost("localhost").setPort(1234)) 72 | .connect() 73 | .onSuccess(connection -> { 74 | // use the connection 75 | }) 76 | .onFailure(err -> 77 | System.out.println( 78 | "Failed to connect to the STOMP server: " + err.toString())); 79 | } 80 | 81 | public void example4(Vertx vertx) { 82 | StompClient client = StompClient 83 | .create(vertx, new StompClientOptions().setHost("localhost").setPort(1234)); 84 | 85 | client 86 | .connect() 87 | .onSuccess(connection -> { 88 | // use the connection 89 | }) 90 | .onFailure(err -> 91 | System.out.println( 92 | "Failed to connect to the STOMP server: " + err.toString())); 93 | 94 | client.close(); 95 | } 96 | 97 | public void example5(Vertx vertx) { 98 | StompClient 99 | .create(vertx, new StompClientOptions().setHost("localhost").setPort(1234)) 100 | .connect() 101 | .onSuccess(connection -> { 102 | // use the connection 103 | connection.disconnect(); 104 | }) 105 | .onFailure(err -> 106 | System.out.println( 107 | "Failed to connect to the STOMP server: " + err.toString())); 108 | } 109 | 110 | public void example6(Vertx vertx) { 111 | StompClient 112 | .create(vertx, new StompClientOptions().setHost("localhost").setPort(1234)) 113 | .connect() 114 | .onSuccess(connection -> { 115 | // use the connection 116 | connection 117 | .errorHandler(frame -> 118 | System.out.println("ERROR frame received : " + frame)); 119 | }) 120 | .onFailure(err -> 121 | System.out.println( 122 | "Failed to connect to the STOMP server: " + err.toString())); 123 | } 124 | 125 | public void example8(Vertx vertx) { 126 | StompClient.create(vertx) 127 | .connect() 128 | .onSuccess(connection -> { 129 | // use the connection 130 | connection.subscribe("/queue", frame -> 131 | System.out.println("Just received a frame from /queue : " + frame)); 132 | }) 133 | .onFailure(err -> 134 | System.out.println( 135 | "Failed to connect to the STOMP server: " + err.toString())); 136 | } 137 | 138 | public void example9(Vertx vertx) { 139 | StompClient.create(vertx) 140 | .connect() 141 | .onSuccess(connection -> { 142 | // use the connection 143 | connection.subscribe("/queue", frame -> 144 | System.out.println("Just received a frame from /queue : " + frame)); 145 | 146 | // .... 147 | 148 | connection.unsubscribe("/queue"); 149 | }) 150 | .onFailure(err -> 151 | System.out.println( 152 | "Failed to connect to the STOMP server: " + err.toString())); 153 | } 154 | 155 | public void example10(Vertx vertx) { 156 | StompClient.create(vertx) 157 | .connect() 158 | .onSuccess(connection -> { 159 | Map headers = new HashMap<>(); 160 | headers.put("header1", "value1"); 161 | connection.send("/queue", headers, Buffer.buffer("Hello")); 162 | // No headers: 163 | connection.send("/queue", Buffer.buffer("World")); 164 | }) 165 | .onFailure(err -> 166 | System.out.println( 167 | "Failed to connect to the STOMP server: " + err.toString())); 168 | } 169 | 170 | public void example11(Vertx vertx) { 171 | StompClient.create(vertx) 172 | .connect() 173 | .onSuccess(connection -> { 174 | connection.subscribe("/queue", frame -> { 175 | connection.ack(frame.getAck()); 176 | // OR 177 | connection.nack(frame.getAck()); 178 | }); 179 | }) 180 | .onFailure(err -> 181 | System.out.println( 182 | "Failed to connect to the STOMP server: " + err.toString())); 183 | } 184 | 185 | public void example12(Vertx vertx) { 186 | StompClient.create(vertx) 187 | .connect() 188 | .onSuccess(connection -> { 189 | Map headers = new HashMap<>(); 190 | headers.put("transaction", "my-transaction"); 191 | connection.beginTX("my-transaction"); 192 | connection.send("/queue", headers, Buffer.buffer("Hello")); 193 | connection.send("/queue", headers, Buffer.buffer("World")); 194 | connection.send("/queue", headers, Buffer.buffer("!!!")); 195 | connection.commit("my-transaction"); 196 | // OR 197 | connection.abort("my-transaction"); 198 | }) 199 | .onFailure(err -> 200 | System.out.println( 201 | "Failed to connect to the STOMP server: " + err.toString())); 202 | } 203 | 204 | public void example13(Vertx vertx) { 205 | StompClient.create(vertx) 206 | .connect() 207 | .onSuccess(connection -> { 208 | connection 209 | .send("/queue", Buffer.buffer("Hello")) 210 | .onSuccess(frame -> 211 | System.out.println("Message processed by the server")); 212 | }) 213 | .onFailure(err -> 214 | System.out.println( 215 | "Failed to connect to the STOMP server: " + err.toString())); 216 | } 217 | 218 | public void example14(Vertx vertx) { 219 | StompClient.create(vertx) 220 | .connect() 221 | .onSuccess(connection -> { 222 | 223 | connection.connectionDroppedHandler(con -> { 224 | // The connection has been lost 225 | // You can reconnect or switch to another server. 226 | }); 227 | 228 | connection.send("/queue", Buffer.buffer("Hello")) 229 | .onSuccess(frame -> System.out.println("Message processed by the server") 230 | ); 231 | }) 232 | .onFailure(err -> 233 | System.out.println( 234 | "Failed to connect to the STOMP server: " + err.toString())); 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /src/main/java/examples/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | @Source 18 | package examples; 19 | 20 | import io.vertx.docgen.Source; 21 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/Acknowledgement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.codegen.annotations.VertxGen; 20 | 21 | import java.util.List; 22 | 23 | /** 24 | * Structure passed to acknowledgement handler called when a {@code ACK} or {@code NACK} frame is received. The handler 25 | * receives an instance of {@link Acknowledgement} with the subscription {@link Frame} and the impacted messages. The 26 | * list of messages depends on the type of acknowledgment used by the subscription. 27 | *

28 | * Subscriptions using the {@code client} mode receives all messages that were waiting for acknowledgment that were 29 | * sent before the acknowledged messages. The list also contains the acknowledged message. This is a cumulative 30 | * acknowledgement. Subscriptions using the {@code client-individual} mode receives a singleton list containing only 31 | * the acknowledged message. 32 | * 33 | * @author Clement Escoffier 34 | */ 35 | @VertxGen 36 | public interface Acknowledgement { 37 | 38 | /** 39 | * @return the subscription frame 40 | */ 41 | Frame subscription(); 42 | 43 | /** 44 | * @return the list of frames that have been acknowledged / not-acknowledged. The content of the list depends on 45 | * the type of subscription. 46 | */ 47 | List frames(); 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/BridgeOptions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.codegen.annotations.DataObject; 20 | import io.vertx.core.json.JsonObject; 21 | import io.vertx.ext.bridge.*; 22 | 23 | import java.util.List; 24 | 25 | /** 26 | * Specify the event bus bridge options. 27 | * 28 | * @author Clement Escoffier 29 | */ 30 | @DataObject 31 | public class BridgeOptions extends io.vertx.ext.bridge.BridgeOptions { 32 | 33 | public static final boolean DEFAULT_POINT_TO_POINT = false; 34 | 35 | private boolean pointToPoint; 36 | 37 | 38 | public BridgeOptions() { 39 | super(); 40 | pointToPoint = DEFAULT_POINT_TO_POINT; 41 | } 42 | 43 | public BridgeOptions(BridgeOptions that) { 44 | super(that); 45 | this.pointToPoint = that.pointToPoint; 46 | } 47 | 48 | public BridgeOptions(JsonObject json) { 49 | super(json); 50 | this.pointToPoint=json.getBoolean("pointToPoint",DEFAULT_POINT_TO_POINT); 51 | } 52 | 53 | public JsonObject toJson() { 54 | JsonObject json = super.toJson(); 55 | json.put("pointToPoint", pointToPoint); 56 | return json; 57 | } 58 | 59 | public BridgeOptions setPointToPoint(boolean v) { 60 | this.pointToPoint = v; 61 | return this; 62 | } 63 | 64 | public boolean isPointToPoint() { 65 | return pointToPoint; 66 | } 67 | 68 | @Override 69 | public BridgeOptions setInboundPermitteds(List inboundPermitted) { 70 | super.setInboundPermitteds(inboundPermitted); 71 | return this; 72 | } 73 | 74 | @Override 75 | public BridgeOptions setOutboundPermitteds(List outboundPermitted) { 76 | super.setOutboundPermitteds(outboundPermitted); 77 | return this; 78 | } 79 | 80 | @Override 81 | public BridgeOptions addInboundPermitted(PermittedOptions permitted) { 82 | super.addInboundPermitted(permitted); 83 | return this; 84 | } 85 | 86 | @Override 87 | public BridgeOptions addOutboundPermitted(PermittedOptions permitted) { 88 | super.addOutboundPermitted(permitted); 89 | return this; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/Command.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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.ext.stomp; 17 | 18 | import io.vertx.codegen.annotations.VertxGen; 19 | 20 | /** 21 | * The list of command defined by the STOMP specification. 22 | * It also contains a {@code PING} command used for heartbeat. It should not be used in frames (as it's not a valid 23 | * command). 24 | */ 25 | @VertxGen 26 | public enum Command { 27 | // Connection 28 | CONNECT, 29 | CONNECTED, 30 | STOMP, 31 | 32 | // Client 33 | SEND, 34 | SUBSCRIBE, 35 | UNSUBSCRIBE, 36 | ACK, 37 | NACK, 38 | BEGIN, 39 | COMMIT, 40 | ABORT, 41 | DISCONNECT, 42 | 43 | // Server 44 | MESSAGE, 45 | RECEIPT, 46 | ERROR, 47 | 48 | // This is not a real STOMP frame, it just notice a ping frame from the client. 49 | PING, 50 | 51 | UNKNOWN 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/DefaultAbortHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.core.Handler; 20 | import io.vertx.ext.stomp.impl.Transactions; 21 | import io.vertx.ext.stomp.utils.Headers; 22 | 23 | /** 24 | * STOMP compliant actions executed when receiving a {@code ABORT} frame. 25 | * This handler is thread safe. 26 | * 27 | * @author Clement Escoffier 28 | */ 29 | public class DefaultAbortHandler implements Handler { 30 | @Override 31 | public void handle(ServerFrame sf) { 32 | String txId = sf.frame().getHeader(Frame.TRANSACTION); 33 | if (txId == null) { 34 | Frame error = Frames.createErrorFrame("Missing transaction id", Headers.create(), "ABORT frames " + 35 | "must contain the 'transaction' header."); 36 | sf.connection().write(error).close(); 37 | return; 38 | } 39 | 40 | if (! Transactions.instance().unregisterTransaction(sf.connection(), txId)) { 41 | Frame error = Frames.createErrorFrame("Unknown transaction", 42 | Headers.create(Frame.TRANSACTION, txId), 43 | "The transaction id is unknown."); 44 | sf.connection().write(error).close(); 45 | return; 46 | } 47 | 48 | Frames.handleReceipt(sf.frame(), sf.connection()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/DefaultAckHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.core.Handler; 20 | import io.vertx.ext.stomp.impl.Transaction; 21 | import io.vertx.ext.stomp.impl.Transactions; 22 | import io.vertx.ext.stomp.utils.Headers; 23 | 24 | import java.util.List; 25 | 26 | /** 27 | * STOMP compliant actions executed when receiving a {@code ACK} frame. It removes the acknowledges messages from the 28 | * list of messages waiting for acknowledgment. If the {@code ACK} frame specifies a transaction id, the 29 | * acknowledgment is delayed until the transaction commit. 30 | *

31 | * This handler is thread safe. 32 | * 33 | * @author Clement Escoffier 34 | */ 35 | public class DefaultAckHandler implements Handler { 36 | 37 | @Override 38 | public void handle(ServerFrame serverFrame) { 39 | Frame frame = serverFrame.frame(); 40 | StompServerConnection connection = serverFrame.connection(); 41 | String id = frame.getId(); 42 | if (id == null) { 43 | connection.write(Frames.createErrorFrame( 44 | "Id header missing", 45 | Headers.create(frame.getHeaders()), "Invalid ACK frame - the " + 46 | "'id' must be set")); 47 | connection.close(); 48 | return; 49 | } 50 | 51 | // Handle transaction 52 | String txId = frame.getHeader(Frame.TRANSACTION); 53 | if (txId != null) { 54 | Transaction transaction = Transactions.instance().getTransaction(connection, txId); 55 | if (transaction == null) { 56 | // No transaction. 57 | Frame errorFrame = Frames.createErrorFrame( 58 | "No transaction", 59 | Headers.create(Frame.ID, id, Frame.TRANSACTION, txId), 60 | "Message delivery failed - unknown transaction id in ACK message"); 61 | connection.write(errorFrame); 62 | connection.close(); 63 | return; 64 | } else { 65 | if (!transaction.addFrameToTransaction(frame)) { 66 | // Frame not added to transaction 67 | Frame errorFrame = Frames.createErrorFrame("Frame not added to transaction", 68 | Headers.create(Frame.ID, id, Frame.TRANSACTION, txId), 69 | "Message delivery failed - the frame cannot be added to the transaction - the number of allowed thread " + 70 | "may have been reached"); 71 | Transactions.instance().unregisterTransactionsFromConnection(connection); 72 | connection.write(errorFrame); 73 | connection.close(); 74 | return; 75 | } 76 | Frames.handleReceipt(frame, connection); 77 | // Nothing else in transactions. 78 | return; 79 | } 80 | } 81 | 82 | final List destinations = connection.handler().getDestinations(); 83 | for (Destination destination : destinations) { 84 | if (destination.ack(connection, frame)) { 85 | break; 86 | } 87 | } 88 | 89 | Frames.handleReceipt(frame, connection); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/DefaultBeginHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.core.Handler; 20 | import io.vertx.ext.stomp.impl.Transactions; 21 | import io.vertx.ext.stomp.utils.Headers; 22 | 23 | /** 24 | * STOMP compliant actions executed when receiving a {@code BEGIN} frame. 25 | *

26 | * This handler is thread safe. 27 | * 28 | * @author Clement Escoffier 29 | */ 30 | public class DefaultBeginHandler implements Handler { 31 | 32 | @Override 33 | public void handle(ServerFrame serverFrame) { 34 | Frame frame = serverFrame.frame(); 35 | StompServerConnection connection = serverFrame.connection(); 36 | String txId = frame.getHeader(Frame.TRANSACTION); 37 | if (txId == null) { 38 | Frame error = Frames.createErrorFrame("Missing transaction id", Headers.create(), "BEGIN frames " + 39 | "must contain the 'transaction' header."); 40 | connection.write(error).close(); 41 | return; 42 | } 43 | 44 | if (!Transactions.instance().registerTransaction(connection, txId)) { 45 | Frame error = Frames.createErrorFrame("Already existing transaction", 46 | Headers.create(Frame.TRANSACTION, txId), 47 | "A transaction using the same id is still active."); 48 | connection.write(error).close(); 49 | return; 50 | } 51 | 52 | Frames.handleReceipt(frame, connection); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/DefaultCommitHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.core.Handler; 20 | import io.vertx.ext.stomp.impl.Transaction; 21 | import io.vertx.ext.stomp.impl.Transactions; 22 | import io.vertx.ext.stomp.utils.Headers; 23 | 24 | import java.util.ArrayList; 25 | import java.util.Iterator; 26 | import java.util.List; 27 | 28 | /** 29 | * STOMP compliant actions executed when receiving a {@code COMMIT} frame. All frames that are part of the 30 | * transactions are processed ({@code ACK/NACK} and {@code SEND} frames). If the {@code COMMIT} frame defines a {@code 31 | * receipt}, the {@code RECEIPT} frame is sent once all frames have been replayed. 32 | *

33 | * This handler is thread safe. 34 | * 35 | * @author Clement Escoffier 36 | */ 37 | public class DefaultCommitHandler implements Handler { 38 | 39 | @Override 40 | public void handle(ServerFrame serverFrame) { 41 | Frame frame = serverFrame.frame(); 42 | StompServerConnection connection = serverFrame.connection(); 43 | String txId = frame.getHeader(Frame.TRANSACTION); 44 | if (txId == null) { 45 | Frame error = Frames.createErrorFrame("Missing transaction id", Headers.create(), "COMMIT frames " + 46 | "must contain the " + 47 | "'transaction' header."); 48 | connection.write(error).close(); 49 | return; 50 | } 51 | 52 | Transaction transaction = Transactions.instance().getTransaction(connection, txId); 53 | if (transaction == null) { 54 | Frame error = Frames.createErrorFrame("Unknown transaction", 55 | Headers.create(Frame.TRANSACTION, txId), 56 | "The transaction id is unknown."); 57 | connection.write(error).close(); 58 | return; 59 | } 60 | 61 | replay(connection, transaction.getFrames()); 62 | transaction.clear(); 63 | Transactions.instance().unregisterTransaction(connection, txId); 64 | 65 | Frames.handleReceipt(frame, connection); 66 | } 67 | 68 | private void replay(StompServerConnection connection, List frames) { 69 | // To avoid blocking the event loop for too long, we replay the transaction chunk by chunk. 70 | Iterator iterator = frames.iterator(); 71 | while (iterator.hasNext()) { 72 | List chunk = new ArrayList<>(); 73 | while (iterator.hasNext() && chunk.size() < connection.server().options().getTransactionChunkSize()) { 74 | chunk.add(iterator.next()); 75 | } 76 | connection.server().vertx().runOnContext(v -> replayChunk(connection, chunk)); 77 | } 78 | } 79 | 80 | private void replayChunk(StompServerConnection connection, List frames) { 81 | final List destinations = connection.handler().getDestinations(); 82 | for (Frame frame : frames) { 83 | switch (frame.getCommand()) { 84 | case SEND: 85 | // We are sure that the destination is set, as the check is made before enqueuing the frame. 86 | String destination = frame.getHeader(Frame.DESTINATION); 87 | Destination dest = connection.handler().getDestination(destination); 88 | if (dest != null) { 89 | dest.dispatch(connection, frame); 90 | } 91 | break; 92 | case ACK: 93 | for (Destination d : destinations) { 94 | if (d.ack(connection, frame)) { 95 | break; 96 | } 97 | } 98 | break; 99 | case NACK: 100 | for (Destination d : destinations) { 101 | if (d.nack(connection, frame)) { 102 | break; 103 | } 104 | } 105 | break; 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/DefaultConnectHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.core.AsyncResult; 20 | import io.vertx.core.Future; 21 | import io.vertx.core.Handler; 22 | import io.vertx.ext.stomp.impl.FrameParser; 23 | import io.vertx.ext.stomp.utils.Headers; 24 | import io.vertx.ext.stomp.utils.Server; 25 | 26 | import java.util.ArrayList; 27 | import java.util.Arrays; 28 | import java.util.List; 29 | 30 | /** 31 | * STOMP compliant actions executed when receiving a {@code CONNECT} frame. It may also be called when receiving a 32 | * {@code STOMP} frame depending on the {@link io.vertx.ext.stomp.StompServerHandler} configuration. 33 | *

34 | * This handler manages the STOMP version negotiation and authentication (if enabled). Once all the checks have been 35 | * passed, the {@code CONNECTED} frame is sent to the client. 36 | *

37 | * This handler is thread safe. 38 | * 39 | * @author Clement Escoffier 40 | */ 41 | public class DefaultConnectHandler implements Handler { 42 | 43 | @Override 44 | public void handle(ServerFrame sf) { 45 | // Server negotiation 46 | List accepted = new ArrayList<>(); 47 | String accept = sf.frame().getHeader(Frame.ACCEPT_VERSION); 48 | if (accept == null) { 49 | accepted.add("1.0"); 50 | } else { 51 | accepted.addAll(Arrays.asList(accept.split(FrameParser.COMMA))); 52 | } 53 | 54 | String version = negotiate(accepted, sf.connection()); 55 | if (version == null) { 56 | // Spec says: if the server and the client do not share any common protocol versions, then the server MUST 57 | // respond with an error. 58 | sf.connection().write(Frames.createErrorFrame( 59 | "Incompatible versions", 60 | Headers.create( 61 | Frame.VERSION, getSupportedVersionsHeaderLine(sf.connection()), 62 | Frame.CONTENT_TYPE, "text/plain"), 63 | "Client protocol requirement does not mach versions supported by the server. " + 64 | "Supported protocol versions are " + getSupportedVersionsHeaderLine(sf.connection())) 65 | ); 66 | sf.connection().close(); 67 | return; 68 | } 69 | 70 | // Login / Passcode 71 | authenticate(sf.frame(), sf.connection(), ar -> { 72 | // Spec says: The server will respond back with the highest version of the protocol -> version 73 | sf.connection().write(new Frame(Command.CONNECTED, Headers.create( 74 | Frame.VERSION, version, 75 | Frame.SERVER, Server.SERVER_NAME, 76 | Frame.SESSION, sf.connection().session(), 77 | Frame.HEARTBEAT, Frame.Heartbeat.create(sf.connection().server().options().getHeartbeat()).toString()), null)); 78 | }); 79 | } 80 | 81 | private void authenticate(Frame frame, StompServerConnection connection, 82 | Handler> remainingActions) { 83 | if (connection.server().options().isSecured()) { 84 | String login = frame.getHeader(Frame.LOGIN); 85 | String passcode = frame.getHeader(Frame.PASSCODE); 86 | 87 | connection.handler().onAuthenticationRequest(connection, login, passcode).onComplete(ar -> { 88 | if (ar.result()) { 89 | remainingActions.handle(Future.succeededFuture()); 90 | } else { 91 | connection.write(Frames.createErrorFrame( 92 | "Authentication failed", 93 | Headers.create( 94 | Frame.VERSION, getSupportedVersionsHeaderLine(connection), 95 | Frame.CONTENT_TYPE, "text/plain"), 96 | "The connection frame does not contain valid credentials.") 97 | ); 98 | connection.close(); 99 | } 100 | }); 101 | } else { 102 | remainingActions.handle(Future.succeededFuture()); 103 | } 104 | } 105 | 106 | private String getSupportedVersionsHeaderLine(StompServerConnection connection) { 107 | StringBuilder builder = new StringBuilder(); 108 | connection.server().options().getSupportedVersions().stream().forEach( 109 | v -> builder.append(builder.length() == 0 ? v : FrameParser.COMMA + v)); 110 | return builder.toString(); 111 | } 112 | 113 | private String negotiate(List accepted, StompServerConnection connection) { 114 | List supported = connection.server().options().getSupportedVersions(); 115 | for (String v : supported) { 116 | if (accepted.contains(v)) { 117 | return v; 118 | } 119 | } 120 | return null; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/DefaultNackHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.core.Handler; 20 | import io.vertx.ext.stomp.impl.Transaction; 21 | import io.vertx.ext.stomp.impl.Transactions; 22 | import io.vertx.ext.stomp.utils.Headers; 23 | 24 | import java.util.List; 25 | 26 | /** 27 | * STOMP compliant actions executed when receiving a {@code NACK} sf.frame(). It removes the acknowledges messages 28 | * from the list of messages waiting for acknowledgment and trigger an 29 | * {@link StompServerHandler#onNack(Subscription, List)} calls. 30 | *

31 | * If the {@code NACK} frame specifies a transaction id, the acknowledgment is delayed until the transaction commit. 32 | *

33 | * This handler is thread safe. 34 | * 35 | * @author Clement Escoffier 36 | */ 37 | public class DefaultNackHandler implements Handler { 38 | @Override 39 | public void handle(ServerFrame sf) { 40 | StompServerConnection connection = sf.connection(); 41 | String id = sf.frame().getId(); 42 | if (id == null) { 43 | connection.write(Frames.createErrorFrame( 44 | "Id header missing", 45 | Headers.create(sf.frame().getHeaders()), "Invalid NACK frame - the " + 46 | "'id' must be set")); 47 | connection.close(); 48 | return; 49 | } 50 | 51 | // Handle transaction 52 | String txId = sf.frame().getHeader(Frame.TRANSACTION); 53 | if (txId != null) { 54 | Transaction transaction = Transactions.instance().getTransaction(connection, txId); 55 | 56 | if (transaction == null) { 57 | // No transaction. 58 | Frame errorFrame = Frames.createErrorFrame( 59 | "No transaction", 60 | Headers.create(Frame.ID, id, Frame.TRANSACTION, txId), 61 | "Message delivery failed - unknown transaction id in NACK message"); 62 | connection.write(errorFrame); 63 | connection.close(); 64 | return; 65 | } else { 66 | if (!transaction.addFrameToTransaction(sf.frame())) { 67 | // Frame not added to transaction 68 | Frame errorFrame = Frames.createErrorFrame("Frame not added to transaction", 69 | Headers.create(Frame.ID, id, Frame.TRANSACTION, txId), 70 | "Message delivery failed - the frame cannot be added to the transaction - the number of allowed thread " + 71 | "may have been reached"); 72 | Transactions.instance().unregisterTransactionsFromConnection(connection); 73 | connection.write(errorFrame); 74 | connection.close(); 75 | return; 76 | } 77 | Frames.handleReceipt(sf.frame(), connection); 78 | // Nothing else in transactions. 79 | return; 80 | } 81 | } 82 | 83 | final List destinations = connection.handler().getDestinations(); 84 | for (Destination destination : destinations) { 85 | if (destination.nack(connection, sf.frame())) { 86 | break; 87 | } 88 | } 89 | 90 | Frames.handleReceipt(sf.frame(), connection); 91 | } 92 | 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/DefaultSendHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.core.Handler; 20 | import io.vertx.ext.stomp.impl.Transaction; 21 | import io.vertx.ext.stomp.impl.Transactions; 22 | import io.vertx.ext.stomp.utils.Headers; 23 | 24 | /** 25 | * STOMP compliant actions executed when receiving a {@code SEND} sf.frame(). 26 | *

27 | * If the {@code SEND} frame specifies a transaction, the message delivery is postponed until the transaction commit. 28 | *

29 | * The handler computes the {@code MESSAGE} frame from the {@code SEND} sf.frame(). It computes a {@code message-id} and 30 | * {@code ack} id if needed. If requested the {@code RECEIPT} frame is sent once the {@code MESSAGE} frame has been 31 | * sent to all matching subscriptions. 32 | *

33 | * If the {@code SEND} frame requires an acknowledgment, the {@code message-id} is added to the list of messages 34 | * waiting for acknowledgment. 35 | *

36 | * This handler is thread safe. 37 | * 38 | * @author Clement Escoffier 39 | */ 40 | public class DefaultSendHandler implements Handler { 41 | 42 | @Override 43 | public void handle(ServerFrame sf) { 44 | String destination = sf.frame().getHeader(Frame.DESTINATION); 45 | if (destination == null) { 46 | sf.connection().write(Frames.createErrorFrame( 47 | "Destination header missing", 48 | Headers.create(sf.frame().getHeaders()), "Invalid send frame - the " + 49 | "'destination' must be set")); 50 | sf.connection().close(); 51 | return; 52 | } 53 | 54 | // Handle transaction 55 | String txId = sf.frame().getHeader(Frame.TRANSACTION); 56 | if (txId != null) { 57 | Transaction transaction = Transactions.instance().getTransaction(sf.connection(), txId); 58 | if (transaction == null) { 59 | // No transaction. 60 | Frame errorFrame = Frames.createErrorFrame( 61 | "No transaction", 62 | Headers.create(Frame.DESTINATION, destination, Frame.TRANSACTION, txId), 63 | "Message delivery failed - unknown transaction id"); 64 | sf.connection().write(errorFrame); 65 | sf.connection().close(); 66 | return; 67 | } else { 68 | if (!transaction.addFrameToTransaction(sf.frame())) { 69 | // Frame not added to transaction 70 | Frame errorFrame = Frames.createErrorFrame("Frame not added to transaction", 71 | Headers.create(Frame.DESTINATION, destination, Frame.TRANSACTION, txId), 72 | "Message delivery failed - the frame cannot be added to the transaction - the number of allowed thread " + 73 | "may have been reached"); 74 | Transactions.instance().unregisterTransactionsFromConnection(sf.connection()); 75 | sf.connection().write(errorFrame); 76 | sf.connection().close(); 77 | return; 78 | } 79 | Frames.handleReceipt(sf.frame(), sf.connection()); 80 | // No delivery in transactions. 81 | return; 82 | } 83 | } 84 | 85 | final Destination dest = sf.connection().handler().getDestination(destination); 86 | if (dest == null && sf.connection().server().options().isSendErrorOnNoSubscriptions()) { 87 | Frame errorFrame = Frames.createErrorFrame( 88 | "No subscriptions", 89 | Headers.create(Frame.DESTINATION, destination), 90 | "Message delivery failed - no subscriptions on this destination"); 91 | sf.connection().write(errorFrame); 92 | sf.connection().close(); 93 | return; 94 | } 95 | 96 | if (dest != null) { 97 | if (dest.dispatch(sf.connection(), sf.frame()) == null) { 98 | // Error managed by the destination. 99 | return; 100 | } 101 | } 102 | 103 | Frames.handleReceipt(sf.frame(), sf.connection()); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/DefaultSubscribeHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.core.Handler; 20 | import io.vertx.ext.stomp.utils.Headers; 21 | 22 | import java.util.List; 23 | 24 | /** 25 | * STOMP compliant actions executed when receiving a {@code SUBSCRIBE} frame. 26 | *

27 | * This handler is thread safe. 28 | * 29 | * @author Clement Escoffier 30 | */ 31 | public class DefaultSubscribeHandler implements Handler { 32 | @Override 33 | public void handle(ServerFrame serverFrame) { 34 | Frame frame = serverFrame.frame(); 35 | StompServerConnection connection = serverFrame.connection(); 36 | String id = frame.getHeader(Frame.ID); 37 | String destination = frame.getHeader(Frame.DESTINATION); 38 | String ack = frame.getHeader(Frame.ACK); 39 | if (ack == null) { 40 | ack = "auto"; 41 | } 42 | 43 | if (destination == null || id == null) { 44 | connection.write(Frames.createErrorFrame( 45 | "Invalid subscription", 46 | Headers.create( 47 | frame.getHeaders()), "The 'destination' and 'id' headers must be set")); 48 | connection.close(); 49 | return; 50 | } 51 | 52 | // Ensure that the subscription id is unique 53 | int count = 0; 54 | for (Destination dest : connection.handler().getDestinations()) { 55 | List ids = dest.getSubscriptions(connection); 56 | count += ids.size(); 57 | if (ids.contains(id)) { 58 | connection.write(Frames.createErrorFrame( 59 | "Invalid subscription", 60 | Headers.create(frame.getHeaders()), "'id'" + 61 | " already used by this connection.")); 62 | connection.close(); 63 | return; 64 | } 65 | if (count + 1 > connection.server().options().getMaxSubscriptionsByClient()) { 66 | connection.write(Frames.createErrorFrame( 67 | "Invalid subscription", 68 | Headers.create(frame.getHeaders()), "Too many subscriptions")); 69 | connection.close(); 70 | return; 71 | } 72 | } 73 | 74 | final Destination dest = connection.handler().getOrCreateDestination(destination); 75 | if (dest != null) { 76 | if (dest.subscribe(connection, frame) == null) { 77 | // Access denied 78 | connection.write(Frames.createErrorFrame( 79 | "Access denied", 80 | Headers.create(frame.getHeaders()), "The destination has been rejected by the server")); 81 | connection.close(); 82 | return; 83 | } 84 | } else { 85 | connection.write(Frames.createErrorFrame( 86 | "Invalid subscription", 87 | Headers.create(frame.getHeaders()), "The destination has been rejected by the server")); 88 | connection.close(); 89 | return; 90 | } 91 | 92 | Frames.handleReceipt(frame, connection); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/DefaultUnsubscribeHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.core.Handler; 20 | import io.vertx.ext.stomp.utils.Headers; 21 | 22 | import java.util.List; 23 | 24 | /** 25 | * STOMP compliant actions executed when receiving a {@code UNSUBSCRIBE} frame. 26 | *

27 | * This handler is thread safe. 28 | * 29 | * @author Clement Escoffier 30 | */ 31 | public class DefaultUnsubscribeHandler implements Handler { 32 | @Override 33 | public void handle(ServerFrame serverFrame) { 34 | Frame frame = serverFrame.frame(); 35 | StompServerConnection connection = serverFrame.connection(); 36 | final String id = frame.getHeader(Frame.ID); 37 | 38 | if (id == null) { 39 | connection.write( 40 | Frames.createErrorFrame( 41 | "Invalid unsubscribe", 42 | Headers.create(frame.getHeaders()), 43 | "The 'id' header must be set")); 44 | connection.close(); 45 | return; 46 | } 47 | 48 | List destinations = connection.handler().getDestinations(); 49 | boolean handled = false; 50 | for (Destination destination : destinations) { 51 | if (destination.unsubscribe(connection, frame)) { 52 | handled = true; 53 | break; 54 | } 55 | } 56 | 57 | if (!handled) { 58 | connection.write(Frames.createErrorFrame( 59 | "Invalid unsubscribe", 60 | Headers.create(frame.getHeaders()), 61 | "No subscription associated with the given 'id'")); 62 | connection.close(); 63 | return; 64 | } 65 | 66 | Frames.handleReceipt(frame, connection); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/Destination.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.codegen.annotations.Fluent; 20 | import io.vertx.codegen.annotations.VertxGen; 21 | import io.vertx.core.Vertx; 22 | import io.vertx.core.shareddata.Shareable; 23 | import io.vertx.ext.stomp.impl.EventBusBridge; 24 | import io.vertx.ext.stomp.impl.Queue; 25 | import io.vertx.ext.stomp.impl.Topic; 26 | 27 | import java.util.List; 28 | 29 | /** 30 | * Represents a STOMP destination. 31 | * Depending on the implementation, the message delivery is different. Queue are sending message to only one 32 | * subscribers, while topics are broadcasting the message to all subscribers. 33 | *

34 | * Implementations must be thread-safe. 35 | * 36 | * @author Clement Escoffier 37 | */ 38 | @VertxGen 39 | public interface Destination extends Shareable { 40 | 41 | static Destination topic(Vertx vertx, String destination) { 42 | return new Topic(vertx, destination); 43 | } 44 | 45 | static Destination queue(Vertx vertx, String destination) { 46 | return new Queue(vertx, destination); 47 | } 48 | 49 | static Destination bridge(Vertx vertx, BridgeOptions options) { 50 | return new EventBusBridge(vertx, options); 51 | } 52 | 53 | /** 54 | * @return the destination address. 55 | */ 56 | String destination(); 57 | 58 | /** 59 | * Dispatches the given frame. 60 | * 61 | * @param connection the connection 62 | * @param frame the frame 63 | * @return the current instance of {@link Destination} 64 | */ 65 | @Fluent 66 | Destination dispatch(StompServerConnection connection, Frame frame); 67 | 68 | /** 69 | * Handles a subscription request to the current {@link Destination}. 70 | * 71 | * @param connection the connection 72 | * @param frame the {@code SUBSCRIBE} frame 73 | * @return the current instance of {@link Destination} 74 | */ 75 | @Fluent 76 | Destination subscribe(StompServerConnection connection, Frame frame); 77 | 78 | /** 79 | * Handles a un-subscription request to the current {@link Destination}. 80 | * 81 | * @param connection the connection 82 | * @param frame the {@code UNSUBSCRIBE} frame 83 | * @return {@code true} if the un-subscription has been handled, {@code false} otherwise. 84 | */ 85 | boolean unsubscribe(StompServerConnection connection, Frame frame); 86 | 87 | /** 88 | * Removes all subscriptions of the given connection 89 | * 90 | * @param connection the connection 91 | * @return the current instance of {@link Destination} 92 | */ 93 | @Fluent 94 | Destination unsubscribeConnection(StompServerConnection connection); 95 | 96 | /** 97 | * Handles a {@code ACK} frame. 98 | * 99 | * @param connection the connection 100 | * @param frame the {@code ACK} frame 101 | * @return {@code true} if the destination has handled the frame (meaning it has sent the message with id) 102 | */ 103 | boolean ack(StompServerConnection connection, Frame frame); 104 | 105 | /** 106 | * Handles a {@code NACK} frame. 107 | * 108 | * @param connection the connection 109 | * @param frame the {@code NACK} frame 110 | * @return {@code true} if the destination has handled the frame (meaning it has sent the message with id) 111 | */ 112 | boolean nack(StompServerConnection connection, Frame frame); 113 | 114 | 115 | /** 116 | * Gets all subscription ids for the given destination hold by the given client 117 | * 118 | * @param connection the connection (client) 119 | * @return the list of subscription id, empty if none 120 | */ 121 | List getSubscriptions(StompServerConnection connection); 122 | 123 | /** 124 | * Gets the number of subscriptions attached to the current {@link Destination}. 125 | * 126 | * @return the number of subscriptions. 127 | */ 128 | int numberOfSubscriptions(); 129 | 130 | /** 131 | * Checks whether or not the given address matches with the current destination. 132 | * 133 | * @param address the address 134 | * @return {@code true} if it matches, {@code false} otherwise. 135 | */ 136 | boolean matches(String address); 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/DestinationFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.codegen.annotations.VertxGen; 20 | import io.vertx.core.Vertx; 21 | 22 | /** 23 | * Interface implemented to customize the destination creation. 24 | * 25 | * @author Clement Escoffier 26 | */ 27 | @VertxGen 28 | public interface DestinationFactory { 29 | 30 | /** 31 | * Creates a destination for the given address. 32 | * 33 | * @param vertx the vert.x instance used by the STOMP server. 34 | * @param name the destination name. 35 | * @return the destination, {@code null} to reject the creation. 36 | * @see Destination#topic(Vertx, String) 37 | * @see Destination#queue(Vertx, String) 38 | */ 39 | Destination create(Vertx vertx, String name); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/Frames.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 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.ext.stomp.impl.FrameException; 23 | import io.vertx.ext.stomp.impl.FrameParser; 24 | import io.vertx.ext.stomp.utils.Headers; 25 | 26 | import java.util.Map; 27 | import java.util.Objects; 28 | 29 | /** 30 | * Utility methods to build common {@link Frame}s. It defines a non-STOMP frame ({@code PING}) that is used for 31 | * heartbeats. When such frame is written on the wire it is just the {@code 0} byte. 32 | *

33 | * This class is thread-safe. 34 | * 35 | * @author Clement Escoffier 36 | */ 37 | @VertxGen 38 | public interface Frames { 39 | 40 | Frame PING = new Frame(Command.PING, Headers.create(), null) { 41 | 42 | @Override 43 | public Buffer toBuffer() { 44 | return Buffer.buffer(FrameParser.EOL); 45 | } 46 | }; 47 | 48 | static Frame createErrorFrame(String message, Map headers, String body) { 49 | Objects.requireNonNull(message); 50 | Objects.requireNonNull(headers); 51 | Objects.requireNonNull(body); 52 | return new Frame(Command.ERROR, 53 | Headers.create(headers) 54 | .add(Frame.MESSAGE, message) 55 | .add(Frame.CONTENT_LENGTH, Integer.toString(body.length())) 56 | .add(Frame.CONTENT_TYPE, "text/plain"), 57 | Buffer.buffer(body)); 58 | } 59 | 60 | @GenIgnore 61 | static Frame createInvalidFrameErrorFrame(FrameException exception) { 62 | return createErrorFrame("Invalid frame received", Headers.create(), exception.getMessage()); 63 | } 64 | 65 | static Frame createReceiptFrame(String receiptId, Map headers) { 66 | Objects.requireNonNull(receiptId); 67 | Objects.requireNonNull(headers); 68 | return new Frame(Command.RECEIPT, 69 | Headers.create(headers) 70 | .add(Frame.RECEIPT_ID, receiptId), 71 | null); 72 | } 73 | 74 | static void handleReceipt(Frame frame, StompServerConnection connection) { 75 | String receipt = frame.getReceipt(); 76 | if (receipt != null) { 77 | connection.write(createReceiptFrame(receipt, Headers.create())); 78 | } 79 | } 80 | 81 | static Frame ping() { 82 | return PING; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/ServerFrame.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.codegen.annotations.VertxGen; 20 | 21 | /** 22 | * Structure passed to server handler when receiving a frame. It provides a reference on the received {@link Frame} 23 | * but also on the {@link StompServerConnection}. 24 | * 25 | * @author Clement Escoffier 26 | */ 27 | @VertxGen 28 | public interface ServerFrame { 29 | 30 | /** 31 | * @return the received frame 32 | */ 33 | Frame frame(); 34 | 35 | /** 36 | * @return the connection 37 | */ 38 | StompServerConnection connection(); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/StompClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.codegen.annotations.Fluent; 20 | import io.vertx.codegen.annotations.VertxGen; 21 | import io.vertx.core.Future; 22 | import io.vertx.core.Handler; 23 | import io.vertx.core.Vertx; 24 | import io.vertx.core.net.NetClient; 25 | import io.vertx.ext.stomp.impl.StompClientImpl; 26 | 27 | /** 28 | * Defines a STOMP client. 29 | * 30 | * @author Clement Escoffier 31 | */ 32 | @VertxGen 33 | public interface StompClient { 34 | 35 | /** 36 | * Creates a {@link StompClient} using the default implementation. 37 | * 38 | * @param vertx the vert.x instance to use 39 | * @return the created {@link StompClient} 40 | */ 41 | static StompClient create(Vertx vertx) { 42 | return create(vertx, new StompClientOptions()); 43 | } 44 | 45 | /** 46 | * Creates a {@link StompClient} using the default implementation. 47 | * 48 | * @param vertx the vert.x instance to use 49 | * @param options the options 50 | * @return the created {@link StompClient} 51 | */ 52 | static StompClient create(Vertx vertx, StompClientOptions options) { 53 | return new StompClientImpl(vertx, options); 54 | } 55 | 56 | /** 57 | * Connects to the server. 58 | * 59 | * @param port the server port 60 | * @param host the server host 61 | * @return a future notified with the connection result 62 | */ 63 | Future connect(int port, String host); 64 | 65 | /** 66 | * Connects to the server. 67 | * 68 | * @param port the server port 69 | * @param host the server host 70 | * @param net the NET client to use 71 | * @return a future notified with the connection result 72 | */ 73 | 74 | /** 75 | * Connects to the server using the host and port configured in the client's options. 76 | * 77 | * @return a future notified with the connection result. A failure will be sent to the handler if a TCP 78 | * level issue happen before the `CONNECTED` frame is received. 79 | */ 80 | Future connect(); 81 | 82 | /** 83 | * Configures a received handler that gets notified when a STOMP frame is received by the client. 84 | * This handler can be used for logging, debugging or ad-hoc behavior. The frame can still be modified at the time. 85 | *

86 | * When a connection is created, the handler is used as 87 | * {@link StompClientConnection#receivedFrameHandler(Handler)}. 88 | * 89 | * @param handler the handler 90 | * @return the current {@link StompClient} 91 | */ 92 | @Fluent 93 | StompClient receivedFrameHandler(Handler handler); 94 | 95 | /** 96 | * Configures a writing handler that gets notified when a STOMP frame is written on the wire. 97 | * This handler can be used for logging, debugging or ad-hoc behavior. The frame can still be modified at the time. 98 | *

99 | * When a connection is created, the handler is used as 100 | * {@link StompClientConnection#writingFrameHandler(Handler)}. 101 | * 102 | * @param handler the handler 103 | * @return the current {@link StompClient} 104 | */ 105 | @Fluent 106 | StompClient writingFrameHandler(Handler handler); 107 | 108 | /** 109 | * A general error frame handler. It can be used to catch {@code ERROR} frame emitted during the connection process 110 | * (wrong authentication). This error handler will be pass to all {@link StompClientConnection} created from this 111 | * client. Obviously, the client can override it when the connection is established. 112 | * 113 | * @param handler the handler 114 | * @return the current {@link StompClient} 115 | */ 116 | @Fluent 117 | StompClient errorFrameHandler(Handler handler); 118 | 119 | /** 120 | * Sets an exception handler notified for TCP-level errors. 121 | * 122 | * @param handler the handler 123 | * @return the current {@link StompClient} 124 | */ 125 | @Fluent 126 | StompClient exceptionHandler(Handler handler); 127 | 128 | /** 129 | * Closes the client. 130 | */ 131 | Future close(); 132 | 133 | /** 134 | * @return the client's options. 135 | */ 136 | StompClientOptions options(); 137 | 138 | /** 139 | * @return the vert.x instance used by the client. 140 | */ 141 | Vertx vertx(); 142 | 143 | /** 144 | * @return whether or not the client is connected to the server. 145 | */ 146 | boolean isClosed(); 147 | } 148 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/StompOptions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.core.json.JsonObject; 20 | 21 | import java.util.Arrays; 22 | import java.util.List; 23 | 24 | /** 25 | * Defines a couples of constants shared by client and server options. 26 | * 27 | * @author Clement Escoffier 28 | */ 29 | public interface StompOptions { 30 | 31 | /** 32 | * UTF-8 encoding name. 33 | */ 34 | String UTF_8 = "utf-8"; 35 | 36 | int DEFAULT_STOMP_PORT = 61613; 37 | String DEFAULT_STOMP_HOST = "0.0.0.0"; 38 | List DEFAULT_SUPPORTED_VERSIONS = Arrays.asList("1.2", "1.1", "1.0"); 39 | 40 | JsonObject DEFAULT_STOMP_HEARTBEAT = new JsonObject().put("x", 1000).put("y", 1000); 41 | 42 | boolean DEFAULT_TRAILING_LINE = false; 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/StompServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import io.vertx.codegen.annotations.Fluent; 20 | import io.vertx.codegen.annotations.VertxGen; 21 | import io.vertx.core.Future; 22 | import io.vertx.core.Handler; 23 | import io.vertx.core.Vertx; 24 | import io.vertx.core.http.ServerWebSocket; 25 | import io.vertx.core.http.ServerWebSocketHandshake; 26 | import io.vertx.core.net.NetServer; 27 | import io.vertx.ext.stomp.impl.StompServerImpl; 28 | 29 | /** 30 | * Defines a STOMP server. STOMP servers delegates to a {@link StompServerHandler} that let customize the behavior of 31 | * the server. By default, it uses a handler compliant with the STOMP specification, but let you change anything. 32 | * 33 | * @author Clement Escoffier 34 | */ 35 | @VertxGen 36 | public interface StompServer { 37 | 38 | /** 39 | * Creates a {@link StompServer} based on the default Stomp Server implementation. 40 | * 41 | * @param vertx the vert.x instance to use 42 | * @param options the server options 43 | * @return the created {@link StompServer} 44 | */ 45 | static StompServer create(Vertx vertx, StompServerOptions options) { 46 | return new StompServerImpl(vertx, null, options); 47 | } 48 | 49 | /** 50 | * Creates a {@link StompServer} based on the default Stomp Server implementation. 51 | * 52 | * @param vertx the vert.x instance to use 53 | * @param netServer the Net server used by the STOMP server 54 | * @return the created {@link StompServer} 55 | */ 56 | static StompServer create(Vertx vertx, NetServer netServer) { 57 | return new StompServerImpl(vertx, netServer, new StompServerOptions()); 58 | } 59 | 60 | /** 61 | * Creates a {@link StompServer} based on the default Stomp Server implementation. 62 | * 63 | * @param vertx the vert.x instance to use 64 | * @param net the Net server used by the STOMP server 65 | * @param options the server options 66 | * @return the created {@link StompServer} 67 | */ 68 | static StompServer create(Vertx vertx, NetServer net, StompServerOptions options) { 69 | return new StompServerImpl(vertx, net, options); 70 | } 71 | 72 | /** 73 | * Creates a {@link StompServer} based on the default Stomp Server implementation, and use the default options. 74 | * 75 | * @param vertx the vert.x instance to use 76 | * @return the created {@link StompServer} 77 | */ 78 | static StompServer create(Vertx vertx) { 79 | return create(vertx, new StompServerOptions()); 80 | } 81 | 82 | /** 83 | * Configures the {@link StompServerHandler}. You must calls this method before calling the {@link #listen()} method. 84 | * 85 | * @param handler the handler 86 | * @return the current {@link StompServer} 87 | */ 88 | @Fluent 89 | StompServer handler(StompServerHandler handler); 90 | 91 | /** 92 | * Connects the STOMP server to the given port. This method use the default host ({@code 0.0.0.0}). Once the socket 93 | * it bounds calls the given handler with the result. The result may be a failure if the socket is already used. 94 | * 95 | * @param port the port 96 | * @return a future resolved with the listen result 97 | */ 98 | Future listen(int port); 99 | 100 | /** 101 | * Connects the STOMP server to the given port / interface. Once the socket it bounds calls the given handler with 102 | * the result. The result may be a failure if the socket is already used. 103 | * 104 | * @param port the port 105 | * @param host the interface 106 | * @return a future resolved with the listen result 107 | */ 108 | Future listen(int port, String host); 109 | 110 | /** 111 | * Connects the STOMP server default port (61613) and network interface ({@code 0.0.0.0}). Once the socket 112 | * it bounds calls the given handler with the result. The result may be a failure if the socket is already used. 113 | * 114 | * @return a future resolved with the listen result 115 | */ 116 | Future listen(); 117 | 118 | /** 119 | * Closes the server. 120 | */ 121 | Future close(); 122 | 123 | /** 124 | * Checks whether or not the server is listening. 125 | * 126 | * @return {@code true} if the server is listening, {@code false} otherwise 127 | */ 128 | boolean isListening(); 129 | 130 | /** 131 | * Gets the port on which the server is listening. 132 | *

133 | * This is useful if you bound the server specifying 0 as port number signifying an ephemeral port. 134 | * 135 | * @return the port 136 | * @see NetServer#actualPort() 137 | */ 138 | int actualPort(); 139 | 140 | /** 141 | * @return the server options 142 | */ 143 | StompServerOptions options(); 144 | 145 | /** 146 | * @return the instance of vert.x used by the server. 147 | */ 148 | Vertx vertx(); 149 | 150 | /** 151 | * @return the {@link StompServerHandler} used by this server. 152 | */ 153 | StompServerHandler stompHandler(); 154 | 155 | /** 156 | * Gets the {@link Handler} able to manage web socket connection handshakes. If the web socket bridge is disabled, it returns 157 | * {@code null}. 158 | * 159 | * @return the handler that can be passed to {@link io.vertx.core.http.HttpServer#webSocketHandshakeHandler(Handler)}. 160 | */ 161 | Handler webSocketHandshakeHandler(); 162 | 163 | /** 164 | * Gets the {@link Handler} able to manage web socket connections. If the web socket bridge is disabled, it returns 165 | * {@code null}. 166 | * 167 | * @return the handler that can be passed to {@link io.vertx.core.http.HttpServer#webSocketHandler(Handler)}. 168 | */ 169 | Handler webSocketHandler(); 170 | 171 | /** 172 | * Configures the handler that is invoked every time a frame is going to be written to the "wire". It lets you log 173 | * the frames, but also adapt the frame if needed. 174 | * 175 | * @param handler the handler, must not be {@code null} 176 | * @return the current {@link StompServer} 177 | */ 178 | @Fluent 179 | StompServer writingFrameHandler(Handler handler); 180 | 181 | 182 | } 183 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/StompServerConnection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp; 18 | 19 | import javax.net.ssl.SSLSession; 20 | 21 | import io.vertx.codegen.annotations.Fluent; 22 | import io.vertx.codegen.annotations.GenIgnore; 23 | import io.vertx.codegen.annotations.VertxGen; 24 | import io.vertx.core.Handler; 25 | import io.vertx.core.buffer.Buffer; 26 | 27 | /** 28 | * Class representing a connection between a STOMP client a the server. It keeps a references on the client socket, 29 | * so let write to this socket. 30 | * 31 | * @author Clement Escoffier 32 | */ 33 | @VertxGen 34 | public interface StompServerConnection { 35 | 36 | /** 37 | * Writes the given frame to the socket. 38 | * 39 | * @param frame the frame, must not be {@code null}. 40 | * @return the current {@link StompServerConnection} 41 | */ 42 | @Fluent 43 | StompServerConnection write(Frame frame); 44 | 45 | /** 46 | * Writes the given buffer to the socket. This is a low level API that should be used carefully. 47 | * 48 | * @param buffer the buffer 49 | * @return the current {@link StompServerConnection} 50 | */ 51 | @Fluent 52 | StompServerConnection write(Buffer buffer); 53 | 54 | /** 55 | * @return the STOMP server serving this connection. 56 | */ 57 | StompServer server(); 58 | 59 | /** 60 | * @return SSLSession associated with the underlying socket. Returns null if connection is 61 | * not SSL. 62 | */ 63 | @GenIgnore({"permitted-type"}) 64 | SSLSession sslSession(); 65 | 66 | /** 67 | * @return the STOMP server handler dealing with this connection 68 | */ 69 | StompServerHandler handler(); 70 | 71 | /** 72 | * @return the STOMP session id computed when the client has established the connection to the server 73 | */ 74 | String session(); 75 | 76 | /** 77 | * Closes the connection with the client. 78 | */ 79 | void close(); 80 | 81 | /** 82 | * Sends a `PING` frame to the client. A `PING` frame is a frame containing only {@code EOL}. 83 | */ 84 | void ping(); 85 | 86 | /** 87 | * Notifies the connection about server activity (the server has sent a frame). This method is used to handle the 88 | * heartbeat. 89 | */ 90 | void onServerActivity(); 91 | 92 | /** 93 | * Configures the heartbeat. 94 | * @param ping ping time 95 | * @param pong pong time 96 | * @param pingHandler the ping handler 97 | */ 98 | void configureHeartbeat(long ping, long pong, Handler pingHandler); 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/impl/AcknowledgementImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp.impl; 18 | 19 | import io.vertx.codegen.annotations.VertxGen; 20 | import io.vertx.ext.stomp.Acknowledgement; 21 | import io.vertx.ext.stomp.Frame; 22 | 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | 26 | /** 27 | * Basic implementation of {@link io.vertx.ext.stomp.Acknowledgement}. 28 | * 29 | * @author Clement Escoffier 30 | */ 31 | @VertxGen 32 | public class AcknowledgementImpl implements Acknowledgement { 33 | 34 | private final Frame subscription; 35 | private final List frames; 36 | 37 | public AcknowledgementImpl(Frame subscription, List frames) { 38 | this.subscription = subscription; 39 | this.frames = new ArrayList<>(frames); 40 | } 41 | 42 | public Frame subscription() { 43 | return subscription; 44 | } 45 | 46 | public List frames() { 47 | return frames; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/impl/FrameException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp.impl; 18 | 19 | /** 20 | * Exception thrown when a STOMP frame is not structured correctly or does nto obey to the specification. 21 | *

22 | * This class is thread safe. 23 | * 24 | * @author Clement Escoffier 25 | */ 26 | public class FrameException extends RuntimeException { 27 | 28 | public FrameException(String message) { 29 | super(message); 30 | } 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/impl/HeaderCodec.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp.impl; 18 | 19 | /** 20 | * Class responsible for the encoding and decoding of the STOMP frame headers. 21 | * This class is thread-safe. 22 | * 23 | * @author Clement Escoffier 24 | */ 25 | public class HeaderCodec { 26 | 27 | private static final String ESCAPE_ESCAPE = String.valueOf(new char[]{(char) 92, (char) 92}); 28 | private static final String COLON_ESCAPE = String.valueOf(new char[]{(char) 92, (char) 99}); 29 | private static final String LINE_FEED_ESCAPE = String.valueOf(new char[]{(char) 92, (char) 110}); 30 | private static final String CARRIAGE_RETURN_ESCAPE = String.valueOf(new char[]{(char) 92, (char) 114}); 31 | 32 | private HeaderCodec() { 33 | //Avoid direct instantiation. 34 | } 35 | 36 | public static String encode(String header, boolean connectOrConnectedFrame) { 37 | StringBuilder builder = new StringBuilder(); 38 | 39 | for (int i = 0; i < header.length(); i++) { 40 | char value = header.charAt(i); 41 | switch (value) { 42 | case FrameParser.ESCAPE: 43 | // Always encoded. 44 | builder.append(ESCAPE_ESCAPE); 45 | break; 46 | case FrameParser.LINE_FEED: 47 | if (connectOrConnectedFrame) { 48 | builder.append(value); 49 | } else { 50 | builder.append(LINE_FEED_ESCAPE); 51 | } 52 | break; 53 | case ':': 54 | if (connectOrConnectedFrame) { 55 | builder.append(value); 56 | } else { 57 | builder.append(COLON_ESCAPE); 58 | } 59 | break; 60 | case '\r': 61 | if (connectOrConnectedFrame) { 62 | builder.append(value); 63 | } else { 64 | builder.append(CARRIAGE_RETURN_ESCAPE); 65 | } 66 | break; 67 | default: 68 | builder.append(value); 69 | } 70 | } 71 | return builder.toString(); 72 | } 73 | 74 | public static String decode(String header, boolean connectOrConnectedFrame) { 75 | StringBuilder builder = new StringBuilder(); 76 | 77 | int i = 0; 78 | while (i < header.length()) { 79 | char value = header.charAt(i); 80 | if (value == 92 && i + 1 < header.length()) { 81 | char next = header.charAt(i + 1); 82 | switch (next) { 83 | case 114: 84 | if (connectOrConnectedFrame) { 85 | builder.append(value); 86 | } else { 87 | builder.append(FrameParser.CARRIAGE_RETURN); 88 | i++; 89 | } 90 | break; 91 | case 110: 92 | if (connectOrConnectedFrame) { 93 | builder.append(value); 94 | } else { 95 | builder.append(FrameParser.LINE_FEED); 96 | i++; 97 | } 98 | break; 99 | case 99: 100 | if (connectOrConnectedFrame) { 101 | builder.append(value); 102 | } else { 103 | builder.append(FrameParser.COLON); 104 | i++; 105 | } 106 | break; 107 | case 92: 108 | // Always decoded. 109 | builder.append(FrameParser.ESCAPE); 110 | i++; 111 | break; 112 | default: 113 | // By spec, all other escape must be treated as a fatal protocol error. 114 | throw new FrameException("Incorrect header value " + 115 | "- the header uses an illegal escaped character '" + next + "' (" + (byte) next + ")"); 116 | } 117 | } else { 118 | builder.append(value); 119 | } 120 | i++; 121 | } 122 | return builder.toString(); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/impl/Queue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp.impl; 18 | 19 | import io.vertx.core.Vertx; 20 | import io.vertx.ext.stomp.Command; 21 | import io.vertx.ext.stomp.Destination; 22 | import io.vertx.ext.stomp.Frame; 23 | import io.vertx.ext.stomp.StompServerConnection; 24 | import io.vertx.ext.stomp.utils.Headers; 25 | 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import java.util.UUID; 29 | import java.util.stream.Collectors; 30 | 31 | /** 32 | * Implementation of {@link Destination} dispatching messages to a single subscriber. It dispatches 33 | * the messages using a round-robin strategy. 34 | * 35 | * @author Clement Escoffier 36 | */ 37 | public class Queue implements Destination { 38 | 39 | private final String destination; 40 | 41 | private final List subscriptions = new ArrayList<>(); 42 | private final Vertx vertx; 43 | private int lastUsedSubscriptions = -1; 44 | 45 | public Queue(Vertx vertx, String destination) { 46 | this.destination = destination; 47 | this.vertx = vertx; 48 | } 49 | 50 | /** 51 | * @return the destination address. 52 | */ 53 | @Override 54 | public String destination() { 55 | return destination; 56 | } 57 | 58 | /** 59 | * Dispatches the given frame. 60 | * 61 | * @param connection the connection 62 | * @param frame the frame ({@code SEND} frame). 63 | * @return the current instance of {@link Destination} 64 | */ 65 | @Override 66 | public synchronized Destination dispatch(StompServerConnection connection, Frame frame) { 67 | if (subscriptions.isEmpty()) { 68 | lastUsedSubscriptions = -1; 69 | return this; 70 | } 71 | Subscription subscription = getNextSubscription(); 72 | String messageId = UUID.randomUUID().toString(); 73 | Frame message = transform(frame, subscription, messageId); 74 | subscription.connection.write(message); 75 | return this; 76 | } 77 | 78 | private Subscription getNextSubscription() { 79 | lastUsedSubscriptions = lastUsedSubscriptions + 1; 80 | if (lastUsedSubscriptions >= subscriptions.size()) { 81 | lastUsedSubscriptions = 0; 82 | } 83 | return subscriptions.get(lastUsedSubscriptions); 84 | } 85 | 86 | public static Frame transform(Frame frame, Subscription subscription, String messageId) { 87 | final Headers headers = Headers.create(frame.getHeaders()) 88 | // Destination already set in the input headers. 89 | .add(Frame.SUBSCRIPTION, subscription.id) 90 | .add(Frame.MESSAGE_ID, messageId); 91 | if (!subscription.ackMode.equals("auto")) { 92 | // We reuse the message Id as ack Id 93 | headers.add(Frame.ACK, messageId); 94 | } 95 | return new Frame(Command.MESSAGE, 96 | headers, 97 | frame.getBody()); 98 | } 99 | 100 | /** 101 | * Handles a subscription request to the current {@link Destination}. All check about the frame format and unicity 102 | * of the id should have been done beforehand. 103 | * 104 | * @param connection the connection 105 | * @param frame the {@code SUBSCRIBE} frame 106 | * @return the current instance of {@link Destination} 107 | */ 108 | @Override 109 | public synchronized Destination subscribe(StompServerConnection connection, Frame frame) { 110 | Subscription subscription = new Subscription(connection, frame); 111 | subscriptions.add(subscription); 112 | return this; 113 | } 114 | 115 | /** 116 | * Handles a un-subscription request to the current {@link Destination}. 117 | * 118 | * @param connection the connection 119 | * @param frame the {@code UNSUBSCRIBE} frame 120 | * @return {@code true} if the un-subscription has been handled, {@code false} otherwise. 121 | */ 122 | @Override 123 | public synchronized boolean unsubscribe(StompServerConnection connection, Frame frame) { 124 | boolean r = false; 125 | for (Subscription subscription : subscriptions) { 126 | if (subscription.connection.equals(connection) && subscription.id.equals(frame.getId())) { 127 | r = subscriptions.remove(subscription); 128 | // Subscription id are unique for a connection. 129 | break; 130 | } 131 | } 132 | if (subscriptions.isEmpty()) { 133 | vertx.sharedData().getLocalMap("stomp.destinations").remove(this); 134 | } 135 | return r; 136 | } 137 | 138 | /** 139 | * Removes all subscriptions of the given connection 140 | * 141 | * @param connection the connection 142 | * @return the current instance of {@link Destination} 143 | */ 144 | @Override 145 | public synchronized Destination unsubscribeConnection(StompServerConnection connection) { 146 | new ArrayList<>(subscriptions) 147 | .stream() 148 | .filter(subscription -> subscription.connection.equals(connection)) 149 | .forEach(subscriptions::remove); 150 | 151 | if (subscriptions.isEmpty()) { 152 | vertx.sharedData().getLocalMap("stomp.destinations").remove(this); 153 | } 154 | return this; 155 | } 156 | 157 | /** 158 | * Handles a {@code ACK} frame. 159 | * 160 | * @param connection the connection 161 | * @param frame the {@code ACK} frame 162 | * @return {@code true} if the destination has handled the frame (meaning it has sent the message with id) 163 | */ 164 | @Override 165 | public synchronized boolean ack(StompServerConnection connection, Frame frame) { 166 | return false; 167 | } 168 | 169 | /** 170 | * Handles a {@code NACK} frame. 171 | * 172 | * @param connection the connection 173 | * @param frame the {@code NACK} frame 174 | * @return {@code true} if the destination has handled the frame (meaning it has sent the message with id) 175 | */ 176 | @Override 177 | public synchronized boolean nack(StompServerConnection connection, Frame frame) { 178 | return false; 179 | } 180 | 181 | /** 182 | * Gets all subscription ids for the given destination hold by the given client 183 | * 184 | * @param connection the connection (client) 185 | * @return the list of subscription id, empty if none 186 | */ 187 | @Override 188 | public synchronized List getSubscriptions(StompServerConnection connection) { 189 | return subscriptions.stream() 190 | .filter(subscription -> subscription.connection.equals(connection)) 191 | .map(s -> s.id) 192 | .collect(Collectors.toList()); 193 | } 194 | 195 | /** 196 | * Gets the number of subscriptions attached to the current {@link Destination}. 197 | * 198 | * @return the number of subscriptions. 199 | */ 200 | @Override 201 | public synchronized int numberOfSubscriptions() { 202 | return subscriptions.size(); 203 | } 204 | 205 | /** 206 | * Checks whether or not the given address matches with the current destination. 207 | * 208 | * @param address the address 209 | * @return {@code true} if it matches, {@code false} otherwise. 210 | */ 211 | @Override 212 | public boolean matches(String address) { 213 | return this.destination.equals(address); 214 | } 215 | 216 | private class Subscription { 217 | private final StompServerConnection connection; 218 | private final String id; 219 | private final String ackMode; 220 | 221 | private Subscription(StompServerConnection connection, Frame frame) { 222 | this.connection = connection; 223 | this.ackMode = frame.getAck() != null ? frame.getAck() : "auto"; 224 | this.id = frame.getId(); 225 | } 226 | } 227 | 228 | } 229 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/impl/ServerFrameImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp.impl; 18 | 19 | import io.vertx.ext.stomp.Frame; 20 | import io.vertx.ext.stomp.ServerFrame; 21 | import io.vertx.ext.stomp.StompServerConnection; 22 | 23 | import java.util.Objects; 24 | 25 | /** 26 | * A basic implementation of {@link ServerFrame}. 27 | * 28 | * @author Clement Escoffier 29 | */ 30 | public class ServerFrameImpl implements ServerFrame { 31 | private final StompServerConnection connection; 32 | private final Frame frame; 33 | 34 | public ServerFrameImpl(Frame frame, StompServerConnection connection) { 35 | Objects.requireNonNull(connection); 36 | Objects.requireNonNull(frame); 37 | this.connection = connection; 38 | this.frame = frame; 39 | } 40 | 41 | 42 | /** 43 | * @return the received frame 44 | */ 45 | @Override 46 | public Frame frame() { 47 | return frame; 48 | } 49 | 50 | /** 51 | * @return the connection 52 | */ 53 | @Override 54 | public StompServerConnection connection() { 55 | return connection; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/impl/StompServerTCPConnectionImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp.impl; 18 | 19 | import javax.net.ssl.SSLSession; 20 | 21 | import io.vertx.core.Handler; 22 | import io.vertx.core.buffer.Buffer; 23 | import io.vertx.core.internal.logging.Logger; 24 | import io.vertx.core.internal.logging.LoggerFactory; 25 | import io.vertx.core.net.NetSocket; 26 | import io.vertx.ext.stomp.*; 27 | 28 | import java.util.Objects; 29 | import java.util.UUID; 30 | import java.util.concurrent.TimeUnit; 31 | import java.util.concurrent.atomic.AtomicBoolean; 32 | 33 | /** 34 | * Default implementation of the {@link StompServerConnection}. 35 | * 36 | * @author Clement Escoffier 37 | */ 38 | public class StompServerTCPConnectionImpl implements StompServerConnection { 39 | 40 | private static final Logger log = LoggerFactory.getLogger(StompServerTCPConnectionImpl.class); 41 | 42 | private final StompServer server; 43 | private final NetSocket socket; 44 | private final String sessionId; 45 | protected final Handler handler; 46 | 47 | public volatile long lastClientActivity; 48 | private long pinger = -1; 49 | private long ponger = -1; 50 | 51 | private final AtomicBoolean closed = new AtomicBoolean(false); 52 | 53 | public StompServerTCPConnectionImpl(NetSocket socket, StompServer server, Handler writingFrameHandler) { 54 | Objects.requireNonNull(socket); 55 | Objects.requireNonNull(server); 56 | this.socket = socket; 57 | this.server = server; 58 | this.sessionId = UUID.randomUUID().toString(); 59 | this.handler = writingFrameHandler; 60 | } 61 | 62 | public StompServerTCPConnectionImpl(StompServer server, Handler writingFrameHandler) { 63 | Objects.requireNonNull(server); 64 | this.socket = null; 65 | this.server = server; 66 | this.handler = writingFrameHandler; 67 | this.sessionId = UUID.randomUUID().toString(); 68 | } 69 | 70 | @Override 71 | public StompServerConnection write(Frame frame) { 72 | if (handler != null) { 73 | handler.handle(new ServerFrameImpl(frame, this)); 74 | } 75 | return write(frame.toBuffer(server.options().isTrailingLine())); 76 | } 77 | 78 | @Override 79 | public StompServerConnection write(Buffer buffer) { 80 | socket.write(buffer); 81 | return this; 82 | } 83 | 84 | @Override 85 | public StompServer server() { 86 | return server; 87 | } 88 | 89 | @Override 90 | public StompServerHandler handler() { 91 | return server.stompHandler(); 92 | } 93 | 94 | @Override 95 | public String session() { 96 | return sessionId; 97 | } 98 | 99 | @Override 100 | public SSLSession sslSession() { 101 | return this.socket.sslSession(); 102 | } 103 | 104 | @Override 105 | public void close() { 106 | if (closed.compareAndSet(false, true)) { 107 | cancelHeartbeat(); 108 | handler().onClose(this); 109 | socket.close(); 110 | } 111 | } 112 | 113 | /** 114 | * Sends a `PING` frame to the client. A `PING` frame is a frame containing only {@code EOL}. 115 | */ 116 | @Override 117 | public void ping() { 118 | if (handler != null) { 119 | handler.handle(new ServerFrameImpl(Frames.PING, this)); 120 | } 121 | socket.write(Buffer.buffer(FrameParser.EOL)); 122 | } 123 | 124 | public synchronized void cancelHeartbeat() { 125 | if (pinger >= 0) { 126 | server.vertx().cancelTimer(pinger); 127 | pinger = -1; 128 | } 129 | 130 | if (ponger >= 0) { 131 | server.vertx().cancelTimer(ponger); 132 | ponger = -1; 133 | } 134 | } 135 | 136 | @Override 137 | public void onServerActivity() { 138 | lastClientActivity = System.nanoTime(); 139 | } 140 | 141 | @Override 142 | public synchronized void configureHeartbeat(long ping, long pong, Handler pingHandler) { 143 | if (ping > 0) { 144 | pinger = server.vertx().setPeriodic(ping, l -> pingHandler.handle(this)); 145 | } 146 | if (pong > 0) { 147 | ponger = server.vertx().setPeriodic(pong, l -> { 148 | long delta = System.nanoTime() - lastClientActivity; 149 | final long deltaInMs = TimeUnit.MILLISECONDS.convert(delta, TimeUnit.NANOSECONDS); 150 | if (deltaInMs > pong * 2) { 151 | log.warn("Disconnecting client " + this + " - no client activity in the last " + deltaInMs + " ms"); 152 | close(); 153 | } 154 | }); 155 | } 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/impl/StompServerWebSocketConnectionImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp.impl; 18 | 19 | import javax.net.ssl.SSLSession; 20 | 21 | import io.vertx.core.Handler; 22 | import io.vertx.core.buffer.Buffer; 23 | import io.vertx.core.http.ServerWebSocket; 24 | import io.vertx.ext.stomp.*; 25 | 26 | import java.util.Objects; 27 | import java.util.concurrent.atomic.AtomicBoolean; 28 | 29 | /** 30 | * Default implementation of the {@link StompServerConnection}. 31 | * 32 | * @author Clement Escoffier 33 | */ 34 | public class StompServerWebSocketConnectionImpl extends StompServerTCPConnectionImpl implements StompServerConnection { 35 | 36 | private final ServerWebSocket socket; 37 | 38 | private final AtomicBoolean closed = new AtomicBoolean(false); 39 | 40 | public StompServerWebSocketConnectionImpl(ServerWebSocket socket, StompServer server, Handler writtenFrameHandler) { 41 | super(server, writtenFrameHandler); 42 | Objects.requireNonNull(socket); 43 | this.socket = socket; 44 | } 45 | 46 | @Override 47 | public SSLSession sslSession() { 48 | return this.socket.sslSession(); 49 | } 50 | 51 | @Override 52 | public StompServerConnection write(Buffer buffer) { 53 | socket.writeBinaryMessage(buffer); 54 | return this; 55 | } 56 | 57 | @Override 58 | public void ping() { 59 | if (handler != null) { 60 | handler.handle(new ServerFrameImpl(Frames.PING, this)); 61 | } 62 | socket.write(Buffer.buffer(FrameParser.EOL)); 63 | } 64 | 65 | @Override 66 | public void close() { 67 | if (closed.compareAndSet(false, true)) { 68 | cancelHeartbeat(); 69 | handler().onClose(this); 70 | socket.close(); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/impl/Topic.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp.impl; 18 | 19 | import io.vertx.core.Vertx; 20 | import io.vertx.ext.stomp.Command; 21 | import io.vertx.ext.stomp.Destination; 22 | import io.vertx.ext.stomp.Frame; 23 | import io.vertx.ext.stomp.StompServerConnection; 24 | import io.vertx.ext.stomp.utils.Headers; 25 | 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import java.util.UUID; 29 | import java.util.stream.Collectors; 30 | 31 | /** 32 | * Implementation of {@link io.vertx.ext.stomp.Destination} dispatching messages to all subscribers. 33 | * 34 | * @author Clement Escoffier 35 | */ 36 | public class Topic implements Destination { 37 | 38 | protected final String destination; 39 | 40 | protected final List subscriptions = new ArrayList<>(); 41 | protected final Vertx vertx; 42 | 43 | public Topic(Vertx vertx, String destination) { 44 | this.destination = destination; 45 | this.vertx = vertx; 46 | } 47 | 48 | /** 49 | * @return the destination address. 50 | */ 51 | @Override 52 | public String destination() { 53 | return destination; 54 | } 55 | 56 | /** 57 | * Dispatches the given frame. 58 | * 59 | * @param connection the connection 60 | * @param frame the frame ({@code SEND} frame). 61 | * @return the current instance of {@link Destination} 62 | */ 63 | @Override 64 | public synchronized Destination dispatch(StompServerConnection connection, Frame frame) { 65 | for (Subscription subscription : subscriptions) { 66 | String messageId = UUID.randomUUID().toString(); 67 | Frame message = transform(frame, subscription, messageId); 68 | subscription.connection.write(message); 69 | } 70 | return this; 71 | } 72 | 73 | public static Frame transform(Frame frame, Subscription subscription, String messageId) { 74 | final Headers headers = Headers.create(frame.getHeaders()) 75 | // Destination already set in the input headers. 76 | .add(Frame.SUBSCRIPTION, subscription.id) 77 | .add(Frame.MESSAGE_ID, messageId); 78 | if (!subscription.ackMode.equals("auto")) { 79 | // We reuse the message Id as ack Id 80 | headers.add(Frame.ACK, messageId); 81 | } 82 | return new Frame(Command.MESSAGE, 83 | headers, 84 | frame.getBody()); 85 | } 86 | 87 | /** 88 | * Handles a subscription request to the current {@link Destination}. All check about the frame format and unicity 89 | * of the id should have been done beforehand. 90 | * 91 | * @param connection the connection 92 | * @param frame the {@code SUBSCRIBE} frame 93 | * @return the current instance of {@link Destination} 94 | */ 95 | @Override 96 | public synchronized Destination subscribe(StompServerConnection connection, Frame frame) { 97 | Subscription subscription = new Subscription(connection, frame); 98 | subscriptions.add(subscription); 99 | return this; 100 | } 101 | 102 | /** 103 | * Handles a un-subscription request to the current {@link Destination}. 104 | * 105 | * @param connection the connection 106 | * @param frame the {@code UNSUBSCRIBE} frame 107 | * @return {@code true} if the un-subscription has been handled, {@code false} otherwise. 108 | */ 109 | @Override 110 | public synchronized boolean unsubscribe(StompServerConnection connection, Frame frame) { 111 | boolean r = false; 112 | for (Subscription subscription : subscriptions) { 113 | if (subscription.connection.equals(connection) && subscription.id.equals(frame.getId())) { 114 | r = subscriptions.remove(subscription); 115 | // Subscription id are unique for a connection. 116 | break; 117 | } 118 | } 119 | if (subscriptions.isEmpty()) { 120 | vertx.sharedData().getLocalMap("stomp.destinations").remove(this); 121 | } 122 | return r; 123 | } 124 | 125 | /** 126 | * Removes all subscriptions of the given connection 127 | * 128 | * @param connection the connection 129 | * @return the current instance of {@link Destination} 130 | */ 131 | @Override 132 | public synchronized Destination unsubscribeConnection(StompServerConnection connection) { 133 | new ArrayList<>(subscriptions) 134 | .stream() 135 | .filter(subscription -> subscription.connection.equals(connection)) 136 | .forEach(subscriptions::remove); 137 | 138 | if (subscriptions.isEmpty()) { 139 | vertx.sharedData().getLocalMap("stomp.destinations").remove(this); 140 | } 141 | return this; 142 | } 143 | 144 | /** 145 | * Handles a {@code ACK} frame. 146 | * 147 | * @param connection the connection 148 | * @param frame the {@code ACK} frame 149 | * @return {@code true} if the destination has handled the frame (meaning it has sent the message with id) 150 | */ 151 | @Override 152 | public synchronized boolean ack(StompServerConnection connection, Frame frame) { 153 | return false; 154 | } 155 | 156 | /** 157 | * Handles a {@code NACK} frame. 158 | * 159 | * @param connection the connection 160 | * @param frame the {@code NACK} frame 161 | * @return {@code true} if the destination has handled the frame (meaning it has sent the message with id) 162 | */ 163 | @Override 164 | public synchronized boolean nack(StompServerConnection connection, Frame frame) { 165 | return false; 166 | } 167 | 168 | /** 169 | * Gets all subscription ids for the given destination hold by the given client 170 | * 171 | * @param connection the connection (client) 172 | * @return the list of subscription id, empty if none 173 | */ 174 | @Override 175 | public synchronized List getSubscriptions(StompServerConnection connection) { 176 | return subscriptions.stream() 177 | .filter(subscription -> subscription.connection.equals(connection)) 178 | .map(s -> s.id) 179 | .collect(Collectors.toList()); 180 | } 181 | 182 | /** 183 | * Gets the number of subscriptions attached to the current {@link Destination}. 184 | * 185 | * @return the number of subscriptions. 186 | */ 187 | @Override 188 | public synchronized int numberOfSubscriptions() { 189 | return subscriptions.size(); 190 | } 191 | 192 | /** 193 | * Checks whether or not the given address matches with the current destination. 194 | * 195 | * @param address the address 196 | * @return {@code true} if it matches, {@code false} otherwise. 197 | */ 198 | @Override 199 | public boolean matches(String address) { 200 | return this.destination.equals(address); 201 | } 202 | 203 | protected static class Subscription { 204 | final StompServerConnection connection; 205 | final String id; 206 | final String ackMode; 207 | final String destination; 208 | 209 | protected Subscription(StompServerConnection connection, Frame frame) { 210 | this.connection = connection; 211 | this.ackMode = frame.getAck() != null ? frame.getAck() : "auto"; 212 | this.id = frame.getId(); 213 | this.destination = frame.getDestination(); 214 | } 215 | } 216 | 217 | } 218 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/impl/Transaction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp.impl; 18 | 19 | import io.vertx.ext.stomp.Frame; 20 | import io.vertx.ext.stomp.StompServerConnection; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | /** 26 | * Represents a STOMP transaction. 27 | * This class is thread safe. 28 | * 29 | * @author Clement Escoffier 30 | */ 31 | public class Transaction { 32 | private final List frames; 33 | private final String id; 34 | private final StompServerConnection connection; 35 | 36 | public Transaction(StompServerConnection connection, String id) { 37 | this.connection = connection; 38 | this.id = id; 39 | this.frames = new ArrayList<>(); 40 | } 41 | 42 | /** 43 | * @return the connection 44 | */ 45 | public StompServerConnection connection() { 46 | return connection; 47 | } 48 | 49 | /** 50 | * @return the transaction id 51 | */ 52 | public String id() { 53 | return id; 54 | } 55 | 56 | /** 57 | * Adds a frame to the transaction. As stated in the STOMP specification, only {@code SEND, ACK and NACK} frames 58 | * can be in transactions. 59 | * 60 | * @param frame the frame to add 61 | * @return {@code true} if the frame was added to the transaction, {@code false otherwise}. Main failure reason is the number of 62 | * frames stored in the transaction that have exceed the number of allowed frames in transaction. 63 | */ 64 | public synchronized boolean addFrameToTransaction(Frame frame) { 65 | return frames.size() < connection.server().options().getMaxFrameInTransaction() && frames.add(frame); 66 | } 67 | 68 | /** 69 | * Clears the list of frames added to the transaction. 70 | * 71 | * @return the current {@link Transaction} 72 | */ 73 | public synchronized Transaction clear() { 74 | frames.clear(); 75 | return this; 76 | } 77 | 78 | /** 79 | * @return the ordered list of frames added to the transaction. To avoid concurrency issue, a copy is returned. 80 | */ 81 | public synchronized List getFrames() { 82 | return new ArrayList<>(frames); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/impl/Transactions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp.impl; 18 | 19 | import io.vertx.ext.stomp.StompServerConnection; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | /** 25 | * Stores the active transactions of the STOMP server. 26 | * Transactions are not shared between server instances, as transactions are 'attached' to a STOMP client (i.e. 27 | * connection). So 2 connections cannot use the same transaction object. 28 | * 29 | * @author Clement Escoffier 30 | */ 31 | public class Transactions { 32 | 33 | private final static Transactions INSTANCE = new Transactions(); 34 | 35 | private final List transactions = new ArrayList<>(); 36 | 37 | public static Transactions instance() { 38 | return INSTANCE; 39 | } 40 | 41 | private Transactions() { 42 | // Avoid direct instantiation. 43 | } 44 | 45 | /** 46 | * Gets an active transaction. 47 | * 48 | * @param connection the connection 49 | * @param txId the transaction id 50 | * @return the transaction object, {@code null} if not found 51 | */ 52 | public synchronized Transaction getTransaction(StompServerConnection connection, String txId) { 53 | return transactions.stream() 54 | .filter(t -> t.connection().equals(connection) && t.id().equals(txId)) 55 | .findFirst().orElse(null); 56 | } 57 | 58 | /** 59 | * Registers a transaction. 60 | * 61 | * @param connection the connection 62 | * @param txId the transaction id 63 | * @return {@code true} if the registration succeed, {@code false} otherwise. The main reason of failure is the 64 | * non-uniqueness of the transaction id for a given client / connection 65 | */ 66 | public synchronized boolean registerTransaction(StompServerConnection connection, String txId) { 67 | if (getTransaction(connection, txId) != null) { 68 | return false; 69 | } 70 | transactions.add(new Transaction(connection, txId)); 71 | return true; 72 | } 73 | 74 | /** 75 | * Unregisters a transaction 76 | * 77 | * @param connection the connection used by the transaction 78 | * @param id the id of the transaction 79 | * @return {@code true} if the transaction is unregistered correctly, {@code false} otherwise. 80 | */ 81 | public synchronized boolean unregisterTransaction(StompServerConnection connection, String id) { 82 | Transaction transaction = getTransaction(connection, id); 83 | return transaction != null && transactions.remove(transaction); 84 | } 85 | 86 | /** 87 | * Unregisters all transactions from the given connection / client. 88 | * 89 | * @param connection the connection 90 | */ 91 | public synchronized void unregisterTransactionsFromConnection(StompServerConnection connection) { 92 | transactions.stream() 93 | .filter(transaction -> transaction.connection().equals(connection)) 94 | .sorted() // Avoid using baking up collection. 95 | .forEach(transactions::remove); 96 | } 97 | 98 | /** 99 | * Gets the number of transaction handled by the server. 100 | * 101 | * @return the number of transaction, {@code 0} if none. 102 | */ 103 | public synchronized int getTransactionCount() { 104 | return transactions.size(); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | @ModuleGen(name = "vertx-stomp", groupPackage = "io.vertx") 18 | package io.vertx.ext.stomp; 19 | 20 | import io.vertx.codegen.annotations.ModuleGen; 21 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/utils/Headers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp.utils; 18 | 19 | import java.util.Arrays; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | 23 | /** 24 | * An implementation of {@link HashMap} to store STOMP frame headers. This implementations offer fluent methods to 25 | * ease the construction of the headers. 26 | * 27 | * @author Clement Escoffier 28 | */ 29 | public class Headers extends HashMap { 30 | 31 | public static Headers create() { 32 | return new Headers(); 33 | } 34 | 35 | public static Headers create(String... kv) { 36 | Headers headers = create(); 37 | if (kv.length % 2 != 0) { 38 | throw new IllegalArgumentException("Wrong number of parameters: " + Arrays.toString(kv)); 39 | } 40 | for (int i = 0; i < kv.length; i = i + 2) { 41 | headers.add(kv[i], kv[i + 1]); 42 | } 43 | return headers; 44 | } 45 | 46 | public Headers add(String header, String value) { 47 | this.put(header, value); 48 | return this; 49 | } 50 | 51 | public Headers addAll(Map other) { 52 | this.putAll(other); 53 | return this; 54 | } 55 | 56 | public static Headers create(Map headers) { 57 | return create().addAll(headers); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/ext/stomp/utils/Server.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.ext.stomp.utils; 18 | 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.util.Scanner; 22 | 23 | /** 24 | * Class responsible for the computation of the server id. From the STOMP specification, this id must be constructed as 25 | * follows: name/version comments. 26 | * 27 | * @author Clement Escoffier 28 | */ 29 | public class Server { 30 | 31 | public static final String SERVER_NAME; 32 | 33 | static { 34 | try (InputStream is = Server.class.getClassLoader().getResourceAsStream("vertx-stomp-version.txt")) { 35 | if (is == null) { 36 | throw new IllegalStateException("Cannot find vertx-stomp-version.txt on classpath"); 37 | } 38 | try (Scanner scanner = new Scanner(is, "UTF-8").useDelimiter("\\A")) { 39 | SERVER_NAME = "vertx-stomp" + (scanner.hasNext() ? "/" + scanner.next() : ""); 40 | } 41 | } catch (IOException e) { 42 | throw new IllegalStateException(e.getMessage()); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | module io.vertx.stomp { 4 | 5 | requires static io.vertx.core.logging; 6 | requires static io.vertx.docgen; 7 | requires static io.vertx.codegen.api; 8 | requires static io.vertx.codegen.json; 9 | 10 | requires io.vertx.auth.common; 11 | requires io.vertx.core; 12 | requires io.vertx.eventbusbridge; 13 | 14 | exports io.vertx.ext.stomp; 15 | 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/resources/vertx-stomp-version.txt: -------------------------------------------------------------------------------- 1 | ${project.version} -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/FrameTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests; 18 | 19 | import io.vertx.core.buffer.Buffer; 20 | import io.vertx.ext.stomp.Command; 21 | import io.vertx.ext.stomp.Frame; 22 | import io.vertx.ext.stomp.Frames; 23 | import io.vertx.ext.stomp.impl.FrameException; 24 | import io.vertx.ext.stomp.impl.FrameParser; 25 | import io.vertx.ext.stomp.utils.Headers; 26 | import org.junit.Test; 27 | 28 | import static org.assertj.core.api.Assertions.assertThat; 29 | 30 | /** 31 | * Check the behavior of {@link Frame}. 32 | * 33 | * @author Clement Escoffier 34 | */ 35 | public class FrameTest { 36 | 37 | private Frame frame; 38 | 39 | @Test 40 | public void testThatPassCodeAreNotInToString() { 41 | Frame frame = new Frame(Command.STOMP, Headers.create("login", "vertx", "passcode", "secret"), null); 42 | assertThat(frame.toString()).doesNotContain("secret").contains("********"); 43 | } 44 | 45 | @Test(expected = FrameException.class) 46 | public void testThatConnectFrameCannotHaveBody() { 47 | new Frame(Command.CONNECT, Headers.create("host", "foo"), 48 | Buffer.buffer("illegal")); 49 | } 50 | 51 | @Test 52 | public void testDefaultEncoding() { 53 | final String content = "This content contains utf-8 characters: ü ß é ø î"; 54 | Frame frame = new Frame(Command.SEND, Headers.create(), Buffer.buffer(content)); 55 | assertThat(frame.getBodyAsString()).isEqualTo(content); 56 | } 57 | 58 | @Test 59 | public void testEncoding() { 60 | final String content = "\u03B1"; 61 | Frame frame = new Frame(Command.SEND, Headers.create("content-type", 62 | "text/plain;charset=utf-16"), Buffer.buffer(content)); 63 | assertThat(frame.encoding()).isEqualTo("utf-16"); 64 | frame = new Frame(Command.SEND, Headers.create("content-type", 65 | "text/plain;charset=utf-8"), Buffer.buffer(content)); 66 | assertThat(frame.encoding()).isEqualTo("utf-8"); 67 | } 68 | 69 | @Test 70 | public void testHeaderEncoding() { 71 | String value = "test-\r\n :\\-test"; 72 | String expected = "test-" + (char) 92 + (char) 114 + (char) 92 + (char) 110 + " " + (char) 92 + (char) 99 + 73 | (char) 92 + (char) 92 + "-test"; 74 | 75 | Frame frame = new Frame(Command.SEND, Headers.create("header", value), null); 76 | assertThat(frame.toBuffer().toString()).contains("header:" + expected + "\n"); 77 | } 78 | 79 | @Test 80 | public void testHeaderEncodingOnConnectAndConnectedFrames() { 81 | String value = "test-\r\n :\\-test"; 82 | String expected = "test-\r\n :" + (char) 92 + (char) 92 + "-test"; 83 | 84 | Frame frame = new Frame(Command.CONNECT, Headers.create("header", value), null); 85 | assertThat(frame.toBuffer().toString()).contains("header:" + expected + "\n"); 86 | 87 | frame = new Frame(Command.CONNECTED, Headers.create("header", value), null); 88 | assertThat(frame.toBuffer().toString()).contains("header:" + expected + "\n"); 89 | } 90 | 91 | @Test 92 | public void testHeartbeatComputationWith00() { 93 | Frame.Heartbeat client = Frame.Heartbeat.parse("0,0"); 94 | Frame.Heartbeat server = Frame.Heartbeat.parse("0,0"); 95 | assertThat(Frame.Heartbeat.computePingPeriod(client, server)).isEqualTo(0); 96 | assertThat(Frame.Heartbeat.computePongPeriod(client, server)).isEqualTo(0); 97 | assertThat(Frame.Heartbeat.computePongPeriod(server, client)).isEqualTo(0); 98 | assertThat(Frame.Heartbeat.computePingPeriod(server, client)).isEqualTo(0); 99 | } 100 | 101 | @Test 102 | public void testHeartbeatComputationWith01() { 103 | Frame.Heartbeat client = Frame.Heartbeat.parse("0,1"); 104 | Frame.Heartbeat server = Frame.Heartbeat.parse("0,1"); 105 | assertThat(Frame.Heartbeat.computePingPeriod(client, server)).isEqualTo(0); 106 | assertThat(Frame.Heartbeat.computePongPeriod(client, server)).isEqualTo(0); 107 | assertThat(Frame.Heartbeat.computePingPeriod(server, client)).isEqualTo(0); 108 | assertThat(Frame.Heartbeat.computePongPeriod(server, client)).isEqualTo(0); 109 | } 110 | 111 | @Test 112 | public void testHeartbeatComputationWith10() { 113 | Frame.Heartbeat client = Frame.Heartbeat.parse("1,0"); 114 | Frame.Heartbeat server = Frame.Heartbeat.parse("1,0"); 115 | assertThat(Frame.Heartbeat.computePingPeriod(client, server)).isEqualTo(0); 116 | assertThat(Frame.Heartbeat.computePongPeriod(client, server)).isEqualTo(0); 117 | assertThat(Frame.Heartbeat.computePingPeriod(server, client)).isEqualTo(0); 118 | assertThat(Frame.Heartbeat.computePongPeriod(server, client)).isEqualTo(0); 119 | } 120 | 121 | @Test 122 | public void testHeartbeatComputationWith11() { 123 | Frame.Heartbeat client = Frame.Heartbeat.parse("1,1"); 124 | Frame.Heartbeat server = Frame.Heartbeat.parse("1,1"); 125 | assertThat(Frame.Heartbeat.computePingPeriod(client, server)).isEqualTo(1); 126 | assertThat(Frame.Heartbeat.computePongPeriod(client, server)).isEqualTo(1); 127 | assertThat(Frame.Heartbeat.computePingPeriod(server, client)).isEqualTo(1); 128 | assertThat(Frame.Heartbeat.computePongPeriod(server, client)).isEqualTo(1); 129 | } 130 | 131 | @Test 132 | public void testHeartbeatComputationNotSymmetric() { 133 | Frame.Heartbeat client = Frame.Heartbeat.parse("1,2"); 134 | Frame.Heartbeat server = Frame.Heartbeat.parse("3,4"); 135 | assertThat(Frame.Heartbeat.computePingPeriod(client, server)).isEqualTo(4); 136 | assertThat(Frame.Heartbeat.computePongPeriod(client, server)).isEqualTo(3); 137 | assertThat(Frame.Heartbeat.computePingPeriod(server, client)).isEqualTo(3); 138 | assertThat(Frame.Heartbeat.computePongPeriod(server, client)).isEqualTo(4); 139 | } 140 | 141 | @Test 142 | public void testWithTrailingSpaces() { 143 | frame = new Frame(Command.MESSAGE, Headers.create("foo", "bar"), Buffer.buffer("hello")); 144 | assertThat(frame.toBuffer(true).toString()).endsWith(FrameParser.NULL + "\n"); 145 | 146 | frame = new Frame(Command.MESSAGE, Headers.create("foo", "bar"), null); 147 | assertThat(frame.toBuffer(true).toString()).endsWith(FrameParser.NULL + "\n"); 148 | 149 | frame = new Frame(Command.MESSAGE, Headers.create(), null); 150 | assertThat(frame.toBuffer(true).toString()).endsWith(FrameParser.NULL + "\n"); 151 | } 152 | 153 | @Test 154 | public void testErrorFrameContentType() { 155 | Frame errorFrame = Frames.createErrorFrame("Test Message", Headers.create("foo", "bar"), "hello"); 156 | assertThat(errorFrame.getHeader(Frame.CONTENT_TYPE)).isEqualTo("text/plain"); 157 | } 158 | 159 | } 160 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/HeadersTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests; 18 | 19 | import io.vertx.ext.stomp.utils.Headers; 20 | import org.junit.Test; 21 | 22 | import java.util.Map; 23 | 24 | import static org.assertj.core.api.Assertions.assertThat; 25 | import static org.assertj.core.api.Assertions.entry; 26 | 27 | /** 28 | * Checks the behavior of {@link Headers}. 29 | * 30 | * @author Clement Escoffier 31 | */ 32 | public class HeadersTest { 33 | 34 | @Test 35 | public void testEmptyCreation() { 36 | assertThat(Headers.create()).isInstanceOf(Map.class); 37 | } 38 | 39 | @Test 40 | public void testCreationWithEmptyArray() { 41 | final Headers headers = Headers.create(new String[0]); 42 | assertThat(headers).isInstanceOf(Map.class).isEmpty(); 43 | } 44 | 45 | @Test 46 | public void testCreationWithArgs() { 47 | Headers map = Headers.create("a", "b", "c", "d"); 48 | assertThat(map).containsExactly(entry("a", "b"), entry("c", "d")); 49 | } 50 | 51 | @Test(expected = IllegalArgumentException.class) 52 | public void testCreationWithWrongNumberOfArgs() { 53 | Headers.create("a", "b", "c", "d", "illegal"); 54 | } 55 | 56 | 57 | @Test 58 | public void testAdd() { 59 | Headers map = Headers.create().add("a", "b").add("c", "d"); 60 | map.put("e", "f"); 61 | assertThat(map).containsExactly(entry("a", "b"), entry("c", "d"), entry("e", "f")); 62 | map.add("e", "f2"); 63 | assertThat(map).containsExactly(entry("a", "b"), entry("c", "d"), entry("e", "f2")); 64 | } 65 | 66 | @Test 67 | public void testAddAll() { 68 | Headers map = Headers.create().add("a", "b").add("c", "d") 69 | .addAll(Headers.create("e", "f")); 70 | assertThat(map).containsExactly(entry("a", "b"), entry("c", "d"), entry("e", "f")); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/StompClientOptionsTest.java: -------------------------------------------------------------------------------- 1 | package io.vertx.stomp.tests; 2 | 3 | import io.vertx.core.json.JsonObject; 4 | import io.vertx.ext.stomp.StompClientOptions; 5 | import org.junit.Test; 6 | 7 | import static io.vertx.ext.stomp.StompOptions.DEFAULT_STOMP_PORT; 8 | import static io.vertx.ext.stomp.StompOptions.DEFAULT_SUPPORTED_VERSIONS; 9 | import static junit.framework.TestCase.assertEquals; 10 | import static junit.framework.TestCase.assertTrue; 11 | import static org.junit.Assert.assertFalse; 12 | 13 | /** 14 | * Check the behavior of {@link StompClientOptions}. 15 | * 16 | */ 17 | public class StompClientOptionsTest { 18 | 19 | @Test 20 | public void testDefaultConstructor(){ 21 | StompClientOptions options = new StompClientOptions(); 22 | assertFalse(options.isReuseAddress()); 23 | assertEquals(options.getAcceptedVersions().indexOf(DEFAULT_SUPPORTED_VERSIONS.get(0)), 24 | (options.getAcceptedVersions().size()-1)); 25 | assertEquals( DEFAULT_STOMP_PORT, options.getPort() ); 26 | } 27 | 28 | @Test 29 | public void testReusedAddressInConstructFromJsonObject(){ 30 | String jsonWithoutReuseAddress = "{\"host\": \"127.0.0.1\"}"; 31 | StompClientOptions options0 = new StompClientOptions(new JsonObject(jsonWithoutReuseAddress)); 32 | assertFalse(options0.isReuseAddress()); 33 | 34 | String jsonWithValidReuseAddress = "{\"reuseAddress\": true}"; 35 | StompClientOptions options1 = new StompClientOptions(new JsonObject(jsonWithValidReuseAddress)); 36 | assertTrue(options1.isReuseAddress()); 37 | 38 | String jsonWithInvalidReuseAddress = "{\"reuseAddress\": \"none\"}"; 39 | StompClientOptions options2 = new StompClientOptions(new JsonObject(jsonWithInvalidReuseAddress)); 40 | assertFalse(options2.isReuseAddress() ); 41 | } 42 | 43 | @Test 44 | public void testAcceptedVersionsInConstructFromJsonObject(){ 45 | String jsonWithoutAcceptedVersions = "{\"host\": \"127.0.0.1\"}"; 46 | StompClientOptions options0 = new StompClientOptions(new JsonObject(jsonWithoutAcceptedVersions)); 47 | assertEquals(options0.getAcceptedVersions(). 48 | indexOf(DEFAULT_SUPPORTED_VERSIONS.get(0)), (options0.getAcceptedVersions().size()-1)); 49 | 50 | String jsonWithValidReuseAddress = "{\"acceptedVersions\": [\"1.2\"]}"; 51 | StompClientOptions options1 = new StompClientOptions(new JsonObject(jsonWithValidReuseAddress)); 52 | assertEquals( 1,options1.getAcceptedVersions().size()); 53 | assertTrue(options1.getAcceptedVersions().contains("1.2")); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/StompServerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests; 18 | 19 | import io.vertx.ext.stomp.utils.Server; 20 | import org.junit.Test; 21 | 22 | import static org.assertj.core.api.Assertions.assertThat; 23 | 24 | /** 25 | * @author Clement Escoffier 26 | */ 27 | public class StompServerTest { 28 | 29 | @Test 30 | public void checkThatVersionIsReadFromFile() { 31 | String server = Server.SERVER_NAME; 32 | assertThat(server).startsWith("vertx-stomp"); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/impl/AsyncLock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.impl; 18 | 19 | import io.vertx.core.AsyncResult; 20 | import io.vertx.core.Handler; 21 | 22 | import java.util.concurrent.CountDownLatch; 23 | import java.util.concurrent.TimeUnit; 24 | import java.util.concurrent.atomic.AtomicReference; 25 | 26 | /** 27 | * An utility class to wait for success. 28 | * 29 | * @author Clement Escoffier 30 | */ 31 | public class AsyncLock { 32 | 33 | private final CountDownLatch latch; 34 | private final AtomicReference> reference = new AtomicReference<>(); 35 | 36 | public AsyncLock() { 37 | latch = new CountDownLatch(1); 38 | } 39 | 40 | public Handler> handler() { 41 | return (ar) -> { 42 | reference.set(ar); 43 | latch.countDown(); 44 | }; 45 | } 46 | 47 | public void waitForSuccess() { 48 | try { 49 | latch.await(10, TimeUnit.SECONDS); 50 | } catch (InterruptedException e) { 51 | // interrupted 52 | Thread.currentThread().interrupt(); 53 | } 54 | 55 | final AsyncResult result = reference.get(); 56 | if (result == null) { 57 | throw new AssertionError("Result not received after timeout"); 58 | } 59 | 60 | if (result.failed()) { 61 | result.cause().printStackTrace(); 62 | throw new AssertionError("Received a failed result " + result.cause().getMessage()); 63 | } 64 | 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/impl/FrameTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.impl; 18 | 19 | import io.vertx.core.buffer.Buffer; 20 | import io.vertx.ext.stomp.Command; 21 | import io.vertx.ext.stomp.Frame; 22 | import org.junit.Test; 23 | 24 | import java.util.HashMap; 25 | import java.util.Map; 26 | 27 | import static org.assertj.core.api.Assertions.assertThat; 28 | 29 | public class FrameTest { 30 | 31 | @Test 32 | public void testCopyFrame() { 33 | Command command = Command.UNKNOWN; 34 | Map headers = new HashMap() {{ 35 | this.put("hdr0", "val0"); 36 | this.put("hdr1", "val1"); 37 | }}; 38 | Buffer body = Buffer.buffer("body content"); 39 | Frame other = new Frame(command, headers, body); 40 | Frame current = new Frame(other); 41 | assertThat(current.getCommand()).isEqualTo(Command.UNKNOWN); 42 | assertThat(current.getHeader("hdr0")).isEqualTo("val0"); 43 | assertThat(current.getHeader("hdr1")).isEqualTo("val1"); 44 | assertThat(current.getBody()).isNotSameAs(other.getBody()); 45 | assertThat(current.getBodyAsString()).isEqualTo("body content"); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/impl/HeaderCodecTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.impl; 18 | 19 | import io.vertx.ext.stomp.impl.FrameException; 20 | import io.vertx.ext.stomp.impl.HeaderCodec; 21 | import org.junit.Test; 22 | 23 | import static org.assertj.core.api.Assertions.assertThat; 24 | 25 | /** 26 | * Checks the behavior of the {@link HeaderCodec}. 27 | * 28 | * @author Clement Escoffier 29 | */ 30 | public class HeaderCodecTest { 31 | 32 | @Test 33 | public void testDecoding() { 34 | assertThat(HeaderCodec.decode("hello", false)).isEqualTo("hello"); 35 | assertThat(HeaderCodec.decode("h\\ce", false)).isEqualTo("h:e"); 36 | assertThat(HeaderCodec.decode("\\c-)", false)).isEqualTo(":-)"); 37 | assertThat(HeaderCodec.decode(")-\\c", false)).isEqualTo(")-:"); 38 | assertThat(HeaderCodec.decode("multi\nline", false)).isEqualTo("multi\nline"); 39 | assertThat(HeaderCodec.decode("multi\r\nline", false)).isEqualTo("multi\r\nline"); 40 | 41 | assertThat(HeaderCodec.decode("multi" + (char) 92 + (char) 110 + "line", false)) 42 | .isEqualTo("multi\nline"); 43 | 44 | assertThat(HeaderCodec.decode("multi" 45 | + (char) 92 + (char) 114 46 | + (char) 92 + (char) 110 + "line", false)).isEqualTo("multi\r\nline"); 47 | 48 | assertThat(HeaderCodec.decode("test" 49 | + (char) 92 + (char) 92 50 | + "slash", false)).isEqualTo("test\\slash"); 51 | } 52 | 53 | @Test 54 | public void testDecodingOnConnectOrConnectedFrames() { 55 | assertThat(HeaderCodec.decode("hello", true)).isEqualTo("hello"); 56 | assertThat(HeaderCodec.decode("h\\ce", true)).isEqualTo("h\\ce"); 57 | assertThat(HeaderCodec.decode("\\c-)", true)).isEqualTo("\\c-)"); 58 | assertThat(HeaderCodec.decode(")-\\c", true)).isEqualTo(")-\\c"); 59 | assertThat(HeaderCodec.decode("multi\nline", true)).isEqualTo("multi\nline"); 60 | assertThat(HeaderCodec.decode("multi\r\nline", true)).isEqualTo("multi\r\nline"); 61 | 62 | assertThat(HeaderCodec.decode("multi" + (char) 92 + (char) 110 + "line", true)) 63 | .isEqualTo("multi" + (char) 92 + (char) 110 + "line"); 64 | 65 | assertThat(HeaderCodec.decode("multi" 66 | + (char) 92 + (char) 114 67 | + (char) 92 + (char) 110 + "line", true)).isEqualTo("multi" 68 | + (char) 92 + (char) 114 69 | + (char) 92 + (char) 110 + "line"); 70 | 71 | // Slash is decoded. 72 | assertThat(HeaderCodec.decode("test" 73 | + (char) 92 + (char) 92 74 | + "slash", true)).isEqualTo("test\\slash"); 75 | } 76 | 77 | @Test(expected = FrameException.class) 78 | public void testDecodingIllegalEscape() { 79 | HeaderCodec.decode("this is an illegal " + (char) 92 + (char) 116 + " escape", false); 80 | } 81 | 82 | @Test 83 | public void testEncoding() { 84 | assertThat(HeaderCodec.encode("hello", false)).isEqualTo("hello"); 85 | assertThat(HeaderCodec.encode("h:e", false)).isEqualTo("h\\ce"); 86 | assertThat(HeaderCodec.encode(":-)", false)).isEqualTo("\\c-)"); 87 | assertThat(HeaderCodec.encode(")-:", false)).isEqualTo(")-\\c"); 88 | assertThat(HeaderCodec.encode("multi\nline", false)).isEqualTo("multi" + (char) 92 + (char) 110 + "line"); 89 | assertThat(HeaderCodec.encode("multi\r\nline", false)).isEqualTo("multi" + (char) 92 + (char) 114 90 | + (char) 92 + (char) 110 91 | + "line"); 92 | assertThat(HeaderCodec.encode("test\\slash", false)).isEqualTo("test" + (char) 92 + (char) 92 93 | + "slash"); 94 | } 95 | 96 | @Test 97 | public void testEncodingOnConnectOrConnectedFrames() { 98 | assertThat(HeaderCodec.encode("hello", true)).isEqualTo("hello"); 99 | assertThat(HeaderCodec.encode("h:e", true)).isEqualTo("h:e"); 100 | assertThat(HeaderCodec.encode(":-)", true)).isEqualTo(":-)"); 101 | assertThat(HeaderCodec.encode(")-:", true)).isEqualTo(")-:"); 102 | assertThat(HeaderCodec.encode("multi\nline", true)).isEqualTo("multi\nline"); 103 | assertThat(HeaderCodec.encode("multi\r\nline", true)).isEqualTo("multi\r\nline"); 104 | // Slash is encoded. 105 | assertThat(HeaderCodec.encode("test\\slash", true)).isEqualTo("test" + (char) 92 + (char) 92 106 | + "slash"); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/impl/Helper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.impl; 18 | 19 | import io.vertx.ext.stomp.Destination; 20 | 21 | import java.util.List; 22 | 23 | /** 24 | * Some helper methods. 25 | * 26 | * @author Clement Escoffier 27 | */ 28 | public class Helper { 29 | public static boolean hasDestination(List destinations, String dest) { 30 | for (Destination d : destinations) { 31 | if (d.matches(dest)) { 32 | return true; 33 | } 34 | } 35 | return false; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/impl/LoadTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.impl; 18 | 19 | import com.jayway.awaitility.Awaitility; 20 | import io.vertx.core.AsyncResult; 21 | import io.vertx.core.Handler; 22 | import io.vertx.core.Vertx; 23 | import io.vertx.core.buffer.Buffer; 24 | import io.vertx.core.json.JsonObject; 25 | import io.vertx.ext.stomp.*; 26 | import io.vertx.ext.unit.TestContext; 27 | import io.vertx.ext.unit.junit.VertxUnitRunner; 28 | import org.junit.After; 29 | import org.junit.Before; 30 | import org.junit.Ignore; 31 | import org.junit.Test; 32 | import org.junit.runner.RunWith; 33 | 34 | import java.util.ArrayList; 35 | import java.util.List; 36 | import java.util.concurrent.TimeUnit; 37 | import java.util.concurrent.atomic.AtomicInteger; 38 | 39 | /** 40 | * A couple of load tests that needs to be launched manually. 41 | * 42 | * @author Clement Escoffier 43 | */ 44 | @RunWith(VertxUnitRunner.class) 45 | @Ignore("Manual launch only") 46 | public class LoadTest { 47 | 48 | private Vertx vertx; 49 | private StompServer server; 50 | 51 | private List clients = new ArrayList<>(); 52 | private List acked = new ArrayList<>(); 53 | private List nacked = new ArrayList<>(); 54 | 55 | 56 | @Before 57 | public void setUp(TestContext context) { 58 | vertx = Vertx.vertx(); 59 | server = StompServer.create(vertx, new StompServerOptions().setHeartbeat( 60 | new JsonObject().put("x", 0).put("y", 0))) 61 | .handler(StompServerHandler.create(vertx) 62 | .onAckHandler(acknowledgement -> acked.addAll(acknowledgement.frames())) 63 | .onNackHandler(acknowledgement -> nacked.addAll(acknowledgement.frames()))); 64 | server.listen().onComplete(context.asyncAssertSuccess()); 65 | } 66 | 67 | @After 68 | public void tearDown(TestContext context) { 69 | System.out.println("Closing clients"); 70 | clients.forEach(StompClient::close); 71 | System.out.println("Closing server"); 72 | server.close().onComplete(context.asyncAssertSuccess()); 73 | vertx.close().onComplete(context.asyncAssertSuccess()); 74 | } 75 | 76 | private void client(Handler> handler) { 77 | StompClient client = StompClient.create(vertx); 78 | clients.add(client); 79 | client.connect().onComplete(handler); 80 | } 81 | 82 | @Test 83 | public void testWithMultiplePublisherAndConsumerOnOneDestination() { 84 | int publisher = 100; 85 | int consumer = 100; 86 | String dest = "/queue"; 87 | int numberOfMessagePerPublisher = 100; 88 | 89 | AtomicInteger received = new AtomicInteger(); 90 | AtomicInteger started = new AtomicInteger(); 91 | 92 | // Init consumers 93 | for (int i = 0; i < consumer; i++) { 94 | 95 | client((ar -> { 96 | if (ar.failed()) { 97 | System.err.println("Consumer connection error " + ar.cause().getMessage()); 98 | ar.cause().printStackTrace(); 99 | return; 100 | } 101 | ar.result() 102 | .errorHandler(frame -> System.err.println("Consumer Error : " + frame)) 103 | .subscribe(dest, frame -> { 104 | received.incrementAndGet(); 105 | }).onComplete(frame -> started.incrementAndGet() 106 | ); 107 | })); 108 | } 109 | 110 | Awaitility.waitAtMost(1, TimeUnit.MINUTES).until(() -> started.get() == consumer); 111 | 112 | long begin = System.currentTimeMillis(); 113 | // Init producers 114 | AtomicInteger global = new AtomicInteger(); 115 | for (int i = 0; i < publisher; i++) { 116 | String p = Integer.toString(i); 117 | client((ar -> { 118 | final StompClientConnection connection = ar.result(); 119 | connection.errorHandler(frame -> System.err.println("Producer Error : " + frame)); 120 | AtomicInteger count = new AtomicInteger(); 121 | vertx.setPeriodic(10, id -> { 122 | connection.send(dest, Buffer.buffer("Hello")); 123 | global.incrementAndGet(); 124 | if (count.incrementAndGet() == numberOfMessagePerPublisher) { 125 | vertx.cancelTimer(id); 126 | connection.disconnect(); 127 | } 128 | }); 129 | })); 130 | } 131 | 132 | Awaitility.await().atMost(1, TimeUnit.MINUTES).until(() -> { 133 | int size = received.get(); 134 | return size == publisher * numberOfMessagePerPublisher * consumer; 135 | }); 136 | 137 | long end = System.currentTimeMillis(); 138 | System.out.println(received.get() + " messages delivered in " + (end - begin) + " ms"); 139 | } 140 | 141 | 142 | @Test 143 | public void testWithASinglePublisherAndMultipleConsumersOnOneDestination() { 144 | int publisher = 1; 145 | int consumer = 200; 146 | String dest = "/queue"; 147 | int numberOfMessagePerPublisher = 800; 148 | 149 | AtomicInteger received = new AtomicInteger(); 150 | AtomicInteger started = new AtomicInteger(); 151 | 152 | // Init consumers 153 | for (int i = 0; i < consumer; i++) { 154 | 155 | client((ar -> { 156 | if (ar.failed()) { 157 | System.err.println("Consumer connection error " + ar.cause().getMessage()); 158 | ar.cause().printStackTrace(); 159 | return; 160 | } 161 | ar.result() 162 | .errorHandler(frame -> System.err.println("Consumer Error : " + frame)) 163 | .subscribe(dest, frame -> { 164 | received.incrementAndGet(); 165 | }).onComplete(frame -> started.incrementAndGet() 166 | ); 167 | })); 168 | } 169 | Awaitility.waitAtMost(1, TimeUnit.MINUTES).until(() -> started.get() == consumer); 170 | 171 | long begin = System.currentTimeMillis(); 172 | // Init producers 173 | AtomicInteger global = new AtomicInteger(); 174 | for (int i = 0; i < publisher; i++) { 175 | String p = Integer.toString(i); 176 | client((ar -> { 177 | final StompClientConnection connection = ar.result(); 178 | connection.errorHandler(frame -> System.err.println("Producer Error : " + frame)); 179 | AtomicInteger count = new AtomicInteger(); 180 | vertx.setPeriodic(10, id -> { 181 | connection.send(dest, Buffer.buffer("Hello")); 182 | global.incrementAndGet(); 183 | if (count.incrementAndGet() == numberOfMessagePerPublisher) { 184 | vertx.cancelTimer(id); 185 | } 186 | }); 187 | })); 188 | } 189 | 190 | Awaitility.await().atMost(1, TimeUnit.MINUTES).until(() -> { 191 | int size = received.get(); 192 | return size == publisher * numberOfMessagePerPublisher * consumer; 193 | }); 194 | 195 | long end = System.currentTimeMillis(); 196 | System.out.println(received.get() + " messages delivered in " + (end - begin) + " ms"); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/impl/QueueManagingAcknowledgmentsFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.impl; 18 | 19 | import io.vertx.core.Vertx; 20 | import io.vertx.ext.stomp.Destination; 21 | import io.vertx.ext.stomp.DestinationFactory; 22 | 23 | /** 24 | * An implementation of destination factory managing acknowledgements. 25 | * 26 | * @author Clement Escoffier 27 | */ 28 | public class QueueManagingAcknowledgmentsFactory implements DestinationFactory { 29 | 30 | 31 | /** 32 | * Creates a destination for the given address. 33 | * 34 | * @param vertx the vert.x instance used by the STOMP server. 35 | * @param name the destination 36 | * @return the destination, {@code null} to reject the creation. 37 | * @see Destination#topic(Vertx, String) 38 | * @see Destination#queue(Vertx, String) 39 | */ 40 | @Override 41 | public Destination create(Vertx vertx, String name) { 42 | return new QueueManagingAcknowledgments(vertx, name); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/impl/TestSendFailure.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.impl; 18 | 19 | import io.vertx.core.AsyncResult; 20 | import io.vertx.core.Handler; 21 | import io.vertx.core.Vertx; 22 | import io.vertx.core.buffer.Buffer; 23 | import io.vertx.ext.stomp.Frame; 24 | import io.vertx.ext.stomp.StompClient; 25 | import io.vertx.ext.stomp.StompServer; 26 | import io.vertx.ext.stomp.StompServerHandler; 27 | import io.vertx.ext.unit.TestContext; 28 | import io.vertx.ext.unit.junit.VertxUnitRunner; 29 | import org.junit.After; 30 | import org.junit.Before; 31 | import org.junit.Test; 32 | import org.junit.runner.RunWith; 33 | 34 | /** 35 | * Checks the {@code ACK} and {@code NACK} handling. 36 | * 37 | * @author Clement Escoffier 38 | */ 39 | @RunWith(VertxUnitRunner.class) 40 | public class TestSendFailure { 41 | 42 | private Vertx vertx; 43 | private StompServer server; 44 | private StompClient client; 45 | 46 | @Before 47 | public void setUp(TestContext context) { 48 | vertx = Vertx.vertx(); 49 | server = StompServer.create(vertx) 50 | .handler(StompServerHandler.create(vertx) 51 | .sendHandler(ignore -> {}) 52 | .receivedFrameHandler(frame -> { 53 | if ("/queue".equalsIgnoreCase(frame.frame().getDestination())) { 54 | server.close(); 55 | } 56 | }) 57 | .destinationFactory(new QueueManagingAcknowledgmentsFactory())); 58 | server.listen().onComplete(context.asyncAssertSuccess()); 59 | } 60 | 61 | @After 62 | public void tearDown(TestContext context) { 63 | if (client != null) { 64 | client.close(); 65 | } 66 | server.close().onComplete(context.asyncAssertSuccess()); 67 | vertx.close().onComplete(context.asyncAssertSuccess()); 68 | } 69 | 70 | 71 | @Test 72 | public void testSimpleAck(TestContext ctx) { 73 | Handler> receiptHandler = ctx.asyncAssertFailure(); 74 | client = StompClient.create(vertx); 75 | client.connect().onComplete(ctx.asyncAssertSuccess(conn -> { 76 | conn.send("/queue", Buffer.buffer("Hello")).onComplete(receiptHandler); 77 | })); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/integration/AbstractClientIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.integration; 18 | 19 | import io.vertx.core.Vertx; 20 | import io.vertx.core.buffer.Buffer; 21 | import io.vertx.ext.stomp.*; 22 | import io.vertx.stomp.tests.impl.AsyncLock; 23 | import org.assertj.core.api.Assertions; 24 | import org.junit.After; 25 | import org.junit.Before; 26 | import org.junit.Test; 27 | 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | import java.util.concurrent.TimeUnit; 31 | import java.util.concurrent.atomic.AtomicReference; 32 | 33 | import static com.jayway.awaitility.Awaitility.await; 34 | import static org.assertj.core.api.Assertions.assertThat; 35 | import static org.assertj.core.data.MapEntry.entry; 36 | 37 | /** 38 | * @author Clement Escoffier 39 | */ 40 | public abstract class AbstractClientIT { 41 | 42 | 43 | protected Vertx vertx; 44 | protected List clients = new ArrayList<>(); 45 | 46 | @Before 47 | public void setUp() { 48 | vertx = Vertx.vertx(); 49 | } 50 | 51 | @After 52 | public void tearDown() { 53 | clients.forEach(StompClient::close); 54 | AsyncLock lock = new AsyncLock<>(); 55 | vertx.close().onComplete(lock.handler()); 56 | lock.waitForSuccess(); 57 | } 58 | 59 | public abstract StompClientOptions getOptions(); 60 | 61 | public abstract StompClientOptions getOptionsWithSSL(); 62 | 63 | public String getDestination() { 64 | return "box"; 65 | } 66 | 67 | /** 68 | * The test is the following: 69 | * 1. Create a client subscribing to the "box" destination 70 | * 2. Create another client sending messages to the "box" destination 71 | * 3. Ensure the the client 1 receives the message. 72 | */ 73 | @Test 74 | public void testRegularConnection() { 75 | AtomicReference receiver = new AtomicReference<>(); 76 | AtomicReference sender = new AtomicReference<>(); 77 | 78 | AtomicReference frame = new AtomicReference<>(); 79 | 80 | // Step 1. 81 | StompClient client1 = StompClient.create(vertx, getOptions()); 82 | client1.connect().onComplete(connection -> { 83 | if (connection.failed()) { 84 | connection.cause().printStackTrace(); 85 | } else { 86 | receiver.set(connection.result()); 87 | connection.result().subscribe(getDestination(), frame::set); 88 | } 89 | }); 90 | clients.add(client1); 91 | 92 | await().atMost(10, TimeUnit.SECONDS).until(() -> receiver.get() != null); 93 | 94 | // Step 2. 95 | StompClient client2 = StompClient.create(vertx, getOptions()); 96 | client2.connect().onComplete(connection -> { 97 | if (connection.failed()) { 98 | connection.cause().printStackTrace(); 99 | } else { 100 | sender.set(connection.result()); 101 | connection.result().send(getDestination(), Buffer.buffer("hello from vert.x")); 102 | } 103 | }); 104 | clients.add(client2); 105 | await().atMost(10, TimeUnit.SECONDS).until(() -> sender.get() != null); 106 | 107 | // Step 3. 108 | await().atMost(10, TimeUnit.SECONDS).until(() -> frame.get() != null); 109 | assertThat(frame.get().getCommand()).isEqualTo(Command.MESSAGE); 110 | assertThat(frame.get().getHeaders()) 111 | .contains(entry("content-length", "17")) 112 | .containsKeys("destination", "message-id", "subscription"); 113 | Assertions.assertThat(frame.get().getBodyAsString()).isEqualToIgnoringCase("hello from vert.x"); 114 | } 115 | 116 | /** 117 | * Same test as the previous one, but using SSL. 118 | */ 119 | @Test 120 | public void testSSLConnection() { 121 | if (getOptionsWithSSL() == null) { 122 | // SSL test disabled. 123 | return; 124 | } 125 | AtomicReference receiver = new AtomicReference<>(); 126 | AtomicReference sender = new AtomicReference<>(); 127 | 128 | AtomicReference frame = new AtomicReference<>(); 129 | 130 | // Step 1. 131 | StompClient client1 = StompClient.create(vertx, getOptionsWithSSL()); 132 | client1.connect().onComplete(connection -> { 133 | if (connection.failed()) { 134 | connection.cause().printStackTrace(); 135 | } else { 136 | receiver.set(connection.result()); 137 | connection.result().subscribe(getDestination(), frame::set); 138 | } 139 | }); 140 | clients.add(client1); 141 | 142 | await().atMost(10, TimeUnit.SECONDS).until(() -> receiver.get() != null); 143 | 144 | // Step 2. 145 | StompClient client2 = StompClient.create(vertx, getOptionsWithSSL()); 146 | client2.connect().onComplete(connection -> { 147 | if (connection.failed()) { 148 | connection.cause().printStackTrace(); 149 | } else { 150 | sender.set(connection.result()); 151 | connection.result().send(getDestination(), Buffer.buffer("hello from vert.x")); 152 | } 153 | }); 154 | clients.add(client2); 155 | await().atMost(10, TimeUnit.SECONDS).until(() -> sender.get() != null); 156 | 157 | // Step 3. 158 | await().atMost(10, TimeUnit.SECONDS).until(() -> frame.get() != null); 159 | assertThat(frame.get().getCommand()).isEqualTo(Command.MESSAGE); 160 | assertThat(frame.get().getHeaders()) 161 | .contains(entry("content-length", "17")) 162 | .containsKeys("destination", "message-id", "subscription"); 163 | assertThat(frame.get().getBodyAsString()).isEqualToIgnoringCase("hello from vert.x"); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/integration/ActivemqDockerIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.integration; 18 | 19 | import io.vertx.ext.stomp.StompClientOptions; 20 | import org.junit.ClassRule; 21 | import org.testcontainers.containers.GenericContainer; 22 | 23 | /** 24 | * Checks that our clients can connect and interact with ActiveMQ. 25 | * 26 | * @author Clement Escoffier 27 | */ 28 | public class ActivemqDockerIT extends AbstractClientIT { 29 | 30 | 31 | @ClassRule 32 | public static final GenericContainer container 33 | = new GenericContainer("rmohr/activemq:5.15.6-alpine") 34 | .withExposedPorts(61613); 35 | 36 | @Override 37 | public StompClientOptions getOptions() { 38 | return new StompClientOptions() 39 | .setHost(container.getContainerIpAddress()) 40 | .setPort(container.getMappedPort(61613)); 41 | } 42 | 43 | @Override 44 | public StompClientOptions getOptionsWithSSL() { 45 | // It would require configuring the SSL on activeMQ and managing the certificates 46 | return null; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/integration/ArtemisHornetQDockerIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.integration; 18 | 19 | import io.vertx.ext.stomp.StompClientOptions; 20 | import org.junit.ClassRule; 21 | import org.testcontainers.containers.GenericContainer; 22 | 23 | /** 24 | * Checks that our clients can connect and interact with ActiveMQ. This test uses the HornetQ port (5445). 25 | * 26 | * @author Clement Escoffier 27 | */ 28 | public class ArtemisHornetQDockerIT extends AbstractClientIT { 29 | 30 | @ClassRule 31 | public static final GenericContainer container 32 | = new GenericContainer("vromero/activemq-artemis:2.6.3-alpine") 33 | .withExposedPorts(61613) 34 | .withExposedPorts(5445); 35 | 36 | @Override 37 | public StompClientOptions getOptions() { 38 | return new StompClientOptions() 39 | .setHost(container.getContainerIpAddress()) 40 | .setPort(container.getMappedPort(5445)) 41 | .setLogin("artemis") 42 | .setPasscode("simetraehcapa"); 43 | } 44 | 45 | @Override 46 | public StompClientOptions getOptionsWithSSL() { 47 | return null; 48 | } 49 | 50 | /** 51 | * Artemis queue requires the "jms.queue" prefix. 52 | * 53 | * @return the destination. 54 | */ 55 | @Override 56 | public String getDestination() { 57 | return "jms.queue.box-hornetq"; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/integration/ArtemisStompDockerIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.integration; 18 | 19 | import io.vertx.ext.stomp.StompClientOptions; 20 | import org.junit.ClassRule; 21 | import org.testcontainers.containers.GenericContainer; 22 | 23 | /** 24 | * Checks that our clients can connect and interact with ActiveMQ. This test uses the STOMP port. 25 | * 26 | * @author Clement Escoffier 27 | */ 28 | public class ArtemisStompDockerIT extends AbstractClientIT { 29 | 30 | @ClassRule 31 | public static final GenericContainer container 32 | = new GenericContainer("vromero/activemq-artemis:2.6.3-alpine") 33 | .withExposedPorts(61613) 34 | .withExposedPorts(61616); 35 | 36 | @Override 37 | public StompClientOptions getOptions() { 38 | return new StompClientOptions() 39 | .setHost(container.getContainerIpAddress()) 40 | .setPort(container.getMappedPort(61616)) 41 | .setLogin("artemis") 42 | .setPasscode("simetraehcapa"); 43 | } 44 | 45 | @Override 46 | public StompClientOptions getOptionsWithSSL() { 47 | return null; 48 | } 49 | 50 | /** 51 | * Artemis queue requires the "jms.queue" prefix. 52 | * 53 | * @return the destination. 54 | */ 55 | @Override 56 | public String getDestination() { 57 | return "jms.queue.box"; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/integration/RabbitMQDockerIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.integration; 18 | 19 | import io.vertx.ext.stomp.StompClientOptions; 20 | import org.junit.ClassRule; 21 | import org.testcontainers.containers.BindMode; 22 | import org.testcontainers.containers.GenericContainer; 23 | 24 | /** 25 | * Checks that our clients can connect and interact with ActiveMQ. 26 | * 27 | * @author Clement Escoffier 28 | */ 29 | public class RabbitMQDockerIT extends AbstractClientIT { 30 | 31 | @ClassRule 32 | public static final GenericContainer container 33 | = new GenericContainer("rabbitmq:latest") 34 | .withClasspathResourceMapping("integration/rabbitmq/enabled_plugins", 35 | "/etc/rabbitmq/enabled_plugins", BindMode.READ_ONLY) 36 | .withExposedPorts(61613); 37 | 38 | @Override 39 | public StompClientOptions getOptions() { 40 | return new StompClientOptions() 41 | .setHost(container.getContainerIpAddress()) 42 | .setPort(container.getMappedPort(61613)) 43 | .setBypassHostHeader(true); 44 | } 45 | 46 | @Override 47 | public StompClientOptions getOptionsWithSSL() { 48 | return null; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/integration/StiltsClientIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.integration; 18 | 19 | import io.vertx.core.Vertx; 20 | import io.vertx.ext.stomp.StompServer; 21 | import io.vertx.ext.stomp.StompServerHandler; 22 | import io.vertx.stomp.tests.impl.AsyncLock; 23 | import org.junit.After; 24 | import org.junit.Before; 25 | import org.junit.Test; 26 | import org.projectodd.stilts.stomp.*; 27 | import org.projectodd.stilts.stomp.client.ClientSubscription; 28 | import org.projectodd.stilts.stomp.client.StompClient; 29 | 30 | import javax.net.ssl.SSLException; 31 | import java.net.URISyntaxException; 32 | import java.util.concurrent.TimeUnit; 33 | import java.util.concurrent.TimeoutException; 34 | import java.util.concurrent.atomic.AtomicReference; 35 | 36 | import static com.jayway.awaitility.Awaitility.await; 37 | import static org.assertj.core.api.Assertions.assertThat; 38 | 39 | /** 40 | * Checks that the Stilts client can connect to our STOMP server. 41 | * 42 | * @author Clement Escoffier 43 | */ 44 | public class StiltsClientIT { 45 | 46 | protected Vertx vertx; 47 | protected StompServer server; 48 | 49 | @Before 50 | public void setUp() { 51 | AsyncLock lock = new AsyncLock<>(); 52 | vertx = Vertx.vertx(); 53 | server = StompServer.create(vertx).handler(StompServerHandler.create(vertx)); 54 | server.listen().onComplete(lock.handler()); 55 | lock.waitForSuccess(); 56 | } 57 | 58 | @After 59 | public void tearDown() { 60 | AsyncLock lock = new AsyncLock<>(); 61 | server.close().onComplete(lock.handler()); 62 | lock.waitForSuccess(); 63 | 64 | lock = new AsyncLock<>(); 65 | vertx.close().onComplete(lock.handler()); 66 | lock.waitForSuccess(); 67 | } 68 | 69 | @Test 70 | public void test() throws URISyntaxException, InterruptedException, TimeoutException, StompException, SSLException { 71 | StompClient client1 = new StompClient("stomp://localhost:61613"); 72 | StompClient client2 = new StompClient("stomp://localhost:61613"); 73 | client1.connect(); 74 | client2.connect(); 75 | 76 | AtomicReference frame = new AtomicReference<>(); 77 | 78 | ClientSubscription subscription1 = 79 | client1.subscribe( "box" ) 80 | .withMessageHandler(frame::set) 81 | .start(); 82 | 83 | Headers headers = new DefaultHeaders(); 84 | headers.put("header", "value"); 85 | client2.send(StompMessages.createStompMessage("box", headers, "hello !")); 86 | 87 | await().atMost(10, TimeUnit.SECONDS).until(()-> frame.get() != null); 88 | 89 | assertThat(frame.get().getDestination()).isEqualTo("box"); 90 | assertThat(frame.get().getContentAsString()).isEqualTo("hello !"); 91 | assertThat(frame.get().getHeaders().get("header")).isEqualTo("value"); 92 | assertThat(frame.get().getHeaders().get("message-id")).isNotNull(); 93 | assertThat(frame.get().getHeaders().get("subscription")).isNotNull(); 94 | 95 | subscription1.unsubscribe(); 96 | client1.disconnect(); 97 | client2.disconnect(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/integration/StompConsumer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.integration; 18 | 19 | import io.vertx.core.Future; 20 | import io.vertx.core.VerticleBase; 21 | import io.vertx.ext.stomp.StompClient; 22 | import io.vertx.ext.stomp.StompClientOptions; 23 | 24 | /** 25 | * A verticle subscribing to a STOMP destination. 26 | * 27 | * @author Clement Escoffier 28 | */ 29 | public class StompConsumer extends VerticleBase { 30 | 31 | private StompClient client; 32 | 33 | @Override 34 | public Future start() throws Exception { 35 | System.out.println("Starting client"); 36 | client = StompClient.create(vertx, new StompClientOptions(config())); 37 | return client 38 | .connect() 39 | .compose(conn -> conn 40 | .subscribe("/queue/event", frame -> System.out.println("Frame received : " + frame.getBodyAsString()))); 41 | } 42 | 43 | @Override 44 | public Future stop() throws Exception { 45 | return client.close(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/integration/StompIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.integration; 18 | 19 | import io.vertx.core.DeploymentOptions; 20 | import io.vertx.core.Vertx; 21 | import io.vertx.core.json.JsonObject; 22 | 23 | /** 24 | * Runs one of the support STOMP Broker, from 'src/test/resources/integration', configure the test, and check 25 | * messages are published and received. 26 | * 27 | * @author Clement Escoffier 28 | */ 29 | public class StompIntegrationTests { 30 | 31 | public static void main(String args[]) { 32 | String host = "192.168.59.103"; // Change to localhost on linux. 33 | int port = 61613; 34 | boolean useHostHeader = true; // Set it to false for RabbitMQ 35 | 36 | JsonObject config = new JsonObject() 37 | .put("host", host) 38 | .put("port", port) 39 | .put("bypassHostHeader", !useHostHeader); 40 | 41 | Vertx vertx = Vertx.vertx(); 42 | vertx.deployVerticle(StompConsumer.class.getName(), new DeploymentOptions().setConfig(config).setInstances(2)); 43 | vertx.deployVerticle(StompPublisher.class.getName(), new DeploymentOptions().setConfig(config)); 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/integration/StompPublisher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.integration; 18 | 19 | import io.vertx.core.Future; 20 | import io.vertx.core.VerticleBase; 21 | import io.vertx.core.buffer.Buffer; 22 | import io.vertx.ext.stomp.StompClient; 23 | import io.vertx.ext.stomp.StompClientOptions; 24 | 25 | /** 26 | * A verticle sending messages to a STOMP destination. 27 | * 28 | * @author Clement Escoffier 29 | */ 30 | public class StompPublisher extends VerticleBase { 31 | 32 | private StompClient client; 33 | 34 | @Override 35 | public Future start() throws Exception { 36 | System.out.println("Starting publisher"); 37 | client = StompClient.create(vertx, new StompClientOptions(config())); 38 | return client 39 | .connect() 40 | .onSuccess(res -> { 41 | vertx.setPeriodic(5000, l -> res.send("/queue/event", Buffer.buffer("Hello")).onComplete(frame -> { 42 | System.out.println("Receipt received"); 43 | })); 44 | }); 45 | } 46 | 47 | @Override 48 | public Future stop() throws Exception { 49 | return client.close(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/verticles/MultiInstanceSubscriptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.verticles; 18 | 19 | import com.jayway.awaitility.Awaitility; 20 | import io.vertx.core.DeploymentOptions; 21 | import io.vertx.core.Vertx; 22 | import io.vertx.core.json.JsonObject; 23 | import io.vertx.ext.unit.TestContext; 24 | import org.junit.After; 25 | import org.junit.Before; 26 | import org.junit.Test; 27 | import org.junit.runner.RunWith; 28 | 29 | import java.util.concurrent.TimeUnit; 30 | 31 | /** 32 | * @author Clement Escoffier 33 | */ 34 | @RunWith(io.vertx.ext.unit.junit.VertxUnitRunner.class) 35 | public class MultiInstanceSubscriptionTest { 36 | 37 | private Vertx vertx; 38 | 39 | @Before 40 | public void setUp() { 41 | ReceiverStompClient.FRAMES.clear(); 42 | vertx = Vertx.vertx(); 43 | } 44 | 45 | @After 46 | public void tearDown() { 47 | vertx.close().await(); 48 | } 49 | 50 | @Test 51 | public void testThatTopicSubscriptionsAreShared() { 52 | vertx.deployVerticle(StompServerVerticle::new, new DeploymentOptions().setInstances(3)).await(); 53 | // Deploy the clients. 54 | vertx.deployVerticle(ReceiverStompClient::new, new DeploymentOptions().setInstances(3)).await(); 55 | vertx.deployVerticle(TxSenderStompClient::new, new DeploymentOptions().setInstances(2)).await(); 56 | Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> ReceiverStompClient.FRAMES.size() == 5 * 3 * 2); 57 | } 58 | 59 | @Test 60 | public void testThatQueueSubscriptionsAreShared(TestContext context) { 61 | vertx.deployVerticle(StompServerVerticle::new, new DeploymentOptions() 62 | .setConfig(new JsonObject().put("useQueue", true)) 63 | .setInstances(3)).await(); 64 | // Deploy the clients. 65 | vertx.deployVerticle(ReceiverStompClient::new, new DeploymentOptions().setInstances(3)).await(); 66 | vertx.deployVerticle(TxSenderStompClient::new, new DeploymentOptions().setInstances(2)).await(); 67 | 68 | Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> ReceiverStompClient.FRAMES.size() == 5 * 2); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/verticles/MultiInstanceTransactionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.verticles; 18 | 19 | import com.jayway.awaitility.Awaitility; 20 | import io.vertx.core.DeploymentOptions; 21 | import io.vertx.core.Vertx; 22 | import org.junit.After; 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | import org.junit.runner.RunWith; 26 | 27 | import java.util.concurrent.TimeUnit; 28 | 29 | /** 30 | * @author Clement Escoffier 31 | */ 32 | @RunWith(io.vertx.ext.unit.junit.VertxUnitRunner.class) 33 | public class MultiInstanceTransactionTest { 34 | 35 | private Vertx vertx; 36 | 37 | @Before 38 | public void setUp() { 39 | ReceiverStompClient.FRAMES.clear(); 40 | vertx = Vertx.vertx(); 41 | } 42 | 43 | @After 44 | public void tearDown() { 45 | vertx.close().await(); 46 | } 47 | 48 | @Test 49 | public void testThatTransactionAreNotShared() { 50 | vertx.deployVerticle(StompServerVerticle::new, new DeploymentOptions().setInstances(3)).await(); 51 | // Deploy the clients. 52 | vertx.deployVerticle(new ReceiverStompClient()).await(); 53 | vertx.deployVerticle(new TxSenderStompClient()).await(); 54 | Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> ReceiverStompClient.FRAMES.size() == 5); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/verticles/ReceiverStompClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.verticles; 18 | 19 | import io.vertx.core.Future; 20 | import io.vertx.core.VerticleBase; 21 | import io.vertx.ext.stomp.Frame; 22 | import io.vertx.ext.stomp.StompClient; 23 | 24 | import java.util.List; 25 | import java.util.concurrent.CopyOnWriteArrayList; 26 | 27 | /** 28 | * A STOMP client receiving messages. 29 | * 30 | * @author Clement Escoffier 31 | */ 32 | public class ReceiverStompClient extends VerticleBase { 33 | 34 | public static final List FRAMES = new CopyOnWriteArrayList<>(); 35 | 36 | @Override 37 | public Future start() throws Exception { 38 | return StompClient 39 | .create(vertx) 40 | .connect() 41 | .compose(connection -> connection 42 | .receivedFrameHandler(frame -> System.out.println("Client receiving:\n" + frame)) 43 | .writingFrameHandler(frame -> System.out.println("Client sending:\n" + frame)) 44 | .subscribe("/queue", FRAMES::add)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/verticles/StompServerVerticle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.verticles; 18 | 19 | import io.vertx.core.Future; 20 | import io.vertx.core.VerticleBase; 21 | import io.vertx.ext.stomp.Destination; 22 | import io.vertx.ext.stomp.StompServer; 23 | import io.vertx.ext.stomp.StompServerHandler; 24 | 25 | /** 26 | * A verticle starting a STOMP server. 27 | * 28 | * @author Clement Escoffier 29 | */ 30 | public class StompServerVerticle extends VerticleBase { 31 | 32 | 33 | private StompServer server; 34 | 35 | @Override 36 | public Future start() throws Exception { 37 | server = StompServer.create(vertx).handler(StompServerHandler.create(vertx) 38 | .destinationFactory((vertx, name) -> { 39 | if (config().getBoolean("useQueue", false)) { 40 | return Destination.queue(vertx, name); 41 | } else { 42 | return Destination.topic(vertx, name); 43 | } 44 | })); 45 | return server.listen(); 46 | } 47 | 48 | @Override 49 | public Future stop() throws Exception { 50 | return server.close(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/stomp/tests/verticles/TxSenderStompClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 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 | package io.vertx.stomp.tests.verticles; 18 | 19 | import io.vertx.core.Future; 20 | import io.vertx.core.VerticleBase; 21 | import io.vertx.core.buffer.Buffer; 22 | import io.vertx.ext.stomp.Frame; 23 | import io.vertx.ext.stomp.StompClient; 24 | import io.vertx.ext.stomp.utils.Headers; 25 | 26 | /** 27 | * A verticle connecting to a STOMP server and sending message in a transaction. 28 | * 29 | * @author Clement Escoffier 30 | */ 31 | public class TxSenderStompClient extends VerticleBase { 32 | 33 | 34 | @Override 35 | public Future start() throws Exception { 36 | return StompClient.create(vertx) 37 | .connect() 38 | .onSuccess(connection -> { 39 | connection.errorHandler(frame -> System.err.println("Tx Sender has received an ERROR frame : \n" + frame)); 40 | connection.beginTX("my-transaction"); 41 | connection.send("/queue", Headers.create(Frame.TRANSACTION, "my-transaction"), Buffer.buffer("Hello")); 42 | connection.send("/queue", Headers.create(Frame.TRANSACTION, "my-transaction"), Buffer.buffer("My")); 43 | connection.send("/queue", Headers.create(Frame.TRANSACTION, "my-transaction"), Buffer.buffer("Name")); 44 | connection.send("/queue", Headers.create(Frame.TRANSACTION, "my-transaction"), Buffer.buffer("Is")); 45 | connection.send("/queue", Headers.create(Frame.TRANSACTION, "my-transaction"), Buffer.buffer("Vert.x")); 46 | connection.commit("my-transaction"); 47 | connection.disconnect(); 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/resources/integration/rabbitmq/enabled_plugins: -------------------------------------------------------------------------------- 1 | [rabbitmq_federation_management,rabbitmq_management,rabbitmq_stomp]. 2 | 3 | -------------------------------------------------------------------------------- /src/test/resources/test-auth.properties: -------------------------------------------------------------------------------- 1 | user.admin = admin --------------------------------------------------------------------------------