├── .github └── workflows │ ├── docs.yml │ ├── main.yml │ ├── release.yml │ └── updater.yml ├── .gitignore ├── .nvmrc ├── .releaserc ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── acceptance └── async-tests │ ├── async-tests.gradle │ └── src │ └── test │ ├── java │ └── org │ │ └── reactivecommons │ │ └── test │ │ ├── CommandsProcessPerfTest.java │ │ ├── DirectGatewayPerfTest.java │ │ ├── DummyMessage.java │ │ ├── DynamicRegistryTest.java │ │ ├── QueryProcessPerfTest.java │ │ ├── SimpleDirectCommunicationTest.java │ │ ├── SimpleEventNotificationTest.java │ │ └── perf │ │ ├── BlockingCommandHandlePerfTest.java │ │ ├── ParallelOnBlockingInSubscriptionTimeTest.java │ │ └── SimpleCommandHandlePerfTest.java │ └── resources │ └── application.properties ├── async ├── async-commons-api │ ├── async-commons-api.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── org │ │ │ └── reactivecommons │ │ │ └── async │ │ │ └── api │ │ │ ├── AsyncQuery.java │ │ │ ├── DefaultCommandHandler.java │ │ │ ├── DefaultQueryHandler.java │ │ │ ├── DirectAsyncGateway.java │ │ │ ├── DynamicRegistry.java │ │ │ ├── From.java │ │ │ ├── HandlerRegistry.java │ │ │ └── handlers │ │ │ ├── CloudCommandHandler.java │ │ │ ├── CloudEventHandler.java │ │ │ ├── CommandHandler.java │ │ │ ├── DomainCommandHandler.java │ │ │ ├── DomainEventHandler.java │ │ │ ├── EventHandler.java │ │ │ ├── GenericHandler.java │ │ │ ├── QueryHandler.java │ │ │ ├── QueryHandlerDelegate.java │ │ │ ├── RawCommandHandler.java │ │ │ ├── RawEventHandler.java │ │ │ └── registered │ │ │ ├── RegisteredCommandHandler.java │ │ │ ├── RegisteredDomainHandlers.java │ │ │ ├── RegisteredEventListener.java │ │ │ └── RegisteredQueryHandler.java │ │ └── test │ │ └── java │ │ └── org │ │ └── reactivecommons │ │ └── async │ │ └── api │ │ └── HandlerRegistryTest.java ├── async-commons │ ├── async-commons.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── org │ │ │ └── reactivecommons │ │ │ └── async │ │ │ └── commons │ │ │ ├── CommandExecutor.java │ │ │ ├── DLQDiscardNotifier.java │ │ │ ├── DiscardNotifier.java │ │ │ ├── EventExecutor.java │ │ │ ├── FallbackStrategy.java │ │ │ ├── HandlerResolver.java │ │ │ ├── Headers.java │ │ │ ├── QueryExecutor.java │ │ │ ├── communications │ │ │ └── Message.java │ │ │ ├── config │ │ │ ├── BrokerConfig.java │ │ │ └── IBrokerConfigProps.java │ │ │ ├── converters │ │ │ ├── MessageConverter.java │ │ │ └── json │ │ │ │ ├── CloudEventBuilderExt.java │ │ │ │ ├── DefaultObjectMapperSupplier.java │ │ │ │ ├── JacksonMessageConverter.java │ │ │ │ └── ObjectMapperSupplier.java │ │ │ ├── exceptions │ │ │ ├── MessageConversionException.java │ │ │ └── SendFailureNoAckException.java │ │ │ ├── ext │ │ │ ├── CustomReporter.java │ │ │ └── DefaultCustomReporter.java │ │ │ ├── reply │ │ │ └── ReactiveReplyRouter.java │ │ │ └── utils │ │ │ ├── ArrayUtils.java │ │ │ ├── LoggerSubscriber.java │ │ │ ├── NameGenerator.java │ │ │ ├── matcher │ │ │ ├── Candidate.java │ │ │ ├── KeyMatcher.java │ │ │ └── Matcher.java │ │ │ └── resolver │ │ │ └── HandlerResolverBuilder.java │ │ └── test │ │ ├── java │ │ └── org │ │ │ └── reactivecommons │ │ │ └── async │ │ │ └── commons │ │ │ ├── converters │ │ │ └── json │ │ │ │ ├── DefaultObjectMapperSupplierTest.java │ │ │ │ └── SampleClass.java │ │ │ ├── ext │ │ │ └── CustomErrorReporterTest.java │ │ │ ├── reply │ │ │ └── ReactiveReplyRouterTest.java │ │ │ └── utils │ │ │ ├── LoggerSubscriberTest.java │ │ │ ├── NameGeneratorTest.java │ │ │ └── matcher │ │ │ ├── KeyMatcherPerformanceManualTest.java │ │ │ ├── KeyMatcherPerformanceWildcardTest.java │ │ │ └── KeyMatcherTest.java │ │ └── resources │ │ ├── candidateNamesForMatching.txt │ │ ├── concrete_names_for_matching.txt │ │ └── wildcard_names_for_matching.txt ├── async-kafka │ ├── async-kafka.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── org │ │ │ └── reactivecommons │ │ │ └── async │ │ │ └── kafka │ │ │ ├── KafkaDirectAsyncGateway.java │ │ │ ├── KafkaDomainEventBus.java │ │ │ ├── KafkaMessage.java │ │ │ ├── communications │ │ │ ├── ReactiveMessageListener.java │ │ │ ├── ReactiveMessageSender.java │ │ │ ├── exceptions │ │ │ │ └── TopicNotFoundException.java │ │ │ └── topology │ │ │ │ ├── KafkaCustomizations.java │ │ │ │ ├── TopicCustomization.java │ │ │ │ └── TopologyCreator.java │ │ │ ├── converters │ │ │ └── json │ │ │ │ └── KafkaJacksonMessageConverter.java │ │ │ └── listeners │ │ │ ├── ApplicationEventListener.java │ │ │ ├── ApplicationNotificationsListener.java │ │ │ └── GenericMessageListener.java │ │ └── test │ │ └── java │ │ └── org │ │ └── reactivecommons │ │ └── async │ │ └── kafka │ │ ├── KafkaDirectAsyncGatewayTest.java │ │ ├── KafkaDomainEventBusTest.java │ │ ├── KafkaMessageTest.java │ │ ├── communications │ │ └── topology │ │ │ ├── KafkaCustomizationsTest.java │ │ │ └── TopologyCreatorTest.java │ │ ├── converters │ │ └── json │ │ │ └── KafkaJacksonMessageConverterTest.java │ │ └── listeners │ │ ├── ApplicationEventListenerTest.java │ │ └── GenericMessageListenerTest.java └── async-rabbit │ ├── async-rabbit.gradle │ └── src │ ├── main │ └── java │ │ └── org │ │ └── reactivecommons │ │ └── async │ │ └── rabbit │ │ ├── DynamicRegistryImp.java │ │ ├── RabbitDirectAsyncGateway.java │ │ ├── RabbitDomainEventBus.java │ │ ├── RabbitMessage.java │ │ ├── communications │ │ ├── ReactiveMessageListener.java │ │ ├── ReactiveMessageSender.java │ │ └── TopologyCreator.java │ │ ├── config │ │ └── ConnectionFactoryProvider.java │ │ ├── converters │ │ └── json │ │ │ └── RabbitJacksonMessageConverter.java │ │ └── listeners │ │ ├── ApplicationCommandListener.java │ │ ├── ApplicationEventListener.java │ │ ├── ApplicationNotificationListener.java │ │ ├── ApplicationQueryListener.java │ │ ├── ApplicationReplyListener.java │ │ └── GenericMessageListener.java │ └── test │ └── java │ └── org │ └── reactivecommons │ └── async │ ├── api │ └── MessageConversionExceptionTest.java │ ├── helpers │ ├── SampleClass.java │ └── TestStubs.java │ ├── rabbit │ ├── DynamicRegistryImpTest.java │ ├── HandlerResolverTest.java │ ├── QueryExecutorTest.java │ ├── RabbitDirectAsyncGatewayTest.java │ ├── RabbitDomainEventBusTest.java │ ├── RabbitMessageTest.java │ ├── communications │ │ └── ReactiveMessageSenderTest.java │ ├── converters │ │ └── json │ │ │ ├── CloudEventBuilderExtTest.java │ │ │ ├── JacksonMessageConverterTest.java │ │ │ └── SampleClass.java │ ├── ext │ │ └── CustomReporterTest.java │ └── listeners │ │ ├── ApplicationCommandListenerPerfTest.java │ │ ├── ApplicationCommandListenerTest.java │ │ ├── ApplicationEventListenerTest.java │ │ ├── ApplicationNotificationListenerTest.java │ │ ├── ApplicationQueryListenerErrorTest.java │ │ ├── ApplicationQueryListenerTest.java │ │ ├── ApplicationReplyListenerTest.java │ │ ├── GenericMessageListenerPerfTest.java │ │ └── ListenerReporterTestSuperClass.java │ └── utils │ └── TestUtils.java ├── build.gradle ├── docs ├── .gitignore ├── .nvmrc ├── README.md ├── babel.config.js ├── docs │ ├── img │ │ └── reactive-commons.png │ ├── intro.md │ ├── reactive-commons │ │ ├── 1-getting-started.md │ │ ├── 10-wildcards.md │ │ ├── 11-creating-a-cloud-event.md │ │ ├── 2-sending-a-domain-event.md │ │ ├── 3-sending-a-command.md │ │ ├── 4-making-an-async-query.md │ │ ├── 5-handler-registry.md │ │ ├── 6-handling-domain-events.md │ │ ├── 7-handling-commands.md │ │ ├── 8-serving-async-queries.md │ │ ├── 9-configuration-properties.md │ │ └── _category_.json │ └── scenarios │ │ ├── 1-single-broker.md │ │ ├── 2-two-brokers-same-type.md │ │ └── _category_.json ├── docusaurus.config.js ├── package-lock.json ├── package.json ├── sidebars.js ├── src │ ├── components │ │ ├── HomepageFeatures │ │ │ ├── index.js │ │ │ └── styles.module.css │ │ └── ThemeImage │ │ │ └── index.js │ ├── css │ │ └── custom.css │ └── pages │ │ ├── index.js │ │ └── index.module.css └── static │ ├── .nojekyll │ └── img │ ├── favicon.ico │ ├── feature-first.svg │ ├── feature-second.svg │ ├── feature-third.svg │ ├── logo.svg │ ├── reactive-commons.png │ ├── reactive-commons.svg │ └── scenarios │ ├── 1-scenario-dark.svg │ ├── 1-scenario.svg │ ├── 2-scenario-dark.svg │ └── 2-scenario.svg ├── domain └── domain-events │ ├── domain-events-api.gradle │ └── src │ ├── main │ └── java │ │ └── org │ │ └── reactivecommons │ │ └── api │ │ └── domain │ │ ├── Command.java │ │ ├── DomainEvent.java │ │ ├── DomainEventBus.java │ │ └── RawMessage.java │ └── test │ └── java │ └── org │ └── reactivecommons │ └── async │ └── api │ └── DomainEventTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lombok.config ├── main.gradle ├── samples └── async │ ├── async-kafka-sender-client │ ├── async-kafka-sender-client.gradle │ └── src │ │ └── main │ │ ├── java │ │ └── sample │ │ │ ├── EDASampleSenderApp.java │ │ │ ├── KafkaConfig.java │ │ │ ├── ListenerConfig.java │ │ │ └── SampleRestController.java │ │ └── resources │ │ └── application.yaml │ ├── async-receiver-responder │ ├── async-receiver-sample.gradle │ └── src │ │ └── main │ │ ├── java │ │ └── sample │ │ │ ├── HandlersConfig.java │ │ │ ├── SampleReceiverApp.java │ │ │ └── UseCase.java │ │ └── resources │ │ └── application.yaml │ ├── async-sender-client │ ├── async-sender-client.gradle │ └── src │ │ └── main │ │ ├── java │ │ └── sample │ │ │ ├── MyRabbitMQConfig.java │ │ │ ├── SampleRestController.java │ │ │ └── SampleSenderApp.java │ │ └── resources │ │ └── application.yaml │ ├── eda-async-receiver-responder │ ├── eda-async-receiver-sample.gradle │ └── src │ │ └── main │ │ ├── java │ │ └── sample │ │ │ ├── EDASampleReceiverApp.java │ │ │ ├── HandlersConfig.java │ │ │ ├── MyDomainConfig.java │ │ │ └── UseCase.java │ │ └── resources │ │ └── application.yaml │ ├── eda-async-sender-client-domain-a │ ├── eda-async-sender-client-domain-a.gradle │ └── src │ │ └── main │ │ ├── java │ │ └── sample │ │ │ ├── EDASampleSenderAppDomainA.java │ │ │ └── SampleRestController.java │ │ └── resources │ │ └── application.yaml │ ├── eda-async-sender-client │ ├── eda-async-sender-client.gradle │ └── src │ │ └── main │ │ ├── java │ │ └── sample │ │ │ ├── EDASampleSenderApp.java │ │ │ └── SampleRestController.java │ │ └── resources │ │ └── application.yaml │ ├── shared │ ├── shared.gradle │ └── src │ │ └── main │ │ └── java │ │ └── sample │ │ └── model │ │ ├── Constants.java │ │ ├── Member.java │ │ ├── Members.java │ │ ├── Team.java │ │ ├── Teams.java │ │ └── broker │ │ ├── AddMemberCommand.java │ │ ├── AnimalEvent.java │ │ └── RemovedMemberEvent.java │ ├── simpleConsumer │ ├── simple-consumer.gradle │ └── src │ │ ├── main │ │ ├── java │ │ │ └── sample │ │ │ │ └── App.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── sample │ │ ├── ReactorSamples.java │ │ └── other │ │ └── Other.java │ ├── thunder-collection_reactive-commons-eda-domain-a.json │ └── thunder-collection_reactive-commons.json ├── settings.gradle └── starters ├── async-commons-starter ├── async-commons-starter.gradle └── src │ ├── main │ └── java │ │ └── org │ │ └── reactivecommons │ │ └── async │ │ ├── impl │ │ └── config │ │ │ └── annotations │ │ │ ├── EnableCommandListeners.java │ │ │ ├── EnableDirectAsyncGateway.java │ │ │ ├── EnableDomainEventBus.java │ │ │ ├── EnableEventListeners.java │ │ │ ├── EnableMessageListeners.java │ │ │ ├── EnableNotificationListener.java │ │ │ └── EnableQueryListeners.java │ │ └── starter │ │ ├── broker │ │ ├── BrokerProvider.java │ │ ├── BrokerProviderFactory.java │ │ └── DiscardProvider.java │ │ ├── config │ │ ├── ConnectionManager.java │ │ ├── DomainHandlers.java │ │ ├── ReactiveCommonsConfig.java │ │ ├── ReactiveCommonsListenersConfig.java │ │ └── health │ │ │ ├── RCHealth.java │ │ │ ├── RCHealthIndicator.java │ │ │ ├── ReactiveCommonsHealthConfig.java │ │ │ └── ReactiveCommonsHealthIndicator.java │ │ ├── exceptions │ │ └── InvalidConfigurationException.java │ │ ├── listeners │ │ ├── AbstractListenerConfig.java │ │ ├── CommandsListenerConfig.java │ │ ├── EventsListenerConfig.java │ │ ├── NotificationEventsListenerConfig.java │ │ └── QueriesListenerConfig.java │ │ ├── props │ │ ├── GenericAsyncProps.java │ │ ├── GenericAsyncPropsDomain.java │ │ └── GenericAsyncPropsDomainProperties.java │ │ └── senders │ │ ├── DirectAsyncGatewayConfig.java │ │ ├── EventBusConfig.java │ │ ├── GenericDirectAsyncGateway.java │ │ └── GenericDomainEventBus.java │ └── test │ ├── java │ └── org │ │ └── reactivecommons │ │ └── async │ │ └── starter │ │ ├── config │ │ ├── ReactiveCommonsConfigTest.java │ │ └── health │ │ │ └── ReactiveCommonsHealthIndicatorTest.java │ │ ├── impl │ │ └── mybroker │ │ │ └── MyBrokerConfig.java │ │ ├── listeners │ │ ├── CommandsListenerConfigTest.java │ │ ├── EventsListenerConfigTest.java │ │ ├── NotificationEventsListenerConfigTest.java │ │ └── QueriesListenerConfigTest.java │ │ ├── mybroker │ │ ├── MyBrokerProvider.java │ │ ├── MyBrokerProviderFactory.java │ │ ├── MyBrokerSecretFiller.java │ │ └── props │ │ │ ├── AsyncMyBrokerPropsDomainProperties.java │ │ │ ├── MyBrokerAsyncProps.java │ │ │ ├── MyBrokerAsyncPropsDomain.java │ │ │ └── MyBrokerConnProps.java │ │ ├── props │ │ └── GenericAsyncPropsDomainTest.java │ │ └── senders │ │ ├── DirectAsyncGatewayConfigTest.java │ │ ├── EventBusConfigTest.java │ │ ├── GenericDirectAsyncGatewayTest.java │ │ └── GenericDomainEventBusTest.java │ └── resources │ └── application.yaml ├── async-kafka-starter ├── async-kafka-starter.gradle └── src │ ├── main │ └── java │ │ └── org │ │ └── reactivecommons │ │ └── async │ │ ├── kafka │ │ ├── KafkaBrokerProvider.java │ │ ├── KafkaBrokerProviderFactory.java │ │ ├── KafkaDiscardProvider.java │ │ ├── KafkaSetupUtils.java │ │ ├── config │ │ │ ├── KafkaProperties.java │ │ │ ├── KafkaPropertiesAutoConfig.java │ │ │ ├── props │ │ │ │ ├── AsyncKafkaProps.java │ │ │ │ ├── AsyncKafkaPropsDomain.java │ │ │ │ ├── AsyncKafkaPropsDomainProperties.java │ │ │ │ └── DomainProps.java │ │ │ └── spring │ │ │ │ └── KafkaPropertiesBase.java │ │ └── health │ │ │ └── KafkaReactiveHealthIndicator.java │ │ └── starter │ │ └── impl │ │ └── common │ │ └── kafka │ │ └── RCKafkaConfig.java │ └── test │ ├── java │ └── org │ │ └── reactivecommons │ │ └── async │ │ ├── kafka │ │ ├── KafkaBrokerProviderFactoryTest.java │ │ ├── KafkaBrokerProviderTest.java │ │ ├── KafkaDiscardProviderTest.java │ │ └── health │ │ │ └── KafkaReactiveHealthIndicatorTest.java │ │ └── starter │ │ └── impl │ │ └── common │ │ └── kafka │ │ └── KafkaConfigTest.java │ └── resources │ └── application.yaml ├── async-rabbit-standalone ├── async-commons-rabbit-standalone.gradle └── src │ └── main │ └── java │ └── org │ └── reactivecommons │ └── async │ └── rabbit │ └── standalone │ └── config │ ├── DirectAsyncGatewayConfig.java │ ├── EventBusConfig.java │ ├── RabbitMqConfig.java │ └── RabbitProperties.java └── async-rabbit-starter ├── async-commons-rabbit-starter.gradle └── src ├── main └── java │ └── org │ └── reactivecommons │ └── async │ ├── rabbit │ ├── RabbitMQBrokerProvider.java │ ├── RabbitMQBrokerProviderFactory.java │ ├── RabbitMQDiscardProvider.java │ ├── RabbitMQSetupUtils.java │ ├── config │ │ ├── RabbitProperties.java │ │ ├── RabbitPropertiesAutoConfig.java │ │ ├── props │ │ │ ├── AsyncProps.java │ │ │ ├── AsyncPropsDomain.java │ │ │ ├── AsyncRabbitPropsDomainProperties.java │ │ │ ├── BrokerConfigProps.java │ │ │ ├── DirectProps.java │ │ │ ├── DomainProps.java │ │ │ ├── EventsProps.java │ │ │ ├── FluxProps.java │ │ │ └── GlobalProps.java │ │ └── spring │ │ │ └── RabbitPropertiesBase.java │ └── health │ │ ├── RabbitMQHealthException.java │ │ └── RabbitReactiveHealthIndicator.java │ └── starter │ └── impl │ ├── common │ └── rabbit │ │ └── RabbitMQConfig.java │ └── listener │ └── rabbit │ └── RabbitMQListenerOnlyConfig.java └── test ├── java └── org │ └── reactivecommons │ └── async │ ├── rabbit │ ├── RabbitMQBrokerProviderFactoryTest.java │ ├── RabbitMQBrokerProviderTest.java │ ├── RabbitMQDiscardProviderTest.java │ └── health │ │ └── RabbitReactiveHealthIndicatorTest.java │ └── starter │ └── impl │ └── common │ └── rabbit │ └── RabbitMQConfigTest.java └── resources └── application.properties /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: 'reactive-commons-docs' 2 | on: 3 | push: 4 | branches: 5 | - master 6 | workflow_dispatch: 7 | permissions: 8 | contents: read 9 | pages: write 10 | id-token: write 11 | concurrency: 12 | group: "pages" 13 | cancel-in-progress: false 14 | jobs: 15 | deploy: 16 | runs-on: ubuntu-latest 17 | environment: 18 | name: github-pages 19 | url: ${{ steps.deployment.outputs.page_url }} 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v4 23 | - name: Setup Node.js 24 | uses: actions/setup-node@v4 25 | with: 26 | node-version-file: 'docs/.nvmrc' 27 | - name: Install dependencies 28 | run: npm ci 29 | working-directory: docs 30 | - name: Build docs 31 | run: npm run build 32 | working-directory: docs 33 | - name: Upload artifact 34 | uses: actions/upload-pages-artifact@v3 35 | with: 36 | path: 'docs/build/' 37 | - name: Deploy to GitHub Pages 38 | id: deployment 39 | uses: actions/deploy-pages@v4 -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: 'reactive-commons-ci-cd' 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | jobs: 10 | build: 11 | if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }} 12 | permissions: 13 | contents: write 14 | issues: write 15 | pull-requests: write 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Verify Conventional Commits 20 | uses: amannn/action-semantic-pull-request@v5 21 | if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | - name: Set up NodeJS 25 | if: github.ref == 'refs/heads/master' 26 | uses: actions/setup-node@v4 27 | with: 28 | node-version-file: '.nvmrc' 29 | - name: Set up Semantic Release 30 | if: github.ref == 'refs/heads/master' 31 | run: npm -g install @semantic-release/git semantic-release@23.0.0 32 | - name: Semantic Release 33 | if: github.ref == 'refs/heads/master' 34 | run: npx semantic-release@23.0.0 35 | env: 36 | GITHUB_TOKEN: ${{ secrets.PA_TOKEN }} 37 | - name: Set up JDK 17 38 | uses: actions/setup-java@v3 39 | with: 40 | distribution: temurin 41 | java-version: 17 42 | - name: Execute build test jacocoTestReport and sonar analysis 43 | if: endsWith(github.REF, '/master') == true || github.event.pull_request.head.repo.fork == false 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 47 | run: ./gradlew clean build generateMergedReport sonar --refresh-dependencies --no-daemon --continue -Denv.ci=true 48 | - name: Execute build test jacocoTestReport pull request 49 | if: github.event.pull_request.head.repo.fork == true 50 | env: 51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 | run: ./gradlew clean build generateMergedReport --refresh-dependencies --no-daemon --continue -Denv.ci=true -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | release: 4 | types: [ released, prereleased ] 5 | jobs: 6 | release: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - name: Set env 11 | run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV 12 | - name: Upgrade Gradle.properties 13 | run: sed -i 's/version=.*/version=${{ env.RELEASE_VERSION }}/g' gradle.properties 14 | - name: Generate Changelog 15 | uses: heinrichreimer/github-changelog-generator-action@v2.3 16 | with: 17 | token: ${{ secrets.GITHUB_TOKEN }} 18 | pullRequests: true 19 | prWoLabels: true 20 | issues: true 21 | issuesWoLabels: true 22 | stripGeneratorNotice: true 23 | - name: Set up JDK 17 24 | uses: actions/setup-java@v3 25 | with: 26 | distribution: temurin 27 | java-version: 17 28 | - name: Build with Gradle 29 | run: ./gradlew build --refresh-dependencies --no-daemon --continue -Denv.ci=true 30 | - name: Prepare gpg key 31 | run: | 32 | echo "${{secrets.SIGNING_KEY_FILE}}" | base64 -d > ~/.gradle/secring.gpg 33 | - name: Publish Libraries 34 | run: ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository -Psigning.keyId=${{ secrets.SIGNING_KEY_ID }} -Psigning.password=${{ secrets.SIGNING_KEY_PASSWORD }} -Psigning.secretKeyRingFile=$(echo ~/.gradle/secring.gpg) 35 | env: 36 | ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.MAVEN_USERNAME }} 37 | ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.MAVEN_PASSWORD }} 38 | - name: Push Changelog 39 | uses: github-actions-x/commit@v2.9 40 | with: 41 | github-token: ${{ secrets.GITHUB_TOKEN }} 42 | push-branch: 'master' 43 | commit-message: 'Automatic docs and changelog generation [skip ci]' 44 | force-add: 'true' 45 | files: CHANGELOG.md gradle.properties 46 | name: ${{ github.actor }} 47 | email: ${{ github.actor }}@users.noreply.github.com -------------------------------------------------------------------------------- /.github/workflows/updater.yml: -------------------------------------------------------------------------------- 1 | name: updater 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: '0 12 * * 5' # every Friday at 07:00 Colombia Time 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | - name: Set up JDK 17 12 | uses: actions/setup-java@v4 13 | with: 14 | distribution: 'temurin' 15 | java-version: 17 16 | - name: Check for updates 17 | run: ./gradlew internalTask --action UPDATE_DEPENDENCIES 18 | - name: Check for changes 19 | id: git_changes 20 | run: | 21 | git diff --name-only 22 | if [[ $(git diff --name-only) ]]; then 23 | echo "Changes detected!" 24 | echo "HAS_CHANGES=true" >> $GITHUB_ENV 25 | else 26 | echo "No changes detected!" 27 | echo "HAS_CHANGES=false" >> $GITHUB_ENV 28 | fi 29 | - name: Create Pull Request 30 | if: env.HAS_CHANGES == 'true' 31 | uses: peter-evans/create-pull-request@v6 32 | with: 33 | token: ${{ secrets.PA_TOKEN }} 34 | committer: Dependencies Bot 35 | commit-message: 'fix(deps): update dependencies' 36 | title: 'fix(deps): update dependencies' 37 | body: 'This PR updates dependencies to latest versions' 38 | branch: 'feature/autoupdate-deps' 39 | base: 'master' 40 | labels: 'dependencies' 41 | reviewers: 'juancgalvis' -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v20.11.0 -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "branches": ["master"], 3 | "plugins": [ 4 | "@semantic-release/commit-analyzer", 5 | "@semantic-release/release-notes-generator", 6 | ["@semantic-release/git", 7 | { 8 | "assets": [ { "path": "build/**/*", "label": "Compiled files" }], 9 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 10 | } 11 | ], 12 | "@semantic-release/github" 13 | ] 14 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | git: 4 | quiet: true 5 | 6 | services: 7 | - rabbitmq 8 | 9 | jdk: openjdk11 10 | 11 | before_cache: 12 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 13 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 14 | cache: 15 | directories: 16 | - $HOME/.gradle/caches/ 17 | - $HOME/.gradle/wrapper/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2019 Bancolombia S.A 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://github.com/reactive-commons/reactive-commons-java/workflows/reactive-commons-ci-cd/badge.svg) 2 | [![Reactor RabbitMQ](https://maven-badges.herokuapp.com/maven-central/org.reactivecommons/async-commons-rabbit-starter/badge.svg)](https://mvnrepository.com/artifact/org.reactivecommons/async-commons-rabbit-starter) 3 | # reactive-commons-java 4 | The purpose of reactive-commons is to provide a set of abstractions and implementations over different patterns and practices that make the foundation of a reactive microservices architecture. 5 | 6 | Docs: https://reactivecommons.org/reactive-commons-java/ 7 | 8 | Other projects: https://github.com/bancolombia 9 | 10 | Sponsor by: https://medium.com/bancolombia-tech 11 | 12 | Even though the main purpose is to provide such abstractions in a mostly generic way such abstractions would be of little use without a concrete implementation so we provide some implementations in a best effors maner that aim to be easy to change, personalize and extend. 13 | 14 | The first approach to this work was to release a very simple abstractions and a corresponding implementation over asyncronous message driven communication between microservices build on top of project-reactor and spring boot. 15 | -------------------------------------------------------------------------------- /acceptance/async-tests/async-tests.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':async-commons-rabbit-starter') 3 | implementation 'org.springframework.boot:spring-boot-starter' 4 | } 5 | 6 | test.onlyIf { false } -------------------------------------------------------------------------------- /acceptance/async-tests/src/test/java/org/reactivecommons/test/DummyMessage.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.test; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.concurrent.ThreadLocalRandom; 6 | 7 | @Data 8 | class DummyMessage { 9 | private String name = "Daniel" + ThreadLocalRandom.current().nextLong(); 10 | private Long age = ThreadLocalRandom.current().nextLong(); 11 | private String field1 = "Field Data " + ThreadLocalRandom.current().nextLong(); 12 | private String field2 = "Field Data " + ThreadLocalRandom.current().nextLong(); 13 | private String field3 = "Field Data " + ThreadLocalRandom.current().nextLong(); 14 | private String field4 = "Field Data " + ThreadLocalRandom.current().nextLong(); 15 | } 16 | -------------------------------------------------------------------------------- /acceptance/async-tests/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=test-app-n5 2 | spring.rabbitmq.virtual-host=test -------------------------------------------------------------------------------- /async/async-commons-api/async-commons-api.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | artifactId = 'async-commons-api' 3 | artifactDescription = 'Async Commons API' 4 | } 5 | 6 | dependencies { 7 | api project(':domain-events-api') 8 | compileOnly 'io.projectreactor:reactor-core' 9 | testImplementation 'io.projectreactor:reactor-test' 10 | implementation 'io.cloudevents:cloudevents-json-jackson:4.0.1' 11 | } -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/AsyncQuery.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class AsyncQuery { 7 | private final String resource; 8 | private final T queryData; 9 | } 10 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/DefaultCommandHandler.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api; 2 | 3 | import org.reactivecommons.async.api.handlers.DomainCommandHandler; 4 | 5 | public interface DefaultCommandHandler extends DomainCommandHandler { 6 | } 7 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/DefaultQueryHandler.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api; 2 | 3 | import org.reactivecommons.async.api.handlers.QueryHandler; 4 | 5 | public interface DefaultQueryHandler extends QueryHandler { 6 | } 7 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/DirectAsyncGateway.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api; 2 | 3 | import io.cloudevents.CloudEvent; 4 | import org.reactivecommons.api.domain.Command; 5 | import reactor.core.publisher.Mono; 6 | 7 | public interface DirectAsyncGateway { 8 | String DELAYED = "rc-delay"; 9 | 10 | Mono sendCommand(Command command, String targetName); 11 | 12 | Mono sendCommand(Command command, String targetName, long delayMillis); 13 | 14 | Mono sendCommand(Command command, String targetName, String domain); 15 | 16 | Mono sendCommand(Command command, String targetName, long delayMillis, String domain); 17 | 18 | Mono sendCommand(CloudEvent command, String targetName); 19 | 20 | Mono sendCommand(CloudEvent command, String targetName, long delayMillis); 21 | 22 | Mono sendCommand(CloudEvent command, String targetName, String domain); 23 | 24 | Mono sendCommand(CloudEvent command, String targetName, long delayMillis, String domain); 25 | 26 | Mono requestReply(AsyncQuery query, String targetName, Class type); 27 | 28 | Mono requestReply(AsyncQuery query, String targetName, Class type, String domain); 29 | 30 | Mono requestReply(CloudEvent query, String targetName, Class type); 31 | 32 | Mono requestReply(CloudEvent query, String targetName, Class type, String domain); 33 | 34 | Mono reply(T response, From from); 35 | } 36 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/DynamicRegistry.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api; 2 | 3 | import org.reactivecommons.async.api.handlers.DomainEventHandler; 4 | import org.reactivecommons.async.api.handlers.QueryHandler; 5 | import org.reactivecommons.async.api.handlers.QueryHandlerDelegate; 6 | import reactor.core.publisher.Mono; 7 | 8 | public interface DynamicRegistry { 9 | 10 | @Deprecated 11 | Mono listenEvent(String eventName, DomainEventHandler fn, Class eventClass); 12 | 13 | void serveQuery(String resource, QueryHandler handler, Class queryClass); 14 | 15 | void serveQuery(String resource, QueryHandlerDelegate handler, Class queryClass); 16 | 17 | Mono startListeningEvent(String eventName); 18 | 19 | Mono stopListeningEvent(String eventName); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/From.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class From { 7 | private String replyID; 8 | private String correlationID; 9 | } 10 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/handlers/CloudCommandHandler.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api.handlers; 2 | 3 | import io.cloudevents.CloudEvent; 4 | 5 | public interface CloudCommandHandler extends CommandHandler { 6 | } 7 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/handlers/CloudEventHandler.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api.handlers; 2 | 3 | import io.cloudevents.CloudEvent; 4 | 5 | public interface CloudEventHandler extends EventHandler { 6 | } 7 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/handlers/CommandHandler.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api.handlers; 2 | 3 | public interface CommandHandler extends GenericHandler { 4 | } 5 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/handlers/DomainCommandHandler.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api.handlers; 2 | 3 | import org.reactivecommons.api.domain.Command; 4 | 5 | public interface DomainCommandHandler extends CommandHandler> { 6 | } 7 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/handlers/DomainEventHandler.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api.handlers; 2 | 3 | import org.reactivecommons.api.domain.DomainEvent; 4 | 5 | public interface DomainEventHandler extends EventHandler>{ 6 | } 7 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/handlers/EventHandler.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api.handlers; 2 | 3 | public interface EventHandler extends GenericHandler { 4 | } 5 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/handlers/GenericHandler.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api.handlers; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | public interface GenericHandler { 6 | Mono handle(M message); 7 | } 8 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/handlers/QueryHandler.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api.handlers; 2 | 3 | public interface QueryHandler extends GenericHandler { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/handlers/QueryHandlerDelegate.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api.handlers; 2 | 3 | import org.reactivecommons.async.api.From; 4 | import reactor.core.publisher.Mono; 5 | 6 | public interface QueryHandlerDelegate { 7 | Mono handle(From from, M message); 8 | } 9 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/handlers/RawCommandHandler.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api.handlers; 2 | 3 | import org.reactivecommons.api.domain.RawMessage; 4 | 5 | public interface RawCommandHandler extends CommandHandler { 6 | } 7 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/handlers/RawEventHandler.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api.handlers; 2 | 3 | import org.reactivecommons.api.domain.RawMessage; 4 | 5 | public interface RawEventHandler extends EventHandler { 6 | } 7 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/handlers/registered/RegisteredCommandHandler.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api.handlers.registered; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | import org.reactivecommons.async.api.handlers.CommandHandler; 6 | 7 | @RequiredArgsConstructor 8 | @Getter 9 | public class RegisteredCommandHandler { 10 | private final String path; 11 | private final CommandHandler handler; 12 | private final Class inputClass; 13 | } 14 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/handlers/registered/RegisteredDomainHandlers.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api.handlers.registered; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | import java.util.concurrent.CopyOnWriteArrayList; 6 | 7 | public class RegisteredDomainHandlers extends ConcurrentHashMap> { 8 | private static final String DEFAULT_DOMAIN = "app"; 9 | 10 | public RegisteredDomainHandlers() { 11 | super(); 12 | put(DEFAULT_DOMAIN, new CopyOnWriteArrayList<>()); 13 | } 14 | 15 | public void add(String domain, T handler) { 16 | computeIfAbsent(domain, ignored -> new CopyOnWriteArrayList<>()).add(handler); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/handlers/registered/RegisteredEventListener.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api.handlers.registered; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | import org.reactivecommons.async.api.handlers.EventHandler; 6 | 7 | @RequiredArgsConstructor 8 | @Getter 9 | public class RegisteredEventListener { 10 | private final String path; 11 | private final EventHandler handler; 12 | private final Class inputClass; 13 | } 14 | -------------------------------------------------------------------------------- /async/async-commons-api/src/main/java/org/reactivecommons/async/api/handlers/registered/RegisteredQueryHandler.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api.handlers.registered; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | import org.reactivecommons.async.api.handlers.QueryHandlerDelegate; 6 | 7 | @RequiredArgsConstructor 8 | @Getter 9 | public class RegisteredQueryHandler { 10 | private final String path; 11 | private final QueryHandlerDelegate handler; 12 | private final Class queryClass; 13 | } 14 | -------------------------------------------------------------------------------- /async/async-commons/async-commons.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | artifactId = 'async-commons' 3 | artifactDescription = 'Async Commons' 4 | } 5 | 6 | dependencies { 7 | api project(':async-commons-api') 8 | api project(':domain-events-api') 9 | 10 | compileOnly 'io.projectreactor:reactor-core' 11 | api 'com.fasterxml.jackson.core:jackson-databind' 12 | api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' 13 | implementation 'commons-io:commons-io:2.18.0' 14 | implementation 'io.cloudevents:cloudevents-json-jackson:4.0.1' 15 | 16 | testImplementation 'io.projectreactor:reactor-test' 17 | } -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/CommandExecutor.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.reactivecommons.async.api.handlers.CommandHandler; 5 | import org.reactivecommons.async.commons.communications.Message; 6 | import reactor.core.publisher.Mono; 7 | 8 | import java.util.function.Function; 9 | 10 | @RequiredArgsConstructor 11 | public class CommandExecutor { 12 | private final CommandHandler eventHandler; 13 | private final Function converter; 14 | 15 | public Mono execute(Message rawMessage) { 16 | return eventHandler.handle(converter.apply(rawMessage)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/DiscardNotifier.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons; 2 | 3 | import org.reactivecommons.async.commons.communications.Message; 4 | import reactor.core.publisher.Mono; 5 | 6 | public interface DiscardNotifier { 7 | 8 | Mono notifyDiscard(Message message); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/EventExecutor.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.reactivecommons.async.api.handlers.EventHandler; 5 | import org.reactivecommons.async.commons.communications.Message; 6 | import reactor.core.publisher.Mono; 7 | 8 | import java.util.function.Function; 9 | 10 | @RequiredArgsConstructor 11 | public class EventExecutor { 12 | private final EventHandler eventHandler; 13 | private final Function converter; 14 | 15 | public Mono execute(Message rawMessage) { 16 | return eventHandler.handle(converter.apply(rawMessage)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/FallbackStrategy.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | 5 | @RequiredArgsConstructor 6 | public enum FallbackStrategy { 7 | FAST_RETRY("ATTENTION!! Fast retry message to same Queue: %s"), 8 | DEFINITIVE_DISCARD("ATTENTION!! DEFINITIVE DISCARD!! of the message: %s"), 9 | RETRY_DLQ("ATTENTION!! Sending message to Retry DLQ: %s"); 10 | 11 | public final String message; 12 | } 13 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/Headers.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons; 2 | 3 | 4 | import lombok.AccessLevel; 5 | import lombok.NoArgsConstructor; 6 | 7 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 8 | public final class Headers { 9 | 10 | public static final String REPLY_ID = "x-reply_id"; 11 | public static final String CORRELATION_ID = "x-correlation-id"; 12 | public static final String COMPLETION_ONLY_SIGNAL = "x-empty-completion"; 13 | public static final String SERVED_QUERY_ID = "x-serveQuery-id"; 14 | public static final String SOURCE_APPLICATION = "sourceApplication"; 15 | public static final String REPLY_TIMEOUT_MILLIS = "x-reply-timeout-millis"; 16 | } 17 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/QueryExecutor.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons; 2 | 3 | 4 | import org.reactivecommons.async.api.From; 5 | import org.reactivecommons.async.api.handlers.QueryHandlerDelegate; 6 | import org.reactivecommons.async.commons.communications.Message; 7 | import reactor.core.publisher.Mono; 8 | 9 | import java.util.function.Function; 10 | 11 | import static org.reactivecommons.async.commons.Headers.CORRELATION_ID; 12 | import static org.reactivecommons.async.commons.Headers.REPLY_ID; 13 | 14 | public class QueryExecutor { 15 | private final QueryHandlerDelegate queryHandler; 16 | private final Function converter; 17 | 18 | public QueryExecutor(QueryHandlerDelegate queryHandler, Function converter) { 19 | this.queryHandler = queryHandler; 20 | this.converter = converter; 21 | } 22 | 23 | public Mono execute(Message rawMessage) { 24 | From from = new From(); 25 | from.setCorrelationID(rawMessage.getProperties().getHeaders().getOrDefault(CORRELATION_ID, "").toString()); 26 | from.setReplyID(rawMessage.getProperties().getHeaders().getOrDefault(REPLY_ID, "").toString()); 27 | return queryHandler.handle(from, converter.apply(rawMessage)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/communications/Message.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.communications; 2 | 3 | import org.reactivecommons.api.domain.RawMessage; 4 | 5 | import java.util.Map; 6 | 7 | /** 8 | * Simple Internal Message representation 9 | * 10 | * @author Daniel Bustamante Ospina 11 | */ 12 | public interface Message extends RawMessage { 13 | 14 | byte[] getBody(); 15 | 16 | Properties getProperties(); 17 | 18 | interface Properties { 19 | String getContentType(); 20 | 21 | default String getContentEncoding() { 22 | return null; 23 | } 24 | 25 | long getContentLength(); 26 | 27 | Map getHeaders(); 28 | 29 | default String getKey() { 30 | return null; 31 | } 32 | 33 | default String getTopic() { 34 | return null; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/config/BrokerConfig.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.config; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | import java.time.Duration; 7 | import java.util.UUID; 8 | 9 | @Getter 10 | @RequiredArgsConstructor 11 | public class BrokerConfig { 12 | private final String routingKey = UUID.randomUUID().toString().replace("-", ""); 13 | private final boolean persistentQueries; 14 | private final boolean persistentCommands; 15 | private final boolean persistentEvents; 16 | private final Duration replyTimeout; 17 | 18 | public BrokerConfig() { 19 | this.persistentQueries = false; 20 | this.persistentCommands = true; 21 | this.persistentEvents = true; 22 | this.replyTimeout = Duration.ofSeconds(15); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/config/IBrokerConfigProps.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.config; 2 | 3 | public interface IBrokerConfigProps { 4 | String getEventsQueue(); 5 | 6 | String getNotificationsQueue(); 7 | 8 | String getQueriesQueue(); 9 | 10 | String getCommandsQueue(); 11 | 12 | String getReplyQueue(); 13 | 14 | String getDomainEventsExchangeName(); 15 | 16 | String getDirectMessagesExchangeName(); 17 | 18 | String getGlobalReplyExchangeName(); 19 | 20 | String getAppName(); 21 | } 22 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/converters/MessageConverter.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.converters; 2 | 3 | import io.cloudevents.CloudEvent; 4 | import org.reactivecommons.api.domain.Command; 5 | import org.reactivecommons.api.domain.DomainEvent; 6 | import org.reactivecommons.async.api.AsyncQuery; 7 | import org.reactivecommons.async.commons.communications.Message; 8 | 9 | public interface MessageConverter { 10 | 11 | AsyncQuery readAsyncQuery(Message message, Class bodyClass); 12 | 13 | DomainEvent readDomainEvent(Message message, Class bodyClass); 14 | 15 | Command readCommand(Message message, Class bodyClass); 16 | 17 | CloudEvent readCloudEvent(Message message); 18 | 19 | T readValue(Message message, Class valueClass); 20 | 21 | Command readCommandStructure(Message message); 22 | 23 | DomainEvent readDomainEventStructure(Message message); 24 | 25 | AsyncQuery readAsyncQueryStructure(Message message); 26 | 27 | Message toMessage(Object object); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/converters/json/CloudEventBuilderExt.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.converters.json; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import io.cloudevents.CloudEvent; 5 | import io.cloudevents.CloudEventData; 6 | import lombok.AccessLevel; 7 | import lombok.AllArgsConstructor; 8 | import lombok.SneakyThrows; 9 | 10 | import java.util.Objects; 11 | 12 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 13 | public final class CloudEventBuilderExt { 14 | private static final ObjectMapper mapper = new ObjectMapper(); 15 | 16 | @SneakyThrows 17 | public static byte[] asBytes(Object object) { 18 | return mapper.writeValueAsBytes(object); 19 | } 20 | 21 | public static CloudEventData asCloudEventData(Object object) { 22 | return () -> asBytes(object); 23 | } 24 | 25 | @SneakyThrows 26 | public static T fromCloudEventData(CloudEvent cloudEvent, Class classValue) { 27 | return mapper.readValue(Objects.requireNonNull(cloudEvent.getData()).toBytes(), classValue); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/converters/json/DefaultObjectMapperSupplier.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.converters.json; 2 | 3 | import com.fasterxml.jackson.databind.DeserializationFeature; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 | import io.cloudevents.jackson.JsonFormat; 7 | 8 | public class DefaultObjectMapperSupplier implements ObjectMapperSupplier { 9 | 10 | @Override 11 | public ObjectMapper get() { 12 | final ObjectMapper objectMapper = new ObjectMapper(); 13 | objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 14 | objectMapper.findAndRegisterModules(); 15 | objectMapper.registerModule(new JavaTimeModule()); 16 | objectMapper.registerModule(JsonFormat.getCloudEventJacksonModule()); 17 | return objectMapper; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/converters/json/ObjectMapperSupplier.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.converters.json; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | 5 | import java.util.function.Supplier; 6 | 7 | public interface ObjectMapperSupplier extends Supplier { 8 | } 9 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/exceptions/MessageConversionException.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.exceptions; 2 | 3 | public class MessageConversionException extends RuntimeException { 4 | 5 | public MessageConversionException(String message, Throwable cause) { 6 | super(message, cause); 7 | } 8 | 9 | public MessageConversionException(String message) { 10 | super(message); 11 | } 12 | 13 | public MessageConversionException(Exception e) { 14 | super(e); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/exceptions/SendFailureNoAckException.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.exceptions; 2 | 3 | public class SendFailureNoAckException extends RuntimeException { 4 | public SendFailureNoAckException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/ext/CustomReporter.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.ext; 2 | 3 | import org.reactivecommons.api.domain.Command; 4 | import org.reactivecommons.api.domain.DomainEvent; 5 | import org.reactivecommons.async.api.AsyncQuery; 6 | import org.reactivecommons.async.commons.communications.Message; 7 | import reactor.core.publisher.Mono; 8 | 9 | public interface CustomReporter { 10 | 11 | String COMMAND_CLASS = "org.reactivecommons.api.domain.Command"; 12 | String EVENT_CLASS = "org.reactivecommons.api.domain.DomainEvent"; 13 | String QUERY_CLASS = "org.reactivecommons.async.api.AsyncQuery"; 14 | 15 | default Mono reportError(Throwable ex, Message rawMessage, Object message, boolean redelivered) { 16 | var name = message.getClass().getName(); 17 | return Mono.just(name) 18 | .filter(COMMAND_CLASS::equals) 19 | .flatMap(n -> reportError(ex, rawMessage, (Command) message, redelivered)) 20 | .switchIfEmpty(Mono.just(name) 21 | .filter(EVENT_CLASS::equals) 22 | .flatMap(n -> reportError(ex, rawMessage, (DomainEvent) message, redelivered)) 23 | .switchIfEmpty(Mono.just(name) 24 | .filter(QUERY_CLASS::equals) 25 | .flatMap(n -> reportError(ex, rawMessage, (AsyncQuery) message, redelivered)) 26 | ) 27 | ); 28 | } 29 | 30 | default void reportMetric(String type, String handlerPath, Long duration, boolean success) { 31 | } 32 | 33 | Mono reportError(Throwable ex, Message rawMessage, Command message, boolean redelivered); 34 | 35 | Mono reportError(Throwable ex, Message rawMessage, DomainEvent message, boolean redelivered); 36 | 37 | Mono reportError(Throwable ex, Message rawMessage, AsyncQuery message, boolean redelivered); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/ext/DefaultCustomReporter.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.ext; 2 | 3 | import org.reactivecommons.api.domain.Command; 4 | import org.reactivecommons.api.domain.DomainEvent; 5 | import org.reactivecommons.async.api.AsyncQuery; 6 | import org.reactivecommons.async.commons.communications.Message; 7 | import reactor.core.publisher.Mono; 8 | 9 | public class DefaultCustomReporter implements CustomReporter { 10 | 11 | @Override 12 | public Mono reportError(Throwable ex, Message rawMessage, Command message, boolean redelivered) { 13 | return Mono.empty(); 14 | } 15 | 16 | @Override 17 | public Mono reportError(Throwable ex, Message rawMessage, DomainEvent message, boolean redelivered) { 18 | return Mono.empty(); 19 | } 20 | 21 | @Override 22 | public Mono reportError(Throwable ex, Message rawMessage, AsyncQuery message, boolean redelivered) { 23 | return Mono.empty(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/reply/ReactiveReplyRouter.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.reply; 2 | 3 | import org.reactivecommons.async.commons.communications.Message; 4 | import reactor.core.publisher.Mono; 5 | import reactor.core.publisher.Sinks; 6 | 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | public class ReactiveReplyRouter { 10 | private final ConcurrentHashMap> processors = new ConcurrentHashMap<>(); 11 | 12 | public Mono register(String correlationID) { 13 | final Sinks.One processor = Sinks.one(); 14 | processors.put(correlationID, processor); 15 | return processor.asMono(); 16 | } 17 | 18 | public void routeReply(String correlationID, Message data) { 19 | final Sinks.One processor = processors.remove(correlationID); 20 | if (processor != null) { 21 | processor.tryEmitValue(data); 22 | } 23 | } 24 | 25 | public void deregister(String correlationID) { 26 | processors.remove(correlationID); 27 | } 28 | 29 | public void routeEmpty(String correlationID) { 30 | final Sinks.One processor = processors.remove(correlationID); 31 | if (processor != null) { 32 | processor.tryEmitEmpty(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/utils/ArrayUtils.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.utils; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | 9 | 10 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 11 | public final class ArrayUtils { 12 | 13 | public static Object[] prefixArray(E head, E[] tail) { 14 | final ArrayList objects = new ArrayList<>(1 + tail.length); 15 | objects.add(head); 16 | objects.addAll(Arrays.asList(tail)); 17 | return objects.toArray(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/utils/LoggerSubscriber.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.utils; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.java.Log; 5 | import reactor.core.publisher.BaseSubscriber; 6 | import reactor.core.publisher.SignalType; 7 | 8 | import java.util.logging.Level; 9 | 10 | import static org.reactivecommons.async.commons.utils.ArrayUtils.prefixArray; 11 | 12 | 13 | @Log 14 | @RequiredArgsConstructor 15 | public class LoggerSubscriber extends BaseSubscriber { 16 | 17 | private final String flowName; 18 | private static final String ON_COMPLETE_MSG = "%s: ##On Complete Hook!!"; 19 | private static final String ON_ERROR_MSG = "%s: ##On Error Hook!! %s"; 20 | private static final String ON_CANCEL_MSG = "%s: ##On Cancel Hook!!"; 21 | private static final String ON_FINALLY_MSG = "%s: ##On Finally Hook! Signal type: %s"; 22 | 23 | @Override 24 | protected void hookOnComplete() { 25 | log.warning(format(ON_COMPLETE_MSG)); 26 | } 27 | 28 | @Override 29 | protected void hookOnError(Throwable throwable) { 30 | log.log(Level.SEVERE, format(ON_ERROR_MSG, throwable.getMessage()), throwable); 31 | } 32 | 33 | @Override 34 | protected void hookOnCancel() { 35 | log.warning(format(ON_CANCEL_MSG)); 36 | } 37 | 38 | @Override 39 | protected void hookFinally(SignalType type) { 40 | log.warning(format(ON_FINALLY_MSG, type.name())); 41 | } 42 | 43 | private String format(String msg, String... args) { 44 | return String.format(msg, prefixArray(flowName, args)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/utils/NameGenerator.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.utils; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | 6 | import java.nio.ByteBuffer; 7 | import java.util.Base64; 8 | import java.util.UUID; 9 | 10 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 11 | public final class NameGenerator { 12 | 13 | public static String fromNameWithSuffix(String appName, String suffix) { 14 | if (suffix != null && !suffix.isEmpty()) { 15 | return appName + "." + suffix; 16 | } 17 | return appName; 18 | } 19 | 20 | public static String generateNameFrom(String applicationName, String suffix) { 21 | return generateName(applicationName, suffix); 22 | } 23 | 24 | public static String generateNameFrom(String applicationName) { 25 | return generateName(applicationName, ""); 26 | } 27 | 28 | private static String generateName(String applicationName, String suffix) { 29 | UUID uuid = UUID.randomUUID(); 30 | ByteBuffer bb = ByteBuffer.wrap(new byte[16]); 31 | bb.putLong(uuid.getMostSignificantBits()) 32 | .putLong(uuid.getLeastSignificantBits()); 33 | // Convert to base64 and remove trailing = 34 | String realSuffix = suffix != null && !suffix.isEmpty() ? suffix + "." : ""; 35 | return applicationName + "." + realSuffix + encodeToUrlSafeString(bb.array()) 36 | .replace("=", ""); 37 | } 38 | 39 | private static String encodeToUrlSafeString(byte[] src) { 40 | return new String(encodeUrlSafe(src)); 41 | } 42 | 43 | private static byte[] encodeUrlSafe(byte[] src) { 44 | if (src.length == 0) { 45 | return src; 46 | } 47 | return Base64.getUrlEncoder().encode(src); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/utils/matcher/Candidate.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.utils.matcher; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.Comparator; 6 | 7 | @Data 8 | public class Candidate implements Comparable, Comparator { 9 | private final String key; 10 | private final long score; 11 | 12 | @Override 13 | public int compareTo(Candidate o) { 14 | return (int) (this.score - o.score); 15 | } 16 | 17 | @Override 18 | public int compare(Candidate o1, Candidate o2) { 19 | return (int) (o1.score - o2.score); 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /async/async-commons/src/main/java/org/reactivecommons/async/commons/utils/matcher/Matcher.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.utils.matcher; 2 | 3 | import java.util.Set; 4 | 5 | public interface Matcher { 6 | String match(Set sources, String target); 7 | } 8 | -------------------------------------------------------------------------------- /async/async-commons/src/test/java/org/reactivecommons/async/commons/converters/json/DefaultObjectMapperSupplierTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.converters.json; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import lombok.Getter; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.io.IOException; 8 | import java.util.Date; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | class DefaultObjectMapperSupplierTest { 13 | 14 | private final DefaultObjectMapperSupplier defaultObjectMapperSupplier = new DefaultObjectMapperSupplier(); 15 | 16 | 17 | @Test 18 | void shouldMapWithUnknownProperties() throws IOException { 19 | ObjectMapper objectMapper = defaultObjectMapperSupplier.get(); 20 | 21 | SampleClassExtra base = new SampleClassExtra("23", "one", new Date(), 45L); 22 | final String serialized = objectMapper.writeValueAsString(base); 23 | 24 | final SampleClass result = objectMapper.readValue(serialized, SampleClass.class); 25 | 26 | assertThat(result).usingRecursiveComparison().isEqualTo(base); 27 | } 28 | 29 | @Getter 30 | private static class SampleClassExtra extends SampleClass { 31 | 32 | public SampleClassExtra(String id, String name, Date date, Long newProp) { 33 | super(id, name, date); 34 | this.newProp = newProp; 35 | } 36 | 37 | private final Long newProp; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /async/async-commons/src/test/java/org/reactivecommons/async/commons/converters/json/SampleClass.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.converters.json; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | import java.util.Date; 7 | 8 | @RequiredArgsConstructor 9 | @Getter 10 | class SampleClass { 11 | private final String id; 12 | private final String name; 13 | private final Date date; 14 | } 15 | -------------------------------------------------------------------------------- /async/async-commons/src/test/java/org/reactivecommons/async/commons/ext/CustomErrorReporterTest.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactive-commons/reactive-commons-java/17c79b99846e6ccb0233eddae2a65add547ffdfb/async/async-commons/src/test/java/org/reactivecommons/async/commons/ext/CustomErrorReporterTest.java -------------------------------------------------------------------------------- /async/async-commons/src/test/java/org/reactivecommons/async/commons/reply/ReactiveReplyRouterTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.reply; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.mockito.Mockito; 5 | import org.reactivecommons.async.commons.communications.Message; 6 | import reactor.core.publisher.Mono; 7 | import reactor.test.StepVerifier; 8 | 9 | import java.time.Duration; 10 | import java.util.UUID; 11 | 12 | class ReactiveReplyRouterTest { 13 | 14 | private final ReactiveReplyRouter replyRouter = new ReactiveReplyRouter(); 15 | 16 | @Test 17 | void shouldRouteReply() { 18 | final String uuid = UUID.randomUUID().toString(); 19 | final Mono registered = replyRouter.register(uuid); 20 | 21 | Message message = Mockito.mock(Message.class); 22 | replyRouter.routeReply(uuid, message); 23 | 24 | StepVerifier.create(registered) 25 | .expectNext(message) 26 | .verifyComplete(); 27 | 28 | } 29 | 30 | @Test 31 | void shouldRouteEmptyResponse() { 32 | final String uuid = UUID.randomUUID().toString(); 33 | final Mono registered = replyRouter.register(uuid); 34 | 35 | replyRouter.routeEmpty(uuid); 36 | 37 | StepVerifier.create(registered) 38 | .verifyComplete(); 39 | } 40 | 41 | @Test 42 | void shouldDeRegisterProcessor() { 43 | final String uuid = UUID.randomUUID().toString(); 44 | final Mono registered = replyRouter.register(uuid); 45 | 46 | replyRouter.deregister(uuid); 47 | replyRouter.routeEmpty(uuid); 48 | 49 | StepVerifier.create(registered.timeout(Duration.ofSeconds(1))) 50 | .expectTimeout(Duration.ofSeconds(3)).verify(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /async/async-commons/src/test/java/org/reactivecommons/async/commons/utils/LoggerSubscriberTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.utils; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.SignalType; 5 | 6 | 7 | class LoggerSubscriberTest { 8 | 9 | private final LoggerSubscriber subscriber = new LoggerSubscriber<>("testFlow"); 10 | 11 | @Test 12 | void shouldPrintOnCancelMessage() { 13 | subscriber.hookOnCancel(); 14 | } 15 | 16 | @Test 17 | void shouldPrintOnErrorMessage() { 18 | subscriber.hookOnError(new RuntimeException()); 19 | } 20 | 21 | @Test 22 | void shouldPrintOnFinallyMessage() { 23 | subscriber.hookFinally(SignalType.ON_ERROR); 24 | } 25 | 26 | @Test 27 | void shouldPrintOnCompleteMessage() { 28 | subscriber.hookOnComplete(); 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /async/async-commons/src/test/java/org/reactivecommons/async/commons/utils/NameGeneratorTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.commons.utils; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.assertEquals; 6 | import static org.junit.jupiter.api.Assertions.assertFalse; 7 | import static org.junit.jupiter.api.Assertions.assertTrue; 8 | 9 | class NameGeneratorTest { 10 | 11 | @Test 12 | void generateNameFromWithoutSuffix() { 13 | String result = NameGenerator.generateNameFrom("application"); 14 | assertFalse(result.contains("=")); 15 | assertTrue(result.startsWith("application.")); 16 | assertEquals(34, result.length()); 17 | } 18 | 19 | @Test 20 | void generateNameFromWithSuffix() { 21 | String result = NameGenerator.generateNameFrom("application", "suffix"); 22 | assertFalse(result.contains("=")); 23 | assertTrue(result.startsWith("application.suffix.")); 24 | assertEquals(41, result.length()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /async/async-commons/src/test/resources/wildcard_names_for_matching.txt: -------------------------------------------------------------------------------- 1 | System.*.service.event.action 2 | System.segment.*.event.action 3 | System.segment.service.*.action 4 | System.segment.service.event.* 5 | System.segment.service.*.action 6 | System2.segment.service.0.action 7 | System0.segment.service.aa.action 8 | System2.segment.service.1.action 9 | System1.segment.service.aa.action 10 | System2.segment.service.2.action -------------------------------------------------------------------------------- /async/async-kafka/async-kafka.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | artifactId = 'async-kafka' 3 | artifactDescription = 'Async Kafka' 4 | } 5 | 6 | dependencies { 7 | api project(':async-commons-api') 8 | api project(':domain-events-api') 9 | api project(':async-commons') 10 | api 'io.projectreactor.kafka:reactor-kafka:1.3.23' 11 | 12 | implementation 'io.cloudevents:cloudevents-json-jackson:4.0.1' 13 | } -------------------------------------------------------------------------------- /async/async-kafka/src/main/java/org/reactivecommons/async/kafka/KafkaDomainEventBus.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.kafka; 2 | 3 | import io.cloudevents.CloudEvent; 4 | import lombok.RequiredArgsConstructor; 5 | import org.reactivecommons.api.domain.DomainEvent; 6 | import org.reactivecommons.api.domain.DomainEventBus; 7 | import org.reactivecommons.api.domain.RawMessage; 8 | import org.reactivecommons.async.kafka.communications.ReactiveMessageSender; 9 | import org.reactivestreams.Publisher; 10 | 11 | @RequiredArgsConstructor 12 | public class KafkaDomainEventBus implements DomainEventBus { 13 | public static final String NOT_IMPLEMENTED_YET = "Not implemented yet"; 14 | private final ReactiveMessageSender sender; 15 | 16 | @Override 17 | public Publisher emit(DomainEvent event) { 18 | return sender.send(event); 19 | } 20 | 21 | @Override 22 | public Publisher emit(String domain, DomainEvent event) { 23 | throw new UnsupportedOperationException(NOT_IMPLEMENTED_YET); 24 | } 25 | 26 | @Override 27 | public Publisher emit(CloudEvent event) { 28 | return sender.send(event); 29 | } 30 | 31 | @Override 32 | public Publisher emit(String domain, CloudEvent event) { 33 | throw new UnsupportedOperationException(NOT_IMPLEMENTED_YET); 34 | } 35 | 36 | @Override 37 | public Publisher emit(RawMessage event) { 38 | return sender.send(event); 39 | } 40 | 41 | @Override 42 | public Publisher emit(String domain, RawMessage event) { 43 | throw new UnsupportedOperationException(NOT_IMPLEMENTED_YET); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /async/async-kafka/src/main/java/org/reactivecommons/async/kafka/KafkaMessage.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.kafka; 2 | 3 | import lombok.Data; 4 | import org.apache.kafka.common.header.Headers; 5 | import org.reactivecommons.async.commons.communications.Message; 6 | import reactor.kafka.receiver.ReceiverRecord; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | import static org.reactivecommons.async.kafka.converters.json.KafkaJacksonMessageConverter.CONTENT_TYPE; 12 | 13 | 14 | @Data 15 | public class KafkaMessage implements Message { 16 | private final byte[] body; 17 | private final Properties properties; 18 | private final String type; 19 | 20 | @Data 21 | public static class KafkaMessageProperties implements Properties { 22 | private long contentLength; 23 | private String key; 24 | private String topic; 25 | private Map headers = new HashMap<>(); 26 | 27 | @Override 28 | public String getContentType() { 29 | return (String) headers.get(CONTENT_TYPE); 30 | } 31 | } 32 | 33 | public static KafkaMessage fromDelivery(ReceiverRecord receiverRecord) { 34 | return fromDelivery(receiverRecord, null); 35 | } 36 | 37 | public static KafkaMessage fromDelivery(ReceiverRecord receiverRecord, String type) { 38 | return new KafkaMessage(receiverRecord.value(), createMessageProps(receiverRecord), type); 39 | } 40 | 41 | private static Properties createMessageProps(ReceiverRecord receiverRecord) { 42 | Map headers = parseHeaders(receiverRecord.headers()); 43 | 44 | final KafkaMessageProperties properties = new KafkaMessageProperties(); 45 | properties.setHeaders(headers); 46 | properties.setKey(receiverRecord.key()); 47 | properties.setTopic(receiverRecord.topic()); 48 | properties.setContentLength(receiverRecord.value().length); 49 | return properties; 50 | } 51 | 52 | private static Map parseHeaders(Headers headers) { 53 | Map parsedHeaders = new HashMap<>(); 54 | headers.forEach(header -> parsedHeaders.put(header.key(), new String(header.value()))); 55 | return parsedHeaders; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /async/async-kafka/src/main/java/org/reactivecommons/async/kafka/communications/ReactiveMessageListener.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.kafka.communications; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import reactor.core.publisher.Flux; 5 | import reactor.kafka.receiver.KafkaReceiver; 6 | import reactor.kafka.receiver.ReceiverOptions; 7 | import reactor.kafka.receiver.ReceiverRecord; 8 | 9 | import java.util.List; 10 | 11 | import static org.apache.kafka.clients.consumer.ConsumerConfig.DEFAULT_MAX_POLL_RECORDS; 12 | import static org.apache.kafka.clients.consumer.ConsumerConfig.GROUP_ID_CONFIG; 13 | import static org.apache.kafka.clients.consumer.ConsumerConfig.MAX_POLL_RECORDS_CONFIG; 14 | 15 | 16 | @RequiredArgsConstructor 17 | public class ReactiveMessageListener { 18 | private final ReceiverOptions receiverOptions; 19 | 20 | public Flux> listen(String groupId, List topics) { // Notification events 21 | ReceiverOptions options = receiverOptions.consumerProperty(GROUP_ID_CONFIG, groupId); 22 | return KafkaReceiver.create(options.subscription(topics)) 23 | .receive(); 24 | } 25 | 26 | public int getMaxConcurrency() { 27 | Object property = receiverOptions.consumerProperty(MAX_POLL_RECORDS_CONFIG); 28 | if (property instanceof Integer) { 29 | return (int) property; 30 | } 31 | return DEFAULT_MAX_POLL_RECORDS; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /async/async-kafka/src/main/java/org/reactivecommons/async/kafka/communications/exceptions/TopicNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.kafka.communications.exceptions; 2 | 3 | public class TopicNotFoundException extends RuntimeException { 4 | public TopicNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /async/async-kafka/src/main/java/org/reactivecommons/async/kafka/communications/topology/KafkaCustomizations.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.kafka.communications.topology; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class KafkaCustomizations { 14 | private Map topics = new HashMap<>(); 15 | 16 | public static KafkaCustomizations withTopic(String topic, TopicCustomization customization) { 17 | KafkaCustomizations customizations = new KafkaCustomizations(); 18 | customizations.getTopics().put(topic, customization); 19 | return customizations; 20 | } 21 | 22 | public KafkaCustomizations addTopic(String topic, TopicCustomization customization) { 23 | this.getTopics().put(topic, customization); 24 | return this; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /async/async-kafka/src/main/java/org/reactivecommons/async/kafka/communications/topology/TopicCustomization.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.kafka.communications.topology; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.Map; 9 | 10 | @Data 11 | @Builder 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class TopicCustomization { 15 | private String topic; 16 | private int partitions; 17 | private short replicationFactor; 18 | private Map config; 19 | } 20 | -------------------------------------------------------------------------------- /async/async-kafka/src/test/java/org/reactivecommons/async/kafka/communications/topology/KafkaCustomizationsTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.kafka.communications.topology; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | import static org.junit.jupiter.api.Assertions.assertNotNull; 11 | 12 | class KafkaCustomizationsTest { 13 | 14 | private KafkaCustomizations customizations; 15 | 16 | @BeforeEach 17 | void setUp() { 18 | customizations = new KafkaCustomizations(); 19 | } 20 | 21 | @Test 22 | void testWithTopic() { 23 | String topic = "testTopic"; 24 | Map config = new HashMap<>(); 25 | config.put("cleanup.policy", "compact"); 26 | TopicCustomization customization = new TopicCustomization(topic, 3, (short) 1, config); 27 | KafkaCustomizations result = KafkaCustomizations.withTopic(topic, customization); 28 | 29 | assertNotNull(result); 30 | assertEquals(1, result.getTopics().size()); 31 | assertEquals(customization, result.getTopics().get(topic)); 32 | } 33 | 34 | @Test 35 | void testAddTopic() { 36 | String topic1 = "testTopic1"; 37 | Map config1 = new HashMap<>(); 38 | config1.put("cleanup.policy", "compact"); 39 | TopicCustomization customization1 = new TopicCustomization(topic1, 3, (short) 1, config1); 40 | customizations.addTopic(topic1, customization1); 41 | 42 | String topic2 = "testTopic2"; 43 | Map config2 = new HashMap<>(); 44 | config2.put("retention.ms", "60000"); 45 | TopicCustomization customization2 = new TopicCustomization(topic2, 5, (short) 2, config2); 46 | customizations.addTopic(topic2, customization2); 47 | 48 | assertEquals(2, customizations.getTopics().size()); 49 | assertEquals(customization1, customizations.getTopics().get(topic1)); 50 | assertEquals(customization2, customizations.getTopics().get(topic2)); 51 | } 52 | } -------------------------------------------------------------------------------- /async/async-rabbit/async-rabbit.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | artifactId = 'async-rabbit' 3 | artifactDescription = 'Async Rabbit' 4 | } 5 | 6 | dependencies { 7 | api project(':async-commons-api') 8 | api project(':domain-events-api') 9 | api project(':async-commons') 10 | 11 | api 'io.projectreactor:reactor-core' 12 | api 'io.projectreactor:reactor-core-micrometer' 13 | api 'io.projectreactor.rabbitmq:reactor-rabbitmq:1.5.6' 14 | api 'com.rabbitmq:amqp-client' 15 | api 'com.fasterxml.jackson.core:jackson-databind' 16 | 17 | implementation 'io.cloudevents:cloudevents-json-jackson:4.0.1' 18 | 19 | testImplementation 'io.projectreactor:reactor-test' 20 | } -------------------------------------------------------------------------------- /async/async-rabbit/src/main/java/org/reactivecommons/async/rabbit/RabbitMessage.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit; 2 | 3 | import com.rabbitmq.client.Delivery; 4 | import lombok.Data; 5 | import org.reactivecommons.async.commons.communications.Message; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | @Data 11 | public class RabbitMessage implements Message { 12 | private final byte[] body; 13 | private final Properties properties; 14 | private final String type; 15 | 16 | @Data 17 | public static class RabbitMessageProperties implements Properties { 18 | private String contentType; 19 | private String contentEncoding; 20 | private long timestamp; 21 | private long contentLength; 22 | private Map headers = new HashMap<>(); 23 | } 24 | 25 | public static RabbitMessage fromDelivery(Delivery delivery) { 26 | return fromDelivery(delivery, null); 27 | } 28 | 29 | public static RabbitMessage fromDelivery(Delivery delivery, String executorPath) { 30 | return new RabbitMessage(delivery.getBody(), createMessageProps(delivery), executorPath); 31 | } 32 | 33 | private static Message.Properties createMessageProps(Delivery msj) { 34 | final RabbitMessage.RabbitMessageProperties properties = new RabbitMessage.RabbitMessageProperties(); 35 | if (msj.getProperties().getTimestamp() != null) { 36 | properties.setTimestamp(msj.getProperties().getTimestamp().getTime()); 37 | } 38 | properties.setHeaders(msj.getProperties().getHeaders()); 39 | properties.setContentType(msj.getProperties().getContentType()); 40 | properties.setContentEncoding(msj.getProperties().getContentEncoding()); 41 | return properties; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /async/async-rabbit/src/main/java/org/reactivecommons/async/rabbit/communications/ReactiveMessageListener.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.communications; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | import reactor.rabbitmq.Receiver; 6 | 7 | 8 | @Getter 9 | @RequiredArgsConstructor 10 | public class ReactiveMessageListener { 11 | 12 | private final Receiver receiver; 13 | private final TopologyCreator topologyCreator; 14 | private final Integer maxConcurrency; 15 | private final Integer prefetchCount; 16 | 17 | public ReactiveMessageListener(Receiver receiver, TopologyCreator topologyCreator) { 18 | this(receiver, topologyCreator, 250, 250); 19 | } 20 | 21 | } 22 | 23 | -------------------------------------------------------------------------------- /async/async-rabbit/src/main/java/org/reactivecommons/async/rabbit/config/ConnectionFactoryProvider.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.config; 2 | 3 | import com.rabbitmq.client.ConnectionFactory; 4 | 5 | @FunctionalInterface 6 | public interface ConnectionFactoryProvider { 7 | ConnectionFactory getConnectionFactory(); 8 | } 9 | -------------------------------------------------------------------------------- /async/async-rabbit/src/main/java/org/reactivecommons/async/rabbit/converters/json/RabbitJacksonMessageConverter.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.converters.json; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import io.cloudevents.CloudEvent; 5 | import org.reactivecommons.async.commons.communications.Message; 6 | import org.reactivecommons.async.commons.converters.json.JacksonMessageConverter; 7 | import org.reactivecommons.async.commons.exceptions.MessageConversionException; 8 | import org.reactivecommons.async.rabbit.RabbitMessage; 9 | 10 | import java.io.IOException; 11 | import java.nio.charset.StandardCharsets; 12 | 13 | public class RabbitJacksonMessageConverter extends JacksonMessageConverter { 14 | 15 | public RabbitJacksonMessageConverter(ObjectMapper objectMapper) { 16 | super(objectMapper); 17 | } 18 | 19 | @Override 20 | public Message toMessage(Object object) { 21 | if (object instanceof RabbitMessage) { 22 | return (RabbitMessage) object; 23 | } 24 | byte[] bytes; 25 | try { 26 | String jsonString = this.objectMapper.writeValueAsString(object); 27 | bytes = jsonString.getBytes(StandardCharsets.UTF_8); 28 | } catch (IOException e) { 29 | throw new MessageConversionException(FAILED_TO_CONVERT_MESSAGE_CONTENT, e); 30 | } 31 | RabbitMessage.RabbitMessageProperties props = new RabbitMessage.RabbitMessageProperties(); 32 | if (object instanceof CloudEvent) { 33 | props.setContentType(APPLICATION_CLOUD_EVENT_JSON); 34 | } else { 35 | props.setContentType(APPLICATION_JSON); 36 | } 37 | props.setContentEncoding(StandardCharsets.UTF_8.name()); 38 | props.setContentLength(bytes.length); 39 | return new RabbitMessage(bytes, props, null); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /async/async-rabbit/src/test/java/org/reactivecommons/async/api/MessageConversionExceptionTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api; 2 | 3 | 4 | import org.junit.jupiter.api.Test; 5 | import org.reactivecommons.async.commons.exceptions.MessageConversionException; 6 | 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | 9 | class MessageConversionExceptionTest { 10 | 11 | @Test 12 | void shouldConstructProperly(){ 13 | String message = "some message"; 14 | Throwable cause = new RuntimeException(); 15 | 16 | final MessageConversionException exception = new MessageConversionException(message, cause); 17 | 18 | assertThat(exception).hasMessage(message).hasCause(cause); 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /async/async-rabbit/src/test/java/org/reactivecommons/async/helpers/SampleClass.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.helpers; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | import java.util.Date; 7 | 8 | @RequiredArgsConstructor 9 | @Getter 10 | public class SampleClass { 11 | private final String id; 12 | private final String name; 13 | private final Date date; 14 | } 15 | -------------------------------------------------------------------------------- /async/async-rabbit/src/test/java/org/reactivecommons/async/helpers/TestStubs.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.helpers; 2 | 3 | import org.reactivecommons.async.commons.communications.Message; 4 | import org.reactivecommons.async.rabbit.RabbitMessage; 5 | 6 | import static org.reactivecommons.async.commons.Headers.CORRELATION_ID; 7 | import static org.reactivecommons.async.commons.Headers.REPLY_ID; 8 | import static org.reactivecommons.async.commons.Headers.SERVED_QUERY_ID; 9 | 10 | public class TestStubs { 11 | 12 | public static Message mockMessage() { 13 | Message.Properties properties = new RabbitMessage.RabbitMessageProperties(); 14 | properties.getHeaders().put(REPLY_ID, "reply"); 15 | properties.getHeaders().put(CORRELATION_ID, "correlation"); 16 | properties.getHeaders().put(SERVED_QUERY_ID, "my-query"); 17 | return new RabbitMessage("{\"id\":\"id\",\"name\":\"name\",\"date\":\"2020-10-22T17:03:26.062Z\"}".getBytes(), 18 | properties, null); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /async/async-rabbit/src/test/java/org/reactivecommons/async/rabbit/RabbitMessageTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit; 2 | 3 | import com.rabbitmq.client.AMQP; 4 | import com.rabbitmq.client.Delivery; 5 | import com.rabbitmq.client.Envelope; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | 13 | class RabbitMessageTest { 14 | 15 | @Test 16 | void shouldCreateFromDelivery() { 17 | Envelope env = new Envelope(2, false, "exchange", "routeKey"); 18 | Map headers = new HashMap<>(); 19 | AMQP.BasicProperties props = new AMQP.BasicProperties.Builder() 20 | .contentType("content").contentEncoding("utf8").headers(headers) 21 | .build(); 22 | byte[] body = new byte[]{3, 4, 5, 6}; 23 | Delivery delivery = new Delivery(env, props, body); 24 | 25 | final RabbitMessage message = RabbitMessage.fromDelivery(delivery); 26 | assertThat(message.getBody()).isEqualTo(body); 27 | assertThat(message.getProperties().getContentEncoding()).isEqualTo(props.getContentEncoding()); 28 | assertThat(message.getProperties().getContentType()).isEqualTo(props.getContentType()); 29 | assertThat(message.getProperties().getHeaders()).isEqualTo(headers); 30 | 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /async/async-rabbit/src/test/java/org/reactivecommons/async/rabbit/converters/json/CloudEventBuilderExtTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.converters.json; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.reactivecommons.async.commons.converters.json.CloudEventBuilderExt; 5 | 6 | import java.util.Date; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | 10 | class CloudEventBuilderExtTest { 11 | 12 | @Test 13 | void asBytes() { 14 | Date date = new Date(); 15 | SampleClass result = new SampleClass("35", "name1", date); 16 | byte[] arrayByte = CloudEventBuilderExt.asBytes(result); 17 | assertThat(arrayByte).isNotEmpty(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /async/async-rabbit/src/test/java/org/reactivecommons/async/rabbit/converters/json/SampleClass.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.converters.json; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.util.Date; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | class SampleClass { 13 | private String id; 14 | private String name; 15 | private Date date; 16 | } 17 | -------------------------------------------------------------------------------- /async/async-rabbit/src/test/java/org/reactivecommons/async/utils/TestUtils.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.utils; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | import reactor.core.publisher.Flux; 6 | import reactor.rabbitmq.AcknowledgableDelivery; 7 | import reactor.rabbitmq.ConsumeOptions; 8 | import reactor.rabbitmq.Receiver; 9 | 10 | import java.util.concurrent.atomic.AtomicReference; 11 | 12 | import static org.mockito.ArgumentMatchers.any; 13 | import static org.mockito.ArgumentMatchers.anyString; 14 | import static org.mockito.Mockito.when; 15 | import static reactor.core.publisher.Flux.defer; 16 | 17 | 18 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 19 | public final class TestUtils { 20 | 21 | public static void instructSafeReceiverMock(final Receiver receiver, final Flux source) { 22 | final AtomicReference> sourceReference = new AtomicReference<>(source); 23 | 24 | when(receiver.consumeManualAck(anyString(), any(ConsumeOptions.class))) 25 | .thenAnswer(invocation -> defer(() -> sourceReference.getAndSet(Flux.never()))); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | maven { url = 'https://repo.spring.io/milestone' } 5 | maven { url = 'https://repo.spring.io/snapshot' } 6 | } 7 | 8 | dependencies { 9 | classpath('com.github.ben-manes:gradle-versions-plugin:0.52.0') 10 | } 11 | } 12 | 13 | plugins { 14 | id 'jacoco' 15 | id 'org.sonarqube' version '6.1.0.5360' 16 | id 'org.springframework.boot' version '3.4.4' apply false 17 | id 'io.github.gradle-nexus.publish-plugin' version '2.0.0' 18 | id 'co.com.bancolombia.cleanArchitecture' version '3.20.15' 19 | } 20 | 21 | repositories { 22 | mavenCentral() 23 | } 24 | 25 | apply from: './main.gradle' 26 | apply plugin: 'com.github.ben-manes.versions' -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* -------------------------------------------------------------------------------- /docs/.nvmrc: -------------------------------------------------------------------------------- 1 | v20.11.1 -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/docs/img/reactive-commons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactive-commons/reactive-commons-java/17c79b99846e6ccb0233eddae2a65add547ffdfb/docs/docs/img/reactive-commons.png -------------------------------------------------------------------------------- /docs/docs/reactive-commons/2-sending-a-domain-event.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Sending a Domain Event 6 | 7 | ## API specification 8 | 9 | ### DomainEvent model 10 | 11 | To emit a Domain Event we need to know the DomainEvent structure, which is represented with the next class: 12 | 13 | ```java 14 | public class DomainEvent { 15 | private final String name; 16 | private final String eventId; 17 | private final T data; 18 | } 19 | ``` 20 | 21 | Where name is the event name, eventId is an unique event identifier and data is a JSON Serializable payload. 22 | 23 | ### DomainEventBus interface 24 | 25 | ```java 26 | public interface DomainEventBus { 27 | Publisher emit(DomainEvent event); 28 | 29 | Publisher emit(CloudEvent event); 30 | } 31 | ``` 32 | 33 | ## Enabling autoconfiguration 34 | 35 | To send Domain Events you should enable the respecting spring boot autoconfiguration using the `@EnableDomainEventBus` annotation 36 | For example: 37 | 38 | ```java 39 | @RequiredArgsConstructor 40 | @EnableDomainEventBus 41 | public class ReactiveEventsGateway { 42 | public static final String SOME_EVENT_NAME = "some.event.name"; 43 | private final DomainEventBus domainEventBus; // Auto injected bean created by the @EnableDomainEventBus annotation 44 | 45 | public Mono emit(Object event) { 46 | return Mono.from(domainEventBus.emit(new DomainEvent<>(SOME_EVENT_NAME, UUID.randomUUID().toString(), event))); 47 | } 48 | } 49 | ``` 50 | 51 | After that you can emit events from you application. 52 | 53 | ## Example 54 | 55 | You can see a real example at [samples/async/async-sender-client](https://github.com/reactive-commons/reactive-commons-java/tree/master/samples/async/async-sender-client) -------------------------------------------------------------------------------- /docs/docs/reactive-commons/6-handling-domain-events.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # Handling DomainEvents 6 | 7 | ## HandlerRegistry configuration 8 | 9 | To listen a DomainEvent you should register it in the HandlerRegistry and make it available as a Bean 10 | 11 | ### Listening Events 12 | 13 | ```java 14 | @Configuration 15 | public class HandlerRegistryConfiguration { 16 | 17 | @Bean 18 | public HandlerRegistry handlerRegistry(EventsHandler events) { 19 | return HandlerRegistry.register() 20 | .listenEvent("some.event.name", events::handleEventA, Object.class/*change for proper model*/); 21 | } 22 | } 23 | ``` 24 | 25 | To effectively start listening events you should add the annotation `@EnableEventListeners` to your MainApplication class or any other spring Configuration class, for example the `EventsHandler` class can be like: 26 | 27 | ```java 28 | @EnableEventListeners 29 | public class EventsHandler { 30 | public Mono handleEventA(DomainEvent event) { 31 | System.out.println("event received: " + event.getName() + " ->" + event.getData()); 32 | return Mono.empty(); 33 | } 34 | } 35 | ``` 36 | 37 | ### Listening Notification Events (broadcast) 38 | 39 | In the same way you can listen the NotificationEvents which has the same DomainEvent definition, but in that case you should add the `@EnableNotificationListener` annotation 40 | 41 | ```java 42 | @Configuration 43 | public class HandlerRegistryConfiguration { 44 | 45 | @Bean 46 | public HandlerRegistry handlerRegistry(EventsHandler events) { 47 | return HandlerRegistry.register() 48 | .listenNotificationEvent("some.broadcast.event.name", events::handleEventA, Object.class/*change for proper model*/); 49 | } 50 | } 51 | ``` 52 | 53 | Then you should create the handler like: 54 | 55 | ```java 56 | @EnableNotificationListener 57 | public class EventsHandler { 58 | public Mono handleEventA(DomainEvent event) { 59 | System.out.println("event received: " + event.getName() + " ->" + event.getData()); 60 | return Mono.empty(); 61 | } 62 | } 63 | ``` -------------------------------------------------------------------------------- /docs/docs/reactive-commons/7-handling-commands.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | --- 4 | 5 | # Handling Commands 6 | 7 | ## HandlerRegistry configuration 8 | 9 | To listen a Command you should register it in the HandlerRegistry and make it available as a Bean 10 | 11 | ### Listening Commands 12 | 13 | ```java 14 | @Configuration 15 | public class HandlerRegistryConfiguration { 16 | 17 | @Bean 18 | public HandlerRegistry handlerRegistry(CommandsHandler commands) { 19 | return HandlerRegistry.register() 20 | .handleCommand("some.command.name", commands::handleCommandA, Object.class/*change for proper model*/); 21 | } 22 | } 23 | ``` 24 | 25 | To effectively start listening commands you should add the annotation `@EnableCommandListeners` to your MainApplication class or any other spring Configuration class, for example the `CommandsHandler` class can be like: 26 | 27 | ```java 28 | @EnableCommandListeners 29 | public class CommandsHandler { 30 | public Mono handleCommandA(Command command) { 31 | System.out.println("command received: " + command.getName() + " ->" + command.getData()); 32 | return Mono.empty(); 33 | } 34 | } 35 | ``` 36 | 37 | As the model of commands is direct, a consumer always can send commands to the service provider, by this reason you may receive commands that you don`t have configured. 38 | 39 | ### Wildcards 40 | 41 | You may need to handle variable command names that have the same structure, in that case you can specfy a pattern with '*' wildcard, for example: 42 | 43 | ```java 44 | @Configuration 45 | public class HandlerRegistryConfiguration { 46 | 47 | @Bean 48 | public HandlerRegistry handlerRegistry(CommandsHandler commands) { 49 | return HandlerRegistry.register() 50 | .handleCommand("send.*.notification", commands::handleCommandA, Object.class/*change for proper model*/); 51 | } 52 | } 53 | ``` 54 | 55 | So any consumer can send a command with a name that matches with pattern, for example: `send.email.notification` 56 | 57 | ## Example 58 | 59 | You can see a real example at [samples/async/async-receiver-responder](https://github.com/reactive-commons/reactive-commons-java/tree/master/samples/async/async-receiver-responder) -------------------------------------------------------------------------------- /docs/docs/reactive-commons/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Reactive Commons", 3 | "position": 2, 4 | "link": { 5 | "type": "generated-index", 6 | "description": "Learn how to build reactive systems using the Reactive Commons library." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/docs/scenarios/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Communication scenarios", 3 | "position": 3, 4 | "link": { 5 | "type": "generated-index", 6 | "description": "Review configuration examples for common scenarios." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "^3.6.3", 18 | "@docusaurus/preset-classic": "^3.6.3", 19 | "@docusaurus/theme-common": "^3.6.3", 20 | "@mdx-js/react": "^3.1.0", 21 | "clsx": "^2.1.1", 22 | "prism-react-renderer": "^2.4.0", 23 | "react": "^18.3.1", 24 | "react-dom": "^18.3.1" 25 | }, 26 | "devDependencies": { 27 | "@docusaurus/module-type-aliases": "^3.6.3", 28 | "@docusaurus/types": "^3.6.3" 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.5%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 3 chrome version", 38 | "last 3 firefox version", 39 | "last 5 safari version" 40 | ] 41 | }, 42 | "engines": { 43 | "node": ">=18.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /docs/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 18 | 19 | // But you can create a sidebar manually 20 | /* 21 | tutorialSidebar: [ 22 | 'intro', 23 | 'hello', 24 | { 25 | type: 'category', 26 | label: 'Tutorial', 27 | items: ['tutorial-basics/create-a-document'], 28 | }, 29 | ], 30 | */ 31 | }; 32 | 33 | export default sidebars; 34 | -------------------------------------------------------------------------------- /docs/src/components/HomepageFeatures/index.js: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import Heading from '@theme/Heading'; 3 | import styles from './styles.module.css'; 4 | 5 | const FeatureList = [ 6 | { 7 | title: 'Easy to Use', 8 | Svg: require('@site/static/img/feature-first.svg').default, 9 | description: ( 10 | <> 11 | Reactive Commons comes with starter setup to start quickly and abstract your Broker. 12 | 13 | ), 14 | }, 15 | { 16 | title: 'Focus on Domain', 17 | Svg: require('@site/static/img/feature-second.svg').default, 18 | description: ( 19 | <> 20 | Reactive Commons lets you focus on your domain solution abstracting the communication 21 | details with the Broker and giving you a semantic specification. 22 | 23 | ), 24 | }, 25 | { 26 | title: 'Powered by Reactor and Reactive Streams', 27 | Svg: require('@site/static/img/feature-third.svg').default, 28 | description: ( 29 | <> 30 | Get the reactive programming benefits out of the box. 31 | 32 | ), 33 | }, 34 | ]; 35 | 36 | function Feature({Svg, title, description}) { 37 | return ( 38 |
39 |
40 | 41 |
42 |
43 | {title} 44 |

{description}

45 |
46 |
47 | ); 48 | } 49 | 50 | export default function HomepageFeatures() { 51 | return ( 52 |
53 |
54 |
55 | {FeatureList.map((props, idx) => ( 56 | 57 | ))} 58 |
59 |
60 |
61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /docs/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /docs/src/components/ThemeImage/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useColorMode } from '@docusaurus/theme-common'; 3 | 4 | const scenarios = { 5 | '1': { 6 | dark: require('@site/static/img/scenarios/1-scenario-dark.svg').default, 7 | light: require('@site/static/img/scenarios/1-scenario.svg').default 8 | }, 9 | '2': { 10 | dark: require('@site/static/img/scenarios/2-scenario-dark.svg').default, 11 | light: require('@site/static/img/scenarios/2-scenario.svg').default 12 | }, 13 | // '3': { 14 | // dark: require('@site/static/img/scenarios/3-scenario-dark.svg').default, 15 | // light: require('@site/static/img/scenarios/3-scenario.svg').default 16 | // }, 17 | } 18 | 19 | console.log(scenarios) 20 | 21 | const ThemeImage = ({ scenario }) => { 22 | const { colorMode } = useColorMode(); 23 | 24 | // Import the images using `require` 25 | const Svg = scenarios[scenario][colorMode]; 26 | 27 | return ( 28 | 29 | ); 30 | }; 31 | 32 | export default ThemeImage; 33 | -------------------------------------------------------------------------------- /docs/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #2e8555; 10 | --ifm-color-primary-dark: #29784c; 11 | --ifm-color-primary-darker: #277148; 12 | --ifm-color-primary-darkest: #205d3b; 13 | --ifm-color-primary-light: #33925d; 14 | --ifm-color-primary-lighter: #359962; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme='dark'] { 22 | --ifm-color-primary: #3ecc5f; 23 | --ifm-color-primary-dark: #21af90; 24 | --ifm-color-primary-darker: #1fa588; 25 | --ifm-color-primary-darkest: #1a8870; 26 | --ifm-color-primary-light: #29d5b0; 27 | --ifm-color-primary-lighter: #32d8b4; 28 | --ifm-color-primary-lightest: #4fddbf; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | -------------------------------------------------------------------------------- /docs/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import Link from '@docusaurus/Link'; 3 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 4 | import Layout from '@theme/Layout'; 5 | import HomepageFeatures from '@site/src/components/HomepageFeatures'; 6 | 7 | import Heading from '@theme/Heading'; 8 | import styles from './index.module.css'; 9 | 10 | function HomepageHeader() { 11 | const {siteConfig} = useDocusaurusContext(); 12 | return ( 13 |
14 |
15 | 16 | {siteConfig.title} 17 | 18 |

{siteConfig.tagline}

19 |
20 | 23 | Introduction 24 | 25 |
26 |
27 |
28 | ); 29 | } 30 | 31 | export default function Home() { 32 | const {siteConfig} = useDocusaurusContext(); 33 | return ( 34 | 37 | 38 |
39 | 40 |
41 |
42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /docs/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactive-commons/reactive-commons-java/17c79b99846e6ccb0233eddae2a65add547ffdfb/docs/static/.nojekyll -------------------------------------------------------------------------------- /docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactive-commons/reactive-commons-java/17c79b99846e6ccb0233eddae2a65add547ffdfb/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /docs/static/img/reactive-commons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactive-commons/reactive-commons-java/17c79b99846e6ccb0233eddae2a65add547ffdfb/docs/static/img/reactive-commons.png -------------------------------------------------------------------------------- /domain/domain-events/domain-events-api.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | artifactId = 'Common domain API' 3 | artifactDescription = 'domain-events-api' 4 | } 5 | 6 | dependencies { 7 | api 'org.reactivestreams:reactive-streams:1.0.4' 8 | api 'io.cloudevents:cloudevents-api:4.0.1' 9 | implementation 'io.cloudevents:cloudevents-json-jackson:4.0.1' 10 | } -------------------------------------------------------------------------------- /domain/domain-events/src/main/java/org/reactivecommons/api/domain/Command.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.api.domain; 2 | 3 | 4 | import lombok.Data; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | @Data 8 | @RequiredArgsConstructor 9 | public class Command { 10 | private final String name; 11 | private final String commandId; 12 | private final T data; 13 | } 14 | -------------------------------------------------------------------------------- /domain/domain-events/src/main/java/org/reactivecommons/api/domain/DomainEvent.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.api.domain; 2 | 3 | import lombok.Data; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | @Data 7 | @RequiredArgsConstructor 8 | public class DomainEvent { 9 | private final String name; 10 | private final String eventId; 11 | private final T data; 12 | } 13 | -------------------------------------------------------------------------------- /domain/domain-events/src/main/java/org/reactivecommons/api/domain/DomainEventBus.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.api.domain; 2 | 3 | import io.cloudevents.CloudEvent; 4 | import org.reactivestreams.Publisher; 5 | 6 | public interface DomainEventBus { 7 | Publisher emit(DomainEvent event); 8 | 9 | Publisher emit(String domain, DomainEvent event); 10 | 11 | Publisher emit(CloudEvent event); 12 | 13 | Publisher emit(String domain, CloudEvent event); 14 | 15 | Publisher emit(RawMessage event); 16 | Publisher emit(String domain, RawMessage event); 17 | } 18 | -------------------------------------------------------------------------------- /domain/domain-events/src/main/java/org/reactivecommons/api/domain/RawMessage.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.api.domain; 2 | 3 | public interface RawMessage { 4 | String getType(); 5 | } 6 | -------------------------------------------------------------------------------- /domain/domain-events/src/test/java/org/reactivecommons/async/api/DomainEventTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.api; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.reactivecommons.api.domain.DomainEvent; 5 | 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | class DomainEventTest { 9 | 10 | DomainEvent event = new DomainEvent<>("testEvent", "id", "data"); 11 | 12 | @Test 13 | void getName() { 14 | assertThat(event.getName()).isEqualTo("testEvent"); 15 | } 16 | 17 | @Test 18 | void getEventId() { 19 | assertThat(event.getEventId()).isEqualTo("id"); 20 | } 21 | 22 | @Test 23 | void getData() { 24 | assertThat(event.getData()).isEqualTo("data"); 25 | } 26 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version=5.4.0 2 | toPublish=domain-events-api,async-commons-api,async-commons,shared-starter,async-rabbit,async-commons-rabbit-standalone,async-commons-rabbit-starter,async-kafka,async-kafka-starter,async-commons-starter 3 | onlyUpdater=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactive-commons/reactive-commons-java/17c79b99846e6ccb0233eddae2a65add547ffdfb/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /lombok.config: -------------------------------------------------------------------------------- 1 | config.stopBubbling = true 2 | lombok.addLombokGeneratedAnnotation = true 3 | lombok.anyConstructor.addConstructorProperties = true -------------------------------------------------------------------------------- /samples/async/async-kafka-sender-client/async-kafka-sender-client.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'org.springframework.boot' 2 | 3 | dependencies { 4 | implementation project(':shared') 5 | implementation project(':async-kafka-starter') 6 | implementation 'org.springframework.boot:spring-boot-starter-webflux' 7 | implementation 'org.springframework.boot:spring-boot-starter-actuator' 8 | implementation 'io.micrometer:micrometer-registry-prometheus' 9 | implementation 'io.cloudevents:cloudevents-json-jackson:4.0.1' 10 | } -------------------------------------------------------------------------------- /samples/async/async-kafka-sender-client/src/main/java/sample/EDASampleSenderApp.java: -------------------------------------------------------------------------------- 1 | package sample; 2 | 3 | import lombok.extern.java.Log; 4 | import org.reactivecommons.async.impl.config.annotations.EnableDomainEventBus; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | 8 | @Log 9 | //@EnableDirectAsyncGateway 10 | @EnableDomainEventBus 11 | @SpringBootApplication 12 | public class EDASampleSenderApp { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(EDASampleSenderApp.class, args); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /samples/async/async-kafka-sender-client/src/main/java/sample/KafkaConfig.java: -------------------------------------------------------------------------------- 1 | package sample; 2 | 3 | import org.reactivecommons.async.kafka.KafkaSetupUtils; 4 | import org.reactivecommons.async.kafka.config.props.AsyncKafkaProps; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.context.annotation.Primary; 8 | 9 | import java.io.IOException; 10 | import java.nio.file.Path; 11 | 12 | @Configuration 13 | public class KafkaConfig { 14 | 15 | @Bean 16 | @Primary 17 | public AsyncKafkaProps kafkaProps() throws IOException { 18 | AsyncKafkaProps kafkaProps = new AsyncKafkaProps(); 19 | kafkaProps.setCreateTopology(true); 20 | kafkaProps.setMaxRetries(5); 21 | kafkaProps.setRetryDelay(1000); 22 | kafkaProps.setWithDLQRetry(true); 23 | kafkaProps.setConnectionProperties(KafkaSetupUtils.readPropsFromDotEnv(Path.of(".kafka-env"))); 24 | return kafkaProps; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /samples/async/async-kafka-sender-client/src/main/java/sample/ListenerConfig.java: -------------------------------------------------------------------------------- 1 | package sample; 2 | 3 | import io.cloudevents.CloudEvent; 4 | import lombok.extern.log4j.Log4j2; 5 | import org.reactivecommons.api.domain.DomainEvent; 6 | import org.reactivecommons.async.api.HandlerRegistry; 7 | import org.reactivecommons.async.impl.config.annotations.EnableEventListeners; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.context.annotation.Primary; 11 | import reactor.core.publisher.Mono; 12 | import sample.model.Constants; 13 | 14 | @Log4j2 15 | @Configuration 16 | @EnableEventListeners 17 | public class ListenerConfig { 18 | 19 | @Bean 20 | @Primary 21 | public HandlerRegistry handlerRegistrySubs() { 22 | return HandlerRegistry.register() 23 | .listenEvent(Constants.DATA_RESET, this::reset, String.class) 24 | .listenCloudEvent("event-name", this::reset2); 25 | } 26 | 27 | private Mono reset2(CloudEvent cloudEvent) { 28 | log.info("reset2: " + cloudEvent); 29 | return Mono.empty(); 30 | } 31 | 32 | public Mono reset(DomainEvent ignored) { 33 | log.info("reset: {}", ignored); 34 | return Mono.empty(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /samples/async/async-kafka-sender-client/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: sender-eda 4 | rabbitmq: 5 | virtual-host: / 6 | server: 7 | port: 4001 8 | management: 9 | endpoint: 10 | health: 11 | show-details: always 12 | endpoints: 13 | web: 14 | exposure: 15 | include: health,prometheus 16 | 17 | 18 | -------------------------------------------------------------------------------- /samples/async/async-receiver-responder/async-receiver-sample.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'org.springframework.boot' 2 | 3 | dependencies { 4 | implementation project(":shared") 5 | implementation project(":async-commons-rabbit-starter") 6 | implementation 'org.springframework.boot:spring-boot-starter' 7 | } -------------------------------------------------------------------------------- /samples/async/async-receiver-responder/src/main/java/sample/SampleReceiverApp.java: -------------------------------------------------------------------------------- 1 | package sample; 2 | 3 | import org.reactivecommons.async.impl.config.annotations.EnableCommandListeners; 4 | import org.reactivecommons.async.impl.config.annotations.EnableDirectAsyncGateway; 5 | import org.reactivecommons.async.impl.config.annotations.EnableDomainEventBus; 6 | import org.reactivecommons.async.impl.config.annotations.EnableEventListeners; 7 | import org.reactivecommons.async.impl.config.annotations.EnableNotificationListener; 8 | import org.reactivecommons.async.impl.config.annotations.EnableQueryListeners; 9 | import org.springframework.boot.SpringApplication; 10 | import org.springframework.boot.autoconfigure.SpringBootApplication; 11 | 12 | @SpringBootApplication 13 | @EnableEventListeners 14 | @EnableNotificationListener 15 | @EnableQueryListeners 16 | @EnableCommandListeners 17 | @EnableDomainEventBus 18 | @EnableDirectAsyncGateway 19 | public class SampleReceiverApp { 20 | public static void main(String[] args) { 21 | SpringApplication.run(SampleReceiverApp.class, args); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /samples/async/async-receiver-responder/src/main/java/sample/UseCase.java: -------------------------------------------------------------------------------- 1 | package sample; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.reactivecommons.api.domain.Command; 5 | import org.reactivecommons.api.domain.DomainEvent; 6 | import org.springframework.stereotype.Component; 7 | import reactor.core.publisher.Mono; 8 | import sample.model.Members; 9 | import sample.model.Team; 10 | import sample.model.Teams; 11 | import sample.model.broker.AddMemberCommand; 12 | import sample.model.broker.RemovedMemberEvent; 13 | 14 | import java.util.Optional; 15 | 16 | @Component 17 | @RequiredArgsConstructor 18 | public class UseCase { 19 | private final Teams teams = new Teams(); 20 | 21 | public Mono getTeams(String ignored) { 22 | return Mono.fromSupplier(() -> teams); 23 | } 24 | 25 | public Mono getTeam(String teamName) { 26 | return Mono.fromSupplier(() -> { 27 | if (teams.containsKey(teamName)) { 28 | return teams.get(teamName).getMembers(); 29 | } 30 | return new Members(); 31 | }); 32 | } 33 | 34 | public Mono addMember(Command command) { 35 | return Mono.fromRunnable(() -> teams.computeIfAbsent(command.getData().getTeamName(), 36 | (name) -> Team.builder().name(command.getData().getTeamName()).members(new Members()).build()) 37 | .getMembers().add(command.getData().getMember())); 38 | } 39 | 40 | public Mono removeMember(DomainEvent event) { 41 | return Mono.fromRunnable(() -> Optional.of(teams.get(event.getData().getTeamName())) 42 | .ifPresent(team -> team.getMembers() 43 | .removeIf(member -> member.getUsername().equals(event.getData().getUsername())))); 44 | } 45 | 46 | public Mono reset(DomainEvent ignored) { 47 | return Mono.fromRunnable(teams::clear); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /samples/async/async-receiver-responder/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: receiver 4 | rabbitmq: 5 | virtual-host: / 6 | app: 7 | async: 8 | createTopology: true 9 | listenReplies: false 10 | maxRetries: 3 11 | 12 | -------------------------------------------------------------------------------- /samples/async/async-sender-client/async-sender-client.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'org.springframework.boot' 2 | 3 | dependencies { 4 | implementation project(':shared') 5 | implementation project(':async-commons-rabbit-starter') 6 | implementation 'org.springframework.boot:spring-boot-starter-webflux' 7 | implementation 'org.springframework.boot:spring-boot-starter-actuator' 8 | implementation 'io.micrometer:micrometer-registry-prometheus' 9 | } -------------------------------------------------------------------------------- /samples/async/async-sender-client/src/main/java/sample/MyRabbitMQConfig.java: -------------------------------------------------------------------------------- 1 | package sample; 2 | 3 | import org.reactivecommons.async.rabbit.config.RabbitProperties; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.context.annotation.Primary; 7 | 8 | @Configuration 9 | public class MyRabbitMQConfig { 10 | 11 | @Bean 12 | @Primary 13 | public RabbitProperties customRabbitProperties(){ 14 | RabbitProperties properties = new RabbitProperties(); 15 | properties.setHost("localhost"); 16 | properties.setPort(5672); 17 | properties.setVirtualHost("/"); 18 | properties.setUsername("guest"); 19 | properties.setPassword("guest"); 20 | return properties; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /samples/async/async-sender-client/src/main/java/sample/SampleSenderApp.java: -------------------------------------------------------------------------------- 1 | package sample; 2 | 3 | import org.reactivecommons.async.impl.config.annotations.EnableDirectAsyncGateway; 4 | import org.reactivecommons.async.impl.config.annotations.EnableDomainEventBus; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | 8 | @EnableDirectAsyncGateway 9 | @EnableDomainEventBus 10 | @SpringBootApplication 11 | public class SampleSenderApp { 12 | public static void main(String[] args) { 13 | SpringApplication.run(SampleSenderApp.class, args); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /samples/async/async-sender-client/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: sender 4 | rabbitmq: 5 | virtual-host: / 6 | server: 7 | port: 4001 8 | management: 9 | endpoint: 10 | health: 11 | show-details: always 12 | endpoints: 13 | web: 14 | exposure: 15 | include: health,prometheus 16 | app: 17 | async: 18 | createTopology: true 19 | listenReplies: true 20 | -------------------------------------------------------------------------------- /samples/async/eda-async-receiver-responder/eda-async-receiver-sample.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'org.springframework.boot' 2 | 3 | dependencies { 4 | implementation project(":shared") 5 | implementation project(":async-commons-rabbit-starter") 6 | implementation 'org.springframework.boot:spring-boot-starter' 7 | implementation 'io.cloudevents:cloudevents-core:4.0.1' 8 | } -------------------------------------------------------------------------------- /samples/async/eda-async-receiver-responder/src/main/java/sample/EDASampleReceiverApp.java: -------------------------------------------------------------------------------- 1 | package sample; 2 | 3 | import org.reactivecommons.async.impl.config.annotations.EnableCommandListeners; 4 | import org.reactivecommons.async.impl.config.annotations.EnableDirectAsyncGateway; 5 | import org.reactivecommons.async.impl.config.annotations.EnableDomainEventBus; 6 | import org.reactivecommons.async.impl.config.annotations.EnableEventListeners; 7 | import org.reactivecommons.async.impl.config.annotations.EnableNotificationListener; 8 | import org.reactivecommons.async.impl.config.annotations.EnableQueryListeners; 9 | import org.springframework.boot.SpringApplication; 10 | import org.springframework.boot.autoconfigure.SpringBootApplication; 11 | 12 | @SpringBootApplication 13 | @EnableEventListeners 14 | @EnableNotificationListener 15 | @EnableQueryListeners 16 | @EnableCommandListeners 17 | @EnableDomainEventBus 18 | @EnableDirectAsyncGateway 19 | public class EDASampleReceiverApp { 20 | public static void main(String[] args) { 21 | SpringApplication.run(EDASampleReceiverApp.class, args); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /samples/async/eda-async-receiver-responder/src/main/java/sample/HandlersConfig.java: -------------------------------------------------------------------------------- 1 | package sample; 2 | 3 | import io.cloudevents.CloudEvent; 4 | import org.reactivecommons.async.api.HandlerRegistry; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.context.annotation.Primary; 8 | import sample.model.Constants; 9 | 10 | @Configuration 11 | public class HandlersConfig { 12 | @Bean 13 | @Primary 14 | public HandlerRegistry handlerRegistrySubs(UseCase useCase) { 15 | return HandlerRegistry.register() 16 | .serveQuery(Constants.GET_TEAMS, useCase::getTeams, CloudEvent.class) 17 | .serveQuery(Constants.GET_TEAM_MEMBERS, useCase::getTeam, CloudEvent.class) 18 | .handleCommand(Constants.ADD_MEMBER, useCase::addMember, CloudEvent.class) 19 | .listenEvent(Constants.MEMBER_REMOVED, useCase::removeMember, CloudEvent.class) 20 | .listenDomainEvent("domain-a", Constants.MEMBER_REMOVED_EXTERNAL_DOMAIN, useCase::removeMember, CloudEvent.class) 21 | .listenNotificationEvent(Constants.DATA_RESET, useCase::reset, String.class); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /samples/async/eda-async-receiver-responder/src/main/java/sample/MyDomainConfig.java: -------------------------------------------------------------------------------- 1 | package sample; 2 | 3 | import org.reactivecommons.async.rabbit.config.RabbitProperties; 4 | import org.reactivecommons.async.rabbit.config.props.AsyncProps; 5 | import org.reactivecommons.async.rabbit.config.props.AsyncRabbitPropsDomainProperties; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Primary; 8 | 9 | //@Configuration 10 | public class MyDomainConfig { 11 | 12 | @Bean 13 | @Primary 14 | public AsyncRabbitPropsDomainProperties customDomainProperties() { 15 | RabbitProperties propertiesApp = new RabbitProperties(); 16 | propertiesApp.setHost("localhost"); 17 | propertiesApp.setPort(5672); 18 | propertiesApp.setVirtualHost("/"); 19 | propertiesApp.setUsername("guest"); 20 | propertiesApp.setPassword("guest"); 21 | 22 | RabbitProperties propertiesAccounts = new RabbitProperties(); 23 | propertiesAccounts.setHost("localhost"); 24 | propertiesAccounts.setPort(5672); 25 | propertiesAccounts.setVirtualHost("/accounts"); 26 | propertiesAccounts.setUsername("guest"); 27 | propertiesAccounts.setPassword("guest"); 28 | 29 | return AsyncRabbitPropsDomainProperties.builder() 30 | .withDomain("app", AsyncProps.builder() 31 | .connectionProperties(propertiesApp) 32 | .build()) 33 | .withDomain("accounts", AsyncProps.builder() 34 | .connectionProperties(propertiesAccounts) 35 | .build()) 36 | .build(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /samples/async/eda-async-receiver-responder/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: receiver-eda 4 | rabbitmq: 5 | virtual-host: / 6 | app: 7 | async: 8 | app: 9 | createTopology: true 10 | listenReplies: false 11 | domain-a: 12 | listenReplies: false 13 | connectionProperties: 14 | virtual-host: domain-a 15 | 16 | -------------------------------------------------------------------------------- /samples/async/eda-async-sender-client-domain-a/eda-async-sender-client-domain-a.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'org.springframework.boot' 2 | 3 | dependencies { 4 | implementation project(':shared') 5 | implementation project(':async-commons-rabbit-starter') 6 | implementation 'org.springframework.boot:spring-boot-starter-webflux' 7 | implementation 'org.springframework.boot:spring-boot-starter-actuator' 8 | implementation 'io.micrometer:micrometer-registry-prometheus' 9 | implementation 'io.cloudevents:cloudevents-core:4.0.1' 10 | 11 | } -------------------------------------------------------------------------------- /samples/async/eda-async-sender-client-domain-a/src/main/java/sample/EDASampleSenderAppDomainA.java: -------------------------------------------------------------------------------- 1 | package sample; 2 | 3 | import lombok.extern.java.Log; 4 | import org.reactivecommons.async.impl.config.annotations.EnableDirectAsyncGateway; 5 | import org.reactivecommons.async.impl.config.annotations.EnableDomainEventBus; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | 9 | @Log 10 | @EnableDomainEventBus 11 | @EnableDirectAsyncGateway 12 | @SpringBootApplication 13 | public class EDASampleSenderAppDomainA { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(EDASampleSenderAppDomainA.class, args); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /samples/async/eda-async-sender-client-domain-a/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: sender-domain-a-eda 4 | rabbitmq: 5 | virtual-host: domain-a 6 | server: 7 | port: 4002 8 | management: 9 | endpoint: 10 | health: 11 | show-details: always 12 | endpoints: 13 | web: 14 | exposure: 15 | include: health,prometheus 16 | app: 17 | async: 18 | app: # domain-a 19 | connectionProperties: 20 | virtualHost: / 21 | teams: 22 | connectionProperties: 23 | virtualHost: / 24 | 25 | 26 | -------------------------------------------------------------------------------- /samples/async/eda-async-sender-client/eda-async-sender-client.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'org.springframework.boot' 2 | 3 | dependencies { 4 | implementation project(':shared') 5 | implementation project(':async-commons-rabbit-starter') 6 | implementation 'org.springframework.boot:spring-boot-starter-webflux' 7 | implementation 'org.springframework.boot:spring-boot-starter-actuator' 8 | implementation 'io.micrometer:micrometer-registry-prometheus' 9 | implementation 'io.cloudevents:cloudevents-core:4.0.1' 10 | 11 | } -------------------------------------------------------------------------------- /samples/async/eda-async-sender-client/src/main/java/sample/EDASampleSenderApp.java: -------------------------------------------------------------------------------- 1 | package sample; 2 | 3 | import lombok.extern.java.Log; 4 | import org.reactivecommons.async.impl.config.annotations.EnableDirectAsyncGateway; 5 | import org.reactivecommons.async.impl.config.annotations.EnableDomainEventBus; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | 9 | @Log 10 | @EnableDomainEventBus 11 | @EnableDirectAsyncGateway 12 | @SpringBootApplication 13 | public class EDASampleSenderApp { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(EDASampleSenderApp.class, args); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /samples/async/eda-async-sender-client/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: sender-eda 4 | rabbitmq: 5 | virtual-host: / 6 | server: 7 | port: 4001 8 | management: 9 | endpoint: 10 | health: 11 | show-details: always 12 | endpoints: 13 | web: 14 | exposure: 15 | include: health,prometheus 16 | app: 17 | async: 18 | app: 19 | appName: ${spring.application.name} 20 | flux: 21 | maxConcurrency: 250 22 | domain: 23 | events: 24 | exchange: domainEvents 25 | eventsSuffix: subEvents 26 | notificationSuffix: notification 27 | direct: 28 | exchange: directMessages 29 | querySuffix: query 30 | commandSuffix: '' 31 | discardTimeoutQueries: false 32 | global: 33 | exchange: globalReply 34 | repliesSuffix: replies 35 | connectionProperties: # RabbitProperties 36 | virtualHost: / 37 | maxRetries: 10 38 | prefetchCount: 250 39 | retryDelay: 1000 40 | listenReplies: true 41 | withDLQRetry: false 42 | delayedCommands: false 43 | createTopology: true 44 | # accounts: 45 | # connectionProps: 46 | # virtualHost: domain-a 47 | 48 | 49 | -------------------------------------------------------------------------------- /samples/async/shared/shared.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | } -------------------------------------------------------------------------------- /samples/async/shared/src/main/java/sample/model/Constants.java: -------------------------------------------------------------------------------- 1 | package sample.model; 2 | 3 | import lombok.experimental.UtilityClass; 4 | 5 | @UtilityClass 6 | public class Constants { 7 | public static String GET_TEAM_MEMBERS = "get-team-members"; 8 | public static String GET_TEAMS = "get-teams"; 9 | public static String ADD_MEMBER = "add-member"; 10 | public static String MEMBER_REMOVED = "member-removed"; 11 | public static String MEMBER_REMOVED_EXTERNAL_DOMAIN = "member-removed-from-active-directory"; 12 | public static String DATA_RESET = "data-reset"; 13 | public static String ANIMALS = "animals.*"; 14 | public static String ANIMALS_MANY = "animals.#"; 15 | } 16 | -------------------------------------------------------------------------------- /samples/async/shared/src/main/java/sample/model/Member.java: -------------------------------------------------------------------------------- 1 | package sample.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class Member { 7 | private String username; 8 | private String name; 9 | } 10 | -------------------------------------------------------------------------------- /samples/async/shared/src/main/java/sample/model/Members.java: -------------------------------------------------------------------------------- 1 | package sample.model; 2 | 3 | 4 | import java.util.ArrayList; 5 | 6 | public class Members extends ArrayList { 7 | } 8 | -------------------------------------------------------------------------------- /samples/async/shared/src/main/java/sample/model/Team.java: -------------------------------------------------------------------------------- 1 | package sample.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | @Getter 10 | @Setter 11 | @Builder 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class Team { 15 | private String name; 16 | private Members members; 17 | } 18 | -------------------------------------------------------------------------------- /samples/async/shared/src/main/java/sample/model/Teams.java: -------------------------------------------------------------------------------- 1 | package sample.model; 2 | 3 | import java.util.HashMap; 4 | 5 | public class Teams extends HashMap { 6 | } 7 | -------------------------------------------------------------------------------- /samples/async/shared/src/main/java/sample/model/broker/AddMemberCommand.java: -------------------------------------------------------------------------------- 1 | package sample.model.broker; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import sample.model.Member; 9 | 10 | @Getter 11 | @Setter 12 | @Builder 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class AddMemberCommand { 16 | private String teamName; 17 | private Member member; 18 | } 19 | -------------------------------------------------------------------------------- /samples/async/shared/src/main/java/sample/model/broker/AnimalEvent.java: -------------------------------------------------------------------------------- 1 | package sample.model.broker; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | @Getter 10 | @Setter 11 | @Builder 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class AnimalEvent { 15 | private String name; 16 | private String type; 17 | } 18 | -------------------------------------------------------------------------------- /samples/async/shared/src/main/java/sample/model/broker/RemovedMemberEvent.java: -------------------------------------------------------------------------------- 1 | package sample.model.broker; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | @Getter 10 | @Setter 11 | @Builder 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class RemovedMemberEvent { 15 | private String teamName; 16 | private String username; 17 | } 18 | -------------------------------------------------------------------------------- /samples/async/simpleConsumer/simple-consumer.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation 'org.springframework.boot:spring-boot-starter' 3 | } -------------------------------------------------------------------------------- /samples/async/simpleConsumer/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactive-commons/reactive-commons-java/17c79b99846e6ccb0233eddae2a65add547ffdfb/samples/async/simpleConsumer/src/main/resources/application.properties -------------------------------------------------------------------------------- /samples/async/simpleConsumer/src/test/java/sample/ReactorSamples.java: -------------------------------------------------------------------------------- 1 | package sample; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import reactor.core.publisher.Mono; 5 | 6 | @RequiredArgsConstructor 7 | public class ReactorSamples { 8 | 9 | private final UserRepository repository; 10 | private final UserCache userCache; 11 | 12 | public Mono findUser(String id) { 13 | return repository.findUser(id).onErrorResume((e) -> userCache.getFromCache(id)); 14 | } 15 | 16 | public Mono findUser2(String id) { 17 | return repository.findUser(id) 18 | .onErrorResume((e) -> userCache.getFromCache(id)); 19 | } 20 | 21 | public Mono findUser3(String id) { 22 | findUser(id).subscribe(); 23 | final Mono user = repository.findUser(id); 24 | return user.onErrorResume((e) -> userCache.getFromCache(id)); 25 | } 26 | 27 | 28 | } 29 | 30 | class User { 31 | } 32 | 33 | interface UserRepository { 34 | Mono findUser(String id); 35 | } 36 | 37 | interface UserCache { 38 | Mono getFromCache(String id); 39 | } -------------------------------------------------------------------------------- /samples/async/simpleConsumer/src/test/java/sample/other/Other.java: -------------------------------------------------------------------------------- 1 | package sample.other; 2 | 3 | import sample.ReactorSamples; 4 | 5 | public class Other { 6 | 7 | public static void main(String[] args) { 8 | new ReactorSamples(null, null).findUser("2"); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'ReactiveArchitectureCommons' 2 | 3 | FileTree buildFiles = fileTree(rootDir) { 4 | List excludes = gradle.startParameter.projectProperties.get("excludeProjects")?.split(",") 5 | include '**/*.gradle' 6 | exclude 'main.gradle', 'build', '**/gradle', '**/settings.gradle', 'buildSrc', '/build.gradle', '.*', 'out' 7 | exclude '**/grails3' 8 | if (excludes) { 9 | exclude excludes 10 | } 11 | } 12 | 13 | String rootDirPath = rootDir.absolutePath + File.separator 14 | buildFiles.each { File buildFile -> 15 | 16 | boolean isDefaultName = 'build.gradle' == buildFile.name 17 | if (isDefaultName) { 18 | String buildFilePath = buildFile.parentFile.absolutePath 19 | String projectPath = buildFilePath.replace(rootDirPath, '').replace(File.separator, ':') 20 | include projectPath 21 | } else { 22 | String projectName = buildFile.name.replace('.gradle', ''); 23 | String projectPath = ':' + projectName; 24 | include projectPath 25 | def project = findProject("${projectPath}") 26 | project.name = projectName 27 | project.projectDir = buildFile.parentFile 28 | project.buildFileName = buildFile.name 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /starters/async-commons-starter/async-commons-starter.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | artifactId = 'async-commons-starter' 3 | artifactDescription = 'Async Commons Starter for Spring Boot' 4 | } 5 | dependencies { 6 | api 'io.projectreactor:reactor-core' 7 | api project(':async-commons') 8 | compileOnly 'org.springframework.boot:spring-boot-starter' 9 | compileOnly 'org.springframework.boot:spring-boot-starter-actuator' 10 | implementation 'org.springframework.boot:spring-boot-starter-aop' 11 | implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' 12 | 13 | annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' 14 | 15 | testImplementation 'io.projectreactor:reactor-test' 16 | testImplementation 'org.springframework.boot:spring-boot-starter-actuator' 17 | } -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/impl/config/annotations/EnableCommandListeners.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.impl.config.annotations; 2 | 3 | import org.reactivecommons.async.starter.listeners.CommandsListenerConfig; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.Import; 6 | 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.ElementType; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | 13 | 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target({ElementType.TYPE}) 16 | @Documented 17 | @Import(CommandsListenerConfig.class) 18 | @Configuration 19 | public @interface EnableCommandListeners { 20 | } 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/impl/config/annotations/EnableDirectAsyncGateway.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.impl.config.annotations; 2 | 3 | import org.reactivecommons.async.starter.senders.DirectAsyncGatewayConfig; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.Import; 6 | 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.ElementType; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | 13 | 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target({ElementType.TYPE}) 16 | @Documented 17 | @Import(DirectAsyncGatewayConfig.class) 18 | @Configuration 19 | public @interface EnableDirectAsyncGateway { 20 | } 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/impl/config/annotations/EnableDomainEventBus.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.impl.config.annotations; 2 | 3 | import org.reactivecommons.async.starter.senders.EventBusConfig; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.Import; 6 | 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.ElementType; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | 13 | 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target({ElementType.TYPE}) 16 | @Documented 17 | @Import(EventBusConfig.class) 18 | @Configuration 19 | public @interface EnableDomainEventBus { 20 | } 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/impl/config/annotations/EnableEventListeners.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.impl.config.annotations; 2 | 3 | import org.reactivecommons.async.starter.listeners.EventsListenerConfig; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.Import; 6 | 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.ElementType; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target({ElementType.TYPE}) 15 | @Documented 16 | @Import(EventsListenerConfig.class) 17 | @Configuration 18 | public @interface EnableEventListeners { 19 | } 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/impl/config/annotations/EnableMessageListeners.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.impl.config.annotations; 2 | 3 | import org.reactivecommons.async.starter.listeners.CommandsListenerConfig; 4 | import org.reactivecommons.async.starter.listeners.EventsListenerConfig; 5 | import org.reactivecommons.async.starter.listeners.NotificationEventsListenerConfig; 6 | import org.reactivecommons.async.starter.listeners.QueriesListenerConfig; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Import; 9 | 10 | import java.lang.annotation.Documented; 11 | import java.lang.annotation.ElementType; 12 | import java.lang.annotation.Retention; 13 | import java.lang.annotation.RetentionPolicy; 14 | import java.lang.annotation.Target; 15 | 16 | /** 17 | * This annotation enables all messages listeners (Query, Commands, Events). If you want to enable separately, please use 18 | * EnableCommandListeners, EnableQueryListeners or EnableEventListeners. 19 | */ 20 | @Retention(RetentionPolicy.RUNTIME) 21 | @Target({ElementType.TYPE}) 22 | @Documented 23 | @Import({CommandsListenerConfig.class, QueriesListenerConfig.class, EventsListenerConfig.class, 24 | NotificationEventsListenerConfig.class}) 25 | @Configuration 26 | public @interface EnableMessageListeners { 27 | } 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/impl/config/annotations/EnableNotificationListener.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.impl.config.annotations; 2 | 3 | import org.reactivecommons.async.starter.listeners.NotificationEventsListenerConfig; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.Import; 6 | 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.ElementType; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | 13 | 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target({ElementType.TYPE}) 16 | @Documented 17 | @Import(NotificationEventsListenerConfig.class) 18 | @Configuration 19 | public @interface EnableNotificationListener { 20 | } 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/impl/config/annotations/EnableQueryListeners.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.impl.config.annotations; 2 | 3 | import org.reactivecommons.async.starter.listeners.QueriesListenerConfig; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.Import; 6 | 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.ElementType; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | 13 | 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target({ElementType.TYPE}) 16 | @Documented 17 | @Import(QueriesListenerConfig.class) 18 | @Configuration 19 | public @interface EnableQueryListeners { 20 | } 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/broker/BrokerProvider.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.broker; 2 | 3 | import org.reactivecommons.api.domain.DomainEventBus; 4 | import org.reactivecommons.async.api.DirectAsyncGateway; 5 | import org.reactivecommons.async.commons.HandlerResolver; 6 | import org.reactivecommons.async.starter.config.health.RCHealth; 7 | import org.reactivecommons.async.starter.props.GenericAsyncProps; 8 | import reactor.core.publisher.Mono; 9 | 10 | @SuppressWarnings("rawtypes") 11 | public interface BrokerProvider { 12 | T getProps(); 13 | 14 | DomainEventBus getDomainBus(); 15 | 16 | DirectAsyncGateway getDirectAsyncGateway(); 17 | 18 | void listenDomainEvents(HandlerResolver resolver); 19 | 20 | void listenNotificationEvents(HandlerResolver resolver); 21 | 22 | void listenCommands(HandlerResolver resolver); 23 | 24 | void listenQueries(HandlerResolver resolver); 25 | 26 | void listenReplies(); 27 | 28 | Mono healthCheck(); 29 | } 30 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/broker/BrokerProviderFactory.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.broker; 2 | 3 | import org.reactivecommons.async.starter.props.GenericAsyncProps; 4 | 5 | @SuppressWarnings("rawtypes") 6 | public interface BrokerProviderFactory { 7 | String getBrokerType(); 8 | 9 | DiscardProvider getDiscardProvider(T props); 10 | 11 | BrokerProvider getProvider(String domain, T props, DiscardProvider discardProvider); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/broker/DiscardProvider.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.broker; 2 | 3 | import org.reactivecommons.async.commons.DiscardNotifier; 4 | 5 | import java.util.function.Supplier; 6 | 7 | public interface DiscardProvider extends Supplier { 8 | } 9 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ConnectionManager.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.config; 2 | 3 | import org.reactivecommons.async.starter.broker.BrokerProvider; 4 | 5 | import java.util.Map; 6 | import java.util.TreeMap; 7 | import java.util.function.BiConsumer; 8 | 9 | @SuppressWarnings("rawtypes") 10 | public class ConnectionManager { 11 | private final Map connections = new TreeMap<>(); 12 | 13 | public void forDomain(BiConsumer consumer) { 14 | connections.forEach(consumer); 15 | } 16 | 17 | public ConnectionManager addDomain(String domain, BrokerProvider domainConn) { 18 | connections.put(domain, domainConn); 19 | return this; 20 | } 21 | 22 | public Map getProviders() { 23 | return connections; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/DomainHandlers.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.config; 2 | 3 | import org.reactivecommons.async.commons.HandlerResolver; 4 | import org.reactivecommons.async.starter.exceptions.InvalidConfigurationException; 5 | 6 | import java.util.Map; 7 | import java.util.TreeMap; 8 | 9 | public class DomainHandlers { 10 | private final Map handlers = new TreeMap<>(); 11 | 12 | public void add(String domain, HandlerResolver resolver) { 13 | this.handlers.put(domain, resolver); 14 | } 15 | 16 | public HandlerResolver get(String domain) { 17 | HandlerResolver handlerResolver = handlers.get(domain); 18 | if (handlerResolver == null) { 19 | throw new InvalidConfigurationException("You are trying to use the domain " + domain 20 | + " but this connection is not defined"); 21 | } 22 | return handlerResolver; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/health/RCHealth.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.config.health; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | @Getter 11 | @Builder 12 | @RequiredArgsConstructor 13 | public class RCHealth { 14 | private final Status status; 15 | private final Map details; 16 | 17 | public enum Status { 18 | UP, 19 | DOWN 20 | } 21 | 22 | public static class RCHealthBuilder { 23 | public RCHealthBuilder() { 24 | this.details = new HashMap<>(); 25 | } 26 | 27 | public RCHealthBuilder up() { 28 | this.status = Status.UP; 29 | return this; 30 | } 31 | 32 | public RCHealthBuilder down() { 33 | this.status = Status.DOWN; 34 | return this; 35 | } 36 | 37 | public RCHealthBuilder withDetail(String key, Object value) { 38 | this.details.put(key, value); 39 | return this; 40 | } 41 | 42 | public RCHealth build() { 43 | return new RCHealth(this.status, this.details); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/health/RCHealthIndicator.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.config.health; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | public abstract class RCHealthIndicator { 6 | 7 | public Mono health() { 8 | return doHealthCheck(RCHealth.builder()) 9 | .onErrorResume(e -> 10 | Mono.just(RCHealth.builder().down().withDetail("error", e.getMessage()).build()) 11 | ); 12 | } 13 | 14 | public abstract Mono doHealthCheck(RCHealth.RCHealthBuilder builder); 15 | } 16 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/health/ReactiveCommonsHealthConfig.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.config.health; 2 | 3 | import org.reactivecommons.async.starter.config.ConnectionManager; 4 | import org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Configuration 11 | @ConditionalOnClass(AbstractReactiveHealthIndicator.class) 12 | public class ReactiveCommonsHealthConfig { 13 | 14 | @Bean 15 | @ConditionalOnProperty(prefix = "management.health.reactive-commons", name = "enabled", havingValue = "true", 16 | matchIfMissing = true) 17 | public ReactiveCommonsHealthIndicator reactiveCommonsHealthIndicator(ConnectionManager manager) { 18 | return new ReactiveCommonsHealthIndicator(manager); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/health/ReactiveCommonsHealthIndicator.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.config.health; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.log4j.Log4j2; 5 | import org.reactivecommons.async.starter.broker.BrokerProvider; 6 | import org.reactivecommons.async.starter.config.ConnectionManager; 7 | import org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator; 8 | import org.springframework.boot.actuate.health.Health; 9 | import reactor.core.publisher.Flux; 10 | import reactor.core.publisher.Mono; 11 | 12 | @Log4j2 13 | @RequiredArgsConstructor 14 | public class ReactiveCommonsHealthIndicator extends AbstractReactiveHealthIndicator { 15 | public static final String DOMAIN = "domain"; 16 | public static final String VERSION = "version"; 17 | private final ConnectionManager manager; 18 | 19 | @Override 20 | @SuppressWarnings("unchecked") 21 | protected Mono doHealthCheck(Health.Builder builder) { 22 | return Flux.fromIterable(manager.getProviders().values()) 23 | .flatMap(BrokerProvider::healthCheck) 24 | .reduceWith(Health::up, (health, status) -> 25 | reduceHealth((Health.Builder) health, (RCHealth) status) 26 | ) 27 | .map(b -> ((Health.Builder) b).build()); 28 | 29 | } 30 | 31 | private Health.Builder reduceHealth(Health.Builder builder, RCHealth status) { 32 | String domain = status.getDetails().get(DOMAIN).toString(); 33 | if (status.getStatus().equals(RCHealth.Status.DOWN)) { 34 | log.error("Broker of domain {} is down", domain); 35 | return builder.down().withDetail(domain, status.getDetails()); 36 | } 37 | return builder.withDetail(domain, status.getDetails()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/exceptions/InvalidConfigurationException.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.exceptions; 2 | 3 | public class InvalidConfigurationException extends RuntimeException { 4 | public InvalidConfigurationException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/listeners/AbstractListenerConfig.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.listeners; 2 | 3 | import org.reactivecommons.async.commons.HandlerResolver; 4 | import org.reactivecommons.async.starter.broker.BrokerProvider; 5 | import org.reactivecommons.async.starter.config.ConnectionManager; 6 | import org.reactivecommons.async.starter.config.DomainHandlers; 7 | 8 | public abstract class AbstractListenerConfig { 9 | 10 | protected AbstractListenerConfig(ConnectionManager manager, DomainHandlers handlers) { 11 | manager.forDomain((domain, provider) -> listen(domain, provider, handlers.get(domain))); 12 | } 13 | 14 | @SuppressWarnings("rawtypes") 15 | abstract void listen(String domain, BrokerProvider provider, HandlerResolver resolver); 16 | } 17 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/listeners/CommandsListenerConfig.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.listeners; 2 | 3 | 4 | import org.reactivecommons.async.commons.HandlerResolver; 5 | import org.reactivecommons.async.starter.broker.BrokerProvider; 6 | import org.reactivecommons.async.starter.config.ConnectionManager; 7 | import org.reactivecommons.async.starter.config.DomainHandlers; 8 | import org.reactivecommons.async.starter.config.ReactiveCommonsConfig; 9 | import org.reactivecommons.async.starter.config.ReactiveCommonsListenersConfig; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.context.annotation.Import; 12 | 13 | @Configuration 14 | @Import({ReactiveCommonsConfig.class, ReactiveCommonsListenersConfig.class}) 15 | public class CommandsListenerConfig extends AbstractListenerConfig { 16 | 17 | public CommandsListenerConfig(ConnectionManager manager, DomainHandlers handlers) { 18 | super(manager, handlers); 19 | } 20 | 21 | @SuppressWarnings("rawtypes") 22 | @Override 23 | void listen(String domain, BrokerProvider provider, HandlerResolver resolver) { 24 | provider.listenCommands(resolver); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/listeners/EventsListenerConfig.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.listeners; 2 | 3 | 4 | import org.reactivecommons.async.commons.HandlerResolver; 5 | import org.reactivecommons.async.starter.broker.BrokerProvider; 6 | import org.reactivecommons.async.starter.config.ConnectionManager; 7 | import org.reactivecommons.async.starter.config.DomainHandlers; 8 | import org.reactivecommons.async.starter.config.ReactiveCommonsConfig; 9 | import org.reactivecommons.async.starter.config.ReactiveCommonsListenersConfig; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.context.annotation.Import; 12 | 13 | @Configuration 14 | @Import({ReactiveCommonsConfig.class, ReactiveCommonsListenersConfig.class}) 15 | public class EventsListenerConfig extends AbstractListenerConfig { 16 | 17 | public EventsListenerConfig(ConnectionManager manager, DomainHandlers handlers) { 18 | super(manager, handlers); 19 | } 20 | 21 | @SuppressWarnings("rawtypes") 22 | @Override 23 | void listen(String domain, BrokerProvider provider, HandlerResolver resolver) { 24 | provider.listenDomainEvents(resolver); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/listeners/NotificationEventsListenerConfig.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.listeners; 2 | 3 | 4 | import org.reactivecommons.async.commons.HandlerResolver; 5 | import org.reactivecommons.async.starter.broker.BrokerProvider; 6 | import org.reactivecommons.async.starter.config.ConnectionManager; 7 | import org.reactivecommons.async.starter.config.DomainHandlers; 8 | import org.reactivecommons.async.starter.config.ReactiveCommonsConfig; 9 | import org.reactivecommons.async.starter.config.ReactiveCommonsListenersConfig; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.context.annotation.Import; 12 | 13 | @Configuration 14 | @Import({ReactiveCommonsConfig.class, ReactiveCommonsListenersConfig.class}) 15 | public class NotificationEventsListenerConfig extends AbstractListenerConfig { 16 | 17 | public NotificationEventsListenerConfig(ConnectionManager manager, DomainHandlers handlers) { 18 | super(manager, handlers); 19 | } 20 | 21 | @SuppressWarnings("rawtypes") 22 | @Override 23 | void listen(String domain, BrokerProvider provider, HandlerResolver resolver) { 24 | provider.listenNotificationEvents(resolver); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/listeners/QueriesListenerConfig.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.listeners; 2 | 3 | 4 | import org.reactivecommons.async.commons.HandlerResolver; 5 | import org.reactivecommons.async.starter.broker.BrokerProvider; 6 | import org.reactivecommons.async.starter.config.ConnectionManager; 7 | import org.reactivecommons.async.starter.config.DomainHandlers; 8 | import org.reactivecommons.async.starter.config.ReactiveCommonsConfig; 9 | import org.reactivecommons.async.starter.config.ReactiveCommonsListenersConfig; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.context.annotation.Import; 12 | 13 | @Configuration 14 | @Import({ReactiveCommonsConfig.class, ReactiveCommonsListenersConfig.class}) 15 | public class QueriesListenerConfig extends AbstractListenerConfig { 16 | 17 | public QueriesListenerConfig(ConnectionManager manager, DomainHandlers handlers) { 18 | super(manager, handlers); 19 | } 20 | 21 | @SuppressWarnings("rawtypes") 22 | @Override 23 | void listen(String domain, BrokerProvider provider, HandlerResolver resolver) { 24 | provider.listenQueries(resolver); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/props/GenericAsyncProps.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.props; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import lombok.experimental.SuperBuilder; 8 | 9 | @Getter 10 | @Setter 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | @SuperBuilder 14 | public abstract class GenericAsyncProps

{ 15 | private String appName; 16 | private String secret; 17 | 18 | public abstract void setConnectionProperties(P properties); 19 | 20 | public abstract P getConnectionProperties(); 21 | 22 | public abstract String getBrokerType(); 23 | 24 | public abstract boolean isEnabled(); 25 | 26 | public abstract void setUseDiscardNotifierPerDomain(boolean enabled); 27 | } -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/props/GenericAsyncPropsDomainProperties.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.props; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import lombok.SneakyThrows; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | @Getter 11 | @Setter 12 | public class GenericAsyncPropsDomainProperties, P> extends HashMap { 13 | 14 | public GenericAsyncPropsDomainProperties(Map m) { 15 | super(m); 16 | } 17 | 18 | public GenericAsyncPropsDomainProperties() { 19 | } 20 | 21 | public static , 22 | P, 23 | X extends GenericAsyncPropsDomainProperties> AsyncPropsDomainPropertiesBuilder 24 | builder(Class returnType) { 25 | return new AsyncPropsDomainPropertiesBuilder<>(returnType); 26 | } 27 | 28 | public static class AsyncPropsDomainPropertiesBuilder, P, 29 | X extends GenericAsyncPropsDomainProperties> { 30 | private final Map domains = new HashMap<>(); 31 | private final Class returnType; 32 | 33 | public AsyncPropsDomainPropertiesBuilder(Class returnType) { 34 | this.returnType = returnType; 35 | } 36 | 37 | public AsyncPropsDomainPropertiesBuilder withDomain(String domain, T props) { 38 | domains.put(domain, props); 39 | return this; 40 | } 41 | 42 | @SneakyThrows 43 | public X build() { 44 | return returnType.getDeclaredConstructor(Map.class).newInstance(domains); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/senders/DirectAsyncGatewayConfig.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.senders; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.java.Log; 5 | import org.reactivecommons.async.api.DirectAsyncGateway; 6 | import org.reactivecommons.async.starter.config.ConnectionManager; 7 | import org.reactivecommons.async.starter.config.ReactiveCommonsConfig; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.context.annotation.Import; 11 | 12 | import java.util.concurrent.ConcurrentHashMap; 13 | import java.util.concurrent.ConcurrentMap; 14 | 15 | @Log 16 | @Configuration 17 | @RequiredArgsConstructor 18 | @Import(ReactiveCommonsConfig.class) 19 | public class DirectAsyncGatewayConfig { 20 | 21 | @Bean 22 | public DirectAsyncGateway genericDirectAsyncGateway(ConnectionManager manager) { 23 | ConcurrentMap directAsyncGateways = new ConcurrentHashMap<>(); 24 | manager.forDomain((domain, provider) -> directAsyncGateways.put(domain, 25 | provider.getDirectAsyncGateway())); 26 | return new GenericDirectAsyncGateway(directAsyncGateways); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/senders/EventBusConfig.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.senders; 2 | 3 | import org.reactivecommons.api.domain.DomainEventBus; 4 | import org.reactivecommons.async.starter.config.ConnectionManager; 5 | import org.reactivecommons.async.starter.config.ReactiveCommonsConfig; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Import; 9 | 10 | import java.util.concurrent.ConcurrentHashMap; 11 | import java.util.concurrent.ConcurrentMap; 12 | 13 | @Configuration 14 | @Import(ReactiveCommonsConfig.class) 15 | public class EventBusConfig { 16 | 17 | @Bean 18 | public DomainEventBus genericDomainEventBus(ConnectionManager manager) { 19 | ConcurrentMap domainEventBuses = new ConcurrentHashMap<>(); 20 | manager.forDomain((domain, provider) -> domainEventBuses.put(domain, provider.getDomainBus())); 21 | return new GenericDomainEventBus(domainEventBuses); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/config/ReactiveCommonsConfigTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.config; 2 | 3 | 4 | import org.junit.jupiter.api.Test; 5 | import org.mockito.Spy; 6 | import org.reactivecommons.async.starter.impl.mybroker.MyBrokerConfig; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.context.ApplicationContext; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertNotNull; 12 | 13 | @SpringBootTest(classes = { 14 | MyBrokerConfig.class, 15 | ReactiveCommonsConfig.class 16 | }) 17 | class ReactiveCommonsConfigTest { 18 | @Spy 19 | @Autowired 20 | private ApplicationContext context; 21 | 22 | @Test 23 | void shouldCreateConnectionManager() { 24 | // Arrange 25 | // Act 26 | ConnectionManager manager = context.getBean(ConnectionManager.class); 27 | // Assert 28 | assertNotNull(manager); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/impl/mybroker/MyBrokerConfig.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.impl.mybroker; 2 | 3 | import org.reactivecommons.async.starter.mybroker.MyBrokerProviderFactory; 4 | import org.reactivecommons.async.starter.mybroker.MyBrokerSecretFiller; 5 | import org.reactivecommons.async.starter.mybroker.props.AsyncMyBrokerPropsDomainProperties; 6 | import org.reactivecommons.async.starter.mybroker.props.MyBrokerAsyncPropsDomain; 7 | import org.reactivecommons.async.starter.mybroker.props.MyBrokerConnProps; 8 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Import; 11 | 12 | @EnableConfigurationProperties(AsyncMyBrokerPropsDomainProperties.class) 13 | @Import({MyBrokerAsyncPropsDomain.class, MyBrokerProviderFactory.class}) 14 | public class MyBrokerConfig { 15 | 16 | @Bean 17 | public MyBrokerConnProps defaultMyBrokerConnProps() { 18 | MyBrokerConnProps myBrokerConnProps = new MyBrokerConnProps(); 19 | myBrokerConnProps.setHost("localhost"); 20 | myBrokerConnProps.setPort("1234"); 21 | return myBrokerConnProps; 22 | } 23 | 24 | @Bean 25 | public MyBrokerSecretFiller defaultMyBrokerSecretFiller() { 26 | return (domain, props) -> { 27 | }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/listeners/CommandsListenerConfigTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.listeners; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | import org.mockito.Mock; 7 | import org.mockito.junit.jupiter.MockitoExtension; 8 | import org.reactivecommons.async.commons.HandlerResolver; 9 | import org.reactivecommons.async.starter.broker.BrokerProvider; 10 | import org.reactivecommons.async.starter.config.ConnectionManager; 11 | import org.reactivecommons.async.starter.config.DomainHandlers; 12 | 13 | import static org.mockito.Mockito.verify; 14 | import static org.reactivecommons.async.api.HandlerRegistry.DEFAULT_DOMAIN; 15 | 16 | @ExtendWith(MockitoExtension.class) 17 | class CommandsListenerConfigTest { 18 | @Mock 19 | private BrokerProvider provider; 20 | @Mock 21 | private HandlerResolver resolver; 22 | 23 | @BeforeEach 24 | void setUp() { 25 | ConnectionManager manager = new ConnectionManager(); 26 | manager.addDomain(DEFAULT_DOMAIN, provider); 27 | DomainHandlers handlers = new DomainHandlers(); 28 | handlers.add(DEFAULT_DOMAIN, resolver); 29 | new CommandsListenerConfig(manager, handlers); 30 | } 31 | 32 | @Test 33 | void shouldListen() { 34 | // Arrange 35 | // Act 36 | // Assert 37 | verify(provider).listenCommands(resolver); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/listeners/EventsListenerConfigTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.listeners; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | import org.mockito.Mock; 7 | import org.mockito.junit.jupiter.MockitoExtension; 8 | import org.reactivecommons.async.commons.HandlerResolver; 9 | import org.reactivecommons.async.starter.broker.BrokerProvider; 10 | import org.reactivecommons.async.starter.config.ConnectionManager; 11 | import org.reactivecommons.async.starter.config.DomainHandlers; 12 | 13 | import static org.mockito.Mockito.verify; 14 | import static org.reactivecommons.async.api.HandlerRegistry.DEFAULT_DOMAIN; 15 | 16 | @ExtendWith(MockitoExtension.class) 17 | class EventsListenerConfigTest { 18 | @Mock 19 | private BrokerProvider provider; 20 | @Mock 21 | private HandlerResolver resolver; 22 | 23 | @BeforeEach 24 | void setUp() { 25 | ConnectionManager manager = new ConnectionManager(); 26 | manager.addDomain(DEFAULT_DOMAIN, provider); 27 | DomainHandlers handlers = new DomainHandlers(); 28 | handlers.add(DEFAULT_DOMAIN, resolver); 29 | new EventsListenerConfig(manager, handlers); 30 | } 31 | 32 | @Test 33 | void shouldListen() { 34 | // Arrange 35 | // Act 36 | // Assert 37 | verify(provider).listenDomainEvents(resolver); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/listeners/NotificationEventsListenerConfigTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.listeners; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | import org.mockito.Mock; 7 | import org.mockito.junit.jupiter.MockitoExtension; 8 | import org.reactivecommons.async.commons.HandlerResolver; 9 | import org.reactivecommons.async.starter.broker.BrokerProvider; 10 | import org.reactivecommons.async.starter.config.ConnectionManager; 11 | import org.reactivecommons.async.starter.config.DomainHandlers; 12 | 13 | import static org.mockito.Mockito.verify; 14 | import static org.reactivecommons.async.api.HandlerRegistry.DEFAULT_DOMAIN; 15 | 16 | @ExtendWith(MockitoExtension.class) 17 | class NotificationEventsListenerConfigTest { 18 | @Mock 19 | private BrokerProvider provider; 20 | @Mock 21 | private HandlerResolver resolver; 22 | 23 | @BeforeEach 24 | void setUp() { 25 | ConnectionManager manager = new ConnectionManager(); 26 | manager.addDomain(DEFAULT_DOMAIN, provider); 27 | DomainHandlers handlers = new DomainHandlers(); 28 | handlers.add(DEFAULT_DOMAIN, resolver); 29 | new NotificationEventsListenerConfig(manager, handlers); 30 | } 31 | 32 | @Test 33 | void shouldListen() { 34 | // Arrange 35 | // Act 36 | // Assert 37 | verify(provider).listenNotificationEvents(resolver); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/listeners/QueriesListenerConfigTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.listeners; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | import org.mockito.Mock; 7 | import org.mockito.junit.jupiter.MockitoExtension; 8 | import org.reactivecommons.async.commons.HandlerResolver; 9 | import org.reactivecommons.async.starter.broker.BrokerProvider; 10 | import org.reactivecommons.async.starter.config.ConnectionManager; 11 | import org.reactivecommons.async.starter.config.DomainHandlers; 12 | 13 | import static org.mockito.Mockito.verify; 14 | import static org.reactivecommons.async.api.HandlerRegistry.DEFAULT_DOMAIN; 15 | 16 | @ExtendWith(MockitoExtension.class) 17 | class QueriesListenerConfigTest { 18 | @Mock 19 | private BrokerProvider provider; 20 | @Mock 21 | private HandlerResolver resolver; 22 | 23 | @BeforeEach 24 | void setUp() { 25 | ConnectionManager manager = new ConnectionManager(); 26 | manager.addDomain(DEFAULT_DOMAIN, provider); 27 | DomainHandlers handlers = new DomainHandlers(); 28 | handlers.add(DEFAULT_DOMAIN, resolver); 29 | new QueriesListenerConfig(manager, handlers); 30 | } 31 | 32 | @Test 33 | void shouldListen() { 34 | // Arrange 35 | // Act 36 | // Assert 37 | verify(provider).listenQueries(resolver); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/mybroker/MyBrokerProvider.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.mybroker; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.reactivecommons.api.domain.DomainEventBus; 5 | import org.reactivecommons.async.api.DirectAsyncGateway; 6 | import org.reactivecommons.async.commons.HandlerResolver; 7 | import org.reactivecommons.async.starter.broker.BrokerProvider; 8 | import org.reactivecommons.async.starter.broker.DiscardProvider; 9 | import org.reactivecommons.async.starter.config.health.RCHealth; 10 | import org.reactivecommons.async.starter.mybroker.props.MyBrokerAsyncProps; 11 | import reactor.core.publisher.Mono; 12 | 13 | @RequiredArgsConstructor 14 | public class MyBrokerProvider implements BrokerProvider { 15 | private final String domain; 16 | private final MyBrokerAsyncProps props; 17 | private final DiscardProvider discardProvider; 18 | 19 | @Override 20 | public MyBrokerAsyncProps getProps() { 21 | return null; 22 | } 23 | 24 | @Override 25 | public DomainEventBus getDomainBus() { 26 | return null; 27 | } 28 | 29 | @Override 30 | public DirectAsyncGateway getDirectAsyncGateway() { 31 | return null; 32 | } 33 | 34 | @Override 35 | public void listenDomainEvents(HandlerResolver resolver) { 36 | // for testing purposes 37 | } 38 | 39 | @Override 40 | public void listenNotificationEvents(HandlerResolver resolver) { 41 | // for testing purposes 42 | } 43 | 44 | @Override 45 | public void listenCommands(HandlerResolver resolver) { 46 | // for testing purposes 47 | } 48 | 49 | @Override 50 | public void listenQueries(HandlerResolver resolver) { 51 | // for testing purposes 52 | } 53 | 54 | @Override 55 | public void listenReplies() { 56 | // for testing purposes 57 | } 58 | 59 | @Override 60 | public Mono healthCheck() { 61 | return null; 62 | } 63 | } -------------------------------------------------------------------------------- /starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/mybroker/MyBrokerProviderFactory.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.mybroker; 2 | 3 | import org.reactivecommons.async.starter.broker.BrokerProvider; 4 | import org.reactivecommons.async.starter.broker.BrokerProviderFactory; 5 | import org.reactivecommons.async.starter.broker.DiscardProvider; 6 | import org.reactivecommons.async.starter.mybroker.props.MyBrokerAsyncProps; 7 | import org.springframework.stereotype.Service; 8 | import reactor.core.publisher.Mono; 9 | 10 | @Service("mybroker") 11 | public class MyBrokerProviderFactory implements BrokerProviderFactory { 12 | 13 | @Override 14 | public String getBrokerType() { 15 | return "mybroker"; 16 | } 17 | 18 | @Override 19 | public DiscardProvider getDiscardProvider(MyBrokerAsyncProps props) { 20 | return () -> message -> Mono.empty(); 21 | } 22 | 23 | @Override 24 | public BrokerProvider getProvider(String domain, MyBrokerAsyncProps props, DiscardProvider discardProvider) { 25 | return new MyBrokerProvider(domain, props, discardProvider); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/mybroker/MyBrokerSecretFiller.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.mybroker; 2 | 3 | import org.reactivecommons.async.starter.mybroker.props.MyBrokerConnProps; 4 | import org.reactivecommons.async.starter.props.GenericAsyncPropsDomain; 5 | 6 | public interface MyBrokerSecretFiller extends GenericAsyncPropsDomain.SecretFiller { 7 | } -------------------------------------------------------------------------------- /starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/mybroker/props/AsyncMyBrokerPropsDomainProperties.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.mybroker.props; 2 | 3 | import org.reactivecommons.async.starter.props.GenericAsyncPropsDomainProperties; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | import java.util.Map; 7 | 8 | @ConfigurationProperties(prefix = "my.broker") 9 | public class AsyncMyBrokerPropsDomainProperties 10 | extends GenericAsyncPropsDomainProperties { 11 | 12 | public AsyncMyBrokerPropsDomainProperties() { 13 | } 14 | 15 | public AsyncMyBrokerPropsDomainProperties(Map m) { 16 | super(m); 17 | } 18 | 19 | public static AsyncPropsDomainPropertiesBuilder builder() { 21 | return GenericAsyncPropsDomainProperties.builder(AsyncMyBrokerPropsDomainProperties.class); 22 | } 23 | } -------------------------------------------------------------------------------- /starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/mybroker/props/MyBrokerAsyncProps.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.mybroker.props; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import lombok.experimental.SuperBuilder; 9 | import org.reactivecommons.async.starter.props.GenericAsyncProps; 10 | 11 | @Getter 12 | @Setter 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | @SuperBuilder 16 | public class MyBrokerAsyncProps extends GenericAsyncProps { 17 | private MyBrokerConnProps connectionProperties; 18 | @Builder.Default 19 | private String brokerType = "mybroker"; 20 | private boolean enabled; 21 | private boolean useDiscardNotifierPerDomain; 22 | } -------------------------------------------------------------------------------- /starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/mybroker/props/MyBrokerAsyncPropsDomain.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.mybroker.props; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import org.reactivecommons.async.starter.mybroker.MyBrokerSecretFiller; 6 | import org.reactivecommons.async.starter.props.GenericAsyncPropsDomain; 7 | import org.springframework.beans.factory.annotation.Value; 8 | 9 | @Getter 10 | @Setter 11 | public class MyBrokerAsyncPropsDomain extends GenericAsyncPropsDomain { 12 | 13 | public MyBrokerAsyncPropsDomain(@Value("${spring.application.name}") String defaultAppName, 14 | MyBrokerConnProps defaultRabbitProperties, 15 | AsyncMyBrokerPropsDomainProperties configured, 16 | MyBrokerSecretFiller secretFiller) { 17 | super(defaultAppName, defaultRabbitProperties, configured, secretFiller, MyBrokerAsyncProps.class, 18 | MyBrokerConnProps.class); 19 | } 20 | } -------------------------------------------------------------------------------- /starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/mybroker/props/MyBrokerConnProps.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.mybroker.props; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class MyBrokerConnProps { 7 | private String host; 8 | private String port; 9 | } -------------------------------------------------------------------------------- /starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/senders/DirectAsyncGatewayConfigTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.senders; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | import org.mockito.Mock; 7 | import org.mockito.junit.jupiter.MockitoExtension; 8 | import org.reactivecommons.async.api.DirectAsyncGateway; 9 | import org.reactivecommons.async.commons.HandlerResolver; 10 | import org.reactivecommons.async.starter.broker.BrokerProvider; 11 | import org.reactivecommons.async.starter.config.ConnectionManager; 12 | 13 | import static org.junit.jupiter.api.Assertions.assertNotNull; 14 | import static org.mockito.Mockito.times; 15 | import static org.mockito.Mockito.verify; 16 | import static org.mockito.Mockito.when; 17 | 18 | @ExtendWith(MockitoExtension.class) 19 | class DirectAsyncGatewayConfigTest { 20 | @Mock 21 | private DirectAsyncGateway domainEventBus; 22 | @Mock 23 | private BrokerProvider brokerProvider; 24 | @Mock 25 | private HandlerResolver resolver; 26 | 27 | private ConnectionManager manager; 28 | private DirectAsyncGatewayConfig directAsyncGatewayConfig; 29 | 30 | @BeforeEach 31 | void setUp() { 32 | directAsyncGatewayConfig = new DirectAsyncGatewayConfig(); 33 | manager = new ConnectionManager(); 34 | manager.addDomain("domain", brokerProvider); 35 | manager.addDomain("domain2", brokerProvider); 36 | } 37 | 38 | @Test 39 | void shouldCreateAllDomainEventBuses() { 40 | // Arrange 41 | when(brokerProvider.getDirectAsyncGateway()).thenReturn(domainEventBus); 42 | // Act 43 | DirectAsyncGateway genericDomainEventBus = directAsyncGatewayConfig.genericDirectAsyncGateway(manager); 44 | // Assert 45 | assertNotNull(genericDomainEventBus); 46 | verify(brokerProvider, times(2)).getDirectAsyncGateway(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/senders/EventBusConfigTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.senders; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | import org.mockito.Mock; 7 | import org.mockito.junit.jupiter.MockitoExtension; 8 | import org.reactivecommons.api.domain.DomainEventBus; 9 | import org.reactivecommons.async.starter.broker.BrokerProvider; 10 | import org.reactivecommons.async.starter.config.ConnectionManager; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertNotNull; 13 | import static org.mockito.Mockito.times; 14 | import static org.mockito.Mockito.verify; 15 | import static org.mockito.Mockito.when; 16 | 17 | @ExtendWith(MockitoExtension.class) 18 | class EventBusConfigTest { 19 | @Mock 20 | private DomainEventBus domainEventBus; 21 | @Mock 22 | private BrokerProvider brokerProvider; 23 | 24 | private ConnectionManager manager; 25 | private EventBusConfig eventBusConfig; 26 | 27 | @BeforeEach 28 | void setUp() { 29 | eventBusConfig = new EventBusConfig(); 30 | manager = new ConnectionManager(); 31 | manager.addDomain("domain", brokerProvider); 32 | manager.addDomain("domain2", brokerProvider); 33 | } 34 | 35 | @Test 36 | void shouldCreateAllDomainEventBuses() { 37 | // Arrange 38 | when(brokerProvider.getDomainBus()).thenReturn(domainEventBus); 39 | // Act 40 | DomainEventBus genericDomainEventBus = eventBusConfig.genericDomainEventBus(manager); 41 | // Assert 42 | assertNotNull(genericDomainEventBus); 43 | verify(brokerProvider, times(2)).getDomainBus(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /starters/async-commons-starter/src/test/resources/application.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: test-app 4 | my: 5 | broker: 6 | app: 7 | enabled: true -------------------------------------------------------------------------------- /starters/async-kafka-starter/async-kafka-starter.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | artifactId = 'async-commons-kafka-starter' 3 | artifactDescription = 'Async Commons Starter' 4 | } 5 | 6 | dependencies { 7 | api project(':async-kafka') 8 | api project(':async-commons-starter') 9 | implementation 'org.apache.kafka:kafka-clients' 10 | compileOnly 'org.springframework.boot:spring-boot-starter' 11 | compileOnly 'org.springframework.boot:spring-boot-starter-actuator' 12 | implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' 13 | 14 | annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' 15 | 16 | testImplementation 'io.projectreactor:reactor-test' 17 | testImplementation 'org.springframework.boot:spring-boot-starter-actuator' 18 | } -------------------------------------------------------------------------------- /starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/KafkaDiscardProvider.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.kafka; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.reactivecommons.async.commons.DiscardNotifier; 5 | import org.reactivecommons.async.commons.converters.MessageConverter; 6 | import org.reactivecommons.async.kafka.communications.ReactiveMessageSender; 7 | import org.reactivecommons.async.kafka.communications.topology.KafkaCustomizations; 8 | import org.reactivecommons.async.kafka.communications.topology.TopologyCreator; 9 | import org.reactivecommons.async.kafka.config.props.AsyncKafkaProps; 10 | import org.reactivecommons.async.starter.broker.DiscardProvider; 11 | import org.springframework.boot.ssl.SslBundles; 12 | 13 | import java.util.Map; 14 | import java.util.concurrent.ConcurrentHashMap; 15 | 16 | @RequiredArgsConstructor 17 | public class KafkaDiscardProvider implements DiscardProvider { 18 | private final AsyncKafkaProps props; 19 | private final MessageConverter converter; 20 | private final KafkaCustomizations customizations; 21 | private final SslBundles sslBundles; 22 | private final Map discardNotifier = new ConcurrentHashMap<>(); 23 | 24 | @Override 25 | public DiscardNotifier get() { 26 | return discardNotifier.computeIfAbsent(true, this::buildDiscardNotifier); 27 | } 28 | 29 | private DiscardNotifier buildDiscardNotifier(boolean ignored) { 30 | TopologyCreator creator = KafkaSetupUtils.createTopologyCreator(props, customizations, sslBundles); 31 | ReactiveMessageSender sender = KafkaSetupUtils.createMessageSender(props, converter, creator, sslBundles); 32 | return KafkaSetupUtils.createDiscardNotifier(sender, converter); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/KafkaProperties.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.kafka.config; 2 | 3 | 4 | import org.reactivecommons.async.kafka.config.spring.KafkaPropertiesBase; 5 | 6 | public class KafkaProperties extends KafkaPropertiesBase { 7 | 8 | } -------------------------------------------------------------------------------- /starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/KafkaPropertiesAutoConfig.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.kafka.config; 2 | 3 | 4 | import org.reactivecommons.async.kafka.config.spring.KafkaPropertiesBase; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | 7 | @ConfigurationProperties(prefix = "spring.kafka") 8 | public class KafkaPropertiesAutoConfig extends KafkaPropertiesBase { 9 | public KafkaPropertiesAutoConfig() { 10 | // put("bootstrap.servers", "localhost:9092"); 11 | } 12 | } -------------------------------------------------------------------------------- /starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaProps.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.kafka.config.props; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import lombok.experimental.SuperBuilder; 9 | import org.reactivecommons.async.kafka.config.KafkaProperties; 10 | import org.reactivecommons.async.starter.props.GenericAsyncProps; 11 | import org.springframework.boot.context.properties.NestedConfigurationProperty; 12 | 13 | 14 | @Getter 15 | @Setter 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | @SuperBuilder 19 | public class AsyncKafkaProps extends GenericAsyncProps { 20 | 21 | @NestedConfigurationProperty 22 | @Builder.Default 23 | private KafkaProperties connectionProperties = new KafkaProperties(); 24 | 25 | @NestedConfigurationProperty 26 | @Builder.Default 27 | private DomainProps domain = new DomainProps(); 28 | 29 | /** 30 | * -1 will be considered default value. 31 | * When withDLQRetry is true, it will be retried 10 times. 32 | * When withDLQRetry is false, it will be retried indefinitely. 33 | */ 34 | @Builder.Default 35 | private Integer maxRetries = -1; 36 | 37 | @Builder.Default 38 | private Integer retryDelay = 1000; 39 | 40 | @Builder.Default 41 | private Boolean withDLQRetry = false; 42 | @Builder.Default 43 | private Boolean createTopology = true; 44 | @Builder.Default 45 | private Boolean checkExistingTopics = true; 46 | 47 | @Builder.Default 48 | private boolean useDiscardNotifierPerDomain = false; 49 | 50 | @Builder.Default 51 | private boolean enabled = true; 52 | 53 | @Builder.Default 54 | private String brokerType = "kafka"; 55 | } 56 | -------------------------------------------------------------------------------- /starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsDomain.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.kafka.config.props; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import org.reactivecommons.async.kafka.config.KafkaProperties; 6 | import org.reactivecommons.async.starter.props.GenericAsyncPropsDomain; 7 | import org.springframework.beans.factory.annotation.Value; 8 | 9 | import java.lang.reflect.Constructor; 10 | 11 | @Getter 12 | @Setter 13 | public class AsyncKafkaPropsDomain extends GenericAsyncPropsDomain { 14 | 15 | public AsyncKafkaPropsDomain(@Value("${spring.application.name}") String defaultAppName, 16 | KafkaProperties defaultKafkaProperties, 17 | AsyncKafkaPropsDomainProperties configured, 18 | KafkaSecretFiller kafkaSecretFiller) { 19 | super(defaultAppName, defaultKafkaProperties, configured, kafkaSecretFiller, AsyncKafkaProps.class, 20 | KafkaProperties.class); 21 | } 22 | 23 | @SuppressWarnings("unchecked") 24 | public static AsyncPropsDomainBuilder builder() { 26 | return GenericAsyncPropsDomain.builder(KafkaProperties.class, 27 | AsyncKafkaPropsDomainProperties.class, 28 | (Constructor) AsyncKafkaPropsDomain.class.getDeclaredConstructors()[0]); 29 | } 30 | 31 | public interface KafkaSecretFiller extends GenericAsyncPropsDomain.SecretFiller { 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsDomainProperties.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.kafka.config.props; 2 | 3 | import org.reactivecommons.async.kafka.config.KafkaProperties; 4 | import org.reactivecommons.async.starter.props.GenericAsyncPropsDomainProperties; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | 7 | import java.util.Map; 8 | 9 | @ConfigurationProperties(prefix = "reactive.commons.kafka") 10 | public class AsyncKafkaPropsDomainProperties extends GenericAsyncPropsDomainProperties { 11 | 12 | public AsyncKafkaPropsDomainProperties(Map m) { 13 | super(m); 14 | } 15 | 16 | public AsyncKafkaPropsDomainProperties() { 17 | } 18 | 19 | public static AsyncPropsDomainPropertiesBuilder builder() { 21 | return GenericAsyncPropsDomainProperties.builder(AsyncKafkaPropsDomainProperties.class); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/DomainProps.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.kafka.config.props; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | @Getter 10 | @Setter 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | @Builder 14 | public class DomainProps { 15 | // 16 | // @NestedConfigurationProperty 17 | // @Builder.Default 18 | // private EventsProps events = new EventsProps(); 19 | @Builder.Default 20 | private boolean ignoreThisListener = false; 21 | } 22 | -------------------------------------------------------------------------------- /starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/health/KafkaReactiveHealthIndicator.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.kafka.health; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.log4j.Log4j2; 5 | import org.apache.kafka.clients.admin.AdminClient; 6 | import org.reactivecommons.async.starter.config.health.RCHealth; 7 | import org.reactivecommons.async.starter.config.health.RCHealthIndicator; 8 | import reactor.core.publisher.Mono; 9 | 10 | import static org.reactivecommons.async.starter.config.health.ReactiveCommonsHealthIndicator.DOMAIN; 11 | import static org.reactivecommons.async.starter.config.health.ReactiveCommonsHealthIndicator.VERSION; 12 | 13 | @Log4j2 14 | @RequiredArgsConstructor 15 | public class KafkaReactiveHealthIndicator extends RCHealthIndicator { 16 | private final String domain; 17 | private final AdminClient adminClient; 18 | 19 | @Override 20 | public Mono doHealthCheck(RCHealth.RCHealthBuilder builder) { 21 | builder.withDetail(DOMAIN, domain); 22 | return checkKafkaHealth() 23 | .map(clusterId -> builder.up().withDetail(VERSION, clusterId).build()) 24 | .onErrorReturn(builder.down().build()); 25 | } 26 | 27 | private Mono checkKafkaHealth() { 28 | return Mono.fromFuture(adminClient.describeCluster().clusterId() 29 | .toCompletionStage() 30 | .toCompletableFuture()) 31 | .doOnError(e -> log.error("Error checking Kafka health in domain {}", domain, e)); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/KafkaDiscardProviderTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.kafka; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.junit.jupiter.api.extension.ExtendWith; 5 | import org.mockito.Mock; 6 | import org.mockito.junit.jupiter.MockitoExtension; 7 | import org.reactivecommons.async.commons.DLQDiscardNotifier; 8 | import org.reactivecommons.async.commons.DiscardNotifier; 9 | import org.reactivecommons.async.kafka.communications.topology.KafkaCustomizations; 10 | import org.reactivecommons.async.kafka.config.KafkaProperties; 11 | import org.reactivecommons.async.kafka.config.props.AsyncKafkaProps; 12 | import org.reactivecommons.async.kafka.converters.json.KafkaJacksonMessageConverter; 13 | import org.springframework.boot.ssl.SslBundles; 14 | 15 | import static org.assertj.core.api.Assertions.assertThat; 16 | 17 | @ExtendWith(MockitoExtension.class) 18 | class KafkaDiscardProviderTest { 19 | @Mock 20 | private KafkaJacksonMessageConverter converter; 21 | @Mock 22 | private KafkaCustomizations customizations; 23 | @Mock 24 | private SslBundles sslBundles; 25 | 26 | @Test 27 | void shouldCreateDiscardNotifier() { 28 | // Arrange 29 | AsyncKafkaProps props = new AsyncKafkaProps(); 30 | props.setCheckExistingTopics(false); 31 | props.setConnectionProperties(new KafkaProperties()); 32 | KafkaDiscardProvider discardProvider = new KafkaDiscardProvider(props, converter, customizations, sslBundles); 33 | // Act 34 | DiscardNotifier notifier = discardProvider.get(); 35 | // Assert 36 | assertThat(notifier).isExactlyInstanceOf(DLQDiscardNotifier.class); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /starters/async-kafka-starter/src/test/java/org/reactivecommons/async/starter/impl/common/kafka/KafkaConfigTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.impl.common.kafka; 2 | 3 | 4 | import org.junit.jupiter.api.Test; 5 | import org.reactivecommons.async.kafka.KafkaBrokerProviderFactory; 6 | import org.reactivecommons.async.kafka.config.props.AsyncKafkaPropsDomain; 7 | import org.reactivecommons.async.kafka.converters.json.KafkaJacksonMessageConverter; 8 | import org.reactivecommons.async.starter.config.ConnectionManager; 9 | import org.reactivecommons.async.starter.config.ReactiveCommonsConfig; 10 | import org.reactivecommons.async.starter.config.ReactiveCommonsListenersConfig; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | 14 | import static org.assertj.core.api.Assertions.assertThat; 15 | 16 | @SpringBootTest(classes = { 17 | RCKafkaConfig.class, 18 | AsyncKafkaPropsDomain.class, 19 | KafkaBrokerProviderFactory.class, 20 | ReactiveCommonsConfig.class, 21 | ReactiveCommonsListenersConfig.class 22 | }) 23 | class KafkaConfigTest { 24 | @Autowired 25 | private KafkaJacksonMessageConverter converter; 26 | @Autowired 27 | private ConnectionManager manager; 28 | 29 | @Test 30 | void shouldHasConverter() { 31 | // Arrange 32 | // Act 33 | // Assert 34 | assertThat(converter).isNotNull(); 35 | } 36 | 37 | @Test 38 | void shouldHasManager() { 39 | // Arrange 40 | // Act 41 | // Assert 42 | assertThat(manager).isNotNull(); 43 | assertThat(manager.getProviders()).isNotEmpty(); 44 | assertThat(manager.getProviders().get("app").getProps().getAppName()).isEqualTo("async-kafka-starter"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /starters/async-kafka-starter/src/test/resources/application.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: async-kafka-starter 4 | reactive: 5 | commons: 6 | kafka: 7 | app: 8 | checkExistingTopics: false -------------------------------------------------------------------------------- /starters/async-rabbit-standalone/async-commons-rabbit-standalone.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | artifactId = 'async-commons-rabbit-standalone' 3 | artifactDescription = 'Async Commons Standalone Config' 4 | } 5 | 6 | dependencies { 7 | api project(':async-rabbit') 8 | } -------------------------------------------------------------------------------- /starters/async-rabbit-standalone/src/main/java/org/reactivecommons/async/rabbit/standalone/config/EventBusConfig.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.standalone.config; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.reactivecommons.api.domain.DomainEventBus; 5 | import org.reactivecommons.async.rabbit.RabbitDomainEventBus; 6 | import org.reactivecommons.async.rabbit.communications.ReactiveMessageSender; 7 | 8 | import static reactor.rabbitmq.ExchangeSpecification.exchange; 9 | 10 | 11 | @RequiredArgsConstructor 12 | public class EventBusConfig { 13 | 14 | private final String domainEventsExchangeName; 15 | 16 | public DomainEventBus domainEventBus(ReactiveMessageSender sender) { 17 | sender.getTopologyCreator().declare(exchange(domainEventsExchangeName).durable(true).type("topic")).subscribe(); 18 | return new RabbitDomainEventBus(sender, domainEventsExchangeName); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /starters/async-rabbit-standalone/src/main/java/org/reactivecommons/async/rabbit/standalone/config/RabbitProperties.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.standalone.config; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class RabbitProperties { 7 | private String host = "localhost"; 8 | private int port = 5672; 9 | private String username = "guest"; 10 | private String password = "guest"; //NOSONAR 11 | private String virtualHost; 12 | private Integer channelPoolMaxCacheSize; 13 | } 14 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/async-commons-rabbit-starter.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | artifactId = 'async-commons-rabbit-starter' 3 | artifactDescription = 'Async Commons Starter' 4 | } 5 | 6 | dependencies { 7 | api project(':async-rabbit') 8 | api project(':async-commons-starter') 9 | compileOnly 'org.springframework.boot:spring-boot-starter' 10 | compileOnly 'org.springframework.boot:spring-boot-starter-actuator' 11 | implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' 12 | 13 | annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' 14 | 15 | testImplementation 'io.projectreactor:reactor-test' 16 | testImplementation 'org.springframework.boot:spring-boot-starter-actuator' 17 | } -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/RabbitMQDiscardProvider.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.reactivecommons.async.commons.DiscardNotifier; 5 | import org.reactivecommons.async.commons.config.BrokerConfig; 6 | import org.reactivecommons.async.commons.converters.MessageConverter; 7 | import org.reactivecommons.async.rabbit.communications.ReactiveMessageSender; 8 | import org.reactivecommons.async.rabbit.config.ConnectionFactoryProvider; 9 | import org.reactivecommons.async.rabbit.config.RabbitProperties; 10 | import org.reactivecommons.async.rabbit.config.props.AsyncProps; 11 | import org.reactivecommons.async.starter.broker.DiscardProvider; 12 | 13 | import java.util.Map; 14 | import java.util.concurrent.ConcurrentHashMap; 15 | 16 | @RequiredArgsConstructor 17 | public class RabbitMQDiscardProvider implements DiscardProvider { 18 | private final AsyncProps props; 19 | private final BrokerConfig config; 20 | private final MessageConverter converter; 21 | private final Map discardNotifier = new ConcurrentHashMap<>(); 22 | 23 | @Override 24 | public DiscardNotifier get() { 25 | return discardNotifier.computeIfAbsent(true, this::buildDiscardNotifier); 26 | } 27 | 28 | private DiscardNotifier buildDiscardNotifier(boolean ignored) { 29 | RabbitProperties properties = props.getConnectionProperties(); 30 | ConnectionFactoryProvider provider = RabbitMQSetupUtils.connectionFactoryProvider(properties); 31 | ReactiveMessageSender sender = RabbitMQSetupUtils.createMessageSender(provider, props, converter); 32 | return RabbitMQSetupUtils.createDiscardNotifier(sender, props, config, converter); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/config/RabbitProperties.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.config; 2 | 3 | import org.reactivecommons.async.rabbit.config.spring.RabbitPropertiesBase; 4 | 5 | public class RabbitProperties extends RabbitPropertiesBase { 6 | } 7 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/config/RabbitPropertiesAutoConfig.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.config; 2 | 3 | import org.reactivecommons.async.rabbit.config.spring.RabbitPropertiesBase; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | @ConfigurationProperties(prefix = "spring.rabbitmq") 7 | public class RabbitPropertiesAutoConfig extends RabbitPropertiesBase { 8 | } 9 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/config/props/AsyncPropsDomain.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.config.props; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import org.reactivecommons.async.rabbit.config.RabbitProperties; 6 | import org.reactivecommons.async.starter.props.GenericAsyncPropsDomain; 7 | import org.springframework.beans.factory.annotation.Value; 8 | 9 | import java.lang.reflect.Constructor; 10 | 11 | @Getter 12 | @Setter 13 | public class AsyncPropsDomain extends GenericAsyncPropsDomain { 14 | public AsyncPropsDomain(@Value("${spring.application.name}") String defaultAppName, 15 | RabbitProperties defaultRabbitProperties, 16 | AsyncRabbitPropsDomainProperties configured, 17 | RabbitSecretFiller secretFiller) { 18 | super(defaultAppName, defaultRabbitProperties, configured, secretFiller, AsyncProps.class, 19 | RabbitProperties.class); 20 | } 21 | 22 | @SuppressWarnings("unchecked") 23 | public static AsyncPropsDomainBuilder builder() { 25 | return GenericAsyncPropsDomain.builder(RabbitProperties.class, 26 | AsyncRabbitPropsDomainProperties.class, 27 | (Constructor) AsyncPropsDomain.class.getDeclaredConstructors()[0]); 28 | } 29 | 30 | @Override 31 | protected void fillCustoms(AsyncProps asyncProps) { 32 | if (asyncProps.getBrokerConfigProps() == null) { 33 | asyncProps.setBrokerConfigProps(new BrokerConfigProps(asyncProps)); 34 | } 35 | } 36 | 37 | public interface RabbitSecretFiller extends SecretFiller { 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/config/props/AsyncRabbitPropsDomainProperties.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.config.props; 2 | 3 | import lombok.NoArgsConstructor; 4 | import org.reactivecommons.async.rabbit.config.RabbitProperties; 5 | import org.reactivecommons.async.starter.props.GenericAsyncPropsDomainProperties; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | 8 | import java.util.Map; 9 | 10 | @NoArgsConstructor 11 | @ConfigurationProperties(prefix = "app.async") 12 | public class AsyncRabbitPropsDomainProperties extends GenericAsyncPropsDomainProperties { 13 | 14 | public AsyncRabbitPropsDomainProperties(Map m) { 15 | super(m); 16 | } 17 | 18 | public static AsyncPropsDomainPropertiesBuilder builder() { 20 | return GenericAsyncPropsDomainProperties.builder(AsyncRabbitPropsDomainProperties.class); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/config/props/DirectProps.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.config.props; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | import java.util.Optional; 10 | 11 | @Getter 12 | @Setter 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | @Builder 16 | public class DirectProps { 17 | 18 | @Builder.Default 19 | private String exchange = "directMessages"; 20 | 21 | @Builder.Default 22 | private String querySuffix = "query"; 23 | 24 | @Builder.Default 25 | private String commandSuffix = ""; 26 | 27 | @Builder.Default 28 | private Optional maxLengthBytes = Optional.empty(); 29 | 30 | @Builder.Default 31 | private boolean discardTimeoutQueries = false; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/config/props/DomainProps.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.config.props; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import org.springframework.boot.context.properties.NestedConfigurationProperty; 9 | 10 | @Getter 11 | @Setter 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | @Builder 15 | public class DomainProps { 16 | 17 | @NestedConfigurationProperty 18 | @Builder.Default 19 | private EventsProps events = new EventsProps(); 20 | @Builder.Default 21 | private boolean ignoreThisListener = false; 22 | } 23 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/config/props/EventsProps.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.config.props; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | import java.util.Optional; 10 | 11 | @Getter 12 | @Setter 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | @Builder 16 | public class EventsProps { 17 | 18 | @Builder.Default 19 | private String exchange = "domainEvents"; 20 | @Builder.Default 21 | private String eventsSuffix = "subsEvents"; 22 | @Builder.Default 23 | private String notificationSuffix = "notification"; 24 | 25 | @Builder.Default 26 | private Optional maxLengthBytes = Optional.empty(); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/config/props/FluxProps.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.config.props; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | @Getter 10 | @Setter 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | @Builder 14 | public class FluxProps { 15 | 16 | @Builder.Default 17 | private Integer maxConcurrency = 250; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/config/props/GlobalProps.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.config.props; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | import java.util.Optional; 10 | 11 | @Getter 12 | @Setter 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | @Builder 16 | public class GlobalProps { 17 | 18 | @Builder.Default 19 | private String exchange = "globalReply"; 20 | 21 | @Builder.Default 22 | private String repliesSuffix = "replies"; 23 | 24 | @Builder.Default 25 | private Optional maxLengthBytes = Optional.empty(); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/health/RabbitMQHealthException.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.health; 2 | 3 | public class RabbitMQHealthException extends RuntimeException { 4 | public RabbitMQHealthException(Throwable throwable) { 5 | super(throwable); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/health/RabbitReactiveHealthIndicator.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit.health; 2 | 3 | import com.rabbitmq.client.Connection; 4 | import com.rabbitmq.client.ConnectionFactory; 5 | import lombok.SneakyThrows; 6 | import lombok.extern.log4j.Log4j2; 7 | import org.reactivecommons.async.starter.config.health.RCHealth; 8 | import org.reactivecommons.async.starter.config.health.RCHealthIndicator; 9 | import reactor.core.publisher.Mono; 10 | 11 | import java.net.SocketException; 12 | 13 | import static org.reactivecommons.async.starter.config.health.ReactiveCommonsHealthIndicator.DOMAIN; 14 | import static org.reactivecommons.async.starter.config.health.ReactiveCommonsHealthIndicator.VERSION; 15 | 16 | @Log4j2 17 | public class RabbitReactiveHealthIndicator extends RCHealthIndicator { 18 | private final String domain; 19 | private final ConnectionFactory connectionFactory; 20 | 21 | public RabbitReactiveHealthIndicator(String domain, ConnectionFactory connectionFactory) { 22 | this.domain = domain; 23 | this.connectionFactory = connectionFactory.clone(); 24 | this.connectionFactory.useBlockingIo(); 25 | } 26 | 27 | @Override 28 | public Mono doHealthCheck(RCHealth.RCHealthBuilder builder) { 29 | builder.withDetail(DOMAIN, domain); 30 | return Mono.fromCallable(() -> getRawVersion(connectionFactory)) 31 | .map(status -> builder.up().withDetail(VERSION, status).build()); 32 | } 33 | 34 | @SneakyThrows 35 | private String getRawVersion(ConnectionFactory factory) { 36 | Connection connection = null; 37 | try { 38 | connection = factory.newConnection(); 39 | return connection.getServerProperties().get(VERSION).toString(); 40 | } catch (SocketException e) { 41 | log.warn("Identified error", e); 42 | throw new RabbitMQHealthException(e); 43 | } finally { 44 | if (connection != null) { 45 | try { 46 | connection.close(); 47 | } catch (Exception e) { 48 | log.error("Error closing health connection", e); 49 | } 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/starter/impl/common/rabbit/RabbitMQConfig.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.impl.common.rabbit; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.java.Log; 5 | import org.reactivecommons.async.commons.converters.json.ObjectMapperSupplier; 6 | import org.reactivecommons.async.rabbit.RabbitMQBrokerProviderFactory; 7 | import org.reactivecommons.async.rabbit.config.RabbitProperties; 8 | import org.reactivecommons.async.rabbit.config.RabbitPropertiesAutoConfig; 9 | import org.reactivecommons.async.rabbit.config.props.AsyncPropsDomain; 10 | import org.reactivecommons.async.rabbit.config.props.AsyncRabbitPropsDomainProperties; 11 | import org.reactivecommons.async.rabbit.converters.json.RabbitJacksonMessageConverter; 12 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 13 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 14 | import org.springframework.context.annotation.Bean; 15 | import org.springframework.context.annotation.Configuration; 16 | import org.springframework.context.annotation.Import; 17 | 18 | @Log 19 | @Configuration 20 | @RequiredArgsConstructor 21 | @EnableConfigurationProperties({RabbitPropertiesAutoConfig.class, AsyncRabbitPropsDomainProperties.class}) 22 | @Import({AsyncPropsDomain.class, RabbitMQBrokerProviderFactory.class}) 23 | public class RabbitMQConfig { 24 | 25 | @Bean 26 | @ConditionalOnMissingBean(RabbitJacksonMessageConverter.class) 27 | public RabbitJacksonMessageConverter messageConverter(ObjectMapperSupplier objectMapperSupplier) { 28 | return new RabbitJacksonMessageConverter(objectMapperSupplier.get()); 29 | } 30 | 31 | @Bean 32 | @ConditionalOnMissingBean(AsyncPropsDomain.RabbitSecretFiller.class) 33 | public AsyncPropsDomain.RabbitSecretFiller defaultRabbitSecretFiller() { 34 | return (ignoredDomain, ignoredProps) -> { 35 | }; 36 | } 37 | 38 | @Bean 39 | @ConditionalOnMissingBean(RabbitProperties.class) 40 | public RabbitProperties defaultRabbitProperties(RabbitPropertiesAutoConfig properties, 41 | ObjectMapperSupplier supplier) { 42 | return supplier.get().convertValue(properties, RabbitProperties.class); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/starter/impl/listener/rabbit/RabbitMQListenerOnlyConfig.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.impl.listener.rabbit; 2 | 3 | import org.reactivecommons.async.api.DynamicRegistry; 4 | import org.reactivecommons.async.commons.config.IBrokerConfigProps; 5 | import org.reactivecommons.async.rabbit.DynamicRegistryImp; 6 | import org.reactivecommons.async.rabbit.RabbitMQSetupUtils; 7 | import org.reactivecommons.async.rabbit.communications.TopologyCreator; 8 | import org.reactivecommons.async.rabbit.config.props.AsyncProps; 9 | import org.reactivecommons.async.rabbit.config.props.AsyncPropsDomain; 10 | import org.reactivecommons.async.rabbit.config.props.BrokerConfigProps; 11 | import org.reactivecommons.async.starter.config.DomainHandlers; 12 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.context.annotation.Configuration; 15 | 16 | import static org.reactivecommons.async.api.HandlerRegistry.DEFAULT_DOMAIN; 17 | 18 | @Configuration 19 | public class RabbitMQListenerOnlyConfig { 20 | 21 | @Bean 22 | @ConditionalOnMissingBean(DynamicRegistry.class) 23 | public DynamicRegistry dynamicRegistry(AsyncPropsDomain asyncPropsDomain, DomainHandlers handlers) { 24 | AsyncProps props = asyncPropsDomain.getProps(DEFAULT_DOMAIN); 25 | TopologyCreator topologyCreator = RabbitMQSetupUtils.createTopologyCreator(props); 26 | IBrokerConfigProps brokerConfigProps = new BrokerConfigProps(asyncPropsDomain.getProps(DEFAULT_DOMAIN)); 27 | return new DynamicRegistryImp(handlers.get(DEFAULT_DOMAIN), topologyCreator, brokerConfigProps); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/test/java/org/reactivecommons/async/rabbit/RabbitMQDiscardProviderTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.rabbit; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.junit.jupiter.api.extension.ExtendWith; 5 | import org.mockito.Mock; 6 | import org.mockito.junit.jupiter.MockitoExtension; 7 | import org.reactivecommons.async.commons.DLQDiscardNotifier; 8 | import org.reactivecommons.async.commons.DiscardNotifier; 9 | import org.reactivecommons.async.commons.config.BrokerConfig; 10 | import org.reactivecommons.async.commons.config.IBrokerConfigProps; 11 | import org.reactivecommons.async.rabbit.config.RabbitProperties; 12 | import org.reactivecommons.async.rabbit.config.props.AsyncProps; 13 | import org.reactivecommons.async.rabbit.config.props.BrokerConfigProps; 14 | import org.reactivecommons.async.rabbit.converters.json.RabbitJacksonMessageConverter; 15 | 16 | import static org.assertj.core.api.Assertions.assertThat; 17 | 18 | @ExtendWith(MockitoExtension.class) 19 | class RabbitMQDiscardProviderTest { 20 | @Mock 21 | private RabbitJacksonMessageConverter converter; 22 | 23 | @Test 24 | void shouldCreateDiscardNotifier() { 25 | // Arrange 26 | AsyncProps props = new AsyncProps(); 27 | props.setConnectionProperties(new RabbitProperties()); 28 | IBrokerConfigProps brokerConfigProps = new BrokerConfigProps(props); 29 | props.setBrokerConfigProps(brokerConfigProps); 30 | BrokerConfig brokerConfig = new BrokerConfig(); 31 | RabbitMQDiscardProvider discardProvider = new RabbitMQDiscardProvider(props, brokerConfig, converter); 32 | // Act 33 | DiscardNotifier notifier = discardProvider.get(); 34 | // Assert 35 | assertThat(notifier).isExactlyInstanceOf(DLQDiscardNotifier.class); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/test/java/org/reactivecommons/async/starter/impl/common/rabbit/RabbitMQConfigTest.java: -------------------------------------------------------------------------------- 1 | package org.reactivecommons.async.starter.impl.common.rabbit; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.reactivecommons.async.rabbit.RabbitMQBrokerProviderFactory; 5 | import org.reactivecommons.async.rabbit.config.props.AsyncPropsDomain; 6 | import org.reactivecommons.async.rabbit.converters.json.RabbitJacksonMessageConverter; 7 | import org.reactivecommons.async.starter.config.ConnectionManager; 8 | import org.reactivecommons.async.starter.config.ReactiveCommonsConfig; 9 | import org.reactivecommons.async.starter.config.ReactiveCommonsListenersConfig; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | 15 | @SpringBootTest(classes = { 16 | RabbitMQConfig.class, 17 | AsyncPropsDomain.class, 18 | RabbitMQBrokerProviderFactory.class, 19 | ReactiveCommonsConfig.class, 20 | ReactiveCommonsListenersConfig.class 21 | }) 22 | class RabbitMQConfigTest { 23 | @Autowired 24 | private RabbitJacksonMessageConverter converter; 25 | @Autowired 26 | private ConnectionManager manager; 27 | 28 | @Test 29 | void shouldHasConverter() { 30 | // Arrange 31 | // Act 32 | // Assert 33 | assertThat(converter).isNotNull(); 34 | } 35 | 36 | @Test 37 | void shouldHasManager() { 38 | // Arrange 39 | // Act 40 | // Assert 41 | assertThat(manager).isNotNull(); 42 | assertThat(manager.getProviders()).isNotEmpty(); 43 | assertThat(manager.getProviders().get("app").getProps().getAppName()).isEqualTo("test-app"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /starters/async-rabbit-starter/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=test-app --------------------------------------------------------------------------------