Browse Source
Add `ConnectionDetails` support for Apache Pulsar and provide adapters for Docker Compose and Testcontainers. See gh-37197pull/37242/head
22 changed files with 549 additions and 14 deletions
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
/* |
||||
* Copyright 2012-2023 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.autoconfigure.pulsar; |
||||
|
||||
/** |
||||
* Adapts {@link PulsarProperties} to {@link PulsarConnectionDetails}. |
||||
* |
||||
* @author Chris Bono |
||||
*/ |
||||
class PropertiesPulsarConnectionDetails implements PulsarConnectionDetails { |
||||
|
||||
private final PulsarProperties pulsarProperties; |
||||
|
||||
PropertiesPulsarConnectionDetails(PulsarProperties pulsarProperties) { |
||||
this.pulsarProperties = pulsarProperties; |
||||
} |
||||
|
||||
@Override |
||||
public String getPulsarBrokerUrl() { |
||||
return this.pulsarProperties.getClient().getServiceUrl(); |
||||
} |
||||
|
||||
@Override |
||||
public String getPulsarAdminUrl() { |
||||
return this.pulsarProperties.getAdmin().getServiceUrl(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
/* |
||||
* Copyright 2012-2023 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.autoconfigure.pulsar; |
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; |
||||
|
||||
/** |
||||
* Details required to establish a connection to a Pulsar service. |
||||
* |
||||
* @author Chris Bono |
||||
* @since 3.2.0 |
||||
*/ |
||||
public interface PulsarConnectionDetails extends ConnectionDetails { |
||||
|
||||
/** |
||||
* Returns the Pulsar service URL for the broker. |
||||
* @return the Pulsar service URL for the broker |
||||
*/ |
||||
String getPulsarBrokerUrl(); |
||||
|
||||
/** |
||||
* Returns the Pulsar web URL for the admin endpoint. |
||||
* @return the Pulsar web URL for the admin endpoint |
||||
*/ |
||||
String getPulsarAdminUrl(); |
||||
|
||||
} |
||||
@ -0,0 +1,46 @@
@@ -0,0 +1,46 @@
|
||||
/* |
||||
* Copyright 2012-2023 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.autoconfigure.pulsar; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link PropertiesPulsarConnectionDetails}. |
||||
* |
||||
* @author Chris Bono |
||||
*/ |
||||
class PropertiesPulsarConnectionDetailsTests { |
||||
|
||||
@Test |
||||
void pulsarBrokerUrlIsObtainedFromPulsarProperties() { |
||||
var pulsarProps = new PulsarProperties(); |
||||
pulsarProps.getClient().setServiceUrl("foo"); |
||||
var connectionDetails = new PropertiesPulsarConnectionDetails(pulsarProps); |
||||
assertThat(connectionDetails.getPulsarBrokerUrl()).isEqualTo("foo"); |
||||
} |
||||
|
||||
@Test |
||||
void pulsarAdminUrlIsObtainedFromPulsarProperties() { |
||||
var pulsarProps = new PulsarProperties(); |
||||
pulsarProps.getAdmin().setServiceUrl("foo"); |
||||
var connectionDetails = new PropertiesPulsarConnectionDetails(pulsarProps); |
||||
assertThat(connectionDetails.getPulsarAdminUrl()).isEqualTo("foo"); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
/* |
||||
* Copyright 2012-2023 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.docker.compose.service.connection.pulsar; |
||||
|
||||
import org.springframework.boot.autoconfigure.pulsar.PulsarConnectionDetails; |
||||
import org.springframework.boot.docker.compose.core.RunningService; |
||||
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory; |
||||
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource; |
||||
|
||||
/** |
||||
* {@link DockerComposeConnectionDetailsFactory} to create {@link PulsarConnectionDetails} |
||||
* for a {@code pulsar} service. |
||||
* |
||||
* @author Chris Bono |
||||
*/ |
||||
class PulsarDockerComposeConnectionDetailsFactory |
||||
extends DockerComposeConnectionDetailsFactory<PulsarConnectionDetails> { |
||||
|
||||
private static final int PULSAR_BROKER_PORT = 6650; |
||||
|
||||
private static final int PULSAR_ADMIN_PORT = 8080; |
||||
|
||||
PulsarDockerComposeConnectionDetailsFactory() { |
||||
super("apachepulsar/pulsar"); |
||||
} |
||||
|
||||
@Override |
||||
protected PulsarConnectionDetails getDockerComposeConnectionDetails(DockerComposeConnectionSource source) { |
||||
return new PulsarDockerComposeConnectionDetails(source.getRunningService()); |
||||
} |
||||
|
||||
/** |
||||
* {@link PulsarConnectionDetails} backed by a {@code pulsar} {@link RunningService}. |
||||
*/ |
||||
static class PulsarDockerComposeConnectionDetails extends DockerComposeConnectionDetails |
||||
implements PulsarConnectionDetails { |
||||
|
||||
private final String brokerUrl; |
||||
|
||||
private final String adminUrl; |
||||
|
||||
PulsarDockerComposeConnectionDetails(RunningService service) { |
||||
super(service); |
||||
this.brokerUrl = "pulsar://%s:%s".formatted(service.host(), service.ports().get(PULSAR_BROKER_PORT)); |
||||
this.adminUrl = "http://%s:%s".formatted(service.host(), service.ports().get(PULSAR_ADMIN_PORT)); |
||||
} |
||||
|
||||
@Override |
||||
public String getPulsarBrokerUrl() { |
||||
return this.brokerUrl; |
||||
} |
||||
|
||||
@Override |
||||
public String getPulsarAdminUrl() { |
||||
return this.adminUrl; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
/* |
||||
* Copyright 2012-2023 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* Auto-configuration for docker compose Pulsar service connections. |
||||
*/ |
||||
package org.springframework.boot.docker.compose.service.connection.pulsar; |
||||
@ -0,0 +1,46 @@
@@ -0,0 +1,46 @@
|
||||
/* |
||||
* Copyright 2023-2023 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.docker.compose.service.connection.pulsar; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.boot.autoconfigure.pulsar.PulsarConnectionDetails; |
||||
import org.springframework.boot.docker.compose.service.connection.test.AbstractDockerComposeIntegrationTests; |
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Integration test for {@link PulsarDockerComposeConnectionDetailsFactory}. |
||||
* |
||||
* @author Chris Bono |
||||
*/ |
||||
class PulsarDockerComposeConnectionDetailsFactoryIntegrationTests extends AbstractDockerComposeIntegrationTests { |
||||
|
||||
PulsarDockerComposeConnectionDetailsFactoryIntegrationTests() { |
||||
super("pulsar-compose.yaml", DockerImageNames.pulsar()); |
||||
} |
||||
|
||||
@Test |
||||
void runCreatesConnectionDetails() { |
||||
PulsarConnectionDetails connectionDetails = run(PulsarConnectionDetails.class); |
||||
assertThat(connectionDetails).isNotNull(); |
||||
assertThat(connectionDetails.getPulsarBrokerUrl()).matches("^pulsar:\\/\\/\\S+:\\d+"); |
||||
assertThat(connectionDetails.getPulsarAdminUrl()).matches("^http:\\/\\/\\S+:\\d+"); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
services: |
||||
pulsar: |
||||
image: '{imageName}' |
||||
ports: |
||||
- '8080' |
||||
- '6650' |
||||
command: bin/pulsar standalone |
||||
healthcheck: |
||||
test: curl http://127.0.0.1:8080/admin/v2/namespaces/public/default |
||||
@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
|
||||
/* |
||||
* Copyright 2012-2023 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.testcontainers.service.connection.pulsar; |
||||
|
||||
import org.testcontainers.containers.PulsarContainer; |
||||
|
||||
import org.springframework.boot.autoconfigure.pulsar.PulsarConnectionDetails; |
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory; |
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource; |
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection; |
||||
|
||||
/** |
||||
* {@link ContainerConnectionDetailsFactory} to create {@link PulsarConnectionDetails} |
||||
* from a {@link ServiceConnection @ServiceConnection}-annotated {@link PulsarContainer}. |
||||
* |
||||
* @author Chris Bono |
||||
*/ |
||||
class PulsarContainerConnectionDetailsFactory |
||||
extends ContainerConnectionDetailsFactory<PulsarContainer, PulsarConnectionDetails> { |
||||
|
||||
@Override |
||||
protected PulsarConnectionDetails getContainerConnectionDetails(ContainerConnectionSource<PulsarContainer> source) { |
||||
return new PulsarContainerConnectionDetails(source); |
||||
} |
||||
|
||||
/** |
||||
* {@link PulsarConnectionDetails} backed by a {@link ContainerConnectionSource}. |
||||
*/ |
||||
private static final class PulsarContainerConnectionDetails extends ContainerConnectionDetails<PulsarContainer> |
||||
implements PulsarConnectionDetails { |
||||
|
||||
private PulsarContainerConnectionDetails(ContainerConnectionSource<PulsarContainer> source) { |
||||
super(source); |
||||
} |
||||
|
||||
@Override |
||||
public String getPulsarBrokerUrl() { |
||||
return getContainer().getPulsarBrokerUrl(); |
||||
} |
||||
|
||||
@Override |
||||
public String getPulsarAdminUrl() { |
||||
return getContainer().getHttpServiceUrl(); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
/* |
||||
* Copyright 2012-2023 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* Support for testcontainers Pulsar service connections. |
||||
*/ |
||||
package org.springframework.boot.testcontainers.service.connection.pulsar; |
||||
@ -0,0 +1,95 @@
@@ -0,0 +1,95 @@
|
||||
/* |
||||
* Copyright 2012-2023 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.testcontainers.service.connection.pulsar; |
||||
|
||||
import java.time.Duration; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import org.apache.pulsar.client.api.PulsarClientException; |
||||
import org.awaitility.Awaitility; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.testcontainers.containers.PulsarContainer; |
||||
import org.testcontainers.junit.jupiter.Container; |
||||
import org.testcontainers.junit.jupiter.Testcontainers; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.pulsar.PulsarAutoConfiguration; |
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection; |
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.pulsar.annotation.PulsarListener; |
||||
import org.springframework.pulsar.core.PulsarTemplate; |
||||
import org.springframework.test.context.TestPropertySource; |
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link PulsarContainerConnectionDetailsFactory}. |
||||
* |
||||
* @author Chris Bono |
||||
*/ |
||||
@SpringJUnitConfig |
||||
@Testcontainers(disabledWithoutDocker = true) |
||||
@TestPropertySource(properties = { "spring.pulsar.consumer.subscription.initial-position=earliest" }) |
||||
class PulsarContainerConnectionDetailsFactoryIntegrationTests { |
||||
|
||||
@Container |
||||
@ServiceConnection |
||||
@SuppressWarnings("unused") |
||||
static final PulsarContainer PULSAR = new PulsarContainer(DockerImageNames.pulsar()) |
||||
.withStartupTimeout(Duration.ofMinutes(3)); |
||||
|
||||
@Autowired |
||||
private PulsarTemplate<String> pulsarTemplate; |
||||
|
||||
@Autowired |
||||
private TestListener listener; |
||||
|
||||
@Test |
||||
void connectionCanBeMadeToPulsarContainer() throws PulsarClientException { |
||||
this.pulsarTemplate.send("test-topic", "test-data"); |
||||
Awaitility.waitAtMost(Duration.ofSeconds(30)) |
||||
.untilAsserted(() -> assertThat(this.listener.messages).containsExactly("test-data")); |
||||
} |
||||
|
||||
@Configuration(proxyBeanMethods = false) |
||||
@ImportAutoConfiguration(PulsarAutoConfiguration.class) |
||||
static class TestConfiguration { |
||||
|
||||
@Bean |
||||
TestListener testListener() { |
||||
return new TestListener(); |
||||
} |
||||
|
||||
} |
||||
|
||||
static class TestListener { |
||||
|
||||
private final List<String> messages = new ArrayList<>(); |
||||
|
||||
@PulsarListener(topics = "test-topic") |
||||
void processMessage(String message) { |
||||
this.messages.add(message); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue