CloudBridge is a Java 21 Spring Boot starter that gives you one portable programming model for queue messaging, listener execution, retry/DLQ handling, and simple key-value storage.
The goal is to keep application code cloud-agnostic while letting adapters handle provider details.
- Implemented messaging providers: AWS, Azure, GCP, OCI
- Implemented storage provider: AWS
- Java version: 21
- Packaging: Spring Boot auto-configuration based starter library
QueueClientfor sending messagesCloudMessageas the normalized message envelopeSendOptionsfor portable send hints such as delay@QueueListenerfor listener methodsAcknowledgementfor explicit ack/nack handling
- Annotation scanning for
@QueueListener - Listener registry and endpoint model
- Thread-pool-backed dispatch
- Method invocation with
CloudMessage, payloadString, andAcknowledgement
- Configurable retry policy
- Backoff support
- Dead-letter publishing through a portable abstraction
- Default DLQ naming with
queue + dlqSuffix
cloud.*configuration binding- Provider selection through
cloud.provider - Auto-configured messaging, listener, retry, and storage beans
KeyValueStoreabstraction- AWS DynamoDB implementation
- AWS SQS queue sender and polling consumer
- Azure Service Bus sender and consumer
- GCP Pub/Sub sender and consumer
| Feature | AWS | Azure | GCP | OCI |
|---|---|---|---|---|
| Queue send | Yes | Yes | Yes | Yes |
| Queue receive | Yes | Yes | Yes | Yes |
| Listener engine | Yes | Yes | Yes | Yes |
| Retry + DLQ pipeline | Yes | Yes | Yes | Yes |
| Delayed delivery | Yes | Yes | No portable scheduler in current adapter | Yes |
| Key-value storage | DynamoDB | No | No | No |
src/main/java/io/cloudbridge
autoconfigure/
aws/
azure/
config/
core/
gcp/
listener/
retry/
flowchart LR
App[Spring Boot App] --> API[QueueClient / @QueueListener / KeyValueStore]
API --> Core[CloudBridge Core]
Core --> Retry[Retry + DLQ]
Core --> Engine[Listener Engine]
Core --> Adapter[Provider Adapter]
Adapter --> AWS[AWS SQS / DynamoDB]
Adapter --> AZ[Azure Service Bus]
Adapter --> GCP[GCP Pub/Sub]
Adapter --> OCI[OCI Queue]
- Java 21
- Gradle 8.x installed locally, or a Gradle wrapper added to the repo
gradle clean buildRun tests:
gradle testPublish to the local Maven cache:
gradle publishToMavenLocalPublish to the local build repository at build/repo:
gradle publishAdd the dependency:
dependencies {
implementation("io.cloudbridge:cloud-bridge-spring-boot-starter:0.1.0-SNAPSHOT")
}CloudBridge uses Spring Boot auto-configuration, so no manual @Configuration import is required.
AWS:
cloud:
provider: AWS
aws:
region: us-east-1
endpoint: http://localhost:4566
storage:
tableName: cloud_bridge_kv
messaging:
retry:
maxAttempts: 3
backoffMs: 2000Azure:
cloud:
provider: AZURE
azure:
connectionString: Endpoint=sb://your-namespace.servicebus.windows.net/;SharedAccessKeyName=...GCP:
cloud:
provider: GCP
gcp:
projectId: your-project-idOCI:
cloud:
provider: OCI
oci:
endpoint: https://cell-1.queue.messaging.<region>.oci.oraclecloud.com
configFilePath: ~/.oci/config
profile: DEFAULTimport io.cloudbridge.core.messaging.CloudMessage;
import io.cloudbridge.core.messaging.QueueClient;
import io.cloudbridge.core.messaging.SendOptions;
import java.time.Duration;
import org.springframework.stereotype.Service;
@Service
public class OrderPublisher {
private final QueueClient queueClient;
public OrderPublisher(QueueClient queueClient) {
this.queueClient = queueClient;
}
public void publish(String payload) {
CloudMessage message = new CloudMessage(payload);
queueClient.send("order-events", message, new SendOptions(Duration.ofSeconds(5)));
}
}import io.cloudbridge.core.messaging.Acknowledgement;
import io.cloudbridge.core.messaging.CloudMessage;
import io.cloudbridge.core.messaging.QueueListener;
import org.springframework.stereotype.Component;
@Component
public class OrderListener {
@QueueListener(value = "order-events", concurrency = 4)
public void handle(CloudMessage message, Acknowledgement acknowledgement) {
process(message.payload());
acknowledgement.ack();
}
private void process(String payload) {
}
}import io.cloudbridge.core.storage.KeyValueStore;
import org.springframework.stereotype.Service;
@Service
public class OrderStateStore {
private final KeyValueStore keyValueStore;
public OrderStateStore(KeyValueStore keyValueStore) {
this.keyValueStore = keyValueStore;
}
public void save(String orderId, String payload) {
keyValueStore.put(orderId, payload);
}
}cloud:
provider: AWS | AZURE | GCP | OCIcloud:
messaging:
dlqSuffix: .dlq
receiveWaitSeconds: 10
idleBackoffMs: 1000
listener:
defaultConcurrency: 1
workerThreads: 4
queueCapacity: 100
retry:
maxAttempts: 3
backoffMs: 2000cloud:
aws:
region: us-east-1
endpoint: http://localhost:4566
queuePrefix: ""
dynamoTable: cloud_bridge_kvcloud:
azure:
connectionString: ...cloud:
gcp:
projectId: your-project-id
emulatorHost: localhost:8085cloud:
oci:
endpoint: https://cell-1.queue.messaging.<region>.oci.oraclecloud.com
configFilePath: ~/.oci/config
profile: DEFAULT
channelConsumptionLimit: 10
visibilityInSeconds: 30
pollingTimeoutSeconds: 10cloud:
storage:
tableName: cloud_bridge_kvDepending on provider and classpath, CloudBridge wires these core beans:
QueueClientQueueConsumerFactoryQueueListenerRegistryQueueListenerMethodInvokerQueueListenerContainerManagerRetryPolicyRetryExecutorDeadLetterPublisherCloudCapabilitiesKeyValueStorefor AWS- Provider SDK clients such as
SqsClient,DynamoDbClient, andServiceBusClientBuilder
- Call
ack()when processing succeeds - Call
nack(Throwable)when processing should fail immediately - In the current AWS adapter,
ack()deletes the SQS message andnack()leaves it for redelivery - The current listener pipeline also acknowledges automatically when the listener method returns successfully
- Current adapter acknowledgement implementations are idempotent, so an explicit
ack()and the framework success path do not conflict
- Listener execution runs through
RetryExecutor - Success triggers
ack() - Repeated failure retries until
maxAttempts - After the final failure, CloudBridge publishes the message to the DLQ destination and then acknowledges the original message
void handle(CloudMessage message)void handle(String payload)void handle(CloudMessage message, Acknowledgement acknowledgement)void handle(String payload, Acknowledgement acknowledgement)
- AWS uses SQS delay seconds when
SendOptions.delay()is set - Azure uses scheduled enqueue time
- GCP adapter currently ignores send delay because the current portable adapter does not implement provider-specific scheduling
- OCI uses per-message delay when
SendOptions.delay()is set
- AWS listener and send destinations are queue names
- Azure listener and send destinations are queue names
- GCP listener and send destinations are topic or subscription names, depending on producer or consumer context
- OCI listener and send destinations are queue OCIDs in the current adapter
Example local config:
cloud:
provider: AWS
aws:
region: us-east-1
endpoint: http://localhost:4566Typical flow:
- Start LocalStack.
- Create the SQS queue and DynamoDB table.
- Start your Spring Boot app.
- Inject
QueueClientandKeyValueStore. - Send messages and verify listener execution.
gradle publishToMavenLocalgradle publishArtifacts are written to:
build/repo/io/cloudbridge/cloud-bridge-spring-boot-starter/
Add a real repository block in build.gradle.kts, for example:
publishing {
repositories {
maven {
name = "companyRepo"
url = uri("https://repo.example.com/releases")
credentials {
username = findProperty("repoUser") as String?
password = findProperty("repoPassword") as String?
}
}
}
}Then run:
gradle publish- Storage is currently implemented only for AWS DynamoDB
- There is no Gradle wrapper committed yet
- External-provider integration tests are not wired yet
- Current publication metadata uses placeholder GitHub coordinates and should be replaced
- spring boot multi cloud
- cloud agnostic java
- aws sqs alternative
- multi-cloud messaging
- portable queue listener
- spring boot messaging starter