├── conf ├── starlight-rabbitmq.conf └── log4j2.xml ├── .gitignore ├── rabbitmq-tests ├── src │ └── test │ │ ├── resources │ │ ├── authentication │ │ │ ├── keystoretls │ │ │ │ ├── broker.keystore.jks │ │ │ │ ├── client.keystore.jks │ │ │ │ ├── broker.truststore.jks │ │ │ │ └── client.truststore.jks │ │ │ └── tls │ │ │ │ ├── client-key.pem │ │ │ │ ├── server-key.pem │ │ │ │ ├── client-cert.pem │ │ │ │ ├── server-cert.pem │ │ │ │ ├── cacert.pem │ │ │ │ └── other-cacert.pem │ │ └── log4j2.xml │ │ └── java │ │ └── com │ │ └── datastax │ │ └── oss │ │ └── starlight │ │ └── rabbitmqtests │ │ ├── javaclient │ │ └── functional │ │ │ ├── InvalidAcks.java │ │ │ ├── RequeueOnChannelClose.java │ │ │ ├── RequeueOnConnectionClose.java │ │ │ ├── ExchangeDeletePredeclared.java │ │ │ ├── DoubleDeletion.java │ │ │ ├── BasicConsume.java │ │ │ ├── Heartbeat.java │ │ │ ├── ExchangeEquivalenceBase.java │ │ │ ├── InvalidAcksBase.java │ │ │ ├── ExceptionMessages.java │ │ │ ├── ExchangeDeleteIfUnused.java │ │ │ ├── AbstractRejectTest.java │ │ │ ├── NoRequeueOnCancel.java │ │ │ ├── DefaultExchange.java │ │ │ ├── BasicGet.java │ │ │ ├── Reject.java │ │ │ ├── ClusteredTestBase.java │ │ │ ├── ConnectionOpen.java │ │ │ ├── Recover.java │ │ │ └── Nowait.java │ │ ├── SystemTest.java │ │ ├── utils │ │ └── PulsarCluster.java │ │ ├── TokenAuthenticationIT.java │ │ └── AuthorizationIT.java └── pom.xml ├── starlight-rabbitmq └── src │ ├── main │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ ├── pulsar-protocol-handler.yml │ │ │ └── pulsar-proxy-extension.yml │ └── java │ │ └── com │ │ └── datastax │ │ └── oss │ │ └── starlight │ │ └── rabbitmq │ │ ├── metadata │ │ ├── ContextMetadata.java │ │ ├── BindingSetMetadata.java │ │ ├── VirtualHostMetadata.java │ │ ├── BindingMetadata.java │ │ ├── ExchangeMetadata.java │ │ └── QueueMetadata.java │ │ ├── MessageConsumerAssociation.java │ │ ├── BasicConsumeMessageConsumerAssociation.java │ │ ├── BasicGetMessageConsumerAssociation.java │ │ ├── Queue.java │ │ ├── HeadersExchange.java │ │ ├── AMQDataBlockEncoder.java │ │ ├── TopicExchangeUpdater.java │ │ ├── GatewayProtocolHandler.java │ │ ├── MessageUtils.java │ │ ├── GatewayProxyProtocolHandler.java │ │ ├── DirectExchange.java │ │ ├── UnacknowledgedMessageMap.java │ │ ├── FanoutExchange.java │ │ ├── Pre0_10CreditManager.java │ │ ├── GatewayServiceStarter.java │ │ ├── AbstractExchange.java │ │ ├── GatewayConfiguration.java │ │ ├── ExecutorProvider.java │ │ └── PulsarConsumer.java │ ├── test │ ├── resources │ │ └── findbugsExclude.xml │ └── java │ │ └── com │ │ └── datastax │ │ └── oss │ │ └── starlight │ │ └── rabbitmq │ │ ├── AMQDataBlockEncoderTest.java │ │ ├── MetricsTest.java │ │ └── Pre0_10CreditManagerTest.java │ └── assemble │ ├── all.xml │ └── jar.xml ├── .github └── workflows │ ├── release.yml │ └── github-ci.yml └── nar-tests ├── src └── test │ ├── resources │ └── ssl │ │ ├── proxy.key-pk8.pem │ │ ├── proxy.cert.pem │ │ └── ca.cert.pem │ └── java │ └── com │ └── datastax │ └── oss │ └── starlight │ └── rabbitmqnartests │ ├── PulsarContainer.java │ └── DockerTest.java └── pom.xml /conf/starlight-rabbitmq.conf: -------------------------------------------------------------------------------- 1 | brokerServiceURL = pulsar://localhost:6650 2 | brokerWebServiceURL = http://localhost:8080 3 | configurationStoreServers = localhost:2181 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .settings 2 | .DS_Store 3 | 4 | .idea 5 | *.iml 6 | .classpath 7 | .project 8 | 9 | .java-version 10 | 11 | target/ 12 | 13 | logs/ 14 | 15 | dependency-reduced-pom.xml 16 | 17 | 18 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/resources/authentication/keystoretls/broker.keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastax/starlight-for-rabbitmq/HEAD/rabbitmq-tests/src/test/resources/authentication/keystoretls/broker.keystore.jks -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/resources/authentication/keystoretls/client.keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastax/starlight-for-rabbitmq/HEAD/rabbitmq-tests/src/test/resources/authentication/keystoretls/client.keystore.jks -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/resources/authentication/keystoretls/broker.truststore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastax/starlight-for-rabbitmq/HEAD/rabbitmq-tests/src/test/resources/authentication/keystoretls/broker.truststore.jks -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/resources/authentication/keystoretls/client.truststore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastax/starlight-for-rabbitmq/HEAD/rabbitmq-tests/src/test/resources/authentication/keystoretls/client.truststore.jks -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/resources/META-INF/services/pulsar-protocol-handler.yml: -------------------------------------------------------------------------------- 1 | name: rabbitmq 2 | description: Starlight for RabbitMQ Protocol Handler 3 | handlerClass: com.datastax.oss.starlight.rabbitmq.GatewayProtocolHandler -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/resources/META-INF/services/pulsar-proxy-extension.yml: -------------------------------------------------------------------------------- 1 | name: rabbitmq 2 | description: Starlight for RabbitMQ Proxy Extension 3 | extensionClass: com.datastax.oss.starlight.rabbitmq.GatewayProxyProtocolHandler -------------------------------------------------------------------------------- /conf/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | create-release: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Set up JDK 17 15 | uses: actions/setup-java@v2 16 | with: 17 | java-version: '17' 18 | distribution: 'adopt' 19 | - name: Build with Maven 20 | run: mvn -B package -DskipTests 21 | - uses: ncipollo/release-action@v1 22 | with: 23 | artifacts: "starlight-rabbitmq/**/*.nar,starlight-rabbitmq/**/*-jar-with-dependencies.jar,starlight-rabbitmq/**/*.tar.gz" 24 | token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/metadata/ContextMetadata.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq.metadata; 17 | 18 | import java.util.Map; 19 | import java.util.concurrent.ConcurrentHashMap; 20 | 21 | public class ContextMetadata { 22 | Map vhosts = new ConcurrentHashMap<>(); 23 | 24 | public Map getVhosts() { 25 | return vhosts; 26 | } 27 | 28 | public void setVhosts(Map vhosts) { 29 | this.vhosts = vhosts; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /.github/workflows/github-ci.yml: -------------------------------------------------------------------------------- 1 | name: Build and test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Set up JDK 17 12 | uses: actions/setup-java@v2 13 | with: 14 | java-version: '17' 15 | distribution: 'adopt' 16 | 17 | - name: Cache local Maven repository 18 | uses: actions/cache@v3 19 | with: 20 | path: ~/.m2/repository 21 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 22 | restore-keys: | 23 | ${{ runner.os }}-maven- 24 | 25 | - name: License check 26 | run: mvn -B license:check 27 | 28 | - name: Build with Maven skipTests 29 | run: mvn -B clean install -DskipTests 30 | 31 | #- name: Style check 32 | # run: mvn -B checkstyle:check 33 | 34 | - name: Spotbugs check 35 | run: mvn -B spotbugs:check 36 | 37 | - name: Unit tests 38 | run: mvn -B test -pl starlight-rabbitmq 39 | 40 | - name: RabbitMQ Java client tests 41 | run: mvn -B integration-test -Dcurator-log-only-first-connection-issue-as-error-level=true -pl rabbitmq-tests 42 | 43 | - name: NAR tests 44 | run: mvn -B clean test -pl nar-tests -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/InvalidAcks.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import com.datastax.oss.starlight.rabbitmqtests.SystemTest; 19 | import java.io.IOException; 20 | import org.junit.experimental.categories.Category; 21 | 22 | @Category(SystemTest.class) 23 | public class InvalidAcks extends InvalidAcksBase { 24 | protected void select() throws IOException {} 25 | 26 | protected void commit() throws IOException {} 27 | } 28 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/RequeueOnChannelClose.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import java.io.IOException; 19 | import org.junit.Ignore; 20 | 21 | @Ignore("Flaky tests. To investigate") 22 | public class RequeueOnChannelClose extends RequeueOnClose { 23 | 24 | protected void open() throws IOException { 25 | openChannel(); 26 | } 27 | 28 | protected void close() throws IOException { 29 | closeChannel(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/test/resources/findbugsExclude.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/RequeueOnConnectionClose.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import java.io.IOException; 19 | import java.util.concurrent.TimeoutException; 20 | import org.junit.Ignore; 21 | 22 | @Ignore("Flaky tests. To investigate") 23 | public class RequeueOnConnectionClose extends RequeueOnClose { 24 | 25 | protected void open() throws IOException, TimeoutException { 26 | openConnection(); 27 | openChannel(); 28 | } 29 | 30 | protected void close() throws IOException { 31 | closeConnection(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/MessageConsumerAssociation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import java.util.concurrent.CompletableFuture; 19 | import org.apache.pulsar.client.api.MessageId; 20 | 21 | public abstract class MessageConsumerAssociation { 22 | private final MessageId messageId; 23 | private final int size; 24 | 25 | MessageConsumerAssociation(MessageId messageId, int size) { 26 | this.messageId = messageId; 27 | this.size = size; 28 | } 29 | 30 | public MessageId getMessageId() { 31 | return messageId; 32 | } 33 | 34 | public int getSize() { 35 | return size; 36 | } 37 | 38 | public abstract boolean isUsesCredit(); 39 | 40 | public abstract CompletableFuture ack(); 41 | 42 | public abstract void requeue(); 43 | } 44 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/metadata/BindingSetMetadata.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq.metadata; 17 | 18 | import java.util.HashMap; 19 | import java.util.HashSet; 20 | import java.util.Map; 21 | import java.util.Set; 22 | 23 | public class BindingSetMetadata { 24 | private Set keys = new HashSet<>(); 25 | private Map subscriptions = new HashMap<>(); 26 | 27 | public BindingSetMetadata() {} 28 | 29 | public Set getKeys() { 30 | return keys; 31 | } 32 | 33 | public void setKeys(Set keys) { 34 | this.keys = keys; 35 | } 36 | 37 | public Map getSubscriptions() { 38 | return subscriptions; 39 | } 40 | 41 | public void setSubscriptions(Map subscriptions) { 42 | this.subscriptions = subscriptions; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/SystemTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmqtests; 17 | 18 | /** 19 | * Test category for system tests. 20 | * 21 | *

System tests are able to run both with local installed Pulsar cluster (e.g. testcontainers) 22 | * and with an external Pulsar cluster. 23 | * 24 | *

