Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.reactivecommons.async.impl.config.annotations;

import org.reactivecommons.async.starter.config.ReactiveCommonsDynamicConfig;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Enables runtime-configurable reactive-commons features driven by a
* {@link org.reactivecommons.async.starter.config.ReactiveCommonsFeatures} bean.
* <p>
* Place this annotation on any {@code @Configuration} class (typically your
* {@code @SpringBootApplication}) and declare a
* {@link org.reactivecommons.async.starter.config.ReactiveCommonsFeatures} bean
* to control which features are activated at runtime:
*
* <pre>{@code
* @EnableReactiveCommonsDynamic
* @SpringBootApplication
* public class MyApplication { ... }
*
* @Bean
* public ReactiveCommonsFeatures reactiveCommonsFeatures() {
* boolean needsEvents = someService.needsEventListening();
* return ReactiveCommonsFeatures.builder()
* .listenEvents(needsEvents)
* .sendEvents(true)
* .listenCommands(true)
* .sendCommands(true)
* .build();
* }
* }</pre>
*
* <p>This is a dynamic alternative to combining the static annotations
* ({@code @EnableEventListeners}, {@code @EnableCommandListeners},
* {@code @EnableDomainEventBus}, etc.). Both approaches coexist — no changes
* are needed to existing code that uses the static annotations.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import(ReactiveCommonsDynamicConfig.class)
@Configuration
public @interface EnableReactiveCommonsDynamic {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.reactivecommons.async.starter.config;

import java.util.HashMap;


public class ReactiveCommonsDomainFeatures extends HashMap<String, ReactiveCommonsFeatures> {

public ReactiveCommonsDomainFeatures() {
super();
}

public ReactiveCommonsFeatures ofDomain(String key) {
return this.get(key);
}

public ReactiveCommonsFeatures withDomain(String key) {
return this.computeIfAbsent(key, k -> new ReactiveCommonsFeatures());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package org.reactivecommons.async.starter.config;

import lombok.extern.log4j.Log4j2;
import org.reactivecommons.api.domain.DomainEventBus;
import org.reactivecommons.async.api.DirectAsyncGateway;
import org.reactivecommons.async.starter.config.disabled.DisabledDirectAsyncGateway;
import org.reactivecommons.async.starter.config.disabled.DisabledDomainEventBus;
import org.reactivecommons.async.starter.senders.GenericDirectAsyncGateway;
import org.reactivecommons.async.starter.senders.GenericDomainEventBus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
* Dynamic alternative to the static {@code @Enable*} annotations.
* <p>
* All beans in this configuration are only created when a {@link ReactiveCommonsFeatures} bean
* is present in the application context. Each feature is then conditionally activated based
* on the flags set in that bean.
* <p>
* Use {@code @EnableReactiveCommonsDynamic} (or {@code @Import(ReactiveCommonsDynamicConfig.class)})
* together with a {@code @Bean} of type {@link ReactiveCommonsFeatures} in your application.
*/
@Log4j2
@Configuration
@Import({ReactiveCommonsConfig.class, ReactiveCommonsListenersConfig.class})
public class ReactiveCommonsDynamicConfig {

// -------------------------------------------------------------------------
// Listener activation — side-effect beans that start broker listeners
// when the corresponding feature flag is true.
// -------------------------------------------------------------------------

@Bean
@SuppressWarnings("rawtypes")
public Object dynamicEventListenerActivator(ConnectionManager manager,
DomainHandlers handlers,
ReactiveCommonsDomainFeatures features) {
manager.forDomain((domain, provider) -> {
if (features.ofDomain(domain).isListenEvents()) {
log.info("ReactiveCommons: activating event listeners for domain '{}'", domain);
provider.listenDomainEvents(handlers.get(domain));
}
});
return new Object();
}

@Bean
@SuppressWarnings("rawtypes")
public Object dynamicNotificationListenerActivator(ConnectionManager manager,
DomainHandlers handlers,
ReactiveCommonsDomainFeatures features) {
manager.forDomain((domain, provider) -> {
if (features.ofDomain(domain).isListenNotificationEvents()) {
log.info("ReactiveCommons: activating notification event listeners for domain '{}'", domain);
provider.listenNotificationEvents(handlers.get(domain));
}
});
return new Object();
}

@Bean
@SuppressWarnings("rawtypes")
public Object dynamicCommandListenerActivator(ConnectionManager manager,
DomainHandlers handlers,
ReactiveCommonsDomainFeatures features) {
manager.forDomain((domain, provider) -> {
if (features.ofDomain(domain).isListenCommands()) {
log.info("ReactiveCommons: activating command listeners for domain '{}'", domain);
provider.listenCommands(handlers.get(domain));
}
});
return new Object();
}

@Bean
@SuppressWarnings("rawtypes")
public Object dynamicQueryListenerActivator(ConnectionManager manager,
DomainHandlers handlers,
ReactiveCommonsDomainFeatures features) {
manager.forDomain((domain, provider) -> {
if (features.ofDomain(domain).isListenQueries()) {
log.info("ReactiveCommons: activating query listeners for domain '{}'", domain);
provider.listenQueries(handlers.get(domain));
}
});
return new Object();
}

@Bean
@SuppressWarnings("rawtypes")
public Object dynamicQueueListenerActivator(ConnectionManager manager,
DomainHandlers handlers,
ReactiveCommonsDomainFeatures features) {
manager.forDomain((domain, provider) -> {
if (features.ofDomain(domain).isListenQueues()) {
log.info("ReactiveCommons: activating queue listeners for domain '{}'", domain);
provider.listenQueues(handlers.get(domain));
}
});
return new Object();
}

// -------------------------------------------------------------------------
// Sender beans — always created when ReactiveCommonsFeatures is present.
// If the corresponding flag is false, calls return a Mono.error to signal
// misconfiguration clearly at use-time rather than at startup.
// -------------------------------------------------------------------------

@Bean
@SuppressWarnings("rawtypes")
public DomainEventBus dynamicDomainEventBus(ConnectionManager manager,
ReactiveCommonsDomainFeatures features) {
ConcurrentMap<String, DomainEventBus> buses = new ConcurrentHashMap<>();
manager.forDomain((domain, provider) -> {
if (features.ofDomain(domain).isSendEvents()) {
log.info("ReactiveCommons: activating DomainEventBus for domain '{}'", domain);
buses.put(domain, provider.getDomainBus());
}
});
if (buses.isEmpty()) {
return new DisabledDomainEventBus();
}
return new GenericDomainEventBus(buses);
}

@Bean
@SuppressWarnings("rawtypes")
public DirectAsyncGateway dynamicDirectAsyncGateway(ConnectionManager manager,
ReactiveCommonsDomainFeatures features) {
ConcurrentMap<String, DirectAsyncGateway> gateways = new ConcurrentHashMap<>();
manager.forDomain((domain, provider) -> {
if (features.ofDomain(domain).isSendCommands()) {
log.info("ReactiveCommons: activating DirectAsyncGateway for domain '{}'", domain);
gateways.put(domain, provider.getDirectAsyncGateway());
}
});
if (gateways.isEmpty()) {
return new DisabledDirectAsyncGateway();
}
return new GenericDirectAsyncGateway(gateways);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.reactivecommons.async.starter.config;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class ReactiveCommonsFeatures {

/**
* Equivalent to {@code @EnableEventListeners}.
* Start listening to domain events from the broker.
*/
private boolean listenEvents;

/**
* Equivalent to {@code @EnableNotificationListener}.
* Start listening to notification events from the broker.
*/
private boolean listenNotificationEvents;

/**
* Equivalent to {@code @EnableCommandListeners}.
* Start listening to commands from the broker.
*/
private boolean listenCommands;

/**
* Equivalent to {@code @EnableQueryListeners}.
* Start listening to queries from the broker.
*/
private boolean listenQueries;

/**
* Equivalent to {@code @EnableQueueListeners}.
* Start listening to queues from the broker.
*/
private boolean listenQueues;

/**
* Equivalent to {@code @EnableDomainEventBus}.
* Exposes a {@link org.reactivecommons.api.domain.DomainEventBus} bean for publishing events.
*/
private boolean sendEvents;

/**
* Equivalent to {@code @EnableDirectAsyncGateway}.
* Exposes a {@link org.reactivecommons.async.api.DirectAsyncGateway} bean for sending
* commands and request-reply queries.
*/
private boolean sendCommands;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package org.reactivecommons.async.starter.config.disabled;

import org.reactivecommons.async.api.DirectAsyncGateway;
import reactor.core.publisher.Mono;

public class DisabledDirectAsyncGateway implements DirectAsyncGateway {

private static final String SEND_COMMANDS_DISABLED =
"sendCommands feature is disabled in ReactiveCommonsFeatures. " +
"Set sendCommands=true to send commands or queries.";

@Override
public <T> Mono<Void> sendCommand(org.reactivecommons.api.domain.Command<T> command, String targetName) {
return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
}

@Override
public <T> Mono<Void> sendCommand(org.reactivecommons.api.domain.Command<T> command, String targetName,
long delayMillis) {
return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
}

@Override
public <T> Mono<Void> sendCommand(org.reactivecommons.api.domain.Command<T> command, String targetName,
String domain) {
return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
}

@Override
public <T> Mono<Void> sendCommand(org.reactivecommons.api.domain.Command<T> command, String targetName,
long delayMillis, String domain) {
return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
}

@Override
public Mono<Void> sendCommand(io.cloudevents.CloudEvent command, String targetName) {
return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
}

@Override
public Mono<Void> sendCommand(io.cloudevents.CloudEvent command, String targetName, long delayMillis) {
return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
}

@Override
public Mono<Void> sendCommand(io.cloudevents.CloudEvent command, String targetName, String domain) {
return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
}

@Override
public Mono<Void> sendCommand(io.cloudevents.CloudEvent command, String targetName, long delayMillis,
String domain) {
return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
}

@Override
public <T, R> Mono<R> requestReply(org.reactivecommons.async.api.AsyncQuery<T> query, String targetName,
Class<R> type) {
return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
}

@Override
public <T, R> Mono<R> requestReply(org.reactivecommons.async.api.AsyncQuery<T> query, String targetName,
Class<R> type, String domain) {
return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
}

@Override
public <R extends io.cloudevents.CloudEvent> Mono<R> requestReply(io.cloudevents.CloudEvent query,
String targetName, Class<R> type) {
return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
}

@Override
public <R extends io.cloudevents.CloudEvent> Mono<R> requestReply(io.cloudevents.CloudEvent query,
String targetName, Class<R> type,
String domain) {
return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
}

@Override
public <T> Mono<Void> reply(T response, org.reactivecommons.async.api.From from) {
return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
}
}
Loading
Loading