Note: it is expected the external Pulsar cluster to have default configuration with the 25 | * rabbitmq protocol handler active. 26 | */ 27 | public interface SystemTest { 28 | /** If true, the test must run using an external Pulsar cluster. */ 29 | boolean enabled = Boolean.parseBoolean(System.getProperty("tests.systemtests.enabled", "false")); 30 | 31 | /** The host to connect used by the client (Broker/Proxy host). */ 32 | String host = System.getProperty("tests.systemtests.pulsar.host", "localhost"); 33 | 34 | /** The AMQP listener port exposed on Pulsar Broker/Proxy. */ 35 | int listenerPort = 36 | Integer.parseInt(System.getProperty("tests.systemtests.pulsar.ampqlistener.port", "5672")); 37 | } 38 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/metadata/VirtualHostMetadata.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq.metadata; 17 | 18 | import java.util.Map; 19 | import java.util.concurrent.ConcurrentHashMap; 20 | 21 | public class VirtualHostMetadata { 22 | 23 | private String namespace; 24 | private final Map exchanges = new ConcurrentHashMap<>(); 25 | private final Map queues = new ConcurrentHashMap<>(); 26 | 27 | private final Map> subscriptions = new ConcurrentHashMap<>(); 28 | 29 | public String getNamespace() { 30 | return namespace; 31 | } 32 | 33 | public void setNamespace(String namespace) { 34 | this.namespace = namespace; 35 | } 36 | 37 | public Map getExchanges() { 38 | return exchanges; 39 | } 40 | 41 | public Map getQueues() { 42 | return queues; 43 | } 44 | 45 | public Map> getSubscriptions() { 46 | return subscriptions; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/BasicConsumeMessageConsumerAssociation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import java.util.concurrent.CompletableFuture; 19 | import org.apache.pulsar.client.api.MessageId; 20 | 21 | public class BasicConsumeMessageConsumerAssociation extends MessageConsumerAssociation { 22 | private final MessageId messageId; 23 | private final PulsarConsumer pulsarConsumer; 24 | private final int size; 25 | 26 | BasicConsumeMessageConsumerAssociation( 27 | MessageId messageId, PulsarConsumer pulsarConsumer, int size) { 28 | super(messageId, size); 29 | this.messageId = messageId; 30 | this.pulsarConsumer = pulsarConsumer; 31 | this.size = size; 32 | } 33 | 34 | public MessageId getMessageId() { 35 | return messageId; 36 | } 37 | 38 | public int getSize() { 39 | return size; 40 | } 41 | 42 | public boolean isUsesCredit() { 43 | return true; 44 | } 45 | 46 | public CompletableFuture ack() { 47 | return pulsarConsumer.ackMessage(messageId); 48 | } 49 | 50 | public void requeue() { 51 | pulsarConsumer.nackMessage(messageId); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /nar-tests/src/test/resources/ssl/proxy.key-pk8.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC7XPqQ8Er+LQ6L 3 | 4uoPromq8s6oHsd4Y/Nbg4+y134yfaFWNV/dCCaJ/kU/6wPeYyPR7lKvIWYj95yO 4 | RcpE+ADy1Ps2pL6qJslchSqVDUWEKCmv5ZOzw5TV2P31zYz6zpHptzzpvDFfl6RA 5 | RCwdBMdv03yORtGMwmuxUFaENOFR/iDOkoU/agNZWzXpPm8XB+rSLQ/e0qa9gT9i 6 | 4DcfuWLIe8p0Pauq3cESLGEgOadqhdT6pVdOQs/fo2Y/yd2TiU/s4XPy8Jiuzs9i 7 | CG7R1X2M7yS5EVYKRl4z+4kcYHnVnbsliMwViPOM+p9GNUwmnhxOB0/fgHjn/9o0 8 | Z5Nvnw+7AgMBAAECggEAH3wnaxXGNGJT8HPoS0xUPBLjYWMSuAtryLQaAL6vqUJt 9 | SzRa8KS+/S9qaaxPkR5t1YhMArwFEwQ4HbximiZm2mep82vJ0oQEEHUcS8HYLbYm 10 | EzXsFShHqYv2WUj6aOcvo+uz479JOLwd0OLj2tiDvyn1417JNfMx77EFaOAOAR3+ 11 | S9wjDwJDvbaXPWoiLlyg7G/UZh+ao1VDarI/0wFXuBeguJxHMp3CaI11StWmnBiO 12 | seG63kq4VXnPLriCERGB5hvv9+NbZer/tozx50zaHSqtY+da1tGCtUT7+6xdW1An 13 | vlluy/SL9qcHfrzdYnBI/PEHH1lT+CHzdfqi73X3iQKBgQD0tpe5YDyNO3j73HG9 14 | 0lwpE6ertt83/EoGSSDB6fsp13AKBcBGj47SiIIt8VUfTlV4Te46SzUf9nH7qgc1 15 | up/du3e2EPUWyNOnEAEVADq4tLatrBcfKpmYmtxhR799AIAQ5HtyQIfINmwU3c/E 16 | lWvI1ZlYUjQtYX9G9uemfA7btwKBgQDEATxZkkxKWK+9OQzECfU0hxwv4OnQjItU 17 | h+o3jr7JZpbPI+tHnGPl/9684ZqCKkt+fH6oCpI9hDZaL01vTSvbyCmPTaWcCvpy 18 | CqaGGGRditGXgsRcbxz823KTsk+PDXlF9lxtiPYbP22NXYhfER922C0oYaf9evl1 19 | vl6AOyk0HQKBgHc/gzASdW9dS9dv1P1rRP2/VjWXJ69ggbIOcKgOHRaGpVI8+zVn 20 | P3HYk5dPwus+gOffEffWLk0lCuZYNKs6Bi1G7U8LdGNl+b2vTtetBPB9H4gCFEJb 21 | v/1deYbuL6H6XG3e6UkDqScnXgPNEzH4nk1/yPoBFOwIVr1+E+RND/CBAoGAMSEK 22 | Kn10J/KA4lIpc/32+1qeYxXiJYcE6YRD3fcmtT7EsFkVgVae56iyAPAflV7am3LD 23 | IIW90DOSmBphM3PJeYZ7fFL/HtTVn74xrUzx40yHao25gF3Ni0fKOWEpdfbvney5 24 | kC8TWRMV96gpJYMaOuTGtOd9fxjS7INyFnw9wLUCgYBh6ywjAX7Ma4Ld0rVMRRL6 25 | 3WkS3F/w++JsNu4WHmPYsTh8hZLRAOx/WnKsWOlpEVvHbTmwouKeWGdIdKBJ9myg 26 | XoIqQZTz0mfzisA/xAlosI4rdiAVfCZptwtoNQ866VlfNbm1dTVOmq1Whift67hF 27 | n7CCjT6pzQ09tmA/I7X87A== 28 | -----END PRIVATE KEY----- -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/resources/authentication/tls/client-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDNQ32YQPmwW7yu 3 | 28ALrSaQluBiOO1osXBGO95E+RRRhhDrypDniOj5kYXg3bW0FLl444bVVG1o7BSS 4 | tPgiWwU97TElZQgFhMrmDCESWDLHGmCjT9JKnigZfEWEAIyJ3N6K5U+Ikcyk8YFF 5 | TH3C/+LBicYSc5XiNr3brotaaGqQUd4riF+qZ/So42PcvhmCzJ1/5o37gr4iAT1W 6 | EztbBLToxRjmLg36ukqN6MZaoVGaSmLXr920/OLVza6ZbFxhVgvXDBp3XPU6alS1 7 | njOsqXUomnav0HpXABuREzH9QoghRwUQAS9Zu8c62eFYTBtscbaY790DglijMtyQ 8 | obamHuELAgMBAAECggEBALGnokJuqiz7mTj2NSdl+6TVEOuyPbiJKpV/J4cm1XEh 9 | ye9qaTQcCRhH3UmcWrG75jM9KevloLRY8A1x1/lUMhtA+XJWGTU9k6a8BLut3nT4 10 | 3X87jNTMQgSczEXNe9WudmZcxhN7rVVtOOdTpt1pP0cnCWna5HTf0D8cuLvM975j 11 | r1YGTjKsCF1W+tp6ZAIIMfJkUI2qBRKvSxVCSs1vZBraox3yUVnq9oRLHxZZoqOd 12 | d51G5phRtn6ReVPBdT8fGUBEGg3jKxTu2/vLQMUyHy0hyCAM20gzOP4FIc2g+QZU 13 | y42byAuc89m0OrdRWsmzHCOxcq9DwY9npaz1RscR/2ECgYEA9bHJQ0Y1afpS5gn2 14 | KnXenRIw9oal1utQZnohCEJ4um+K/BCEHtDnI825LPNf34IKM2rSmssvHrYN51o0 15 | 92j9lHHXsf6MVluwsTsIu8MtNaJ1BLt96dub4ScGT6vvzObKTwsajUfIHk+FNsKq 16 | zps8yh1q0qyyfAcvR82+Xr6JIsMCgYEA1d+RHGewi/Ub/GCG99A1KFKsgbiIJnWB 17 | IFmrcyPWignhzDUcw2SV9XqAzeK8EOIHNq3e5U/tkA7aCWxtLb5UsQ8xvmwQY2cy 18 | X2XvSdIhO4K2PgRLgjlzZ8RHSULglqyjB2i6TjwjFl8TsRzYr6JlV6+2cMujw4Bl 19 | g3a8gz071BkCgYBLP7BMkmw5kRliqxph1sffg3rLhmG0eU2elTkYtoMTVqZSnRxZ 20 | 89FW/eMBCWkLo2BMbyMhlalQ1qFbgh1GyTkhBdzx/uwsZtiu7021dAmcq6z7ThE6 21 | VrBfPPyJ2jcPon/DxbrUGnAIGILMSsLVlGYB4RCehZYEto6chz8O9Xw60QKBgCnd 22 | us1BqviqwZC04JbQJie/j09RbS2CIQXRJ9PBNzUMXCwaVYgWP5ivI1mqQcBYTqsw 23 | fAqNi+aAUcQ4emLS+Ec0vzsUclzTDbRJAv+DZ8f7fWtEcfeLAYFVldLMiaRVJRDF 24 | OnsoIII3mGY6TFyNQKNanS8VXfheQQDsFFjoera5AoGBALXYEXkESXpw4LT6qJFz 25 | ktQuTZDfS6LtR14/+NkYL9c5wBC4Otkg4bNbT8xGlUjethRfpkm8xRTB6zfC1/p/ 26 | Cg6YU1cwqlkRurAhE3PEv1dCc1IDbzou8xnwqHrd6sGPDQmQ3aEtU5eJhDZKIZfx 27 | nQqPGK92+Jtne7+W1mFZooxs 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/resources/authentication/tls/server-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCvv7ctmK2d9tqj 3 | E9RiD5i+HKKJIrpv1f0fZ+ORA5iAgQ7t2PZwfyw2aD1T6lg6ptWJZku9HldxE21L 4 | EeVApXaEJJJAWICWyR8sxFXro3lzcFw3montL7pr44J8aUoCVIuBXjy/TIrL6ixe 5 | g+e3EAhfglijidHakroqKO4wKD9brhBxlsfhEsWwGq1Eb0Q6EUqaPA+NBoB7NO8/ 6 | bPRexURUHsjdx4CFgNlo5sZTA3fh/hhhB3cFTO1ZvF1BOGrvXaGyYJjUSCiVAooO 7 | /c97G9IRzBAMUHPXzDhsg915JqqQyJuEhrxZ6WJp9JgbxIB4fqAagZ3S4WbdxMz8 8 | YwSs7Kc1AgMBAAECggEAAaWEK9MwXTiA1+JJrRmETtOp2isPIBkbI/4vLZ6hASM0 9 | ZpoPxQIMAf58BJs/dF03xu/EaeMs4oxSC9ABG9fxAk/tZtjta3w65Ip6W5jOfHxj 10 | AMpb3HMEBhq9kDjUTq1IGVAutYQcEMkC3WfS9e4ahfqMpguWgbu6LsbvZFgcL9mv 11 | pGnKv9YVe6Xk6isvqtq6G1af0rd7c//xF0i0e/qEo83Buok3gLEZOELZbcRxjUYc 12 | jnyglnXnwkGjuL4E3wgS3l73ZKsb6+AYoqhMPVz8t4/PN3tTrsBJKOSYo8KzIm0U 13 | ek9T8XmPbP0cuheRxp9Dp8TXJJQZK0N9jz+EL0ogQQKBgQDnavm8GpR4pap9cDOc 14 | +YI5s823b507pNdSU8elO9gLsP0JlFzv+sqghVko29r85D7Vn3MkgYTy0S4ANLCs 15 | 0NFDY8N2QH6U1dTkk1QXZydVZDuKJ5SSpC4v+Vafl8yDxhB4Nlxhbm9vJEMfLcXh 16 | 2kL6UlAuFDtYD0AdczwnHu5DjQKBgQDCauocm55FpcyDMMBO2CjurxcjBYS3S1xT 17 | Bz+sPtxJLjlKbAt8kSHUQcCcX9zhrQBfsT38LATCmKaOFqUW5/PPh2LcrxiMqlL1 18 | OJBUJ3Te2LTjlUn8r+DHv/69UIh5tchwRr3YgB0DuIs7jfmr4VfiOWTBtPVhoGFR 19 | 1Wt60j30SQKBgHzreS26J2VNAFBALgxRf6OIVMbtgDG/FOCDCyU9vazp+F2gcd61 20 | QYYPFYcBzx9uUiDctroBFHRCyJMh3jEbc6ruAogl3m6XUxmkEeOkMk5dEerM3N2f 21 | tLL+5Gy385U6aI+LwKhzhcG4EGeXPNdjC362ykNldnddnB2Jo/H2N2XNAoGAdnft 22 | xpbxP+GDGKIZXTIM5zzcLWQMdiC+1n1BSHVZiGJZWMczzKknYw7aDq+/iekApE79 23 | xW8RS373ZvfXi3i2Mcx+6pjrrbOQL4tTL2SHq8+DknaDCi4mG7IbyUKMlxW1WO1S 24 | e929UGogtZ6S+DCte9WbVwosyFuRUetpvgLk67kCgYBWetihZjgBWrqVYT24TTRH 25 | KxzSzH1JgzzF9qgTdlhXDv9hC+Kc0uTKsgViesDqVuCOjkwzY5OQr9c6duO0fwwP 26 | qNk/qltdgjMC5iiv7duyukfbEuqKEdGGer9HFb7en96dZdVQJpYHaaslAGurtD80 27 | ejCQZgzR2XaHSuIQb0IUVQ== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/BasicGetMessageConsumerAssociation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import java.util.concurrent.CompletableFuture; 19 | import org.apache.pulsar.client.api.Consumer; 20 | import org.apache.pulsar.client.api.MessageId; 21 | 22 | public class BasicGetMessageConsumerAssociation extends MessageConsumerAssociation { 23 | private final MessageId messageId; 24 | private final Consumer consumer; 25 | private final int size; 26 | 27 | BasicGetMessageConsumerAssociation(MessageId messageId, Consumer consumer, int size) { 28 | super(messageId, size); 29 | this.messageId = messageId; 30 | this.consumer = consumer; 31 | this.size = size; 32 | } 33 | 34 | public MessageId getMessageId() { 35 | return messageId; 36 | } 37 | 38 | public int getSize() { 39 | return size; 40 | } 41 | 42 | public boolean isUsesCredit() { 43 | return false; 44 | } 45 | 46 | public CompletableFuture ack() { 47 | return consumer.acknowledgeAsync(messageId).thenCompose(it -> consumer.closeAsync()); 48 | } 49 | 50 | public void requeue() { 51 | consumer.closeAsync(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/test/java/com/datastax/oss/starlight/rabbitmq/AMQDataBlockEncoderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | 20 | import io.netty.buffer.ByteBuf; 21 | import io.netty.channel.embedded.EmbeddedChannel; 22 | import java.nio.charset.StandardCharsets; 23 | import org.apache.qpid.server.protocol.ProtocolVersion; 24 | import org.apache.qpid.server.protocol.v0_8.transport.ProtocolInitiation; 25 | import org.junit.jupiter.api.Test; 26 | 27 | class AMQDataBlockEncoderTest { 28 | 29 | @Test 30 | void encodeAMQDataBlock() { 31 | EmbeddedChannel channel = new EmbeddedChannel(new AMQDataBlockEncoder()); 32 | 33 | ProtocolInitiation protocolInitiation = new ProtocolInitiation(ProtocolVersion.v0_91); 34 | channel.writeOutbound(protocolInitiation); 35 | ByteBuf encodedBuf = channel.readOutbound(); 36 | 37 | assertEquals(8, encodedBuf.readableBytes()); 38 | assertEquals("AMQP", encodedBuf.readCharSequence(4, StandardCharsets.UTF_8).toString()); 39 | assertEquals(0, encodedBuf.readByte()); 40 | assertEquals(0, encodedBuf.readByte()); 41 | assertEquals(9, encodedBuf.readByte()); 42 | assertEquals(1, encodedBuf.readByte()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/ExchangeDeletePredeclared.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import com.datastax.oss.starlight.rabbitmqtests.SystemTest; 19 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.BrokerTestCase; 20 | import com.rabbitmq.client.AMQP; 21 | import java.io.IOException; 22 | import org.junit.Test; 23 | import org.junit.experimental.categories.Category; 24 | 25 | @Category(SystemTest.class) 26 | public class ExchangeDeletePredeclared extends BrokerTestCase { 27 | 28 | @Test 29 | public void testDeletingPredeclaredAmqExchange() throws IOException { 30 | try { 31 | channel.exchangeDelete("amq.fanout"); 32 | } catch (IOException e) { 33 | checkShutdownSignal(AMQP.ACCESS_REFUSED, e); 34 | } 35 | } 36 | 37 | @Test 38 | public void testDeletingPredeclaredAmqRabbitMQExchange() throws IOException { 39 | try { 40 | channel.exchangeDelete("amq.rabbitmq.log"); 41 | } catch (IOException e) { 42 | checkShutdownSignal(AMQP.ACCESS_REFUSED, e); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/assemble/all.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | all 21 | 22 | tar.gz 23 | 24 | false 25 | 26 | 27 | ${project.build.directory} 28 | . 29 | 30 | **/*-jar-with-dependencies.jar 31 | **/*.nar 32 | 33 | 34 | 35 | 36 | 37 | ${basedir}/../README.md 38 | README 39 | . 40 | 644 41 | 42 | 43 | ${basedir}/../LICENSE.txt 44 | LICENSE.txt 45 | . 46 | 644 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/DoubleDeletion.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import com.datastax.oss.starlight.rabbitmqtests.SystemTest; 19 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.BrokerTestCase; 20 | import java.io.IOException; 21 | import org.junit.Test; 22 | import org.junit.experimental.categories.Category; 23 | 24 | @Category(SystemTest.class) 25 | public class DoubleDeletion extends BrokerTestCase { 26 | protected static final String Q = "DoubleDeletionQueue"; 27 | protected static final String X = "DoubleDeletionExchange"; 28 | 29 | @Test 30 | public void doubleDeletionQueue() throws IOException { 31 | channel.queueDelete(Q); 32 | channel.queueDeclare(Q, false, false, false, null); 33 | channel.queueDelete(Q); 34 | channel.queueDelete(Q); 35 | } 36 | 37 | @Test 38 | public void doubleDeletionExchange() throws IOException { 39 | channel.exchangeDelete(X); 40 | channel.exchangeDeclare(X, "direct"); 41 | channel.exchangeDelete(X); 42 | channel.exchangeDelete(X); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/Queue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import java.util.List; 19 | import java.util.concurrent.CopyOnWriteArrayList; 20 | 21 | public class Queue { 22 | 23 | private volatile AMQConsumer _exclusiveSubscriber; 24 | 25 | private final List consumers = new CopyOnWriteArrayList<>(); 26 | 27 | public Queue() {} 28 | 29 | public int getQueueDepthMessages() { 30 | // TODO: implement message count in queue ? 31 | return 0; 32 | } 33 | 34 | public int getConsumerCount() { 35 | return consumers.size(); 36 | } 37 | 38 | public long clearQueue() { 39 | // TODO: implement queue purge 40 | return 0; 41 | } 42 | 43 | public void addConsumer(AMQConsumer consumer, boolean exclusive) { 44 | if (exclusive) { 45 | _exclusiveSubscriber = consumer; 46 | } 47 | consumers.add(consumer); 48 | consumer.consume(); 49 | } 50 | 51 | public void unregisterConsumer(AMQConsumer consumer) { 52 | consumers.remove(consumer); 53 | _exclusiveSubscriber = null; 54 | } 55 | 56 | public List getConsumers() { 57 | return consumers; 58 | } 59 | 60 | public boolean hasExclusiveConsumer() { 61 | return _exclusiveSubscriber != null; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/BasicConsume.java: -------------------------------------------------------------------------------- 1 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import com.datastax.oss.starlight.rabbitmqtests.SystemTest; 6 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.BrokerTestCase; 7 | import com.rabbitmq.client.AMQP; 8 | import com.rabbitmq.client.Channel; 9 | import com.rabbitmq.client.DefaultConsumer; 10 | import com.rabbitmq.client.Envelope; 11 | import java.io.IOException; 12 | import java.util.concurrent.CountDownLatch; 13 | import java.util.concurrent.TimeUnit; 14 | import org.junit.Test; 15 | import org.junit.experimental.categories.Category; 16 | 17 | /** */ 18 | @Category(SystemTest.class) 19 | public class BasicConsume extends BrokerTestCase { 20 | 21 | @Test 22 | public void basicConsumeOk() throws IOException, InterruptedException { 23 | String q = channel.queueDeclare().getQueue(); 24 | basicPublishPersistent("msg".getBytes("UTF-8"), q); 25 | basicPublishPersistent("msg".getBytes("UTF-8"), q); 26 | 27 | CountDownLatch latch = new CountDownLatch(2); 28 | channel.basicConsume(q, new CountDownLatchConsumer(channel, latch)); 29 | 30 | boolean nbOfExpectedMessagesHasBeenConsumed = latch.await(1, TimeUnit.SECONDS); 31 | assertTrue("Not all the messages have been received", nbOfExpectedMessagesHasBeenConsumed); 32 | } 33 | 34 | static class CountDownLatchConsumer extends DefaultConsumer { 35 | 36 | private final CountDownLatch latch; 37 | 38 | public CountDownLatchConsumer(Channel channel, CountDownLatch latch) { 39 | super(channel); 40 | this.latch = latch; 41 | } 42 | 43 | @Override 44 | public void handleDelivery( 45 | String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) 46 | throws IOException { 47 | latch.countDown(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/Heartbeat.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import static org.junit.Assert.*; 19 | 20 | import com.datastax.oss.starlight.rabbitmqtests.SystemTest; 21 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.BrokerTestCase; 22 | import com.rabbitmq.client.ConnectionFactory; 23 | import com.rabbitmq.client.impl.recovery.AutorecoveringConnection; 24 | import org.junit.Test; 25 | import org.junit.experimental.categories.Category; 26 | 27 | @Category(SystemTest.class) 28 | public class Heartbeat extends BrokerTestCase { 29 | 30 | @Override 31 | protected ConnectionFactory newConnectionFactory() { 32 | ConnectionFactory cf = super.newConnectionFactory(); 33 | cf.setRequestedHeartbeat(1); 34 | return cf; 35 | } 36 | 37 | @Test 38 | public void heartbeat() throws InterruptedException { 39 | assertEquals(1, connection.getHeartbeat()); 40 | Thread.sleep(3100); 41 | assertTrue(connection.isOpen()); 42 | ((AutorecoveringConnection) connection).getDelegate().setHeartbeat(0); 43 | assertEquals(0, connection.getHeartbeat()); 44 | Thread.sleep(3100); 45 | assertFalse(connection.isOpen()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/ExchangeEquivalenceBase.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import static org.junit.Assert.fail; 19 | 20 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.BrokerTestCase; 21 | import com.rabbitmq.client.AMQP; 22 | import java.io.IOException; 23 | import java.util.Map; 24 | 25 | public abstract class ExchangeEquivalenceBase extends BrokerTestCase { 26 | public void verifyEquivalent( 27 | String name, String type, boolean durable, boolean autoDelete, Map args) 28 | throws IOException { 29 | channel.exchangeDeclarePassive(name); 30 | channel.exchangeDeclare(name, type, durable, autoDelete, args); 31 | } 32 | 33 | // Note: this will close the channel 34 | public void verifyNotEquivalent( 35 | String name, String type, boolean durable, boolean autoDelete, Map args) 36 | throws IOException { 37 | channel.exchangeDeclarePassive(name); 38 | try { 39 | channel.exchangeDeclare(name, type, durable, autoDelete, args); 40 | fail("Exchange was supposed to be not equivalent"); 41 | } catch (IOException ioe) { 42 | checkShutdownSignal(AMQP.PRECONDITION_FAILED, ioe); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/HeadersExchange.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import com.datastax.oss.starlight.rabbitmq.metadata.VirtualHostMetadata; 19 | import java.util.concurrent.CompletableFuture; 20 | import org.apache.qpid.server.model.LifetimePolicy; 21 | 22 | public class HeadersExchange extends AbstractExchange { 23 | 24 | public HeadersExchange(String name, boolean durable, LifetimePolicy lifetimePolicy) { 25 | super(name, Type.headers, durable, lifetimePolicy); 26 | } 27 | 28 | @Override 29 | public CompletableFuture bind( 30 | VirtualHostMetadata vhost, 31 | String exchange, 32 | String queue, 33 | String routingKey, 34 | GatewayConnection connection) { 35 | CompletableFuture result = new CompletableFuture<>(); 36 | result.completeExceptionally( 37 | new UnsupportedOperationException("Binding header exchange not supported at the moment")); 38 | return result; 39 | } 40 | 41 | @Override 42 | public CompletableFuture unbind( 43 | VirtualHostMetadata vhost, 44 | String exchange, 45 | String queue, 46 | String routingKey, 47 | GatewayConnection gatewayConnection) { 48 | CompletableFuture result = new CompletableFuture<>(); 49 | result.completeExceptionally( 50 | new UnsupportedOperationException("Unbinding header exchange not supported at the moment")); 51 | return result; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /nar-tests/src/test/resources/ssl/proxy.cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIF1DCCA7ygAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgYYxCzAJBgNVBAYTAlVT 3 | MRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlQYWxvIEFsdG8xEzARBgNV 4 | BAoMCk15IGNvbXBhbnkxCzAJBgNVBAsMAklUMRIwEAYDVQQDDAlsb2NhbGhvc3Qx 5 | GDAWBgkqhkiG9w0BCQEWCWxvY2FsaG9zdDAeFw0yMjA0MjExMDQxNDlaFw0yNTAx 6 | MTUxMDQxNDlaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRMw 7 | EQYDVQQKDApNeSBjb21wYW55MQswCQYDVQQLDAJJVDEUMBIGA1UEAwwLcHVsc2Fy 8 | cHJveHkxGjAYBgkqhkiG9w0BCQEWC3B1bHNhcnByb3h5MIIBIjANBgkqhkiG9w0B 9 | AQEFAAOCAQ8AMIIBCgKCAQEAu1z6kPBK/i0Oi+LqD66JqvLOqB7HeGPzW4OPstd+ 10 | Mn2hVjVf3Qgmif5FP+sD3mMj0e5SryFmI/ecjkXKRPgA8tT7NqS+qibJXIUqlQ1F 11 | hCgpr+WTs8OU1dj99c2M+s6R6bc86bwxX5ekQEQsHQTHb9N8jkbRjMJrsVBWhDTh 12 | Uf4gzpKFP2oDWVs16T5vFwfq0i0P3tKmvYE/YuA3H7liyHvKdD2rqt3BEixhIDmn 13 | aoXU+qVXTkLP36NmP8ndk4lP7OFz8vCYrs7PYghu0dV9jO8kuRFWCkZeM/uJHGB5 14 | 1Z27JYjMFYjzjPqfRjVMJp4cTgdP34B45//aNGeTb58PuwIDAQABo4IBWTCCAVUw 15 | CQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4QgENBCYWJE9w 16 | ZW5TU0wgR2VuZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUyyv6 17 | CZhPwizSTRqLFYGAxDSDVmYwgbsGA1UdIwSBszCBsIAUa9x+2kDWKiQ5frcESgJy 18 | QkPXfBOhgYykgYkwgYYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh 19 | MRIwEAYDVQQHDAlQYWxvIEFsdG8xEzARBgNVBAoMCk15IGNvbXBhbnkxCzAJBgNV 20 | BAsMAklUMRIwEAYDVQQDDAlsb2NhbGhvc3QxGDAWBgkqhkiG9w0BCQEWCWxvY2Fs 21 | aG9zdIIJAML6+aMIP8HVMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEF 22 | BQcDATANBgkqhkiG9w0BAQsFAAOCAgEAAxBlr8dlcu80u0Dr7G8DGcAv8GsOlr3G 23 | 0IMpdL9aPOI1kbpNCDFUfMP9soYCV+rN6ioLG9gdOXq6jCuqUJSNHwcKXdcnFNGF 24 | ZW9jWRpjft2XqgFRlMtelgG4M+c9D2SUpTL6psLBlJsAXnrZg/uIOaRnPHOOUWF/ 25 | rioSG0ffMMSfGv1JD949RcjOZLsHVTzTleljv8pDfPJSglotsShuv64FAMaPYIDw 26 | C0nrGdPLkc93qFZC1dCePGoLDWn82NvKxOfK8U0YQnRKa1XgqsmnhjjU347F+TVQ 27 | KtWj1tQ6yCE2PUV/T9dckjvLUWkqZgcYyGn5TWh1Legn/KuO7ZjHKZfIRuao8EQm 28 | O9Zw6taV/0QBIvC0NG4O4dTrEqHBLbHgEO0Nd4BkwE8ZAU2tGLs7NF5iIaLk53bN 29 | D6R2h473+TGb3ZhZ4HtdJo6qBvR3a/UyAReWZM0Z9Vy8pEwiJ8ILn12esJxwJDpo 30 | Q7gs5UXRj5I3LU35sSF8yjQWfwzJnPkvfk2HmaXBlKhGrRa/jSQKmW3BRSzSnseZ 31 | mD5QzhbyWMXqVAq1mGn/FjNdHPxpIcRd+7sjXZV/yFs6DOUekHBfJNaDm1R9ZXpK 32 | M/HMVPlt5Y1kC2tkfRzl6tG1TNKNMI4EO5ZsW8Fj0u9x5ovfdh7v0oHSzeWZJLSv 33 | bijHjDwjEjA= 34 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /starlight-rabbitmq/src/assemble/jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 30 | 31 | jar-with-dependencies 32 | 33 | jar 34 | 35 | false 36 | 37 | 38 | / 39 | true 40 | true 41 | 42 | 43 | / 44 | true 45 | true 46 | provided 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/metadata/BindingMetadata.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq.metadata; 17 | 18 | import java.util.Set; 19 | import java.util.concurrent.ConcurrentHashMap; 20 | 21 | public class BindingMetadata { 22 | private String exchange; 23 | private String topic; 24 | private String subscription; 25 | private Set keys = ConcurrentHashMap.newKeySet(); 26 | private byte[] lastMessageId; 27 | 28 | public BindingMetadata() {} 29 | 30 | public BindingMetadata(String exchange, String topic, String subscription) { 31 | this.exchange = exchange; 32 | this.topic = topic; 33 | this.subscription = subscription; 34 | } 35 | 36 | public String getTopic() { 37 | return topic; 38 | } 39 | 40 | public void setTopic(String topic) { 41 | this.topic = topic; 42 | } 43 | 44 | public String getSubscription() { 45 | return subscription; 46 | } 47 | 48 | public void setSubscription(String subscription) { 49 | this.subscription = subscription; 50 | } 51 | 52 | public Set getKeys() { 53 | return keys; 54 | } 55 | 56 | public void setKeys(Set keys) { 57 | this.keys = keys; 58 | } 59 | 60 | public byte[] getLastMessageId() { 61 | return lastMessageId; 62 | } 63 | 64 | public void setLastMessageId(byte[] lastMessageId) { 65 | this.lastMessageId = lastMessageId; 66 | } 67 | 68 | public String getExchange() { 69 | return exchange; 70 | } 71 | 72 | public void setExchange(String exchange) { 73 | this.exchange = exchange; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /nar-tests/src/test/resources/ssl/ca.cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIF9DCCA9ygAwIBAgIJAML6+aMIP8HVMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD 3 | VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJUGFsbyBBbHRv 4 | MRMwEQYDVQQKDApNeSBjb21wYW55MQswCQYDVQQLDAJJVDESMBAGA1UEAwwJbG9j 5 | YWxob3N0MRgwFgYJKoZIhvcNAQkBFglsb2NhbGhvc3QwHhcNMjIwNDIxMTA0MDU3 6 | WhcNNDIwNDE2MTA0MDU3WjCBhjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm 7 | b3JuaWExEjAQBgNVBAcMCVBhbG8gQWx0bzETMBEGA1UECgwKTXkgY29tcGFueTEL 8 | MAkGA1UECwwCSVQxEjAQBgNVBAMMCWxvY2FsaG9zdDEYMBYGCSqGSIb3DQEJARYJ 9 | bG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxc5Ec77x 10 | 7a4NjSdzDKWMwQW1pGxG7VYqlJXWy3zwzhnmvHPs/SzAg013D5gUPTjHKAFW28AJ 11 | qpEhOi8/k90o90KlkngTZfqv5hNTx8Ondcbi7IBtOyFHo3F+Uda5zmbcPNUPThAN 12 | Lqlr5uC2PUeFT+15LpeaLlUY8T+TTX5Yv3zB1hhVDkANkIWDOYjyT3N9zCSKp6++ 13 | tU4Kv2/9KuFYG0mGImVUMBHYh4n/B3P0S6mE9XWw8nWLVJKGG8tV6YMhBG2zyzdy 14 | x792y9vKCbSvb5o8I+YFm6W9/57gdHs0HGJrFeYpov79nGcaSQVLVIwcEOStd4c2 15 | OG8tr7qcMZXt0yVuL/vBwDJJLahRV63oXYow47rNYrQnSDAvLPk6764eDHGNy2a5 16 | buNuY7SmhxftoMz15vFQRm26SrUYq21jFjM1KVSJmNE6lnMTpePedrQIjR/mwedk 17 | b9C6bWYspUCdMESQ3Qnq+J7W5BzkkAM+PO3lviWzlTUncH2txU/aEeQB7iYJZxfr 18 | yBcab2mPEk87SHhNK69S4zQ/Yq9IDNgg/DOkBqk+0JdP1xqRrekHktnJnyJ+43Ky 19 | /jnIUYfTdTTdnxydCr7BkNyM6v8r85wEkvCEs05fP9xOSKHzDgRMIvM2LECAAaU8 20 | FZm4PNEFy7OXRtYQWDlF6igW7wQCwnS6ogcCAwEAAaNjMGEwHQYDVR0OBBYEFGvc 21 | ftpA1iokOX63BEoCckJD13wTMB8GA1UdIwQYMBaAFGvcftpA1iokOX63BEoCckJD 22 | 13wTMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB 23 | CwUAA4ICAQCv85SGmM3cUvqCdmlzNae/zs4Nhu0VejCxn7BNIaj0W5Dh/yxK+uIV 24 | 4vOrbxP1nMFRiQw67K9BGOcIGfVdIASqB/r1oVvvQVRAhmAIAAedJlbHay1hotvA 25 | 2VYRKdFs9VuAGWcjMdieQOF3WWWRDIdN0MVGJPti4aAfaOiilthsszjCSt8Cu+Et 26 | rdwA1xT1gTtqtik0RMNxufOX10TiGnB66p4ktgK+6jikX0pnQBfyvFyGQ8tKHhUG 27 | Dur0P+ySneJ8UbsuaBvGtcDn312f5rL02MJ51UCkRUMtInsFM1c2fHjwBUgaVYPA 28 | C39HcmhBBtIHteZTQ08YyHVItWG4IfLXluOalGvpdxIr3/7yqb0ciFM3r/O5hj1p 29 | SmP6Kg9C6RS0+ugAHeuhGGT4Pzlm8j4kdyBhBewrszgiR9P0S0oOc7BxJ0sXR5bk 30 | 9ATpPh158NK1eSrGnoM3ecVatsQhSsmpUmqUZT3CbjVcDkt6cD4Dc6eL4EfAnlGb 31 | ZWNyEwfJG39Ow3kSBp5E3OfyBDd1IaEAULR69pl35k8LkvbyJ9LjwqZiEDl6224J 32 | 10g3EmQZhkiBA+RbDNMQti3crs+haG4Y1lipX9NwuKNlmjI6i+kW3Rba1EsmAXXe 33 | KPL/f3JVNwjsL1+DyGgVOXt4uJxZE1fCgUHhWNX4KyeD8/yv+Ssmpw== 34 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/InvalidAcksBase.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.BrokerTestCase; 19 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.TestUtils; 20 | import com.rabbitmq.client.AMQP; 21 | import java.io.IOException; 22 | import org.junit.Test; 23 | 24 | /** 25 | * See bug 21846: Basic.Ack is now required to signal a channel error immediately upon detecting an 26 | * invalid deliveryTag, even if the channel is (Tx-)transacted. Specifically, a client MUST not 27 | * acknowledge the same message more than once. 28 | */ 29 | public abstract class InvalidAcksBase extends BrokerTestCase { 30 | protected abstract void select() throws IOException; 31 | 32 | protected abstract void commit() throws IOException; 33 | 34 | @Test 35 | public void doubleAck() throws Exception { 36 | select(); 37 | String q = channel.queueDeclare().getQueue(); 38 | basicPublishVolatile(q); 39 | commit(); 40 | 41 | long tag = TestUtils.basicGet(channel, q, false).getEnvelope().getDeliveryTag(); 42 | channel.basicAck(tag, false); 43 | channel.basicAck(tag, false); 44 | 45 | expectError(AMQP.PRECONDITION_FAILED); 46 | } 47 | 48 | @Test 49 | public void crazyAck() throws IOException { 50 | select(); 51 | channel.basicAck(123456, false); 52 | expectError(AMQP.PRECONDITION_FAILED); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/metadata/ExchangeMetadata.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq.metadata; 17 | 18 | import java.util.Map; 19 | import java.util.concurrent.ConcurrentHashMap; 20 | import org.apache.qpid.server.model.LifetimePolicy; 21 | 22 | public class ExchangeMetadata { 23 | 24 | public enum Type { 25 | direct, 26 | fanout, 27 | topic, 28 | headers 29 | } 30 | 31 | private Type type; 32 | private boolean durable; 33 | private LifetimePolicy lifetimePolicy; 34 | private Map bindings = new ConcurrentHashMap<>(); 35 | 36 | public ExchangeMetadata() { 37 | // For Jackson 38 | } 39 | 40 | public ExchangeMetadata(Type type, boolean durable, LifetimePolicy lifetimePolicy) { 41 | this.type = type; 42 | this.durable = durable; 43 | this.lifetimePolicy = lifetimePolicy; 44 | } 45 | 46 | public Type getType() { 47 | return type; 48 | } 49 | 50 | public void setType(Type type) { 51 | this.type = type; 52 | } 53 | 54 | public boolean isDurable() { 55 | return durable; 56 | } 57 | 58 | public void setDurable(boolean durable) { 59 | this.durable = durable; 60 | } 61 | 62 | public LifetimePolicy getLifetimePolicy() { 63 | return lifetimePolicy; 64 | } 65 | 66 | public void setLifetimePolicy(LifetimePolicy lifetimePolicy) { 67 | this.lifetimePolicy = lifetimePolicy; 68 | } 69 | 70 | public Map getBindings() { 71 | return bindings; 72 | } 73 | 74 | public void setBindings(Map bindings) { 75 | this.bindings = bindings; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/AMQDataBlockEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import io.netty.buffer.ByteBuf; 19 | import io.netty.channel.ChannelHandlerContext; 20 | import io.netty.handler.codec.MessageToByteEncoder; 21 | import org.apache.qpid.server.bytebuffer.QpidByteBuffer; 22 | import org.apache.qpid.server.protocol.v0_8.transport.AMQDataBlock; 23 | import org.apache.qpid.server.transport.ByteBufferSender; 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | /** Converts {@link AMQDataBlock} to Netty's {@link ByteBuf} */ 28 | public class AMQDataBlockEncoder extends MessageToByteEncoder { 29 | 30 | private static final Logger log = LoggerFactory.getLogger(AMQDataBlockEncoder.class); 31 | 32 | @Override 33 | protected void encode(ChannelHandlerContext ctx, AMQDataBlock amqDataBlock, ByteBuf byteBuf) { 34 | amqDataBlock.writePayload( 35 | new ByteBufferSender() { 36 | @Override 37 | public boolean isDirectBufferPreferred() { 38 | return true; 39 | } 40 | 41 | @Override 42 | public void send(QpidByteBuffer msg) { 43 | try { 44 | // TODO: verify that there's no copy here and no issue with lifecycle 45 | byteBuf.writeBytes(msg.asInputStream(), msg.remaining()); 46 | } catch (Exception e) { 47 | log.error("Error while encoding message: {}", e.getMessage(), e); 48 | ctx.close(); 49 | } 50 | } 51 | 52 | @Override 53 | public void flush() {} 54 | 55 | @Override 56 | public void close() {} 57 | }); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/TopicExchangeUpdater.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import com.datastax.oss.starlight.rabbitmq.metadata.VirtualHostMetadata; 19 | import java.io.Closeable; 20 | import java.io.IOException; 21 | import java.util.List; 22 | import java.util.concurrent.TimeUnit; 23 | import org.apache.curator.framework.CuratorFramework; 24 | import org.apache.curator.framework.recipes.leader.LeaderSelector; 25 | import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter; 26 | 27 | public class TopicExchangeUpdater extends LeaderSelectorListenerAdapter implements Closeable { 28 | 29 | private final GatewayService service; 30 | private final LeaderSelector leaderSelector; 31 | 32 | public TopicExchangeUpdater(GatewayService service, CuratorFramework client) { 33 | this.service = service; 34 | leaderSelector = new LeaderSelector(client, "/topic_exchange_updater", this); 35 | leaderSelector.autoRequeue(); 36 | } 37 | 38 | public void start() throws IOException { 39 | leaderSelector.start(); 40 | } 41 | 42 | @Override 43 | public void close() throws IOException { 44 | leaderSelector.close(); 45 | } 46 | 47 | @Override 48 | public void takeLeadership(CuratorFramework client) throws Exception { 49 | while (true) { 50 | Thread.sleep(TimeUnit.SECONDS.toMillis(60)); 51 | for (VirtualHostMetadata vhost : service.getContextMetadata().model().getVhosts().values()) { 52 | List topics = service.getPulsarAdmin().namespaces().getTopics(vhost.getNamespace()); 53 | for (String topic : topics) { 54 | service.updateQueueSubscriptionsToTopicExchanges(topic).get(5, TimeUnit.SECONDS); 55 | } 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/ExceptionMessages.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import static org.junit.Assert.assertTrue; 19 | import static org.junit.Assert.fail; 20 | 21 | import com.datastax.oss.starlight.rabbitmqtests.SystemTest; 22 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.BrokerTestCase; 23 | import com.rabbitmq.client.AlreadyClosedException; 24 | import java.io.IOException; 25 | import java.util.UUID; 26 | import org.junit.Test; 27 | import org.junit.experimental.categories.Category; 28 | 29 | @Category(SystemTest.class) 30 | public class ExceptionMessages extends BrokerTestCase { 31 | @Test 32 | public void alreadyClosedExceptionMessageWithChannelError() throws IOException { 33 | String uuid = UUID.randomUUID().toString(); 34 | try { 35 | channel.queueDeclarePassive(uuid); 36 | fail("expected queueDeclarePassive to throw"); 37 | } catch (IOException e) { 38 | // ignored 39 | } 40 | 41 | try { 42 | channel.queueDeclarePassive(uuid); 43 | fail("expected queueDeclarePassive to throw"); 44 | } catch (AlreadyClosedException ace) { 45 | assertTrue(ace.getMessage().startsWith("channel is already closed due to channel error")); 46 | } 47 | } 48 | 49 | @Test 50 | public void alreadyClosedExceptionMessageWithCleanClose() throws IOException { 51 | String uuid = UUID.randomUUID().toString(); 52 | 53 | try { 54 | channel.abort(); 55 | channel.queueDeclare(uuid, false, false, false, null); 56 | } catch (AlreadyClosedException ace) { 57 | assertTrue( 58 | ace.getMessage().startsWith("channel is already closed due to clean channel shutdown")); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/ExchangeDeleteIfUnused.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import static org.junit.Assert.fail; 19 | 20 | import com.datastax.oss.starlight.rabbitmqtests.SystemTest; 21 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.BrokerTestCase; 22 | import com.rabbitmq.client.AMQP; 23 | import java.io.IOException; 24 | import java.util.concurrent.TimeoutException; 25 | import org.junit.Test; 26 | import org.junit.experimental.categories.Category; 27 | 28 | /* Declare an exchange, bind a queue to it, then try to delete it, 29 | * setting if-unused to true. This should throw an exception. */ 30 | @Category(SystemTest.class) 31 | public class ExchangeDeleteIfUnused extends BrokerTestCase { 32 | private static final String EXCHANGE_NAME = "xchg1"; 33 | private static final String ROUTING_KEY = "something"; 34 | 35 | protected void createResources() throws IOException, TimeoutException { 36 | super.createResources(); 37 | channel.exchangeDeclare(EXCHANGE_NAME, "direct"); 38 | String queueName = channel.queueDeclare().getQueue(); 39 | channel.queueBind(queueName, EXCHANGE_NAME, ROUTING_KEY); 40 | } 41 | 42 | protected void releaseResources() throws IOException { 43 | channel.exchangeDelete(EXCHANGE_NAME); 44 | super.releaseResources(); 45 | } 46 | 47 | /* Attempt to Exchange.Delete(ifUnused = true) a used exchange. 48 | * Should throw an exception. */ 49 | @Test 50 | public void exchangeDelete() { 51 | try { 52 | channel.exchangeDelete(EXCHANGE_NAME, true); 53 | fail("Exception expected if exchange in use"); 54 | } catch (IOException e) { 55 | checkShutdownSignal(AMQP.PRECONDITION_FAILED, e); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/metadata/QueueMetadata.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq.metadata; 17 | 18 | import com.fasterxml.jackson.annotation.JsonIgnore; 19 | import org.apache.qpid.server.model.ExclusivityPolicy; 20 | import org.apache.qpid.server.model.LifetimePolicy; 21 | 22 | public class QueueMetadata { 23 | private boolean durable; 24 | private LifetimePolicy lifetimePolicy; 25 | private ExclusivityPolicy exclusivityPolicy; 26 | 27 | public QueueMetadata() { 28 | // for Jackson 29 | } 30 | 31 | public QueueMetadata( 32 | boolean durable, LifetimePolicy lifetimePolicy, ExclusivityPolicy exclusivityPolicy) { 33 | this.durable = durable; 34 | this.lifetimePolicy = lifetimePolicy; 35 | this.exclusivityPolicy = exclusivityPolicy; 36 | } 37 | 38 | public boolean isDurable() { 39 | return durable; 40 | } 41 | 42 | public void setDurable(boolean durable) { 43 | this.durable = durable; 44 | } 45 | 46 | public LifetimePolicy getLifetimePolicy() { 47 | return lifetimePolicy; 48 | } 49 | 50 | public void setLifetimePolicy(LifetimePolicy lifetimePolicy) { 51 | this.lifetimePolicy = lifetimePolicy; 52 | } 53 | 54 | public ExclusivityPolicy getExclusivityPolicy() { 55 | return exclusivityPolicy; 56 | } 57 | 58 | public void setExclusivityPolicy(ExclusivityPolicy exclusivityPolicy) { 59 | this.exclusivityPolicy = exclusivityPolicy; 60 | } 61 | 62 | @JsonIgnore 63 | public int getQueueDepthMessages() { 64 | // TODO: implement message count in queue ? 65 | return 0; 66 | } 67 | 68 | @JsonIgnore 69 | public int getConsumerCount() { 70 | // TODO: implement distributed count of consumers 71 | return 0; 72 | } 73 | 74 | @JsonIgnore 75 | public boolean isUnused() { 76 | return getConsumerCount() == 0; 77 | } 78 | 79 | @JsonIgnore 80 | public boolean isEmpty() { 81 | return getQueueDepthMessages() == 0; 82 | } 83 | 84 | @JsonIgnore 85 | public boolean isExclusive() { 86 | return exclusivityPolicy != ExclusivityPolicy.NONE; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/GatewayProtocolHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import io.netty.channel.ChannelInitializer; 19 | import io.netty.channel.socket.SocketChannel; 20 | import java.net.InetSocketAddress; 21 | import java.util.Map; 22 | import lombok.SneakyThrows; 23 | import org.apache.pulsar.broker.ServiceConfiguration; 24 | import org.apache.pulsar.broker.protocol.ProtocolHandler; 25 | import org.apache.pulsar.broker.service.BrokerService; 26 | 27 | public class GatewayProtocolHandler implements ProtocolHandler { 28 | public static final String PROTOCOL_NAME = "rabbitmq"; 29 | public static final String METRICS_PREFIX = "pulsar"; 30 | 31 | private GatewayConfiguration config; 32 | private GatewayService service; 33 | 34 | @Override 35 | public String protocolName() { 36 | return PROTOCOL_NAME; 37 | } 38 | 39 | @Override 40 | public boolean accept(String protocol) { 41 | return PROTOCOL_NAME.equals(protocol); 42 | } 43 | 44 | @SneakyThrows 45 | @Override 46 | public void initialize(ServiceConfiguration conf) { 47 | config = ConfigurationUtils.create(conf.getProperties(), GatewayConfiguration.class); 48 | } 49 | 50 | @Override 51 | public String getProtocolDataToAdvertise() { 52 | return String.join(",", config.getAmqpListeners()); 53 | } 54 | 55 | @SneakyThrows 56 | @Override 57 | public void start(BrokerService brokerService) { 58 | service = 59 | new GatewayService( 60 | config, 61 | brokerService.getAuthenticationService(), 62 | brokerService.getPulsar().getSafeBrokerServiceUrl(), 63 | brokerService.getPulsar().getSafeWebServiceAddress(), 64 | METRICS_PREFIX); 65 | service.start(false); 66 | } 67 | 68 | @Override 69 | public Map> newChannelInitializers() { 70 | return service.newChannelInitializers(); 71 | } 72 | 73 | @SneakyThrows 74 | @Override 75 | public void close() { 76 | if (service != null) { 77 | service.close(); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/AbstractRejectTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import static org.junit.Assert.assertNotNull; 19 | import static org.junit.Assert.assertTrue; 20 | 21 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.BrokerTestCase; 22 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.QueueingConsumer; 23 | import com.rabbitmq.client.Channel; 24 | import com.rabbitmq.client.Envelope; 25 | import com.rabbitmq.client.GetResponse; 26 | import java.io.IOException; 27 | import java.util.Arrays; 28 | import java.util.concurrent.TimeoutException; 29 | 30 | abstract class AbstractRejectTest extends BrokerTestCase { 31 | 32 | protected Channel secondaryChannel; 33 | 34 | @Override 35 | public void setUp() throws IOException, TimeoutException { 36 | super.setUp(); 37 | secondaryChannel = connection.createChannel(); 38 | } 39 | 40 | @Override 41 | public void tearDown() throws IOException, TimeoutException { 42 | if (secondaryChannel != null) { 43 | secondaryChannel.abort(); 44 | secondaryChannel = null; 45 | } 46 | super.tearDown(); 47 | } 48 | 49 | protected long checkDelivery(QueueingConsumer.Delivery d, byte[] msg, boolean redelivered) { 50 | assertNotNull(d); 51 | return checkDelivery(d.getEnvelope(), d.getBody(), msg, redelivered); 52 | } 53 | 54 | protected long checkDelivery(GetResponse r, byte[] msg, boolean redelivered) { 55 | assertNotNull(r); 56 | return checkDelivery(r.getEnvelope(), r.getBody(), msg, redelivered); 57 | } 58 | 59 | protected long checkDelivery(Envelope e, byte[] m, byte[] msg, boolean redelivered) { 60 | assertNotNull(e); 61 | assertTrue(Arrays.equals(m, msg)); 62 | 63 | // Pulsar-RabbitMQ edit: redelivery count doesn't work well with BasicGet 64 | // as closing the consumer will not increment the redelivery counter 65 | // assertEquals(e.isRedeliver(), redelivered); 66 | 67 | return e.getDeliveryTag(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/NoRequeueOnCancel.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import static org.junit.Assert.assertNotNull; 19 | import static org.junit.Assert.assertNull; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import com.datastax.oss.starlight.rabbitmqtests.SystemTest; 23 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.BrokerTestCase; 24 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.TestUtils; 25 | import com.rabbitmq.client.*; 26 | import java.io.IOException; 27 | import java.util.concurrent.CountDownLatch; 28 | import java.util.concurrent.TimeUnit; 29 | import org.junit.experimental.categories.Category; 30 | 31 | @Category(SystemTest.class) 32 | public class NoRequeueOnCancel extends BrokerTestCase { 33 | protected final String Q = "NoRequeueOnCancel"; 34 | 35 | protected void createResources() throws IOException { 36 | channel.queueDeclare(Q, false, false, false, null); 37 | } 38 | 39 | protected void releaseResources() throws IOException { 40 | channel.queueDelete(Q); 41 | } 42 | 43 | // Contrary to AMQP spec, canceling the consumer requeues the unacked messages 44 | // @Test 45 | public void noRequeueOnCancel() throws IOException, InterruptedException { 46 | channel.basicPublish("", Q, null, "1".getBytes()); 47 | 48 | final CountDownLatch latch = new CountDownLatch(1); 49 | Consumer c = 50 | new DefaultConsumer(channel) { 51 | @Override 52 | public void handleDelivery( 53 | String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) 54 | throws IOException { 55 | latch.countDown(); 56 | } 57 | }; 58 | String consumerTag = channel.basicConsume(Q, false, c); 59 | assertTrue(latch.await(5, TimeUnit.SECONDS)); 60 | 61 | channel.basicCancel(consumerTag); 62 | 63 | assertNull(TestUtils.basicGet(channel, Q, true)); 64 | 65 | closeChannel(); 66 | openChannel(); 67 | 68 | assertNotNull(TestUtils.basicGet(channel, Q, true)); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/DefaultExchange.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import static org.junit.Assert.fail; 19 | 20 | import com.datastax.oss.starlight.rabbitmqtests.SystemTest; 21 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.BrokerTestCase; 22 | import com.rabbitmq.client.AMQP; 23 | import java.io.IOException; 24 | import org.junit.Test; 25 | import org.junit.experimental.categories.Category; 26 | 27 | @Category(SystemTest.class) 28 | public class DefaultExchange extends BrokerTestCase { 29 | String queueName; 30 | 31 | @Override 32 | protected void createResources() throws IOException { 33 | queueName = channel.queueDeclare().getQueue(); 34 | } 35 | 36 | // Publish and declare are the only operations 37 | // permitted on the default exchange 38 | 39 | @Test 40 | public void defaultExchangePublish() throws Exception { 41 | basicPublishVolatile("", queueName); // Implicit binding 42 | 43 | assertDelivered(queueName, 1); 44 | } 45 | 46 | @Test 47 | public void bindToDefaultExchange() throws IOException { 48 | try { 49 | channel.queueBind(queueName, "", "foobar"); 50 | fail(); 51 | } catch (IOException ioe) { 52 | checkShutdownSignal(AMQP.ACCESS_REFUSED, ioe); 53 | } 54 | } 55 | 56 | @Test 57 | public void unbindFromDefaultExchange() throws IOException { 58 | try { 59 | channel.queueUnbind(queueName, "", queueName); 60 | fail(); 61 | } catch (IOException ioe) { 62 | checkShutdownSignal(AMQP.ACCESS_REFUSED, ioe); 63 | } 64 | } 65 | 66 | @Test 67 | public void declareDefaultExchange() throws IOException { 68 | try { 69 | channel.exchangeDeclare("", "direct", true); 70 | fail(); 71 | } catch (IOException ioe) { 72 | checkShutdownSignal(AMQP.ACCESS_REFUSED, ioe); 73 | } 74 | } 75 | 76 | @Test 77 | public void deleteDefaultExchange() throws IOException { 78 | try { 79 | channel.exchangeDelete(""); 80 | fail(); 81 | } catch (IOException ioe) { 82 | checkShutdownSignal(AMQP.ACCESS_REFUSED, ioe); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/BasicGet.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import static org.junit.Assert.assertFalse; 19 | import static org.junit.Assert.assertNotNull; 20 | import static org.junit.Assert.assertNull; 21 | import static org.junit.Assert.assertTrue; 22 | import static org.junit.Assert.fail; 23 | 24 | import com.datastax.oss.starlight.rabbitmqtests.SystemTest; 25 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.BrokerTestCase; 26 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.TestUtils; 27 | import com.rabbitmq.client.AlreadyClosedException; 28 | import com.rabbitmq.client.Channel; 29 | import java.io.IOException; 30 | import java.util.concurrent.TimeoutException; 31 | import org.junit.Test; 32 | import org.junit.experimental.categories.Category; 33 | 34 | @Category(SystemTest.class) 35 | public class BasicGet extends BrokerTestCase { 36 | @Test 37 | public void basicGetWithEnqueuedMessages() throws IOException, InterruptedException { 38 | assertTrue(channel.isOpen()); 39 | String q = channel.queueDeclare().getQueue(); 40 | 41 | basicPublishPersistent("msg".getBytes("UTF-8"), q); 42 | 43 | assertNotNull(TestUtils.basicGet(channel, q, true)); 44 | channel.queuePurge(q); 45 | assertNull(TestUtils.basicGet(channel, q, true)); 46 | channel.queueDelete(q); 47 | } 48 | 49 | @Test 50 | public void basicGetWithEmptyQueue() throws IOException, InterruptedException { 51 | assertTrue(channel.isOpen()); 52 | String q = channel.queueDeclare().getQueue(); 53 | 54 | assertNull(TestUtils.basicGet(channel, q, true)); 55 | channel.queueDelete(q); 56 | } 57 | 58 | @Test 59 | public void basicGetWithClosedChannel() 60 | throws IOException, InterruptedException, TimeoutException { 61 | assertTrue(channel.isOpen()); 62 | String q = channel.queueDeclare().getQueue(); 63 | 64 | channel.close(); 65 | assertFalse(channel.isOpen()); 66 | try { 67 | channel.basicGet(q, true); 68 | fail("expected basic.get on a closed channel to fail"); 69 | } catch (AlreadyClosedException e) { 70 | // passed 71 | } finally { 72 | Channel tch = connection.createChannel(); 73 | tch.queueDelete(q); 74 | tch.close(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/MessageUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import java.util.Base64; 19 | import org.apache.pulsar.client.api.Message; 20 | import org.apache.pulsar.common.naming.TopicName; 21 | import org.apache.qpid.server.bytebuffer.QpidByteBuffer; 22 | import org.apache.qpid.server.protocol.v0_8.AMQFrameDecodingException; 23 | import org.apache.qpid.server.protocol.v0_8.AMQShortString; 24 | import org.apache.qpid.server.protocol.v0_8.transport.BasicContentHeaderProperties; 25 | import org.apache.qpid.server.protocol.v0_8.transport.ContentHeaderBody; 26 | import org.apache.qpid.server.protocol.v0_8.transport.MessagePublishInfo; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | public final class MessageUtils { 31 | 32 | private static final Logger LOGGER = LoggerFactory.getLogger(AMQChannel.class); 33 | 34 | public static final String MESSAGE_PROPERTY_AMQP_HEADERS = "amqp-headers"; 35 | public static final String MESSAGE_PROPERTY_AMQP_IMMEDIATE = "amqp-immediate"; 36 | public static final String MESSAGE_PROPERTY_AMQP_MANDATORY = "amqp-mandatory"; 37 | 38 | public static MessagePublishInfo getMessagePublishInfo(Message message) { 39 | String localName = TopicName.get(message.getTopicName()).getLocalName(); 40 | String[] split = localName.split(".__", 2); 41 | String routingKey = ""; 42 | String exchange = split[0]; 43 | if (split.length > 1) { 44 | routingKey = split[1]; 45 | } 46 | return new MessagePublishInfo( 47 | AMQShortString.createAMQShortString(exchange), 48 | Boolean.parseBoolean(message.getProperty(MessageUtils.MESSAGE_PROPERTY_AMQP_IMMEDIATE)), 49 | Boolean.parseBoolean(message.getProperty(MessageUtils.MESSAGE_PROPERTY_AMQP_MANDATORY)), 50 | AMQShortString.createAMQShortString(routingKey)); 51 | } 52 | 53 | public static ContentHeaderBody getContentHeaderBody(Message message) { 54 | try { 55 | String headersProperty = message.getProperty(MessageUtils.MESSAGE_PROPERTY_AMQP_HEADERS); 56 | if (headersProperty != null) { 57 | byte[] headers = Base64.getDecoder().decode(headersProperty); 58 | QpidByteBuffer buf = QpidByteBuffer.wrap(headers); 59 | return ContentHeaderBody.createFromBuffer(buf, headers.length); 60 | } 61 | } catch (AMQFrameDecodingException | IllegalArgumentException e) { 62 | LOGGER.error("Couldn't decode AMQP headers", e); 63 | } 64 | return new ContentHeaderBody(new BasicContentHeaderProperties(), message.size()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/Reject.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import static org.junit.Assert.assertNull; 19 | 20 | import com.datastax.oss.starlight.rabbitmqtests.SystemTest; 21 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.QueueingConsumer; 22 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.TestUtils; 23 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.TestUtils.CallableFunction; 24 | import com.rabbitmq.client.AMQP; 25 | import com.rabbitmq.client.Channel; 26 | import java.util.Collections; 27 | import java.util.UUID; 28 | import org.junit.Test; 29 | import org.junit.experimental.categories.Category; 30 | import org.junit.runner.RunWith; 31 | import org.junit.runners.Parameterized; 32 | 33 | @Category(SystemTest.class) 34 | @RunWith(Parameterized.class) 35 | public class Reject extends AbstractRejectTest { 36 | 37 | @Parameterized.Parameters 38 | public static Object[] queueCreators() { 39 | return new Object[] { 40 | (CallableFunction) 41 | channel -> { 42 | String q = UUID.randomUUID().toString(); 43 | channel.queueDeclare( 44 | q, true, false, false, Collections.singletonMap("x-queue-type", "quorum")); 45 | return q; 46 | }, 47 | (CallableFunction) 48 | channel -> { 49 | String q = UUID.randomUUID().toString(); 50 | channel.queueDeclare( 51 | q, true, false, false, Collections.singletonMap("x-queue-type", "classic")); 52 | return q; 53 | } 54 | }; 55 | } 56 | 57 | @Parameterized.Parameter public TestUtils.CallableFunction queueCreator; 58 | 59 | @Test 60 | public void reject() throws Exception { 61 | String q = queueCreator.apply(channel); 62 | 63 | channel.confirmSelect(); 64 | 65 | byte[] m1 = "1".getBytes(); 66 | byte[] m2 = "2".getBytes(); 67 | 68 | basicPublishVolatile(m1, q); 69 | basicPublishVolatile(m2, q); 70 | 71 | channel.waitForConfirmsOrDie(1000); 72 | 73 | long tag1 = checkDelivery(TestUtils.basicGet(channel, q, false), m1, false); 74 | long tag2 = checkDelivery(TestUtils.basicGet(channel, q, false), m2, false); 75 | QueueingConsumer c = new QueueingConsumer(secondaryChannel); 76 | String consumerTag = secondaryChannel.basicConsume(q, false, c); 77 | channel.basicReject(tag2, true); 78 | long tag3 = checkDelivery(c.nextDelivery(), m2, true); 79 | 80 | // Pulsar-RabbitMQ edit: contrary to AMQP spec, canceling the consumer requeues the unacked 81 | // messages. 82 | // secondaryChannel.basicCancel(consumerTag); 83 | 84 | secondaryChannel.basicReject(tag3, false); 85 | assertNull(TestUtils.basicGet(channel, q, false)); 86 | channel.basicAck(tag1, false); 87 | channel.basicReject(tag3, false); 88 | expectError(AMQP.PRECONDITION_FAILED); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/GatewayProxyProtocolHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import static com.datastax.oss.starlight.rabbitmq.ConfigurationUtils.convertFrom; 19 | import static org.apache.commons.lang3.StringUtils.isBlank; 20 | 21 | import io.netty.channel.ChannelInitializer; 22 | import io.netty.channel.socket.SocketChannel; 23 | import java.net.InetSocketAddress; 24 | import java.util.List; 25 | import java.util.Map; 26 | import lombok.SneakyThrows; 27 | import org.apache.pulsar.broker.PulsarServerException; 28 | import org.apache.pulsar.broker.authentication.AuthenticationService; 29 | import org.apache.pulsar.policies.data.loadbalancer.ServiceLookupData; 30 | import org.apache.pulsar.proxy.extensions.ProxyExtension; 31 | import org.apache.pulsar.proxy.server.ProxyConfiguration; 32 | import org.apache.pulsar.proxy.server.ProxyService; 33 | 34 | public class GatewayProxyProtocolHandler implements ProxyExtension { 35 | 36 | private static final String PROTOCOL_NAME = "rabbitmq"; 37 | public static final String METRICS_PREFIX = "pulsar_proxy"; 38 | 39 | private GatewayConfiguration config; 40 | private GatewayService service; 41 | 42 | @Override 43 | public String extensionName() { 44 | return PROTOCOL_NAME; 45 | } 46 | 47 | @Override 48 | public boolean accept(String protocol) { 49 | return PROTOCOL_NAME.equals(protocol); 50 | } 51 | 52 | @SneakyThrows 53 | @Override 54 | public void initialize(ProxyConfiguration conf) { 55 | config = ConfigurationUtils.create(conf.getProperties(), GatewayConfiguration.class); 56 | } 57 | 58 | @SneakyThrows 59 | @Override 60 | public void start(ProxyService proxyService) { 61 | if (isBlank(config.getBrokerServiceURL())) { 62 | List availableBrokers = 63 | proxyService.getDiscoveryProvider().getAvailableBrokers(); 64 | if (availableBrokers.size() == 0) { 65 | throw new PulsarServerException("No active broker is available"); 66 | } 67 | ServiceLookupData lookupData = availableBrokers.get(0); 68 | service = 69 | new GatewayService( 70 | config, 71 | new AuthenticationService(convertFrom(config)), 72 | config.isTlsEnabledWithBroker() 73 | ? lookupData.getPulsarServiceUrlTls() 74 | : lookupData.getPulsarServiceUrl(), 75 | config.isTlsEnabledWithBroker() 76 | ? lookupData.getWebServiceUrlTls() 77 | : lookupData.getWebServiceUrl(), 78 | METRICS_PREFIX); 79 | } else { 80 | service = 81 | new GatewayService( 82 | config, new AuthenticationService(convertFrom(config)), "pulsar_proxy"); 83 | } 84 | service.start(false); 85 | } 86 | 87 | @Override 88 | public Map> newChannelInitializers() { 89 | return service.newChannelInitializers(); 90 | } 91 | 92 | @SneakyThrows 93 | @Override 94 | public void close() { 95 | if (service != null) { 96 | service.close(); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /nar-tests/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | starlight-rabbitmq-parent 22 | com.datastax.oss 23 | 2.10.1.2-SNAPSHOT 24 | 25 | 4.0.0 26 | nar-tests 27 | jar 28 | Starlight for RabbitMQ tests of the NAR archive 29 | 30 | ${project.build.directory} 31 | 32 | 33 | 34 | ${pulsar.groupId} 35 | pulsar-proxy 36 | 37 | 38 | com.rabbitmq 39 | amqp-client 40 | test 41 | 42 | 43 | org.apache.curator 44 | curator-test 45 | test 46 | 47 | 48 | org.testcontainers 49 | pulsar 50 | test 51 | 52 | 53 | org.junit.jupiter 54 | junit-jupiter-engine 55 | test 56 | 57 | 58 | org.junit.jupiter 59 | junit-jupiter-params 60 | test 61 | 62 | 63 | ${pulsar.groupId} 64 | pulsar-broker 65 | test 66 | 67 | 68 | 69 | 70 | 71 | org.apache.maven.plugins 72 | maven-antrun-plugin 73 | 74 | 75 | process-test-resources 76 | 77 | run 78 | 79 | 80 | 81 | copy proxy protocol handler 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | org.apache.maven.plugins 90 | maven-failsafe-plugin 91 | 92 | 93 | 94 | integration-test 95 | verify 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/resources/authentication/tls/client-cert.pem: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 1 (0x0) 4 | Serial Number: 5 | 0c:26:15:df:8f:71:1d:6a:31:d0:da:af:64:ef:80:de:ac:9a:46:74 6 | Signature Algorithm: sha256WithRSAEncryption 7 | Issuer: CN = CARoot 8 | Validity 9 | Not Before: Apr 23 17:08:51 2021 GMT 10 | Not After : Apr 21 17:08:51 2031 GMT 11 | Subject: C = US, ST = CA, O = Apache, OU = Apache Pulsar, CN = superUser 12 | Subject Public Key Info: 13 | Public Key Algorithm: rsaEncryption 14 | RSA Public-Key: (2048 bit) 15 | Modulus: 16 | 00:cd:43:7d:98:40:f9:b0:5b:bc:ae:db:c0:0b:ad: 17 | 26:90:96:e0:62:38:ed:68:b1:70:46:3b:de:44:f9: 18 | 14:51:86:10:eb:ca:90:e7:88:e8:f9:91:85:e0:dd: 19 | b5:b4:14:b9:78:e3:86:d5:54:6d:68:ec:14:92:b4: 20 | f8:22:5b:05:3d:ed:31:25:65:08:05:84:ca:e6:0c: 21 | 21:12:58:32:c7:1a:60:a3:4f:d2:4a:9e:28:19:7c: 22 | 45:84:00:8c:89:dc:de:8a:e5:4f:88:91:cc:a4:f1: 23 | 81:45:4c:7d:c2:ff:e2:c1:89:c6:12:73:95:e2:36: 24 | bd:db:ae:8b:5a:68:6a:90:51:de:2b:88:5f:aa:67: 25 | f4:a8:e3:63:dc:be:19:82:cc:9d:7f:e6:8d:fb:82: 26 | be:22:01:3d:56:13:3b:5b:04:b4:e8:c5:18:e6:2e: 27 | 0d:fa:ba:4a:8d:e8:c6:5a:a1:51:9a:4a:62:d7:af: 28 | dd:b4:fc:e2:d5:cd:ae:99:6c:5c:61:56:0b:d7:0c: 29 | 1a:77:5c:f5:3a:6a:54:b5:9e:33:ac:a9:75:28:9a: 30 | 76:af:d0:7a:57:00:1b:91:13:31:fd:42:88:21:47: 31 | 05:10:01:2f:59:bb:c7:3a:d9:e1:58:4c:1b:6c:71: 32 | b6:98:ef:dd:03:82:58:a3:32:dc:90:a1:b6:a6:1e: 33 | e1:0b 34 | Exponent: 65537 (0x10001) 35 | Signature Algorithm: sha256WithRSAEncryption 36 | 33:40:2a:38:48:99:a0:fe:68:4d:07:3b:08:ae:af:a1:7c:ea: 37 | 70:ab:a7:c8:32:b4:ff:9f:5a:51:3b:2b:a2:aa:21:75:44:7d: 38 | be:e7:fb:08:b9:81:e5:4c:cf:01:86:f9:06:63:4f:ce:7a:1d: 39 | cb:1e:9e:8f:d5:0a:54:53:69:91:05:10:2c:b0:4f:d4:3a:b5: 40 | 25:0e:25:4c:eb:67:64:d7:85:29:77:63:30:da:2a:77:3f:59: 41 | c2:8c:e9:02:57:49:93:3a:51:91:1a:b2:59:4d:d5:69:c9:9d: 42 | cc:e2:4f:b2:6c:5b:ba:45:68:c7:f5:18:f4:1d:b8:0c:eb:fd: 43 | 0a:cf:10:5d:dc:3e:26:49:03:33:37:40:f7:96:88:82:99:5c: 44 | 38:8d:cc:3b:de:b5:b9:ee:f9:ac:ae:ce:03:9a:1e:a7:f8:02: 45 | 73:2e:af:e7:b0:22:cb:3d:a3:ca:85:16:e9:e6:e2:d6:bf:1c: 46 | 1a:4c:ea:14:49:52:84:67:38:97:c7:b3:30:72:cc:c6:08:e5: 47 | 40:0a:87:da:19:98:26:4f:0b:54:43:a2:a0:ea:51:b2:23:88: 48 | d2:b4:0e:82:4f:02:92:a4:fb:27:e2:06:15:76:e7:27:f2:a2: 49 | e4:23:7b:24:ca:e6:80:93:2b:cd:54:ca:1b:9b:fd:d9:59:d1: 50 | 96:31:25:7b 51 | -----BEGIN CERTIFICATE----- 52 | MIIC7zCCAdcCFAwmFd+PcR1qMdDar2TvgN6smkZ0MA0GCSqGSIb3DQEBCwUAMBEx 53 | DzANBgNVBAMMBkNBUm9vdDAeFw0yMTA0MjMxNzA4NTFaFw0zMTA0MjExNzA4NTFa 54 | MFcxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEPMA0GA1UEChMGQXBhY2hlMRYw 55 | FAYDVQQLEw1BcGFjaGUgUHVsc2FyMRIwEAYDVQQDEwlzdXBlclVzZXIwggEiMA0G 56 | CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNQ32YQPmwW7yu28ALrSaQluBiOO1o 57 | sXBGO95E+RRRhhDrypDniOj5kYXg3bW0FLl444bVVG1o7BSStPgiWwU97TElZQgF 58 | hMrmDCESWDLHGmCjT9JKnigZfEWEAIyJ3N6K5U+Ikcyk8YFFTH3C/+LBicYSc5Xi 59 | Nr3brotaaGqQUd4riF+qZ/So42PcvhmCzJ1/5o37gr4iAT1WEztbBLToxRjmLg36 60 | ukqN6MZaoVGaSmLXr920/OLVza6ZbFxhVgvXDBp3XPU6alS1njOsqXUomnav0HpX 61 | ABuREzH9QoghRwUQAS9Zu8c62eFYTBtscbaY790DglijMtyQobamHuELAgMBAAEw 62 | DQYJKoZIhvcNAQELBQADggEBADNAKjhImaD+aE0HOwiur6F86nCrp8gytP+fWlE7 63 | K6KqIXVEfb7n+wi5geVMzwGG+QZjT856Hcseno/VClRTaZEFECywT9Q6tSUOJUzr 64 | Z2TXhSl3YzDaKnc/WcKM6QJXSZM6UZEasllN1WnJncziT7JsW7pFaMf1GPQduAzr 65 | /QrPEF3cPiZJAzM3QPeWiIKZXDiNzDvetbnu+ayuzgOaHqf4AnMur+ewIss9o8qF 66 | Funm4ta/HBpM6hRJUoRnOJfHszByzMYI5UAKh9oZmCZPC1RDoqDqUbIjiNK0DoJP 67 | ApKk+yfiBhV25yfyouQjeyTK5oCTK81Uyhub/dlZ0ZYxJXs= 68 | -----END CERTIFICATE----- 69 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/resources/authentication/tls/server-cert.pem: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 1 (0x0) 4 | Serial Number: 5 | 0c:26:15:df:8f:71:1d:6a:31:d0:da:af:64:ef:80:de:ac:9a:46:75 6 | Signature Algorithm: sha256WithRSAEncryption 7 | Issuer: CN = CARoot 8 | Validity 9 | Not Before: Apr 23 17:08:51 2021 GMT 10 | Not After : Apr 21 17:08:51 2031 GMT 11 | Subject: C = US, ST = CA, O = Apache, OU = Apache Pulsar, CN = localhost 12 | Subject Public Key Info: 13 | Public Key Algorithm: rsaEncryption 14 | RSA Public-Key: (2048 bit) 15 | Modulus: 16 | 00:af:bf:b7:2d:98:ad:9d:f6:da:a3:13:d4:62:0f: 17 | 98:be:1c:a2:89:22:ba:6f:d5:fd:1f:67:e3:91:03: 18 | 98:80:81:0e:ed:d8:f6:70:7f:2c:36:68:3d:53:ea: 19 | 58:3a:a6:d5:89:66:4b:bd:1e:57:71:13:6d:4b:11: 20 | e5:40:a5:76:84:24:92:40:58:80:96:c9:1f:2c:c4: 21 | 55:eb:a3:79:73:70:5c:37:9a:89:ed:2f:ba:6b:e3: 22 | 82:7c:69:4a:02:54:8b:81:5e:3c:bf:4c:8a:cb:ea: 23 | 2c:5e:83:e7:b7:10:08:5f:82:58:a3:89:d1:da:92: 24 | ba:2a:28:ee:30:28:3f:5b:ae:10:71:96:c7:e1:12: 25 | c5:b0:1a:ad:44:6f:44:3a:11:4a:9a:3c:0f:8d:06: 26 | 80:7b:34:ef:3f:6c:f4:5e:c5:44:54:1e:c8:dd:c7: 27 | 80:85:80:d9:68:e6:c6:53:03:77:e1:fe:18:61:07: 28 | 77:05:4c:ed:59:bc:5d:41:38:6a:ef:5d:a1:b2:60: 29 | 98:d4:48:28:95:02:8a:0e:fd:cf:7b:1b:d2:11:cc: 30 | 10:0c:50:73:d7:cc:38:6c:83:dd:79:26:aa:90:c8: 31 | 9b:84:86:bc:59:e9:62:69:f4:98:1b:c4:80:78:7e: 32 | a0:1a:81:9d:d2:e1:66:dd:c4:cc:fc:63:04:ac:ec: 33 | a7:35 34 | Exponent: 65537 (0x10001) 35 | Signature Algorithm: sha256WithRSAEncryption 36 | 81:a7:27:69:49:e6:1b:c0:f2:a6:10:c2:ef:c7:64:27:69:53: 37 | 3c:bd:8e:7c:b7:b8:bd:2a:02:d4:ab:4b:f3:7b:25:e8:1e:d8: 38 | 3d:88:00:04:6c:a0:da:67:57:65:5d:a2:b6:1d:9a:8c:c7:bd: 39 | 27:53:78:6a:61:3f:61:c1:23:d5:34:65:f1:49:ec:20:5d:f1: 40 | 01:90:99:e8:e6:99:17:ae:c3:ed:e5:da:c4:f1:8c:89:e8:38: 41 | c1:01:e0:84:27:bf:01:f5:ee:62:87:55:6c:63:fc:45:12:d3: 42 | 2f:f7:e2:b9:f0:33:d0:84:1e:6b:23:7b:3e:ae:25:f6:ff:11: 43 | 12:f4:12:63:b6:88:5d:01:aa:ce:c9:e4:d8:78:a2:2d:4c:9a: 44 | 50:4d:57:80:6a:4b:2d:19:4c:61:21:6a:7a:06:2b:cf:82:ae: 45 | f3:61:b0:ef:62:ae:3b:2d:2d:0d:c8:da:75:49:72:5a:1c:8b: 46 | 15:c2:bb:07:5b:37:81:f6:42:e4:84:29:4c:cb:fc:4d:e1:86: 47 | 9b:86:af:1f:03:08:58:b0:15:4c:72:fd:e6:62:e2:b2:37:ca: 48 | eb:a4:67:ec:12:8f:95:57:d7:e7:cf:fe:b5:f9:4a:55:66:c4: 49 | 2f:af:e9:65:a9:54:a8:9d:1a:1e:9a:9e:ec:60:bf:b5:ef:2b: 50 | b6:d5:02:e9 51 | -----BEGIN CERTIFICATE----- 52 | MIIC7zCCAdcCFAwmFd+PcR1qMdDar2TvgN6smkZ1MA0GCSqGSIb3DQEBCwUAMBEx 53 | DzANBgNVBAMMBkNBUm9vdDAeFw0yMTA0MjMxNzA4NTFaFw0zMTA0MjExNzA4NTFa 54 | MFcxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEPMA0GA1UEChMGQXBhY2hlMRYw 55 | FAYDVQQLEw1BcGFjaGUgUHVsc2FyMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0G 56 | CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvv7ctmK2d9tqjE9RiD5i+HKKJIrpv 57 | 1f0fZ+ORA5iAgQ7t2PZwfyw2aD1T6lg6ptWJZku9HldxE21LEeVApXaEJJJAWICW 58 | yR8sxFXro3lzcFw3montL7pr44J8aUoCVIuBXjy/TIrL6ixeg+e3EAhfglijidHa 59 | kroqKO4wKD9brhBxlsfhEsWwGq1Eb0Q6EUqaPA+NBoB7NO8/bPRexURUHsjdx4CF 60 | gNlo5sZTA3fh/hhhB3cFTO1ZvF1BOGrvXaGyYJjUSCiVAooO/c97G9IRzBAMUHPX 61 | zDhsg915JqqQyJuEhrxZ6WJp9JgbxIB4fqAagZ3S4WbdxMz8YwSs7Kc1AgMBAAEw 62 | DQYJKoZIhvcNAQELBQADggEBAIGnJ2lJ5hvA8qYQwu/HZCdpUzy9jny3uL0qAtSr 63 | S/N7Jege2D2IAARsoNpnV2VdorYdmozHvSdTeGphP2HBI9U0ZfFJ7CBd8QGQmejm 64 | mReuw+3l2sTxjInoOMEB4IQnvwH17mKHVWxj/EUS0y/34rnwM9CEHmsjez6uJfb/ 65 | ERL0EmO2iF0Bqs7J5Nh4oi1MmlBNV4BqSy0ZTGEhanoGK8+CrvNhsO9irjstLQ3I 66 | 2nVJclocixXCuwdbN4H2QuSEKUzL/E3hhpuGrx8DCFiwFUxy/eZi4rI3yuukZ+wS 67 | j5VX1+fP/rX5SlVmxC+v6WWpVKidGh6anuxgv7XvK7bVAuk= 68 | -----END CERTIFICATE----- 69 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/DirectExchange.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import com.datastax.oss.starlight.rabbitmq.metadata.BindingMetadata; 19 | import com.datastax.oss.starlight.rabbitmq.metadata.BindingSetMetadata; 20 | import com.datastax.oss.starlight.rabbitmq.metadata.VirtualHostMetadata; 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | import java.util.UUID; 24 | import java.util.concurrent.CompletableFuture; 25 | import org.apache.pulsar.client.api.MessageId; 26 | import org.apache.qpid.server.model.LifetimePolicy; 27 | 28 | public final class DirectExchange extends AbstractExchange { 29 | 30 | public DirectExchange(String name, boolean durable, LifetimePolicy lifetimePolicy) { 31 | super(name, Type.direct, durable, lifetimePolicy); 32 | } 33 | 34 | @Override 35 | public CompletableFuture bind( 36 | VirtualHostMetadata vhost, 37 | String exchange, 38 | String queue, 39 | String routingKey, 40 | GatewayConnection connection) { 41 | BindingSetMetadata bindings = vhost.getExchanges().get(exchange).getBindings().get(queue); 42 | if (bindings != null && !bindings.getKeys().contains(routingKey)) { 43 | bindings.getKeys().add(routingKey); 44 | /* 45 | TODO: Pulsar 2.8 has an issue with subscriptions containing a / in their name. Forge another name ? 46 | */ 47 | String topic = getTopicName(connection.getNamespace(), name, routingKey).toString(); 48 | String subscriptionName = (topic + "-" + UUID.randomUUID()).replace("/", "_"); 49 | return connection 50 | .getGatewayService() 51 | .getPulsarAdmin() 52 | .topics() 53 | .createSubscriptionAsync(topic, subscriptionName, MessageId.latest) 54 | .thenAccept( 55 | it -> { 56 | BindingMetadata bindingMetadata = 57 | new BindingMetadata(exchange, topic, subscriptionName); 58 | bindingMetadata.getKeys().add(routingKey); 59 | Map subscriptions = 60 | vhost.getSubscriptions().computeIfAbsent(queue, q -> new HashMap<>()); 61 | subscriptions.put(subscriptionName, bindingMetadata); 62 | }); 63 | } 64 | return CompletableFuture.completedFuture(null); 65 | } 66 | 67 | @Override 68 | public CompletableFuture unbind( 69 | VirtualHostMetadata vhost, 70 | String exchange, 71 | String queue, 72 | String routingKey, 73 | GatewayConnection connection) { 74 | 75 | BindingSetMetadata bindings = vhost.getExchanges().get(exchange).getBindings().get(queue); 76 | if (bindings.getKeys().remove(routingKey)) { 77 | Map queueSubscriptions = vhost.getSubscriptions().get(queue); 78 | for (BindingMetadata subscription : queueSubscriptions.values()) { 79 | if (subscription.getLastMessageId() == null 80 | && subscription.getExchange().equals(exchange) 81 | && subscription.getKeys().contains(routingKey)) { 82 | return connection 83 | .getGatewayService() 84 | .getPulsarAdmin() 85 | .topics() 86 | .getLastMessageIdAsync(subscription.getTopic()) 87 | .thenAccept( 88 | lastMessageId -> subscription.setLastMessageId(lastMessageId.toByteArray())); 89 | } 90 | } 91 | } 92 | return CompletableFuture.completedFuture(null); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/UnacknowledgedMessageMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import java.util.Collection; 19 | import java.util.Collections; 20 | import java.util.HashMap; 21 | import java.util.LinkedHashMap; 22 | import java.util.Map; 23 | import java.util.concurrent.ConcurrentHashMap; 24 | import org.apache.pulsar.client.api.MessageId; 25 | import org.apache.qpid.server.flow.FlowCreditManager; 26 | import org.apache.qpid.server.util.ConnectionScopedRuntimeException; 27 | 28 | /** 29 | * See {@link org.apache.qpid.server.protocol.v0_8.UnacknowledgedMessageMap} and 30 | * org.apache.qpid.server.protocol.v0_8.UnacknowledgedMessageMapImpl 31 | */ 32 | public class UnacknowledgedMessageMap { 33 | 34 | private final Map _map = new ConcurrentHashMap<>(); 35 | 36 | private final FlowCreditManager creditManager; 37 | 38 | UnacknowledgedMessageMap(FlowCreditManager creditManager) { 39 | this.creditManager = creditManager; 40 | } 41 | 42 | public void collect( 43 | long deliveryTag, boolean multiple, Map msgs) { 44 | if (multiple) { 45 | collect(deliveryTag, msgs); 46 | } else { 47 | final MessageConsumerAssociation messageConsumerAssociation = _map.get(deliveryTag); 48 | if (messageConsumerAssociation != null) { 49 | msgs.put(deliveryTag, messageConsumerAssociation); 50 | } 51 | } 52 | } 53 | 54 | private void remove(Collection msgs) { 55 | for (Long deliveryTag : msgs) { 56 | remove(deliveryTag, true); 57 | } 58 | } 59 | 60 | public MessageConsumerAssociation remove(long deliveryTag, final boolean restoreCredit) { 61 | MessageConsumerAssociation entry = _map.remove(deliveryTag); 62 | if (entry != null) { 63 | if (restoreCredit && entry.isUsesCredit()) { 64 | creditManager.restoreCredit(1, entry.getSize()); 65 | } 66 | } 67 | return entry; 68 | } 69 | 70 | public void add(long deliveryTag, MessageConsumerAssociation messageConsumerAssociation) { 71 | if (_map.put(deliveryTag, messageConsumerAssociation) != null) { 72 | throw new ConnectionScopedRuntimeException("Unexpected duplicate delivery tag created"); 73 | } 74 | } 75 | 76 | public MessageId get(long key) { 77 | MessageConsumerAssociation association = _map.get(key); 78 | return association == null ? null : association.getMessageId(); 79 | } 80 | 81 | public Map all() { 82 | return new HashMap<>(_map); 83 | } 84 | 85 | public Collection acknowledge(long deliveryTag, boolean multiple) { 86 | if (multiple) { 87 | Map ackedMessageMap = new LinkedHashMap<>(); 88 | collect(deliveryTag, true, ackedMessageMap); 89 | remove(ackedMessageMap.keySet()); 90 | return ackedMessageMap.values(); 91 | } else { 92 | final MessageConsumerAssociation association = remove(deliveryTag, true); 93 | if (association != null) { 94 | final MessageId messageId = association.getMessageId(); 95 | if (messageId != null) { 96 | return Collections.singleton(association); 97 | } 98 | } 99 | return Collections.emptySet(); 100 | } 101 | } 102 | 103 | private void collect(long key, Map msgs) { 104 | for (Map.Entry entry : _map.entrySet()) { 105 | if (entry.getKey() <= key) { 106 | msgs.put(entry.getKey(), entry.getValue()); 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/resources/authentication/tls/cacert.pem: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 5 | 7f:c3:12:28:23:73:86:8e:bb:d6:e6:21:43:e3:72:e8:01:17:3e:d1 6 | Signature Algorithm: sha256WithRSAEncryption 7 | Issuer: CN = CARoot 8 | Validity 9 | Not Before: Apr 23 17:08:51 2021 GMT 10 | Not After : Apr 21 17:08:51 2031 GMT 11 | Subject: CN = CARoot 12 | Subject Public Key Info: 13 | Public Key Algorithm: rsaEncryption 14 | RSA Public-Key: (2048 bit) 15 | Modulus: 16 | 00:b3:6a:94:67:7c:33:90:4e:db:b9:94:b0:a6:1a: 17 | 69:77:bb:33:31:fe:3c:8b:6d:8a:f1:cf:07:d9:87: 18 | 86:ad:45:cf:4c:e3:e7:35:d5:4b:a3:76:27:9b:30: 19 | b1:82:3f:57:29:c9:f0:be:25:49:25:16:64:58:cc: 20 | b0:f1:01:2e:19:69:52:c8:38:64:61:16:b4:a7:ba: 21 | 76:2b:54:e6:a5:80:6c:b6:6c:8a:3c:c1:06:c2:e1: 22 | c1:f3:18:6b:87:08:4b:bb:54:f4:b3:72:1d:f2:ce: 23 | 47:18:5f:82:d3:88:c9:39:7b:71:fc:71:1a:aa:7e: 24 | 55:6c:35:7f:83:c1:60:e7:7d:b1:80:d0:17:7a:ed: 25 | e7:0d:87:8b:59:e3:18:47:e9:cf:de:0d:0e:c6:3e: 26 | 5c:eb:6e:f4:43:95:31:01:2d:e8:f2:ba:8a:bf:ed: 27 | 82:0c:7c:14:14:13:0e:fb:ae:f0:3a:7c:29:ee:55: 28 | 29:ca:46:7a:be:05:9f:fa:75:65:4c:f5:fb:cf:fe: 29 | 92:8d:78:e2:e1:41:55:32:2c:36:a2:ac:96:43:aa: 30 | e2:60:5a:ff:a6:e2:3f:5b:fc:d4:d3:af:cf:78:45: 31 | b5:e7:6e:7d:b6:fa:c4:05:84:a6:49:a7:ac:16:8e: 32 | b2:17:ac:75:76:f0:29:df:c8:da:a2:01:05:25:08: 33 | 4d:8f 34 | Exponent: 65537 (0x10001) 35 | X509v3 extensions: 36 | X509v3 Subject Key Identifier: 37 | 09:93:47:8E:5F:F3:BD:19:A2:77:FD:09:BA:13:A9:B6:C6:75:4E:B0 38 | X509v3 Authority Key Identifier: 39 | keyid:09:93:47:8E:5F:F3:BD:19:A2:77:FD:09:BA:13:A9:B6:C6:75:4E:B0 40 | 41 | X509v3 Basic Constraints: critical 42 | CA:TRUE 43 | Signature Algorithm: sha256WithRSAEncryption 44 | a1:52:44:1e:c0:a1:73:48:98:dd:91:b9:a7:e1:da:c5:48:65: 45 | d2:6d:38:77:b5:fa:f6:f7:c5:e4:b7:51:28:ea:f1:6c:9e:82: 46 | 80:6d:6f:56:9c:3b:31:b8:71:0e:ad:17:f9:8e:c6:7e:87:a9: 47 | 5f:30:1c:0e:17:c8:c7:c2:3c:96:3d:7d:01:a9:ce:d0:cd:c3: 48 | 55:6b:ce:64:35:53:93:c6:8c:4c:3d:0d:38:01:17:7b:e2:d8: 49 | b3:a5:78:46:77:fc:7e:da:16:f8:96:d0:72:35:89:c3:15:8c: 50 | 38:37:8b:7f:ff:01:f9:84:b2:e9:8d:11:64:82:36:e7:ef:86: 51 | a6:de:11:d9:78:b4:07:6c:18:89:aa:d6:6d:a2:d8:24:98:40: 52 | 85:5d:ba:5c:36:75:ad:e8:25:03:2d:94:69:d1:ce:d9:8f:9b: 53 | fd:79:5d:4b:30:7a:de:18:08:5a:54:e9:7b:7d:e2:cb:20:65: 54 | 99:4c:5a:31:de:c8:2c:01:b1:c8:d1:30:1d:33:bd:ef:9b:43: 55 | 4d:ac:7d:20:1f:c3:10:53:2e:1a:99:d5:6c:62:0e:15:b3:bd: 56 | 3c:88:58:88:0c:4f:06:21:b7:a4:8c:eb:9f:63:2e:5e:1d:c8: 57 | 91:39:9a:2b:e3:bf:e4:0a:bd:6e:4d:71:15:4d:e1:af:01:15: 58 | 99:38:25:12 59 | -----BEGIN CERTIFICATE----- 60 | MIIDAzCCAeugAwIBAgIUf8MSKCNzho671uYhQ+Ny6AEXPtEwDQYJKoZIhvcNAQEL 61 | BQAwETEPMA0GA1UEAwwGQ0FSb290MB4XDTIxMDQyMzE3MDg1MVoXDTMxMDQyMTE3 62 | MDg1MVowETEPMA0GA1UEAwwGQ0FSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 63 | MIIBCgKCAQEAs2qUZ3wzkE7buZSwphppd7szMf48i22K8c8H2YeGrUXPTOPnNdVL 64 | o3YnmzCxgj9XKcnwviVJJRZkWMyw8QEuGWlSyDhkYRa0p7p2K1TmpYBstmyKPMEG 65 | wuHB8xhrhwhLu1T0s3Id8s5HGF+C04jJOXtx/HEaqn5VbDV/g8Fg532xgNAXeu3n 66 | DYeLWeMYR+nP3g0Oxj5c6270Q5UxAS3o8rqKv+2CDHwUFBMO+67wOnwp7lUpykZ6 67 | vgWf+nVlTPX7z/6SjXji4UFVMiw2oqyWQ6riYFr/puI/W/zU06/PeEW15259tvrE 68 | BYSmSaesFo6yF6x1dvAp38jaogEFJQhNjwIDAQABo1MwUTAdBgNVHQ4EFgQUCZNH 69 | jl/zvRmid/0JuhOptsZ1TrAwHwYDVR0jBBgwFoAUCZNHjl/zvRmid/0JuhOptsZ1 70 | TrAwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAoVJEHsChc0iY 71 | 3ZG5p+HaxUhl0m04d7X69vfF5LdRKOrxbJ6CgG1vVpw7MbhxDq0X+Y7GfoepXzAc 72 | DhfIx8I8lj19AanO0M3DVWvOZDVTk8aMTD0NOAEXe+LYs6V4Rnf8ftoW+JbQcjWJ 73 | wxWMODeLf/8B+YSy6Y0RZII25++Gpt4R2Xi0B2wYiarWbaLYJJhAhV26XDZ1regl 74 | Ay2UadHO2Y+b/XldSzB63hgIWlTpe33iyyBlmUxaMd7ILAGxyNEwHTO975tDTax9 75 | IB/DEFMuGpnVbGIOFbO9PIhYiAxPBiG3pIzrn2MuXh3IkTmaK+O/5Aq9bk1xFU3h 76 | rwEVmTglEg== 77 | -----END CERTIFICATE----- 78 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/resources/authentication/tls/other-cacert.pem: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 5 | 37:55:7a:ae:71:6b:5f:f0:0d:f7:11:df:b5:f9:ce:e1:65:a4:0c:a4 6 | Signature Algorithm: sha256WithRSAEncryption 7 | Issuer: CN = CARoot 8 | Validity 9 | Not Before: Apr 23 17:08:51 2021 GMT 10 | Not After : Apr 21 17:08:51 2031 GMT 11 | Subject: CN = CARoot 12 | Subject Public Key Info: 13 | Public Key Algorithm: rsaEncryption 14 | RSA Public-Key: (2048 bit) 15 | Modulus: 16 | 00:ce:29:c8:45:af:07:8e:79:1e:55:66:7b:93:af: 17 | 09:2c:72:fd:d5:33:38:30:a9:b5:50:92:90:33:b0: 18 | 55:b0:c4:6b:37:4a:ba:5b:76:4d:52:0b:9f:58:b2: 19 | c5:95:8c:47:6d:2b:07:0a:f5:74:43:ec:7d:36:bf: 20 | 3e:8c:d6:13:31:ce:fc:d1:77:b0:ac:3c:ae:69:4b: 21 | bd:5d:93:bd:84:57:51:a7:ef:03:2e:ae:3e:93:73: 22 | 8b:1e:39:90:8b:32:e2:0a:dd:b8:20:83:98:76:91: 23 | 75:d6:d5:db:43:7b:f4:c9:4e:23:52:e3:11:55:05: 24 | 48:b8:82:47:ea:32:0b:56:1b:07:11:f3:06:c7:4a: 25 | d5:6b:87:c2:2e:e2:9a:8c:9d:54:ca:5e:96:08:02: 26 | 5d:17:42:4d:73:86:08:ab:6e:2e:f3:a8:c3:a3:c1: 27 | bd:88:63:5e:69:7e:fa:af:31:8d:3a:49:ed:e8:cf: 28 | 80:15:ca:d4:2b:fe:84:3d:aa:27:7e:98:36:48:4f: 29 | 3b:27:90:1d:c1:fe:4e:13:b0:5e:a5:32:6e:16:38: 30 | 2e:b7:d1:f3:6b:18:a5:3e:b6:d7:07:42:21:c7:d9: 31 | 8e:d6:8c:a5:bf:25:9e:5c:fc:c7:12:18:59:23:b9: 32 | 3d:39:45:3d:1c:81:e2:f2:29:91:05:20:46:b2:52: 33 | 06:51 34 | Exponent: 65537 (0x10001) 35 | X509v3 extensions: 36 | X509v3 Subject Key Identifier: 37 | EF:DA:58:74:AA:21:F9:9E:19:7E:44:2B:84:32:93:F4:0F:79:18:3B 38 | X509v3 Authority Key Identifier: 39 | keyid:EF:DA:58:74:AA:21:F9:9E:19:7E:44:2B:84:32:93:F4:0F:79:18:3B 40 | 41 | X509v3 Basic Constraints: critical 42 | CA:TRUE 43 | Signature Algorithm: sha256WithRSAEncryption 44 | 2e:f5:b6:f7:fc:50:89:16:1e:ea:8c:ec:57:54:f6:ca:d3:19: 45 | 65:fe:da:c5:73:53:f6:d0:1e:26:96:f2:d3:03:55:8d:6e:c4: 46 | cd:8c:2d:7a:ea:fa:38:6c:ed:fa:d5:23:b8:52:c1:e3:52:04: 47 | 3d:46:8c:2d:b6:b2:47:68:41:92:f6:47:24:50:78:47:5e:2a: 48 | 9b:df:85:a8:92:0d:49:17:eb:51:e8:b2:69:3c:4a:f3:9f:5f: 49 | ea:fd:b2:08:3c:30:1a:93:be:d3:c3:b3:c7:60:7c:ea:f4:15: 50 | 43:bd:3f:b1:d0:69:3c:84:5b:05:01:55:d7:d5:87:fb:58:53: 51 | 03:d8:91:5f:e8:e0:37:88:82:ea:dc:1c:2d:a0:8d:82:68:65: 52 | 6e:ea:0d:2a:e1:aa:cc:b3:d1:ce:a8:2b:2d:ed:e4:ba:0f:7f: 53 | 51:48:d2:4b:2f:7c:eb:02:01:4f:2c:b6:06:c1:9a:97:2c:b7: 54 | 6c:b7:06:86:d1:8b:cc:d6:d4:c3:ff:b5:65:c5:92:eb:9c:68: 55 | 6d:99:d8:4a:6d:7a:ac:fe:dc:f3:12:f8:bb:2b:0a:b9:d8:1e: 56 | 87:b6:e9:8b:51:32:f3:7b:0b:1a:29:57:4c:7d:5a:b6:9c:83: 57 | 23:e5:35:2b:98:83:aa:7c:ef:24:3a:74:a8:86:22:32:06:fb: 58 | 03:b7:01:9d 59 | -----BEGIN CERTIFICATE----- 60 | MIIDAzCCAeugAwIBAgIUN1V6rnFrX/AN9xHftfnO4WWkDKQwDQYJKoZIhvcNAQEL 61 | BQAwETEPMA0GA1UEAwwGQ0FSb290MB4XDTIxMDQyMzE3MDg1MVoXDTMxMDQyMTE3 62 | MDg1MVowETEPMA0GA1UEAwwGQ0FSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 63 | MIIBCgKCAQEAzinIRa8HjnkeVWZ7k68JLHL91TM4MKm1UJKQM7BVsMRrN0q6W3ZN 64 | UgufWLLFlYxHbSsHCvV0Q+x9Nr8+jNYTMc780XewrDyuaUu9XZO9hFdRp+8DLq4+ 65 | k3OLHjmQizLiCt24IIOYdpF11tXbQ3v0yU4jUuMRVQVIuIJH6jILVhsHEfMGx0rV 66 | a4fCLuKajJ1Uyl6WCAJdF0JNc4YIq24u86jDo8G9iGNeaX76rzGNOknt6M+AFcrU 67 | K/6EPaonfpg2SE87J5Adwf5OE7BepTJuFjgut9HzaxilPrbXB0Ihx9mO1oylvyWe 68 | XPzHEhhZI7k9OUU9HIHi8imRBSBGslIGUQIDAQABo1MwUTAdBgNVHQ4EFgQU79pY 69 | dKoh+Z4ZfkQrhDKT9A95GDswHwYDVR0jBBgwFoAU79pYdKoh+Z4ZfkQrhDKT9A95 70 | GDswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEALvW29/xQiRYe 71 | 6ozsV1T2ytMZZf7axXNT9tAeJpby0wNVjW7EzYwteur6OGzt+tUjuFLB41IEPUaM 72 | LbayR2hBkvZHJFB4R14qm9+FqJINSRfrUeiyaTxK859f6v2yCDwwGpO+08Ozx2B8 73 | 6vQVQ70/sdBpPIRbBQFV19WH+1hTA9iRX+jgN4iC6twcLaCNgmhlbuoNKuGqzLPR 74 | zqgrLe3kug9/UUjSSy986wIBTyy2BsGalyy3bLcGhtGLzNbUw/+1ZcWS65xobZnY 75 | Sm16rP7c8xL4uysKudgeh7bpi1Ey83sLGilXTH1atpyDI+U1K5iDqnzvJDp0qIYi 76 | Mgb7A7cBnQ== 77 | -----END CERTIFICATE----- 78 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/FanoutExchange.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import com.datastax.oss.starlight.rabbitmq.metadata.BindingMetadata; 19 | import com.datastax.oss.starlight.rabbitmq.metadata.BindingSetMetadata; 20 | import com.datastax.oss.starlight.rabbitmq.metadata.VirtualHostMetadata; 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | import java.util.Set; 24 | import java.util.UUID; 25 | import java.util.concurrent.CompletableFuture; 26 | import org.apache.pulsar.client.api.MessageId; 27 | import org.apache.qpid.server.model.LifetimePolicy; 28 | 29 | public final class FanoutExchange extends AbstractExchange { 30 | 31 | public FanoutExchange(String name, boolean durable, LifetimePolicy lifetimePolicy) { 32 | super(name, Type.fanout, durable, lifetimePolicy); 33 | } 34 | 35 | @Override 36 | public CompletableFuture bind( 37 | VirtualHostMetadata vhost, 38 | String exchange, 39 | String queue, 40 | String routingKey, 41 | GatewayConnection connection) { 42 | BindingSetMetadata bindings = vhost.getExchanges().get(exchange).getBindings().get(queue); 43 | if (bindings != null && !bindings.getKeys().contains(routingKey)) { 44 | bindings.getKeys().add(routingKey); 45 | Map subscriptions = 46 | vhost.getSubscriptions().computeIfAbsent(queue, q -> new HashMap<>()); 47 | for (BindingMetadata bindingMetadata : subscriptions.values()) { 48 | if (bindingMetadata.getLastMessageId() == null 49 | && bindingMetadata.getExchange().equals(exchange)) { 50 | // There's already an active subscription 51 | return CompletableFuture.completedFuture(null); 52 | } 53 | } 54 | String topic = getTopicName(connection.getNamespace(), name, "").toString(); 55 | String subscriptionName = (topic + "-" + UUID.randomUUID()).replace("/", "_"); 56 | return connection 57 | .getGatewayService() 58 | .getPulsarAdmin() 59 | .topics() 60 | .createSubscriptionAsync(topic, subscriptionName, MessageId.latest) 61 | .thenAccept( 62 | it -> 63 | subscriptions.put( 64 | subscriptionName, new BindingMetadata(exchange, topic, subscriptionName))); 65 | } 66 | return CompletableFuture.completedFuture(null); 67 | } 68 | 69 | @Override 70 | public CompletableFuture unbind( 71 | VirtualHostMetadata vhost, 72 | String exchange, 73 | String queue, 74 | String routingKey, 75 | GatewayConnection connection) { 76 | Map bindings = vhost.getExchanges().get(exchange).getBindings(); 77 | Set keys = bindings.get(queue).getKeys(); 78 | boolean removedKey = keys.remove(routingKey); 79 | if (removedKey && keys.isEmpty()) { 80 | bindings.remove(queue); 81 | Map queueSubscriptions = vhost.getSubscriptions().get(queue); 82 | for (BindingMetadata subscription : queueSubscriptions.values()) { 83 | if (subscription.getLastMessageId() == null 84 | && subscription.getExchange().equals(exchange)) { 85 | return connection 86 | .getGatewayService() 87 | .getPulsarAdmin() 88 | .topics() 89 | .getLastMessageIdAsync(subscription.getTopic()) 90 | .thenAccept( 91 | lastMessageId -> { 92 | subscription.setLastMessageId(lastMessageId.toByteArray()); 93 | }); 94 | } 95 | } 96 | } 97 | return CompletableFuture.completedFuture(null); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/utils/PulsarCluster.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmqtests.utils; 17 | 18 | import java.nio.file.Path; 19 | import java.util.Collections; 20 | import java.util.Optional; 21 | import org.apache.bookkeeper.util.PortManager; 22 | import org.apache.pulsar.broker.PulsarService; 23 | import org.apache.pulsar.broker.ServiceConfiguration; 24 | import org.apache.pulsar.common.policies.data.ClusterData; 25 | import org.apache.pulsar.common.policies.data.TenantInfo; 26 | 27 | /** Pulsar cluster. */ 28 | public class PulsarCluster implements AutoCloseable { 29 | private final PulsarService service; 30 | private final BookKeeperCluster bookKeeperCluster; 31 | 32 | public PulsarCluster(Path tempDir) throws Exception { 33 | this(tempDir, new ServiceConfiguration()); 34 | } 35 | 36 | public PulsarCluster(Path tempDir, ServiceConfiguration config) throws Exception { 37 | this.bookKeeperCluster = new BookKeeperCluster(tempDir, PortManager.nextFreePort()); 38 | config.setZookeeperServers(bookKeeperCluster.getZooKeeperAddress()); 39 | config.setConfigurationStoreServers(bookKeeperCluster.getZooKeeperAddress()); 40 | config.setClusterName("localhost"); 41 | config.setManagedLedgerDefaultEnsembleSize(1); 42 | config.setManagedLedgerDefaultWriteQuorum(1); 43 | config.setManagedLedgerDefaultAckQuorum(1); 44 | config.setBrokerServicePort(Optional.of(PortManager.nextFreePort())); 45 | config.setAllowAutoTopicCreation(true); 46 | config.setWebSocketServiceEnabled(false); 47 | config.setSystemTopicEnabled(true); 48 | config.setBookkeeperNumberOfChannelsPerBookie(1); 49 | config.setBookkeeperExplicitLacIntervalInMills(500); 50 | config.setTransactionCoordinatorEnabled(true); 51 | config.setBookkeeperMetadataServiceUri(bookKeeperCluster.getBookKeeperMetadataURI()); 52 | config.setWebServicePort(Optional.of(PortManager.nextFreePort())); 53 | config.setBookkeeperUseV2WireProtocol(false); 54 | service = new PulsarService(config); 55 | } 56 | 57 | public PulsarService getService() { 58 | return service; 59 | } 60 | 61 | public String getAddress() { 62 | return service.getWebServiceAddress(); 63 | } 64 | 65 | public void start() throws Exception { 66 | bookKeeperCluster.startBookie(); 67 | service.start(); 68 | service.getAdminClient().clusters().createCluster("localhost", ClusterData.builder().build()); 69 | service 70 | .getAdminClient() 71 | .tenants() 72 | .createTenant( 73 | "public", 74 | TenantInfo.builder() 75 | .adminRoles(Collections.singleton("admin")) 76 | .allowedClusters(Collections.singleton("localhost")) 77 | .build()); 78 | service.getAdminClient().namespaces().createNamespace("public/default"); 79 | 80 | service 81 | .getAdminClient() 82 | .tenants() 83 | .createTenant( 84 | "pulsar", 85 | TenantInfo.builder() 86 | .adminRoles(Collections.singleton("admin")) 87 | .allowedClusters(Collections.singleton("localhost")) 88 | .build()); 89 | service.getAdminClient().namespaces().createNamespace("pulsar/system"); 90 | 91 | service 92 | .getAdminClient() 93 | .topics() 94 | .createPartitionedTopic("persistent://pulsar/system/transaction_coordinator_assign", 1); 95 | 96 | service 97 | .getAdminClient() 98 | .topics() 99 | .createNonPartitionedTopic("persistent://public/default/__transaction_buffer_snapshot"); 100 | } 101 | 102 | public void close() throws Exception { 103 | service.close(); 104 | bookKeeperCluster.close(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /rabbitmq-tests/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | starlight-rabbitmq-parent 22 | com.datastax.oss 23 | 2.10.1.2-SNAPSHOT 24 | 25 | 4.0.0 26 | rabbitmq-tests 27 | jar 28 | Starlight for RabbitMQ integration tests with RabbitMQ client 29 | 30 | ${project.build.directory} 31 | 32 | 33 | 34 | com.datastax.oss 35 | starlight-rabbitmq 36 | ${project.version} 37 | test 38 | 39 | 40 | ${pulsar.groupId} 41 | pulsar-broker 42 | test 43 | 44 | 45 | ${pulsar.groupId} 46 | pulsar-proxy 47 | test 48 | 49 | 50 | org.apache.curator 51 | curator-test 52 | test 53 | 54 | 55 | com.rabbitmq 56 | amqp-client 57 | test 58 | 59 | 60 | com.github.spotbugs 61 | spotbugs-annotations 62 | 63 | 64 | org.junit.jupiter 65 | junit-jupiter-engine 66 | test 67 | 68 | 69 | org.junit.vintage 70 | junit-vintage-engine 71 | test 72 | 73 | 74 | org.assertj 75 | assertj-core 76 | test 77 | 78 | 79 | org.awaitility 80 | awaitility 81 | test 82 | 83 | 84 | 85 | 86 | 87 | org.apache.maven.plugins 88 | maven-surefire-plugin 89 | 90 | true 91 | 92 | 93 | 94 | org.apache.maven.plugins 95 | maven-failsafe-plugin 96 | 97 | --add-opens java.base/jdk.internal.loader=ALL-UNNAMED 98 | --add-opens java.base/java.lang=ALL-UNNAMED 99 | --add-opens java.base/java.io=ALL-UNNAMED 100 | --add-opens java.base/java.util=ALL-UNNAMED 101 | --add-opens java.base/sun.net=ALL-UNNAMED 102 | 103 | 104 | 105 | 106 | 107 | integration-test 108 | verify 109 | 110 | 111 | 112 | **/*.java 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/Pre0_10CreditManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import java.util.concurrent.atomic.LongAdder; 19 | import org.apache.qpid.server.protocol.v0_8.FlowCreditManager_0_8; 20 | 21 | /** 22 | * Copy of package-private org.apache.qpid.server.protocol.v0_8.Pre0_10CreditManager with fixes for 23 | * concurrency 24 | */ 25 | public class Pre0_10CreditManager implements FlowCreditManager_0_8 { 26 | 27 | private final long _highPrefetchLimit; 28 | private final long _batchLimit; 29 | private volatile long _bytesCreditLimit; 30 | private volatile long _messageCreditLimit; 31 | 32 | private final LongAdder _bytesCredit = new LongAdder(); 33 | private final LongAdder _messageCredit = new LongAdder(); 34 | 35 | Pre0_10CreditManager( 36 | long bytesCreditLimit, long messageCreditLimit, long highPrefetchLimit, long batchLimit) { 37 | _bytesCreditLimit = bytesCreditLimit; 38 | _messageCreditLimit = messageCreditLimit; 39 | _bytesCredit.add(bytesCreditLimit); 40 | _messageCredit.add(messageCreditLimit); 41 | _highPrefetchLimit = highPrefetchLimit; 42 | _batchLimit = batchLimit; 43 | } 44 | 45 | void setCreditLimits(final long bytesCreditLimit, final long messageCreditLimit) { 46 | long bytesCreditChange = bytesCreditLimit - _bytesCreditLimit; 47 | long messageCreditChange = messageCreditLimit - _messageCreditLimit; 48 | 49 | if (bytesCreditChange != 0L) { 50 | _bytesCredit.add(bytesCreditChange); 51 | } 52 | 53 | if (messageCreditChange != 0L) { 54 | _messageCredit.add(messageCreditChange); 55 | } 56 | 57 | _bytesCreditLimit = bytesCreditLimit; 58 | _messageCreditLimit = messageCreditLimit; 59 | } 60 | 61 | @Override 62 | public void restoreCredit(final long messageCredit, final long bytesCredit) { 63 | _messageCredit.add(messageCredit); 64 | long messageCreditSum = _messageCredit.longValue(); 65 | if (_messageCreditLimit != 0 && messageCreditSum > _messageCreditLimit) { 66 | throw new IllegalStateException( 67 | String.format( 68 | "Consumer credit accounting error. Restored more credit than we ever had: messageCredit=%d messageCreditLimit=%d", 69 | messageCreditSum, _messageCreditLimit)); 70 | } 71 | 72 | _bytesCredit.add(bytesCredit); 73 | long _bytesCreditSum = _bytesCredit.longValue(); 74 | if (_bytesCreditLimit != 0 && _bytesCreditSum > _bytesCreditLimit) { 75 | throw new IllegalStateException( 76 | String.format( 77 | "Consumer credit accounting error. Restored more credit than we ever had: bytesCredit=%d bytesCreditLimit=%d", 78 | _bytesCreditSum, _bytesCreditLimit)); 79 | } 80 | } 81 | 82 | @Override 83 | public boolean hasCredit() { 84 | return (_bytesCreditLimit == 0L || _bytesCredit.longValue() > 0) 85 | && (_messageCreditLimit == 0L || _messageCredit.longValue() > 0); 86 | } 87 | 88 | @Override 89 | public boolean useCreditForMessage(final long msgSize) { 90 | if (_messageCreditLimit != 0) { 91 | if (_messageCredit.longValue() <= 0) { 92 | return false; 93 | } 94 | } 95 | if (_bytesCreditLimit != 0) { 96 | long _bytesCreditSum = _bytesCredit.longValue(); 97 | if ((_bytesCreditSum < msgSize) && (_bytesCreditSum != _bytesCreditLimit)) { 98 | return false; 99 | } 100 | } 101 | 102 | _messageCredit.decrement(); 103 | _bytesCredit.add(-msgSize); 104 | return true; 105 | } 106 | 107 | @Override 108 | public boolean isNotBytesLimitedAndHighPrefetch() { 109 | return _bytesCreditLimit == 0L && _messageCreditLimit > _highPrefetchLimit; 110 | } 111 | 112 | @Override 113 | public boolean isBytesLimited() { 114 | return _bytesCredit.longValue() != 0; 115 | } 116 | 117 | @Override 118 | public boolean isCreditOverBatchLimit() { 119 | return _messageCredit.longValue() > _batchLimit; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/GatewayServiceStarter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import static org.apache.commons.lang3.StringUtils.isBlank; 19 | 20 | import com.beust.jcommander.JCommander; 21 | import com.beust.jcommander.Parameter; 22 | import io.prometheus.client.exporter.MetricsServlet; 23 | import java.text.DateFormat; 24 | import java.text.SimpleDateFormat; 25 | import java.util.Collections; 26 | import java.util.Date; 27 | import org.apache.pulsar.broker.PulsarServerException; 28 | import org.apache.pulsar.broker.authentication.AuthenticationService; 29 | import org.apache.pulsar.proxy.server.WebServer; 30 | import org.eclipse.jetty.servlet.ServletHolder; 31 | import org.slf4j.Logger; 32 | import org.slf4j.LoggerFactory; 33 | 34 | /** Starts an instance of Starlight for RabbitMQ */ 35 | public class GatewayServiceStarter { 36 | 37 | @Parameter( 38 | names = {"-c", "--config"}, 39 | description = "Configuration file path", 40 | required = true 41 | ) 42 | private String configFile = null; 43 | 44 | @Parameter( 45 | names = {"-h", "--help"}, 46 | description = "Show this help message" 47 | ) 48 | private boolean help = false; 49 | 50 | public GatewayServiceStarter(String[] args) throws Exception { 51 | try { 52 | 53 | DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss,SSS"); 54 | 55 | Thread.setDefaultUncaughtExceptionHandler( 56 | (thread, exception) -> 57 | System.out.printf( 58 | "%s [%s] error Uncaught exception in thread %s: %s%n", 59 | dateFormat.format(new Date()), 60 | thread.getContextClassLoader(), 61 | thread.getName(), 62 | exception.getMessage())); 63 | 64 | JCommander jcommander = new JCommander(); 65 | try { 66 | jcommander.addObject(this); 67 | jcommander.parse(args); 68 | if (help || isBlank(configFile)) { 69 | jcommander.usage(); 70 | return; 71 | } 72 | } catch (Exception e) { 73 | jcommander.usage(); 74 | System.exit(-1); 75 | } 76 | 77 | // load config file 78 | final GatewayConfiguration config = 79 | ConfigurationUtils.create(configFile, GatewayConfiguration.class); 80 | 81 | AuthenticationService authenticationService = 82 | new AuthenticationService(ConfigurationUtils.convertFrom(config)); 83 | 84 | // create gateway service 85 | GatewayService gatewayService = new GatewayService(config, authenticationService); 86 | WebServer webServer = new WebServer(config, authenticationService); 87 | 88 | Runtime.getRuntime() 89 | .addShutdownHook( 90 | new Thread( 91 | () -> { 92 | try { 93 | gatewayService.close(); 94 | } catch (Exception e) { 95 | log.warn("gRPC server couldn't stop gracefully {}", e.getMessage(), e); 96 | } 97 | try { 98 | webServer.stop(); 99 | } catch (Exception e) { 100 | log.warn("Web server couldn't stop gracefully {}", e.getMessage(), e); 101 | } 102 | })); 103 | 104 | gatewayService.start(true); 105 | 106 | webServer.addServlet( 107 | "/metrics", 108 | new ServletHolder(MetricsServlet.class), 109 | Collections.emptyList(), 110 | config.isAuthenticateMetricsEndpoint()); 111 | 112 | webServer.start(); 113 | 114 | } catch (Exception e) { 115 | log.error("Failed to start Starlight for RabbitMQ. error msg " + e.getMessage(), e); 116 | throw new PulsarServerException(e); 117 | } 118 | } 119 | 120 | public static void main(String[] args) throws Exception { 121 | new GatewayServiceStarter(args); 122 | } 123 | 124 | private static final Logger log = LoggerFactory.getLogger(GatewayServiceStarter.class); 125 | } 126 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/ClusteredTestBase.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.BrokerTestCase; 19 | import com.rabbitmq.client.AMQP; 20 | import com.rabbitmq.client.Channel; 21 | import com.rabbitmq.client.Connection; 22 | import com.rabbitmq.client.ConnectionFactory; 23 | import java.io.IOException; 24 | import java.util.concurrent.TimeoutException; 25 | 26 | /** Base class for tests which would like a second, clustered node. */ 27 | // TODO: When clustering is supported in Pulsar RMQ GW, start a secondary gateway 28 | public class ClusteredTestBase extends BrokerTestCase { 29 | // If these are non-null then the secondary node is up and clustered 30 | public Channel clusteredChannel; 31 | public Connection clusteredConnection; 32 | 33 | // These will always be non-null - if there is clustering they will point 34 | // to the secondary node, otherwise the primary 35 | public Channel alternateChannel; 36 | public Connection alternateConnection; 37 | 38 | @Override 39 | public void openChannel() throws IOException { 40 | super.openChannel(); 41 | 42 | if (clusteredConnection != null) { 43 | clusteredChannel = clusteredConnection.createChannel(); 44 | } 45 | 46 | alternateChannel = clusteredChannel == null ? channel : clusteredChannel; 47 | } 48 | 49 | private static boolean nonClusteredWarningPrinted; 50 | 51 | @Override 52 | public void openConnection() throws IOException, TimeoutException { 53 | super.openConnection(); 54 | if (clusteredConnection == null) { 55 | try { 56 | ConnectionFactory cf2 = connectionFactory.clone(); 57 | cf2.setHost("localhost"); 58 | cf2.setPort(5673); 59 | clusteredConnection = cf2.newConnection(); 60 | } catch (IOException e) { 61 | // Must be no secondary node 62 | } 63 | } 64 | 65 | if (clusteredConnection != null && !clustered(connection, clusteredConnection)) { 66 | clusteredConnection.close(); 67 | clusteredConnection = null; 68 | 69 | if (!nonClusteredWarningPrinted) { 70 | System.out.println("NOTE: Only one clustered node was detected - certain tests that"); 71 | System.out.println("could test clustering will not do so."); 72 | nonClusteredWarningPrinted = true; 73 | } 74 | } 75 | 76 | alternateConnection = clusteredConnection == null ? connection : clusteredConnection; 77 | } 78 | 79 | private boolean clustered(Connection c1, Connection c2) throws IOException { 80 | Channel ch1 = c1.createChannel(); 81 | Channel ch2 = c2.createChannel(); 82 | // autodelete but not exclusive 83 | String q = ch1.queueDeclare("", false, false, true, null).getQueue(); 84 | 85 | try { 86 | ch2.queueDeclarePassive(q); 87 | } catch (IOException e) { 88 | checkShutdownSignal(AMQP.NOT_FOUND, e); 89 | // If we can't see the queue, secondary node must be up but not 90 | // clustered, hence not interesting to us 91 | return false; 92 | } 93 | 94 | ch1.queueDelete(q); 95 | ch1.abort(); 96 | ch2.abort(); 97 | 98 | return true; 99 | } 100 | 101 | @Override 102 | public void closeChannel() throws IOException { 103 | if (clusteredChannel != null) { 104 | clusteredChannel.abort(); 105 | clusteredChannel = null; 106 | alternateChannel = null; 107 | } 108 | super.closeChannel(); 109 | } 110 | 111 | @Override 112 | public void closeConnection() throws IOException { 113 | if (clusteredConnection != null) { 114 | clusteredConnection.abort(); 115 | clusteredConnection = null; 116 | alternateConnection = null; 117 | } 118 | super.closeConnection(); 119 | } 120 | 121 | protected void stopSecondary() throws IOException {} 122 | 123 | protected void startSecondary() throws IOException {} 124 | } 125 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/ConnectionOpen.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertTrue; 20 | import static org.junit.Assert.fail; 21 | 22 | import com.datastax.oss.starlight.rabbitmqtests.SystemTest; 23 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.BrokerTestCase; 24 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.TestUtils; 25 | import com.rabbitmq.client.AMQP; 26 | import com.rabbitmq.client.ConnectionFactory; 27 | import com.rabbitmq.client.MalformedFrameException; 28 | import com.rabbitmq.client.Method; 29 | import com.rabbitmq.client.impl.AMQCommand; 30 | import com.rabbitmq.client.impl.SocketFrameHandler; 31 | import java.io.DataInputStream; 32 | import java.io.IOException; 33 | import java.net.Socket; 34 | import java.util.concurrent.TimeoutException; 35 | import javax.net.SocketFactory; 36 | import org.junit.Test; 37 | import org.junit.experimental.categories.Category; 38 | 39 | /** Check that protocol negotiation works */ 40 | @Category(SystemTest.class) 41 | public class ConnectionOpen extends BrokerTestCase { 42 | @Test 43 | public void correctProtocolHeader() throws IOException { 44 | SocketFrameHandler fh = 45 | new SocketFrameHandler( 46 | SocketFactory.getDefault().createSocket("localhost", pulsarListenerPort)); 47 | fh.sendHeader(); 48 | AMQCommand command = new AMQCommand(); 49 | while (!command.handleFrame(fh.readFrame())) {} 50 | Method m = command.getMethod(); 51 | 52 | assertTrue("First command must be Connection.start", m instanceof AMQP.Connection.Start); 53 | AMQP.Connection.Start start = (AMQP.Connection.Start) m; 54 | assertTrue( 55 | "Version in Connection.start is <= what we sent", 56 | start.getVersionMajor() < AMQP.PROTOCOL.MAJOR 57 | || (start.getVersionMajor() == AMQP.PROTOCOL.MAJOR 58 | && start.getVersionMinor() <= AMQP.PROTOCOL.MINOR)); 59 | } 60 | 61 | @Test 62 | public void crazyProtocolHeader() throws IOException { 63 | ConnectionFactory factory = TestUtils.connectionFactory(); 64 | // keep the frame handler's socket 65 | Socket fhSocket = SocketFactory.getDefault().createSocket("localhost", pulsarListenerPort); 66 | SocketFrameHandler fh = new SocketFrameHandler(fhSocket); 67 | fh.sendHeader(100, 3); // major, minor 68 | DataInputStream in = fh.getInputStream(); 69 | // we should get a valid protocol header back 70 | byte[] header = new byte[4]; 71 | in.read(header); 72 | // The protocol header is "AMQP" plus a version that the server 73 | // supports. We can really only test for the first bit. 74 | assertEquals("AMQP", new String(header)); 75 | in.read(header); 76 | assertEquals(in.available(), 0); 77 | // At this point the socket should have been closed. We can 78 | // directly test for this, but since Socket.isClosed is purported to be 79 | // unreliable, we can also test whether trying to read more bytes 80 | // gives an error. 81 | if (!fhSocket.isClosed()) { 82 | fh.setTimeout(500); 83 | // NB the frame handler will return null if the socket times out 84 | try { 85 | fh.readFrame(); 86 | fail("Expected socket read to fail due to socket being closed"); 87 | } catch (MalformedFrameException mfe) { 88 | fail("Expected nothing, rather than a badly-formed something"); 89 | } catch (IOException ioe) { 90 | } 91 | } 92 | } 93 | 94 | @Test 95 | public void frameMaxLessThanFrameMinSize() throws IOException, TimeoutException { 96 | ConnectionFactory factory = newConnectionFactory(); 97 | factory.setRequestedFrameMax(100); 98 | try { 99 | factory.newConnection(); 100 | } catch (IOException ioe) { 101 | return; 102 | } 103 | fail("Broker should have closed the connection since our frame max < frame_min_size"); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/AbstractExchange.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import static org.apache.commons.lang3.StringUtils.isBlank; 19 | import static org.apache.commons.lang3.StringUtils.isNotBlank; 20 | 21 | import com.datastax.oss.starlight.rabbitmq.metadata.ExchangeMetadata; 22 | import com.datastax.oss.starlight.rabbitmq.metadata.VirtualHostMetadata; 23 | import java.util.concurrent.CompletableFuture; 24 | import org.apache.pulsar.common.naming.NamespaceName; 25 | import org.apache.pulsar.common.naming.TopicName; 26 | import org.apache.qpid.server.model.LifetimePolicy; 27 | 28 | public abstract class AbstractExchange { 29 | 30 | enum Type { 31 | direct, 32 | fanout, 33 | topic, 34 | headers 35 | } 36 | 37 | protected final String name; 38 | protected final AbstractExchange.Type type; 39 | protected final boolean durable; 40 | protected final LifetimePolicy lifetimePolicy; 41 | 42 | protected AbstractExchange( 43 | String name, Type type, boolean durable, LifetimePolicy lifetimePolicy) { 44 | this.name = name; 45 | this.type = type; 46 | this.durable = durable; 47 | this.lifetimePolicy = lifetimePolicy; 48 | } 49 | 50 | public String getName() { 51 | return name; 52 | } 53 | 54 | public String getType() { 55 | return type.toString(); 56 | } 57 | 58 | public boolean isDurable() { 59 | return durable; 60 | } 61 | 62 | public abstract CompletableFuture bind( 63 | VirtualHostMetadata vhost, 64 | String exchange, 65 | String queue, 66 | String routingKey, 67 | GatewayConnection connection); 68 | 69 | public abstract CompletableFuture unbind( 70 | VirtualHostMetadata vhost, 71 | String exchange, 72 | String queue, 73 | String routingKey, 74 | GatewayConnection gatewayConnection); 75 | 76 | public static AbstractExchange createExchange( 77 | Type type, String name, boolean durable, LifetimePolicy lifetimePolicy) { 78 | switch (type) { 79 | case direct: 80 | return new DirectExchange(name, durable, lifetimePolicy); 81 | case fanout: 82 | return new FanoutExchange(name, durable, lifetimePolicy); 83 | case topic: 84 | return new TopicExchange(name, durable, lifetimePolicy); 85 | case headers: 86 | return new HeadersExchange(name, durable, lifetimePolicy); 87 | default: 88 | throw new IllegalArgumentException("Unknown exchange type"); 89 | } 90 | } 91 | 92 | public static TopicName getTopicName(String vHost, String exchangeName, String routingKey) { 93 | StringBuilder topic = new StringBuilder(isBlank(exchangeName) ? "amq.default" : exchangeName); 94 | if (isNotBlank(routingKey)) { 95 | topic.append(".__").append(routingKey); 96 | } 97 | return TopicName.get("persistent", NamespaceName.get(vHost), topic.toString()); 98 | } 99 | 100 | public static ExchangeMetadata.Type convertType(Type type) { 101 | switch (type) { 102 | case direct: 103 | return ExchangeMetadata.Type.direct; 104 | case fanout: 105 | return ExchangeMetadata.Type.fanout; 106 | case topic: 107 | return ExchangeMetadata.Type.topic; 108 | case headers: 109 | return ExchangeMetadata.Type.headers; 110 | default: 111 | throw new IllegalArgumentException("Unknown exchange type"); 112 | } 113 | } 114 | 115 | public static Type convertMetadataType(ExchangeMetadata.Type type) { 116 | switch (type) { 117 | case direct: 118 | return Type.direct; 119 | case fanout: 120 | return Type.fanout; 121 | case topic: 122 | return Type.topic; 123 | case headers: 124 | return Type.headers; 125 | default: 126 | throw new IllegalArgumentException("Unknown exchange type"); 127 | } 128 | } 129 | 130 | public static AbstractExchange fromMetadata(String name, ExchangeMetadata metadata) { 131 | return AbstractExchange.createExchange( 132 | convertMetadataType(metadata.getType()), 133 | name, 134 | metadata.isDurable(), 135 | metadata.getLifetimePolicy()); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/test/java/com/datastax/oss/starlight/rabbitmq/MetricsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertNull; 20 | import static org.junit.jupiter.api.Assertions.assertTrue; 21 | import static org.mockito.Mockito.mock; 22 | import static org.mockito.Mockito.when; 23 | 24 | import io.prometheus.client.CollectorRegistry; 25 | import java.nio.ByteBuffer; 26 | import java.nio.charset.StandardCharsets; 27 | import java.util.concurrent.CompletableFuture; 28 | import org.apache.pulsar.client.api.TypedMessageBuilder; 29 | import org.apache.pulsar.client.impl.MessageIdImpl; 30 | import org.apache.qpid.server.protocol.v0_8.transport.AMQFrame; 31 | import org.apache.qpid.server.protocol.v0_8.transport.BasicContentHeaderProperties; 32 | import org.apache.qpid.server.protocol.v0_8.transport.BasicPublishBody; 33 | import org.apache.qpid.server.protocol.v0_8.transport.ContentBody; 34 | import org.apache.qpid.server.protocol.v0_8.transport.ContentHeaderBody; 35 | import org.junit.jupiter.api.Test; 36 | 37 | public class MetricsTest extends AbstractBaseTest { 38 | public static final String TEST_QUEUE = "test-queue"; 39 | public static final byte[] TEST_MESSAGE = "test-message".getBytes(StandardCharsets.UTF_8); 40 | 41 | @Test 42 | void testBytesInOutMetrics() { 43 | CollectorRegistry registry = CollectorRegistry.defaultRegistry; 44 | assertNull( 45 | registry.getSampleValue( 46 | "server_rabbitmq_in_bytes", 47 | new String[] {"namespace"}, 48 | new String[] {"public/default"})); 49 | assertNull( 50 | registry.getSampleValue( 51 | "server_rabbitmq_out_bytes", 52 | new String[] {"namespace"}, 53 | new String[] {"public/default"})); 54 | 55 | TypedMessageBuilder messageBuilder = mock(TypedMessageBuilder.class); 56 | 57 | when(producer.newMessage()).thenReturn(messageBuilder); 58 | when(messageBuilder.sendAsync()) 59 | .thenReturn(CompletableFuture.completedFuture(new MessageIdImpl(1, 2, 3))); 60 | 61 | openConnection(); 62 | sendChannelOpen(); 63 | sendBasicPublish(); 64 | 65 | BasicContentHeaderProperties props = new BasicContentHeaderProperties(); 66 | props.setContentType("application/json"); 67 | props.setTimestamp(1234); 68 | 69 | exchangeData(ContentHeaderBody.createAMQFrame(CHANNEL_ID, props, TEST_MESSAGE.length)); 70 | sendMessageContent(); 71 | 72 | assertTrue( 73 | registry.getSampleValue( 74 | "server_rabbitmq_in_bytes", 75 | new String[] {"namespace"}, 76 | new String[] {"public/default"}) 77 | > 0); 78 | assertTrue( 79 | registry.getSampleValue( 80 | "server_rabbitmq_out_bytes", 81 | new String[] {"namespace"}, 82 | new String[] {"public/default"}) 83 | > 0); 84 | assertTrue( 85 | registry.getSampleValue( 86 | "server_rabbitmq_in_messages_total", 87 | new String[] {"namespace"}, 88 | new String[] {"public/default"}) 89 | > 0); 90 | } 91 | 92 | @Test 93 | void testActiveAndNewConnections() { 94 | CollectorRegistry registry = CollectorRegistry.defaultRegistry; 95 | 96 | assertEquals(1, registry.getSampleValue("server_rabbitmq_active_connections")); 97 | assertEquals(1, registry.getSampleValue("server_rabbitmq_new_connections")); 98 | 99 | channel.close(); 100 | 101 | assertEquals(0, registry.getSampleValue("server_rabbitmq_active_connections")); 102 | assertEquals(1, registry.getSampleValue("server_rabbitmq_new_connections")); 103 | } 104 | 105 | private AMQFrame sendBasicPublish() { 106 | BasicPublishBody basicPublishBody = new BasicPublishBody(0, null, null, false, false); 107 | return exchangeData(basicPublishBody.generateFrame(CHANNEL_ID)); 108 | } 109 | 110 | private AMQFrame sendMessageContent() { 111 | ContentBody contentBody = new ContentBody(ByteBuffer.wrap(TEST_MESSAGE)); 112 | return exchangeData(ContentBody.createAMQFrame(CHANNEL_ID, contentBody)); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/GatewayConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import java.util.Collections; 19 | import java.util.Set; 20 | import lombok.Getter; 21 | import lombok.Setter; 22 | import org.apache.pulsar.common.configuration.FieldContext; 23 | import org.apache.pulsar.proxy.server.ProxyConfiguration; 24 | 25 | @Getter 26 | @Setter 27 | public class GatewayConfiguration extends ProxyConfiguration { 28 | private static final String CATEGORY_AMQP = "AMQP"; 29 | 30 | @FieldContext( 31 | category = CATEGORY_AMQP, 32 | required = true, 33 | doc = 34 | "Used to specify multiple advertised listeners for the proxy." 35 | + " The value must format as amqp[s]://:," 36 | + "multiple listeners should be separated with commas." 37 | ) 38 | private Set amqpListeners = Collections.singleton("amqp://127.0.0.1:5672"); 39 | 40 | @FieldContext( 41 | category = CATEGORY_AMQP, 42 | doc = 43 | "Authentication mechanism name list for AMQP (a comma-separated list of mecanisms. Eg: PLAIN,EXTERNAL)" 44 | ) 45 | private Set amqpAuthenticationMechanisms = Collections.singleton("PLAIN"); 46 | 47 | @FieldContext( 48 | category = CATEGORY_AMQP, 49 | doc = 50 | "If set, the RabbitMQ service will use these parameters to authenticate on Pulsar's brokers. If not set, the brokerClientAuthenticationParameters setting will be used. This setting allows to have different credentials for the proxy and for the RabbitMQ service" 51 | ) 52 | private String amqpBrokerClientAuthenticationParameters; 53 | 54 | @FieldContext( 55 | category = CATEGORY_AMQP, 56 | doc = "The maximum number of sessions which can exist concurrently on an AMQP connection." 57 | ) 58 | private int amqpSessionCountLimit = 256; 59 | 60 | @FieldContext( 61 | category = CATEGORY_AMQP, 62 | doc = 63 | "The default period with which Broker and client will exchange" 64 | + " heartbeat messages (in seconds) when using AMQP. Clients may negotiate a different heartbeat" 65 | + " frequency or disable it altogether." 66 | ) 67 | private int amqpHeartbeatDelay = 0; 68 | 69 | @FieldContext( 70 | category = CATEGORY_AMQP, 71 | doc = 72 | "Factor to determine the maximum length of that may elapse between heartbeats being" 73 | + " received from the peer before an AMQP0.9 connection is deemed to have been broken." 74 | ) 75 | private int amqpHeartbeatTimeoutFactor = 2; 76 | 77 | @FieldContext(category = CATEGORY_AMQP, doc = "AMQP Network buffer size.") 78 | // TODO: Network buffer size must be bigger than Netty's receive buffer. Also configure Netty with 79 | // this. 80 | private int amqpNetworkBufferSize = 2 * 1024 * 1024; 81 | 82 | @FieldContext(category = CATEGORY_AMQP, doc = "AMQP Max message size.") 83 | private int amqpMaxMessageSize = 100 * 1024 * 1024; 84 | 85 | @FieldContext(category = CATEGORY_AMQP, doc = "AMQP Length of binary data sent to debug log.") 86 | private int amqpDebugBinaryDataLength = 80; 87 | 88 | @FieldContext( 89 | category = CATEGORY_AMQP, 90 | doc = 91 | "Timeout in ms after which the AMQP connection closes even if a ConnectionCloseOk frame is not received" 92 | ) 93 | private int amqpConnectionCloseTimeout = 2000; 94 | 95 | @FieldContext(category = CATEGORY_AMQP, doc = "Whether batching messages is enabled in AMQP") 96 | private boolean amqpBatchingEnabled = true; 97 | 98 | @FieldContext( 99 | category = CATEGORY_AMQP, 100 | doc = "Default Pulsar tenant used to map short or empty VHosts" 101 | ) 102 | private String amqpDefaultTenant = "public"; 103 | 104 | @FieldContext( 105 | category = CATEGORY_AMQP, 106 | doc = "Default Pulsar namespace used to map short or empty VHosts" 107 | ) 108 | private String amqpDefaultNamespace = "default"; 109 | 110 | @FieldContext( 111 | category = CATEGORY_AMQP, 112 | doc = 113 | "By default short VHosts (not containing a slash character) will be mapped to a namespace on the default tenant." 114 | + "Set this parameter to true to map short VHosts to tenants with the default namespace instead." 115 | ) 116 | private boolean amqpMapShortVhostToTenant = false; 117 | } 118 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/TokenAuthenticationIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmqtests; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertTrue; 19 | import static org.junit.jupiter.api.Assertions.fail; 20 | 21 | import com.datastax.oss.starlight.rabbitmq.ConfigurationUtils; 22 | import com.datastax.oss.starlight.rabbitmq.GatewayConfiguration; 23 | import com.datastax.oss.starlight.rabbitmq.GatewayService; 24 | import com.datastax.oss.starlight.rabbitmqtests.utils.PulsarCluster; 25 | import com.rabbitmq.client.AuthenticationFailureException; 26 | import com.rabbitmq.client.Connection; 27 | import com.rabbitmq.client.ConnectionFactory; 28 | import com.rabbitmq.client.impl.DefaultCredentialsProvider; 29 | import io.jsonwebtoken.Jwts; 30 | import io.jsonwebtoken.SignatureAlgorithm; 31 | import io.prometheus.client.CollectorRegistry; 32 | import java.net.ConnectException; 33 | import java.nio.file.Path; 34 | import java.util.Base64; 35 | import java.util.Collections; 36 | import javax.crypto.SecretKey; 37 | import org.apache.bookkeeper.util.PortManager; 38 | import org.apache.pulsar.broker.authentication.AuthenticationService; 39 | import org.apache.pulsar.broker.authentication.utils.AuthTokenUtils; 40 | import org.junit.jupiter.api.AfterAll; 41 | import org.junit.jupiter.api.BeforeAll; 42 | import org.junit.jupiter.api.Test; 43 | import org.junit.jupiter.api.io.TempDir; 44 | 45 | public class TokenAuthenticationIT { 46 | @TempDir public static Path tempDir; 47 | private static PulsarCluster cluster; 48 | private static GatewayService gatewayService; 49 | private static ConnectionFactory factory; 50 | 51 | private static final SecretKey SECRET_KEY = 52 | AuthTokenUtils.createSecretKey(SignatureAlgorithm.HS256); 53 | private final String CLIENT_ROLE = "client"; 54 | private final String CLIENT_TOKEN = 55 | Jwts.builder().setSubject(CLIENT_ROLE).signWith(SECRET_KEY).compact(); 56 | 57 | @BeforeAll 58 | public static void before() throws Exception { 59 | CollectorRegistry.defaultRegistry.clear(); 60 | cluster = new PulsarCluster(tempDir); 61 | cluster.start(); 62 | GatewayConfiguration config = new GatewayConfiguration(); 63 | config.setBrokerServiceURL(cluster.getAddress()); 64 | config.setBrokerWebServiceURL(cluster.getAddress()); 65 | int port = PortManager.nextFreePort(); 66 | config.setAmqpListeners(Collections.singleton("amqp://127.0.0.1:" + port)); 67 | config.setConfigurationMetadataStoreUrl( 68 | cluster.getService().getConfig().getConfigurationMetadataStoreUrl()); 69 | 70 | config.setAuthenticationEnabled(true); 71 | config 72 | .getProperties() 73 | .setProperty( 74 | "tokenSecretKey", 75 | "data:;base64," + Base64.getEncoder().encodeToString(SECRET_KEY.getEncoded())); 76 | 77 | gatewayService = 78 | new GatewayService( 79 | config, new AuthenticationService(ConfigurationUtils.convertFrom(config))); 80 | gatewayService.start(); 81 | 82 | factory = new ConnectionFactory(); 83 | factory.setPort(port); 84 | } 85 | 86 | @AfterAll 87 | public static void after() throws Exception { 88 | if (cluster != null) { 89 | cluster.close(); 90 | } 91 | if (gatewayService != null) { 92 | gatewayService.close(); 93 | } 94 | } 95 | 96 | @Test 97 | void testTokenAuthenticationSuccess() throws Exception { 98 | factory.setCredentialsProvider(new DefaultCredentialsProvider("", CLIENT_TOKEN)); 99 | Connection conn = factory.newConnection(); 100 | assertTrue(conn.isOpen()); 101 | } 102 | 103 | @Test 104 | void testTokenAuthenticationInvalidUser() throws Exception { 105 | factory.setCredentialsProvider(new DefaultCredentialsProvider("nobody", CLIENT_TOKEN)); 106 | try { 107 | factory.newConnection(); 108 | fail("Should have failed authentication"); 109 | } catch (AuthenticationFailureException | ConnectException e) { 110 | // expected 111 | } 112 | } 113 | 114 | @Test 115 | void testTokenAuthenticationInvalidToken() throws Exception { 116 | SecretKey secretKey = AuthTokenUtils.createSecretKey(SignatureAlgorithm.HS256); 117 | String jwt = Jwts.builder().setSubject(CLIENT_ROLE).signWith(secretKey).compact(); 118 | factory.setCredentialsProvider(new DefaultCredentialsProvider("", jwt)); 119 | try { 120 | factory.newConnection(); 121 | fail("Should have failed authentication"); 122 | } catch (AuthenticationFailureException | ConnectException e) { 123 | // expected 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /nar-tests/src/test/java/com/datastax/oss/starlight/rabbitmqnartests/PulsarContainer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 18 | * in compliance with the License. You may obtain a copy of the License at 19 | * 20 | *

http://www.apache.org/licenses/LICENSE-2.0 21 | * 22 | *

Unless required by applicable law or agreed to in writing, software distributed under the 23 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 24 | * express or implied. See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | package com.datastax.oss.starlight.rabbitmqnartests; 28 | 29 | import org.slf4j.Logger; 30 | import org.slf4j.LoggerFactory; 31 | import org.testcontainers.containers.BindMode; 32 | import org.testcontainers.containers.GenericContainer; 33 | import org.testcontainers.containers.Network; 34 | import org.testcontainers.containers.wait.strategy.Wait; 35 | import org.testcontainers.utility.DockerImageName; 36 | 37 | public class PulsarContainer implements AutoCloseable { 38 | 39 | private static final Logger log = LoggerFactory.getLogger(PulsarContainer.class); 40 | 41 | protected static final String PROTOCOLS_TEST_PROTOCOL_HANDLER_NAR = "/test-protocol-handler.nar"; 42 | 43 | private GenericContainer pulsarContainer; 44 | private GenericContainer proxyContainer; 45 | private final Network network; 46 | private final boolean startProxy; 47 | private final String image; 48 | 49 | public PulsarContainer(Network network, boolean startProxy, String image) { 50 | this.network = network; 51 | this.startProxy = startProxy; 52 | this.image = image; 53 | } 54 | 55 | public void start() throws Exception { 56 | pulsarContainer = 57 | new org.testcontainers.containers.PulsarContainer( 58 | DockerImageName.parse(image).asCompatibleSubstituteFor("apachepulsar/pulsar")) 59 | .withNetwork(network) 60 | .withNetworkAliases("pulsar") 61 | .withExposedPorts(8080, 5672) // ensure that the ports are listening 62 | .withEnv("PULSAR_STANDALONE_USE_ZOOKEEPER", "true") 63 | .withClasspathResourceMapping( 64 | PROTOCOLS_TEST_PROTOCOL_HANDLER_NAR, 65 | "/pulsar/protocols/starlight-for-rabbitmq.nar", 66 | BindMode.READ_ONLY) 67 | .withClasspathResourceMapping( 68 | "standalone_with_s4r.conf", "/pulsar/conf/standalone.conf", BindMode.READ_ONLY) 69 | .withLogConsumer( 70 | (f) -> { 71 | String text = f.getUtf8String().trim(); 72 | log.info(text); 73 | }); 74 | pulsarContainer.start(); 75 | 76 | if (startProxy) { 77 | proxyContainer = 78 | new GenericContainer<>(image) 79 | .withNetwork(network) 80 | .withNetworkAliases("pulsarproxy") 81 | .withExposedPorts(8089, 5672, 5671) // ensure that the ports are listening 82 | .withClasspathResourceMapping( 83 | PROTOCOLS_TEST_PROTOCOL_HANDLER_NAR, 84 | "/pulsar/proxyextensions/starlight-for-rabbitmq.nar", 85 | BindMode.READ_ONLY) 86 | .withClasspathResourceMapping( 87 | "proxy_with_s4r.conf", "/pulsar/conf/proxy.conf", BindMode.READ_ONLY) 88 | .withClasspathResourceMapping( 89 | "ssl/proxy.cert.pem", "/pulsar/conf/proxy.cert.pem", BindMode.READ_ONLY) 90 | .withClasspathResourceMapping( 91 | "ssl/proxy.key-pk8.pem", "/pulsar/conf/proxy.key-pk8.pem", BindMode.READ_ONLY) 92 | .withClasspathResourceMapping( 93 | "ssl/ca.cert.pem", "/pulsar/conf/ca.cert.pem", BindMode.READ_ONLY) 94 | .withCommand("bin/pulsar", "proxy") 95 | .waitingFor(Wait.forLogMessage(".*Server started at end point.*", 1)) 96 | .withLogConsumer( 97 | (f) -> { 98 | String text = f.getUtf8String().trim(); 99 | log.info(text); 100 | }); 101 | proxyContainer.start(); 102 | } 103 | } 104 | 105 | @Override 106 | public void close() { 107 | if (proxyContainer != null) { 108 | proxyContainer.stop(); 109 | } 110 | if (pulsarContainer != null) { 111 | pulsarContainer.stop(); 112 | } 113 | } 114 | 115 | public GenericContainer getPulsarContainer() { 116 | return pulsarContainer; 117 | } 118 | 119 | public GenericContainer getProxyContainer() { 120 | return proxyContainer; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /nar-tests/src/test/java/com/datastax/oss/starlight/rabbitmqnartests/DockerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 18 | * in compliance with the License. You may obtain a copy of the License at 19 | * 20 | *

http://www.apache.org/licenses/LICENSE-2.0 21 | * 22 | *

Unless required by applicable law or agreed to in writing, software distributed under the 23 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 24 | * express or implied. See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | package com.datastax.oss.starlight.rabbitmqnartests; 28 | 29 | import static org.junit.jupiter.api.Assertions.assertTrue; 30 | 31 | import com.rabbitmq.client.AMQP; 32 | import com.rabbitmq.client.Channel; 33 | import com.rabbitmq.client.Connection; 34 | import com.rabbitmq.client.ConnectionFactory; 35 | import com.rabbitmq.client.Consumer; 36 | import com.rabbitmq.client.DefaultConsumer; 37 | import com.rabbitmq.client.Envelope; 38 | import java.io.IOException; 39 | import java.net.URI; 40 | import java.nio.charset.StandardCharsets; 41 | import java.util.Arrays; 42 | import java.util.HashMap; 43 | import java.util.List; 44 | import java.util.UUID; 45 | import java.util.concurrent.CountDownLatch; 46 | import java.util.concurrent.TimeUnit; 47 | import org.junit.jupiter.params.ParameterizedTest; 48 | import org.junit.jupiter.params.provider.ValueSource; 49 | import org.testcontainers.containers.Network; 50 | 51 | public class DockerTest { 52 | 53 | private static final String IMAGE_LUNASTREAMING210 = "datastax/lunastreaming:2.10_2.9"; 54 | private static final String IMAGE_PULSAR210 = "apachepulsar/pulsar:2.10.2"; 55 | private static final String IMAGE_PULSAR211 = "apachepulsar/pulsar:2.11.0"; 56 | 57 | @ParameterizedTest 58 | @ValueSource(strings = {IMAGE_PULSAR211, IMAGE_PULSAR210, IMAGE_LUNASTREAMING210}) 59 | public void test(String image) throws Exception { 60 | // create a docker network 61 | try (Network network = Network.newNetwork(); 62 | PulsarContainer pulsarContainer = new PulsarContainer(network, true, image)) { 63 | // start Pulsar and wait for it to be ready to accept requests 64 | pulsarContainer.start(); 65 | 66 | List amqpUris = 67 | Arrays.asList( 68 | "amqp://pulsar:5672", "amqp://pulsarproxy:5672", "amqps://pulsarproxy:5671"); 69 | for (String amqpUri : amqpUris) { 70 | URI uri = URI.create(amqpUri); 71 | URI containerUri = uri; 72 | if (uri.getHost().equals("pulsar")) { 73 | containerUri = 74 | new URI( 75 | uri.getScheme(), 76 | uri.getUserInfo(), 77 | "localhost", 78 | pulsarContainer.getPulsarContainer().getMappedPort(uri.getPort()), 79 | uri.getPath(), 80 | uri.getQuery(), 81 | uri.getFragment()); 82 | } 83 | if (uri.getHost().equals("pulsarproxy")) { 84 | containerUri = 85 | new URI( 86 | uri.getScheme(), 87 | uri.getUserInfo(), 88 | "localhost", 89 | pulsarContainer.getProxyContainer().getMappedPort(uri.getPort()), 90 | uri.getPath(), 91 | uri.getQuery(), 92 | uri.getFragment()); 93 | } 94 | 95 | ConnectionFactory factory = new ConnectionFactory(); 96 | factory.setUri(containerUri); 97 | try (Connection connection = factory.newConnection(); 98 | Channel channel = connection.createChannel()) { 99 | final String queue = "test-queue-" + UUID.randomUUID(); 100 | channel.queueDeclare(queue, true, false, false, new HashMap<>()); 101 | channel.basicPublish("", queue, null, "test".getBytes(StandardCharsets.UTF_8)); 102 | 103 | final CountDownLatch latch = new CountDownLatch(1); 104 | Consumer c = 105 | new DefaultConsumer(channel) { 106 | @Override 107 | public void handleDelivery( 108 | String consumerTag, 109 | Envelope envelope, 110 | AMQP.BasicProperties properties, 111 | byte[] body) 112 | throws IOException { 113 | if (new String(body, StandardCharsets.UTF_8).equals("test")) { 114 | latch.countDown(); 115 | } 116 | } 117 | }; 118 | channel.basicConsume(queue, c); 119 | assertTrue(latch.await(10, TimeUnit.SECONDS)); 120 | } 121 | } 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/ExecutorProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import static com.google.common.base.Preconditions.checkArgument; 19 | import static com.google.common.base.Preconditions.checkNotNull; 20 | 21 | import com.google.common.collect.Lists; 22 | import io.netty.util.concurrent.DefaultThreadFactory; 23 | import java.util.List; 24 | import java.util.concurrent.ExecutorService; 25 | import java.util.concurrent.Executors; 26 | import java.util.concurrent.ScheduledExecutorService; 27 | import java.util.concurrent.TimeUnit; 28 | import java.util.concurrent.atomic.AtomicInteger; 29 | import lombok.Getter; 30 | import lombok.extern.slf4j.Slf4j; 31 | import org.apache.commons.lang3.tuple.Pair; 32 | import org.apache.pulsar.common.util.Murmur3_32Hash; 33 | 34 | @Slf4j 35 | public class ExecutorProvider { 36 | private final int numThreads; 37 | private final List> executors; 38 | private final AtomicInteger currentThread = new AtomicInteger(0); 39 | private final String poolName; 40 | private volatile boolean isShutdown; 41 | 42 | private static class ExtendedThreadFactory extends DefaultThreadFactory { 43 | 44 | @Getter private Thread thread; 45 | 46 | public ExtendedThreadFactory(String poolName, boolean daemon) { 47 | super(poolName, daemon); 48 | } 49 | 50 | @Override 51 | public Thread newThread(Runnable r) { 52 | thread = super.newThread(r); 53 | return thread; 54 | } 55 | } 56 | 57 | public ExecutorProvider(int numThreads, String poolName) { 58 | checkArgument(numThreads > 0); 59 | this.numThreads = numThreads; 60 | checkNotNull(poolName); 61 | executors = Lists.newArrayListWithCapacity(numThreads); 62 | for (int i = 0; i < numThreads; i++) { 63 | ExtendedThreadFactory threadFactory = 64 | new ExtendedThreadFactory(poolName, Thread.currentThread().isDaemon()); 65 | ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(threadFactory); 66 | executors.add(Pair.of(executor, threadFactory)); 67 | } 68 | isShutdown = false; 69 | this.poolName = poolName; 70 | } 71 | 72 | public ExecutorService getExecutor() { 73 | return executors 74 | .get((currentThread.getAndIncrement() & Integer.MAX_VALUE) % numThreads) 75 | .getKey(); 76 | } 77 | 78 | public ExecutorService getExecutor(Object object) { 79 | return getExecutorInternal(object == null ? -1 : object.hashCode() & Integer.MAX_VALUE); 80 | } 81 | 82 | public ExecutorService getExecutor(byte[] bytes) { 83 | int keyHash = Murmur3_32Hash.getInstance().makeHash(bytes); 84 | return getExecutorInternal(keyHash); 85 | } 86 | 87 | private ExecutorService getExecutorInternal(int hash) { 88 | return executors.get((hash & Integer.MAX_VALUE) % numThreads).getKey(); 89 | } 90 | 91 | public void shutdownNow() { 92 | executors.forEach( 93 | entry -> { 94 | ExecutorService executor = entry.getKey(); 95 | ExtendedThreadFactory threadFactory = entry.getValue(); 96 | executor.shutdownNow(); 97 | try { 98 | if (!executor.awaitTermination(10, TimeUnit.SECONDS)) { 99 | log.warn( 100 | "Failed to terminate executor with pool name {} within timeout. The following are stack traces of still running threads.\n{}", 101 | poolName, 102 | getThreadDump(threadFactory.getThread())); 103 | } 104 | } catch (InterruptedException e) { 105 | log.warn("Shutdown of thread pool was interrupted"); 106 | } 107 | }); 108 | isShutdown = true; 109 | } 110 | 111 | public boolean isShutdown() { 112 | return isShutdown; 113 | } 114 | 115 | private String getThreadDump(Thread thread) { 116 | StringBuilder dump = new StringBuilder(); 117 | dump.append('\n'); 118 | dump.append( 119 | String.format( 120 | "\"%s\" %s prio=%d tid=%d %s%njava.lang.Thread.State: %s", 121 | thread.getName(), 122 | (thread.isDaemon() ? "daemon" : ""), 123 | thread.getPriority(), 124 | thread.getId(), 125 | Thread.State.WAITING.equals(thread.getState()) 126 | ? "in Object.wait()" 127 | : thread.getState().name(), 128 | Thread.State.WAITING.equals(thread.getState()) 129 | ? "WAITING (on object monitor)" 130 | : thread.getState())); 131 | for (StackTraceElement stackTraceElement : thread.getStackTrace()) { 132 | dump.append("\n at "); 133 | dump.append(stackTraceElement); 134 | } 135 | dump.append("\n"); 136 | return dump.toString(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/AuthorizationIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmqtests; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertThrows; 19 | import static org.junit.jupiter.api.Assertions.assertTrue; 20 | 21 | import com.datastax.oss.starlight.rabbitmq.ConfigurationUtils; 22 | import com.datastax.oss.starlight.rabbitmq.GatewayConfiguration; 23 | import com.datastax.oss.starlight.rabbitmq.GatewayService; 24 | import com.datastax.oss.starlight.rabbitmqtests.utils.PulsarCluster; 25 | import com.rabbitmq.client.Connection; 26 | import com.rabbitmq.client.ConnectionFactory; 27 | import com.rabbitmq.client.impl.DefaultCredentialsProvider; 28 | import io.jsonwebtoken.Jwts; 29 | import io.jsonwebtoken.SignatureAlgorithm; 30 | import io.prometheus.client.CollectorRegistry; 31 | import java.io.IOException; 32 | import java.nio.file.Path; 33 | import java.util.Arrays; 34 | import java.util.Base64; 35 | import java.util.Collections; 36 | import java.util.HashSet; 37 | import javax.crypto.SecretKey; 38 | import org.apache.bookkeeper.util.PortManager; 39 | import org.apache.pulsar.broker.authentication.AuthenticationService; 40 | import org.apache.pulsar.broker.authentication.utils.AuthTokenUtils; 41 | import org.apache.pulsar.common.policies.data.AuthAction; 42 | import org.junit.jupiter.api.AfterAll; 43 | import org.junit.jupiter.api.BeforeAll; 44 | import org.junit.jupiter.api.Test; 45 | import org.junit.jupiter.api.io.TempDir; 46 | 47 | public class AuthorizationIT { 48 | 49 | @TempDir public static Path tempDir; 50 | private static PulsarCluster cluster; 51 | private static GatewayService gatewayService; 52 | private static ConnectionFactory factory; 53 | 54 | private static final SecretKey SECRET_KEY = 55 | AuthTokenUtils.createSecretKey(SignatureAlgorithm.HS256); 56 | private static final String CLIENT_ROLE = "client"; 57 | private final String CLIENT_TOKEN = 58 | Jwts.builder().setSubject(CLIENT_ROLE).signWith(SECRET_KEY).compact(); 59 | private static final String ADMIN_ROLE = "admin"; 60 | private final String ADMIN_TOKEN = 61 | Jwts.builder().setSubject(ADMIN_ROLE).signWith(SECRET_KEY).compact(); 62 | 63 | @BeforeAll 64 | public static void before() throws Exception { 65 | CollectorRegistry.defaultRegistry.clear(); 66 | cluster = new PulsarCluster(tempDir); 67 | cluster.start(); 68 | 69 | cluster 70 | .getService() 71 | .getAdminClient() 72 | .namespaces() 73 | .grantPermissionOnNamespace( 74 | "public/default", 75 | CLIENT_ROLE, 76 | new HashSet<>(Arrays.asList(AuthAction.consume, AuthAction.produce))); 77 | 78 | GatewayConfiguration config = new GatewayConfiguration(); 79 | config.setBrokerServiceURL(cluster.getAddress()); 80 | config.setBrokerWebServiceURL(cluster.getAddress()); 81 | int port = PortManager.nextFreePort(); 82 | config.setAmqpListeners(Collections.singleton("amqp://127.0.0.1:" + port)); 83 | config.setConfigurationMetadataStoreUrl( 84 | cluster.getService().getConfig().getConfigurationMetadataStoreUrl()); 85 | 86 | config.setAuthenticationEnabled(true); 87 | config 88 | .getProperties() 89 | .setProperty( 90 | "tokenSecretKey", 91 | "data:;base64," + Base64.getEncoder().encodeToString(SECRET_KEY.getEncoded())); 92 | 93 | config.setAuthorizationEnabled(true); 94 | 95 | gatewayService = 96 | new GatewayService( 97 | config, new AuthenticationService(ConfigurationUtils.convertFrom(config))); 98 | gatewayService.start(); 99 | 100 | factory = new ConnectionFactory(); 101 | factory.setPort(port); 102 | } 103 | 104 | @AfterAll 105 | public static void after() throws Exception { 106 | if (cluster != null) { 107 | cluster.close(); 108 | } 109 | if (gatewayService != null) { 110 | gatewayService.close(); 111 | } 112 | } 113 | 114 | @Test 115 | void testAuthorizationSuccess() throws Exception { 116 | factory.setCredentialsProvider(new DefaultCredentialsProvider("", ADMIN_TOKEN)); 117 | factory.setVirtualHost("/"); 118 | Connection conn = factory.newConnection(); 119 | assertTrue(conn.isOpen()); 120 | } 121 | 122 | @Test 123 | void testAuthorizationUnknownVhost() { 124 | factory.setCredentialsProvider(new DefaultCredentialsProvider("", ADMIN_TOKEN)); 125 | factory.setVirtualHost("unknown"); 126 | assertThrows(IOException.class, () -> factory.newConnection()); 127 | } 128 | 129 | @Test 130 | void testTokenAuthenticationUnauthorizedUser() { 131 | factory.setCredentialsProvider(new DefaultCredentialsProvider("", CLIENT_TOKEN)); 132 | factory.setVirtualHost("/"); 133 | assertThrows(IOException.class, () -> factory.newConnection()); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/Recover.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import static org.junit.Assert.assertNotNull; 19 | import static org.junit.Assert.assertNull; 20 | import static org.junit.Assert.assertTrue; 21 | import static org.junit.Assert.fail; 22 | 23 | import com.datastax.oss.starlight.rabbitmqtests.SystemTest; 24 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.BrokerTestCase; 25 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.QueueingConsumer; 26 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.TestUtils; 27 | import com.rabbitmq.client.*; 28 | import java.io.IOException; 29 | import java.util.Arrays; 30 | import java.util.concurrent.CountDownLatch; 31 | import java.util.concurrent.TimeUnit; 32 | import java.util.concurrent.atomic.AtomicReference; 33 | import org.junit.Test; 34 | import org.junit.experimental.categories.Category; 35 | 36 | @Category(SystemTest.class) 37 | public class Recover extends BrokerTestCase { 38 | 39 | String queue; 40 | final byte[] body = "message".getBytes(); 41 | 42 | public void createResources() throws IOException { 43 | AMQP.Queue.DeclareOk ok = channel.queueDeclare(); 44 | queue = ok.getQueue(); 45 | } 46 | 47 | static interface RecoverCallback { 48 | void recover(Channel channel) throws IOException; 49 | } 50 | 51 | // The AMQP specification under-specifies the behaviour when 52 | // requeue=false. So we can't really test any scenarios for 53 | // requeue=false. 54 | 55 | void verifyRedeliverOnRecover(RecoverCallback call) throws IOException, InterruptedException { 56 | QueueingConsumer consumer = new QueueingConsumer(channel); 57 | channel.basicConsume(queue, false, consumer); // require acks. 58 | channel.basicPublish("", queue, new AMQP.BasicProperties.Builder().build(), body); 59 | QueueingConsumer.Delivery delivery = consumer.nextDelivery(); 60 | assertTrue("consumed message body not as sent", Arrays.equals(body, delivery.getBody())); 61 | // Don't ack it, and get it redelivered to the same consumer 62 | call.recover(channel); 63 | QueueingConsumer.Delivery secondDelivery = consumer.nextDelivery(5000); 64 | assertNotNull("timed out waiting for redelivered message", secondDelivery); 65 | assertTrue( 66 | "consumed (redelivered) message body not as sent", Arrays.equals(body, delivery.getBody())); 67 | } 68 | 69 | void verifyNoRedeliveryWithAutoAck(RecoverCallback call) 70 | throws IOException, InterruptedException { 71 | final CountDownLatch latch = new CountDownLatch(1); 72 | final AtomicReference bodyReference = new AtomicReference(); 73 | Consumer consumer = 74 | new DefaultConsumer(channel) { 75 | @Override 76 | public void handleDelivery( 77 | String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) 78 | throws IOException { 79 | bodyReference.set(body); 80 | latch.countDown(); 81 | } 82 | }; 83 | channel.basicConsume(queue, true, consumer); // auto ack. 84 | channel.basicPublish("", queue, new AMQP.BasicProperties.Builder().build(), body); 85 | assertTrue(latch.await(5, TimeUnit.SECONDS)); 86 | assertTrue("consumed message body not as sent", Arrays.equals(body, bodyReference.get())); 87 | call.recover(channel); 88 | assertNull("should be no message available", TestUtils.basicGet(channel, queue, true)); 89 | } 90 | 91 | final RecoverCallback recoverSync = 92 | new RecoverCallback() { 93 | public void recover(Channel channel) throws IOException { 94 | channel.basicRecover(true); 95 | } 96 | }; 97 | 98 | final RecoverCallback recoverSyncConvenience = 99 | new RecoverCallback() { 100 | public void recover(Channel channel) throws IOException { 101 | channel.basicRecover(); 102 | } 103 | }; 104 | 105 | @Test 106 | public void redeliveryOnRecover() throws IOException, InterruptedException { 107 | verifyRedeliverOnRecover(recoverSync); 108 | } 109 | 110 | @Test 111 | public void redeliverOnRecoverConvenience() throws IOException, InterruptedException { 112 | verifyRedeliverOnRecover(recoverSyncConvenience); 113 | } 114 | 115 | @Test 116 | public void noRedeliveryWithAutoAck() throws IOException, InterruptedException { 117 | verifyNoRedeliveryWithAutoAck(recoverSync); 118 | } 119 | 120 | @Test 121 | public void requeueFalseNotSupported() throws Exception { 122 | try { 123 | channel.basicRecover(false); 124 | fail("basicRecover(false) should not be supported"); 125 | } catch (IOException ioe) { 126 | checkShutdownSignal(AMQP.NOT_IMPLEMENTED, ioe); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/test/java/com/datastax/oss/starlight/rabbitmq/Pre0_10CreditManagerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertFalse; 19 | import static org.junit.jupiter.api.Assertions.assertTrue; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | public class Pre0_10CreditManagerTest { 24 | 25 | @Test 26 | public void testBasicMessageCredit() { 27 | Pre0_10CreditManager _creditManager = new Pre0_10CreditManager(0, 0, 100L, 10L); 28 | _creditManager.setCreditLimits(0, 2); 29 | assertTrue(_creditManager.hasCredit(), "Creditmanager should have credit"); 30 | assertTrue(_creditManager.useCreditForMessage(37), "Creditmanager should be able to useCredit"); 31 | assertTrue(_creditManager.hasCredit(), "Creditmanager should have credit"); 32 | assertTrue(_creditManager.useCreditForMessage(37), "Creditmanager should be able to useCredit"); 33 | assertFalse(_creditManager.hasCredit(), "Creditmanager should have credit"); 34 | assertFalse( 35 | _creditManager.useCreditForMessage(37), "Creditmanager should be able to useCredit"); 36 | _creditManager.restoreCredit(1, 37); 37 | assertTrue(_creditManager.hasCredit(), "Creditmanager should have credit"); 38 | assertTrue(_creditManager.useCreditForMessage(37), "Creditmanager should be able to useCredit"); 39 | } 40 | 41 | @Test 42 | public void testBytesLimitDoesNotPreventLargeMessage() { 43 | Pre0_10CreditManager _creditManager = new Pre0_10CreditManager(0, 0, 100L, 10L); 44 | _creditManager.setCreditLimits(10, 0); 45 | assertTrue(_creditManager.useCreditForMessage(3), "Creditmanager should be able to useCredit"); 46 | assertFalse( 47 | _creditManager.useCreditForMessage(30), "Creditmanager should not be able to useCredit"); 48 | _creditManager.restoreCredit(1, 3); 49 | assertTrue(_creditManager.useCreditForMessage(30), "Creditmanager should be able to useCredit"); 50 | } 51 | 52 | @Test 53 | public void testUseCreditWithNegativeMessageCredit() { 54 | Pre0_10CreditManager _creditManager = new Pre0_10CreditManager(0, 0, 100L, 10L); 55 | _creditManager.setCreditLimits(0, 3); 56 | assertTrue(_creditManager.useCreditForMessage(37), "Creditmanager should be able to useCredit"); 57 | assertTrue(_creditManager.useCreditForMessage(37), "Creditmanager should be able to useCredit"); 58 | assertTrue(_creditManager.useCreditForMessage(37), "Creditmanager should be able to useCredit"); 59 | _creditManager.setCreditLimits(0, 1); // This should get us to credit=-2 60 | assertFalse(_creditManager.hasCredit(), "Creditmanager should not have credit"); 61 | assertFalse( 62 | _creditManager.useCreditForMessage(37), "Creditmanager should not be able to useCredit"); 63 | _creditManager.restoreCredit(1, 37); 64 | assertFalse(_creditManager.hasCredit(), "Creditmanager should not have credit"); 65 | _creditManager.restoreCredit(1, 37); 66 | assertFalse(_creditManager.hasCredit(), "Creditmanager should not have credit"); 67 | _creditManager.restoreCredit(1, 37); 68 | assertTrue(_creditManager.hasCredit(), "Creditmanager should have credit"); 69 | } 70 | 71 | @Test 72 | public void testUseCreditWithNegativeBytesCredit() { 73 | Pre0_10CreditManager _creditManager = new Pre0_10CreditManager(0, 0, 100L, 10L); 74 | _creditManager.setCreditLimits(3, 0); 75 | assertTrue(_creditManager.useCreditForMessage(1), "Creditmanager should be able to useCredit"); 76 | assertTrue(_creditManager.useCreditForMessage(1), "Creditmanager should be able to useCredit"); 77 | assertTrue(_creditManager.useCreditForMessage(1), "Creditmanager should be able to useCredit"); 78 | _creditManager.setCreditLimits(1, 0); // This should get us to credit=-2 79 | assertFalse(_creditManager.hasCredit(), "Creditmanager should not have credit"); 80 | assertFalse( 81 | _creditManager.useCreditForMessage(1), "Creditmanager should not be able to useCredit"); 82 | _creditManager.restoreCredit(1, 1); 83 | assertFalse(_creditManager.hasCredit(), "Creditmanager should not have credit"); 84 | _creditManager.restoreCredit(1, 1); 85 | assertFalse(_creditManager.hasCredit(), "Creditmanager should not have credit"); 86 | _creditManager.restoreCredit(1, 1); 87 | assertTrue(_creditManager.hasCredit(), "Creditmanager should have credit"); 88 | } 89 | 90 | @Test 91 | public void testCreditAccountingWhileMessageLimitNotSet() { 92 | Pre0_10CreditManager _creditManager = new Pre0_10CreditManager(0, 0, 100L, 10L); 93 | assertTrue(_creditManager.useCreditForMessage(37), "Creditmanager should be able to useCredit"); 94 | assertTrue(_creditManager.useCreditForMessage(37), "Creditmanager should be able to useCredit"); 95 | assertTrue(_creditManager.useCreditForMessage(37), "Creditmanager should be able to useCredit"); 96 | _creditManager.restoreCredit(1, 37); 97 | _creditManager.setCreditLimits(37, 1); // This should get us to credit=-1 98 | assertFalse(_creditManager.hasCredit(), "Creditmanager should not have credit"); 99 | assertFalse( 100 | _creditManager.useCreditForMessage(37), "Creditmanager should not be able to useCredit"); 101 | _creditManager.restoreCredit(1, 37); 102 | assertFalse(_creditManager.hasCredit(), "Creditmanager should not have credit"); 103 | _creditManager.restoreCredit(1, 37); 104 | assertTrue(_creditManager.hasCredit(), "Creditmanager should have credit"); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /rabbitmq-tests/src/test/java/com/datastax/oss/starlight/rabbitmqtests/javaclient/functional/Nowait.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. 2 | // 3 | // This software, the RabbitMQ Java client library, is triple-licensed under the 4 | // Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2 5 | // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see 6 | // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, 7 | // please see LICENSE-APACHE2. 8 | // 9 | // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 10 | // either express or implied. See the LICENSE file for specific language governing 11 | // rights and limitations of this software. 12 | // 13 | // If you have any questions regarding licensing, please contact us at 14 | // info@rabbitmq.com. 15 | 16 | package com.datastax.oss.starlight.rabbitmqtests.javaclient.functional; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertTrue; 19 | 20 | import com.datastax.oss.starlight.rabbitmqtests.javaclient.BrokerTestCase; 21 | import java.io.IOException; 22 | import java.time.Duration; 23 | import org.awaitility.Awaitility; 24 | import org.junit.Ignore; 25 | import org.junit.Test; 26 | 27 | public class Nowait extends BrokerTestCase { 28 | 29 | @Test 30 | public void testQueueDeclareWithNowait() throws Exception { 31 | String q = generateQueueName(); 32 | channel.queueDeclareNoWait(q, false, true, true, null); 33 | 34 | Awaitility.await() 35 | .atMost(Duration.ofSeconds(5)) 36 | .until( 37 | () -> 38 | gatewayService 39 | .getContextMetadata() 40 | .model() 41 | .getVhosts() 42 | .get("public/default") 43 | .getQueues() 44 | .containsKey(q)); 45 | 46 | channel.queueDeclarePassive(q); 47 | } 48 | 49 | @Test 50 | public void testQueueBindWithNowait() throws Exception { 51 | String q = generateQueueName(); 52 | channel.queueDeclare(q, false, true, true, null); 53 | channel.queueBindNoWait(q, "amq.fanout", "", null); 54 | 55 | Awaitility.await() 56 | .atMost(Duration.ofSeconds(5)) 57 | .until( 58 | () -> 59 | gatewayService 60 | .getContextMetadata() 61 | .model() 62 | .getVhosts() 63 | .get("public/default") 64 | .getExchanges() 65 | .get("amq.fanout") 66 | .getBindings() 67 | .containsKey(q)); 68 | } 69 | 70 | @Test 71 | public void testExchangeDeclareWithNowait() throws Exception { 72 | String x = generateExchangeName(); 73 | try { 74 | channel.exchangeDeclareNoWait(x, "fanout", false, false, false, null); 75 | 76 | Awaitility.await() 77 | .atMost(Duration.ofSeconds(5)) 78 | .until( 79 | () -> 80 | gatewayService 81 | .getContextMetadata() 82 | .model() 83 | .getVhosts() 84 | .get("public/default") 85 | .getExchanges() 86 | .containsKey(x)); 87 | 88 | channel.exchangeDeclarePassive(x); 89 | } finally { 90 | channel.exchangeDelete(x); 91 | } 92 | } 93 | 94 | @Test 95 | @Ignore("Need to implement exchange to exchange bindings first") 96 | public void testExchangeBindWithNowait() throws IOException { 97 | String x = generateExchangeName(); 98 | try { 99 | channel.exchangeDeclareNoWait(x, "fanout", false, false, false, null); 100 | channel.exchangeBindNoWait(x, "amq.fanout", "", null); 101 | } finally { 102 | channel.exchangeDelete(x); 103 | } 104 | } 105 | 106 | @Test 107 | @Ignore("Need to implement exchange to exchange bindings first") 108 | public void testExchangeUnbindWithNowait() throws IOException { 109 | String x = generateExchangeName(); 110 | try { 111 | channel.exchangeDeclare(x, "fanout", false, false, false, null); 112 | channel.exchangeBind(x, "amq.fanout", "", null); 113 | channel.exchangeUnbindNoWait(x, "amq.fanout", "", null); 114 | } finally { 115 | channel.exchangeDelete(x); 116 | } 117 | } 118 | 119 | @Test 120 | public void testQueueDeleteWithNowait() throws Exception { 121 | String q = generateQueueName(); 122 | channel.queueDeclare(q, false, true, true, null); 123 | assertTrue( 124 | gatewayService 125 | .getContextMetadata() 126 | .model() 127 | .getVhosts() 128 | .get("public/default") 129 | .getQueues() 130 | .containsKey(q)); 131 | 132 | channel.queueDeleteNoWait(q, false, false); 133 | 134 | Awaitility.await() 135 | .atMost(Duration.ofSeconds(5)) 136 | .until( 137 | () -> 138 | !gatewayService 139 | .getContextMetadata() 140 | .model() 141 | .getVhosts() 142 | .get("public/default") 143 | .getQueues() 144 | .containsKey(q)); 145 | } 146 | 147 | @Test 148 | public void testExchangeDeleteWithNowait() throws Exception { 149 | String x = generateExchangeName(); 150 | channel.exchangeDeclare(x, "fanout", false, false, false, null); 151 | assertTrue( 152 | gatewayService 153 | .getContextMetadata() 154 | .model() 155 | .getVhosts() 156 | .get("public/default") 157 | .getExchanges() 158 | .containsKey(x)); 159 | 160 | channel.exchangeDeleteNoWait(x, false); 161 | Awaitility.await() 162 | .atMost(Duration.ofSeconds(5)) 163 | .until( 164 | () -> 165 | !gatewayService 166 | .getContextMetadata() 167 | .model() 168 | .getVhosts() 169 | .get("public/default") 170 | .getExchanges() 171 | .containsKey(x)); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /starlight-rabbitmq/src/main/java/com/datastax/oss/starlight/rabbitmq/PulsarConsumer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright DataStax, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.datastax.oss.starlight.rabbitmq; 17 | 18 | import java.util.concurrent.CompletableFuture; 19 | import java.util.concurrent.ScheduledExecutorService; 20 | import java.util.concurrent.TimeUnit; 21 | import java.util.concurrent.atomic.AtomicBoolean; 22 | import org.apache.pulsar.client.api.Consumer; 23 | import org.apache.pulsar.client.api.Message; 24 | import org.apache.pulsar.client.api.MessageId; 25 | import org.apache.pulsar.client.api.PulsarClientException; 26 | import org.apache.pulsar.client.api.SubscriptionType; 27 | 28 | public class PulsarConsumer { 29 | 30 | private final String topic; 31 | private final GatewayService gatewayService; 32 | private final AMQConsumer amqConsumer; 33 | private volatile Consumer pulsarConsumer; 34 | 35 | private final String subscriptionName; 36 | 37 | private volatile MessageId lastMessageId; 38 | 39 | private final ScheduledExecutorService internalPinnedExecutor; 40 | 41 | private final AtomicBoolean closing; 42 | 43 | PulsarConsumer( 44 | String topic, String subscriptionName, GatewayService service, AMQConsumer amqConsumer) { 45 | this.topic = topic; 46 | this.subscriptionName = subscriptionName; 47 | this.gatewayService = service; 48 | this.amqConsumer = amqConsumer; 49 | this.internalPinnedExecutor = (ScheduledExecutorService) service.getInternalExecutorService(); 50 | this.closing = new AtomicBoolean(false); 51 | } 52 | 53 | public CompletableFuture subscribe() { 54 | return gatewayService 55 | .getPulsarClient() 56 | .newConsumer() 57 | .topic(topic) 58 | .subscriptionName(subscriptionName) 59 | .subscriptionType(SubscriptionType.Shared) 60 | .negativeAckRedeliveryDelay(0, TimeUnit.MILLISECONDS) 61 | .receiverQueueSize(10000) 62 | .maxTotalReceiverQueueSizeAcrossPartitions(Integer.MAX_VALUE) 63 | .enableBatchIndexAcknowledgment(true) 64 | .subscribeAsync() 65 | .thenAccept( 66 | consumer -> { 67 | if (!closing.get()) { 68 | this.pulsarConsumer = consumer; 69 | } else { 70 | consumer.closeAsync(); 71 | } 72 | }); 73 | } 74 | 75 | private CompletableFuture receiveMessageAsync() { 76 | return pulsarConsumer 77 | .receiveAsync() 78 | .thenApply( 79 | msg -> { 80 | if (lastMessageId != null) { 81 | if (msg.getMessageId().compareTo(lastMessageId) > 0) { 82 | // No more consumption needed. Now wait for acks before closing. 83 | pulsarConsumer.acknowledgeAsync(msg); 84 | 85 | // Drain the receiver queue 86 | pulsarConsumer.pause(); 87 | Message message = null; 88 | do { 89 | try { 90 | message = pulsarConsumer.receive(0, TimeUnit.SECONDS); 91 | } catch (PulsarClientException e) { 92 | // TODO: handle exception 93 | } 94 | if (message != null) { 95 | if (message.getMessageId().compareTo(lastMessageId) > 0) { 96 | pulsarConsumer.acknowledgeAsync(message); 97 | } else { 98 | return new PulsarConsumerMessage(message, this); 99 | } 100 | } 101 | } while (message != null); 102 | 103 | // Receive messages again after some time to check if we get unacked messages 104 | // Note: unacked messages are sent in priority by the broker 105 | this.internalPinnedExecutor.schedule( 106 | this::resumeConsumption, 100, TimeUnit.MILLISECONDS); 107 | return null; 108 | } else { 109 | pulsarConsumer.resume(); 110 | } 111 | } 112 | return new PulsarConsumerMessage(msg, this); 113 | }); 114 | } 115 | 116 | public void setLastMessageId(MessageId lastMessageId) { 117 | this.lastMessageId = lastMessageId; 118 | } 119 | 120 | public CompletableFuture receiveAndDeliverMessages() { 121 | return receiveMessageAsync().thenAccept(amqConsumer::deliverMessage); 122 | } 123 | 124 | private CompletableFuture resumeConsumption() { 125 | pulsarConsumer.resume(); 126 | return receiveAndDeliverMessages(); 127 | } 128 | 129 | public void close() { 130 | closing.set(true); 131 | if (pulsarConsumer != null) { 132 | pulsarConsumer.closeAsync(); 133 | } 134 | } 135 | 136 | public CompletableFuture ackMessage(MessageId messageId) { 137 | return pulsarConsumer.acknowledgeAsync(messageId); 138 | } 139 | 140 | public void nackMessage(MessageId messageId) { 141 | pulsarConsumer.negativeAcknowledge(messageId); 142 | } 143 | 144 | public static class PulsarConsumerMessage { 145 | private final Message message; 146 | private final PulsarConsumer consumer; 147 | 148 | public PulsarConsumerMessage(Message message, PulsarConsumer consumer) { 149 | this.message = message; 150 | this.consumer = consumer; 151 | } 152 | 153 | public Message getMessage() { 154 | return message; 155 | } 156 | 157 | public PulsarConsumer getConsumer() { 158 | return consumer; 159 | } 160 | } 161 | } 162 | --------------------------------------------------------------------------------