├── .editorconfig ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dco.yml ├── dependabot.yml └── workflows │ ├── deploy-docs.yml │ └── maven.yaml ├── .gitignore ├── .java-version ├── .mvn ├── jvm.config ├── maven.config └── wrapper │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .sdkmanrc ├── .settings.xml ├── .springformat ├── Guardfile ├── LICENSE.txt ├── README.adoc ├── SECURITY.md ├── docs ├── .java-version ├── antora-playbook.yml ├── antora.yml ├── modules │ └── ROOT │ │ ├── nav.adoc │ │ ├── pages │ │ ├── _attributes.adoc │ │ ├── appendix.adoc │ │ ├── configprops.adoc │ │ ├── index.adoc │ │ ├── intro.adoc │ │ ├── quickstart.adoc │ │ ├── spring-cloud-bus.adoc │ │ └── spring-cloud-bus │ │ │ ├── addressing.adoc │ │ │ ├── bus-endpoints.adoc │ │ │ ├── configuration.adoc │ │ │ └── custom-events.adoc │ │ └── partials │ │ └── _configprops.adoc ├── package.json ├── pom.xml └── src │ └── main │ ├── antora │ └── resources │ │ └── antora-resources │ │ └── antora.yml │ └── asciidoc │ ├── README.adoc │ ├── ghpages.sh │ └── sagan-index.adoc ├── mvnw ├── mvnw.cmd ├── pom.xml ├── spring-cloud-bus-dependencies └── pom.xml ├── spring-cloud-bus-tests ├── pom.xml └── src │ └── test │ └── java │ └── org │ └── springframework │ └── cloud │ └── bus │ ├── BusAmqpIntegrationTests.java │ ├── BusJmxEndpointTests.java │ ├── ShutdownListenerIntegrationTests.java │ └── jackson │ └── BusJacksonIntegrationTests.java ├── spring-cloud-bus ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── springframework │ │ │ └── cloud │ │ │ └── bus │ │ │ ├── BusAutoConfiguration.java │ │ │ ├── BusBridge.java │ │ │ ├── BusConstants.java │ │ │ ├── BusConsumer.java │ │ │ ├── BusEnvironmentPostProcessor.java │ │ │ ├── BusPathMatcher.java │ │ │ ├── BusProperties.java │ │ │ ├── BusRefreshAutoConfiguration.java │ │ │ ├── BusShutdownAutoConfiguration.java │ │ │ ├── BusStreamAutoConfiguration.java │ │ │ ├── ConditionalOnBusEnabled.java │ │ │ ├── DefaultBusPathMatcher.java │ │ │ ├── PathServiceMatcher.java │ │ │ ├── PathServiceMatcherAutoConfiguration.java │ │ │ ├── RemoteApplicationEventListener.java │ │ │ ├── ServiceMatcher.java │ │ │ ├── StreamBusBridge.java │ │ │ ├── endpoint │ │ │ ├── AbstractBusEndpoint.java │ │ │ ├── EnvironmentBusEndpoint.java │ │ │ ├── RefreshBusEndpoint.java │ │ │ └── ShutdownBusEndpoint.java │ │ │ ├── event │ │ │ ├── AckRemoteApplicationEvent.java │ │ │ ├── Destination.java │ │ │ ├── EnvironmentChangeListener.java │ │ │ ├── EnvironmentChangeRemoteApplicationEvent.java │ │ │ ├── PathDestinationFactory.java │ │ │ ├── RefreshListener.java │ │ │ ├── RefreshRemoteApplicationEvent.java │ │ │ ├── RemoteApplicationEvent.java │ │ │ ├── SentApplicationEvent.java │ │ │ ├── ShutdownListener.java │ │ │ ├── ShutdownRemoteApplicationEvent.java │ │ │ ├── TraceListener.java │ │ │ └── UnknownRemoteApplicationEvent.java │ │ │ └── jackson │ │ │ ├── BusJacksonAutoConfiguration.java │ │ │ ├── RemoteApplicationEventRegistrar.java │ │ │ ├── RemoteApplicationEventScan.java │ │ │ └── SubtypeModule.java │ └── resources │ │ └── META-INF │ │ ├── additional-spring-configuration-metadata.json │ │ ├── spring.factories │ │ └── spring │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── test │ ├── java │ ├── org │ │ └── springframework │ │ │ └── cloud │ │ │ └── bus │ │ │ ├── BusAutoConfigurationClassPathTests.java │ │ │ ├── BusAutoConfigurationTests.java │ │ │ ├── BusEnvironmentPostProcessorTests.java │ │ │ ├── ConditionalOnBusEnabledTests.java │ │ │ ├── PathServiceMatcherTests.java │ │ │ ├── PathServiceMatcherWithConfigNamesTests.java │ │ │ ├── RefreshListenerIntegrationTests.java │ │ │ ├── endpoint │ │ │ └── RefreshBusEndpointTests.java │ │ │ ├── event │ │ │ └── test │ │ │ │ ├── TestRemoteApplicationEvent.java │ │ │ │ └── TypedRemoteApplicationEvent.java │ │ │ └── jackson │ │ │ ├── RemoteApplicationEventScanTests.java │ │ │ ├── SerializationTests.java │ │ │ └── SubtypeModuleTests.java │ └── test │ │ └── foo │ │ └── bar │ │ └── FooBarTestRemoteApplicationEvent.java │ └── resources │ └── application.properties ├── spring-cloud-starter-bus-amqp └── pom.xml ├── spring-cloud-starter-bus-kafka └── pom.xml ├── spring-cloud-starter-bus-stream └── pom.xml └── src └── checkstyle └── checkstyle-suppressions.xml /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = tab 8 | indent_size = 4 9 | end_of_line = lf 10 | insert_final_newline = true 11 | 12 | [*.yml] 13 | indent_style = space 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing 3 | 4 | Spring Cloud is released under the non-restrictive Apache 2.0 license, 5 | and follows a very standard Github development process, using Github 6 | tracker for issues and merging pull requests into master. If you want 7 | to contribute even something trivial please do not hesitate, but 8 | follow the guidelines below. 9 | 10 | ## Sign the Contributor License Agreement 11 | Before we accept a non-trivial patch or pull request we will need you to sign the 12 | [Contributor License Agreement](https://cla.pivotal.io/sign/spring). 13 | Signing the contributor's agreement does not grant anyone commit rights to the main 14 | repository, but it does mean that we can accept your contributions, and you will get an 15 | author credit if we do. Active contributors might be asked to join the core team, and 16 | given the ability to merge pull requests. 17 | 18 | ## Code of Conduct 19 | This project adheres to the Contributor Covenant [code of 20 | conduct](https://github.com/spring-cloud/spring-cloud-build/blob/main/docs/modules/ROOT/partials/code-of-conduct.adoc). By participating, you are expected to uphold this code. Please report 21 | unacceptable behavior to spring-code-of-conduct@pivotal.io. 22 | 23 | ## Code Conventions and Housekeeping 24 | None of these is essential for a pull request, but they will all help. They can also be 25 | added after the original pull request but before a merge. 26 | 27 | * Use the Spring Framework code format conventions. If you use Eclipse 28 | you can import formatter settings using the 29 | `eclipse-code-formatter.xml` file from the 30 | [Spring Cloud Build](https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-dependencies-parent/eclipse-code-formatter.xml) project. If using IntelliJ, you can use the 31 | [Eclipse Code Formatter Plugin](https://plugins.jetbrains.com/plugin/6546) to import the same file. 32 | * Make sure all new `.java` files to have a simple Javadoc class comment with at least an 33 | `@author` tag identifying you, and preferably at least a paragraph on what the class is 34 | for. 35 | * Add the ASF license header comment to all new `.java` files (copy from existing files 36 | in the project) 37 | * Add yourself as an `@author` to the .java files that you modify substantially (more 38 | than cosmetic changes). 39 | * Add some Javadocs and, if you change the namespace, some XSD doc elements. 40 | * A few unit tests would help a lot as well -- someone has to do it. 41 | * If no-one else is using your branch, please rebase it against the current master (or 42 | other target branch in the main project). 43 | * When writing a commit message please follow [these conventions](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), 44 | if you are fixing an existing issue please add `Fixes gh-XXXX` at the end of the commit 45 | message (where XXXX is the issue number). 46 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | Please provide details of the problem, including the version of Spring Cloud that you 12 | are using. 13 | 14 | **Sample** 15 | If possible, please provide a test case or sample application that reproduces 16 | the problem. This makes it much easier for us to diagnose the problem and to verify that 17 | we have fixed it. 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dco.yml: -------------------------------------------------------------------------------- 1 | require: 2 | members: false 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | target-branch: "main" 11 | schedule: 12 | interval: "weekly" 13 | - package-ecosystem: maven 14 | directory: / 15 | schedule: 16 | interval: daily 17 | target-branch: main 18 | #ignore: 19 | # only upgrade by minor or patch 20 | #- dependency-name: "*" 21 | # update-types: 22 | # - version-update:semver-major 23 | - package-ecosystem: maven 24 | directory: / 25 | schedule: 26 | interval: daily 27 | target-branch: 4.3.x 28 | ignore: 29 | # only upgrade by minor or patch 30 | - dependency-name: "*" 31 | update-types: 32 | - version-update:semver-major 33 | - version-update:semver-minor 34 | - package-ecosystem: maven 35 | directory: / 36 | schedule: 37 | interval: daily 38 | target-branch: 4.2.x 39 | ignore: 40 | # only upgrade by minor or patch 41 | - dependency-name: "*" 42 | update-types: 43 | - version-update:semver-major 44 | - version-update:semver-minor 45 | - package-ecosystem: maven 46 | directory: / 47 | schedule: 48 | interval: daily 49 | target-branch: 4.1.x 50 | ignore: 51 | # only upgrade by minor or patch 52 | - dependency-name: "*" 53 | update-types: 54 | - version-update:semver-major 55 | - version-update:semver-minor 56 | - package-ecosystem: npm 57 | target-branch: docs-build 58 | directory: / 59 | schedule: 60 | interval: weekly 61 | - package-ecosystem: npm 62 | target-branch: main 63 | directory: /docs 64 | schedule: 65 | interval: weekly 66 | - package-ecosystem: npm 67 | target-branch: 4.2.x 68 | directory: /docs 69 | schedule: 70 | interval: weekly 71 | - package-ecosystem: npm 72 | target-branch: 4.2.x 73 | directory: /docs 74 | schedule: 75 | interval: weekly 76 | - package-ecosystem: npm 77 | target-branch: 4.1.x 78 | directory: /docs 79 | schedule: 80 | interval: weekly 81 | -------------------------------------------------------------------------------- /.github/workflows/deploy-docs.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Docs 2 | on: 3 | push: 4 | branches-ignore: [ gh-pages ] 5 | tags: '**' 6 | repository_dispatch: 7 | types: request-build-reference # legacy 8 | #schedule: 9 | #- cron: '0 10 * * *' # Once per day at 10am UTC 10 | workflow_dispatch: 11 | permissions: 12 | actions: write 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | # if: github.repository_owner == 'spring-cloud' 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v3 20 | with: 21 | ref: docs-build 22 | fetch-depth: 1 23 | - name: Dispatch (partial build) 24 | if: github.ref_type == 'branch' 25 | env: 26 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }} 28 | - name: Dispatch (full build) 29 | if: github.ref_type == 'tag' 30 | env: 31 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) 33 | -------------------------------------------------------------------------------- /.github/workflows/maven.yaml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Build 5 | on: 6 | push: 7 | branches: [ main, 4.1.x, 4.0.x, 3.1.x ] 8 | pull_request: 9 | branches: [ main, 4.1.x, 4.0.x, 3.1.x ] 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Set up JDK 16 | uses: actions/setup-java@v4 17 | with: 18 | distribution: 'temurin' 19 | java-version: '17' 20 | - name: Cache local Maven repository 21 | uses: actions/cache@v4 22 | with: 23 | path: ~/.m2/repository 24 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 25 | restore-keys: | 26 | ${{ runner.os }}-maven- 27 | - name: Build with Maven 28 | run: ./mvnw -s .settings.xml clean org.jacoco:jacoco-maven-plugin:prepare-agent install -U -P sonar -nsu --batch-mode -Dmaven.test.redirectTestOutputToFile=true -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn 29 | - name: Publish Test Report 30 | uses: mikepenz/action-junit-report@v5 31 | if: always() # always run even if the previous step fails 32 | with: 33 | report_paths: '**/surefire-reports/TEST-*.xml' 34 | - name: Archive code coverage results 35 | uses: actions/upload-artifact@v4 36 | with: 37 | name: surefire-reports 38 | path: '**/surefire-reports/*' 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | #* 3 | *# 4 | .#* 5 | .classpath 6 | .project 7 | .settings 8 | .springBeans 9 | .gradle 10 | build 11 | bin 12 | target/ 13 | _site/ 14 | *.swp 15 | .idea 16 | *.iml 17 | .factorypath 18 | .vscode/ 19 | .flattened-pom.xml 20 | 21 | 22 | node 23 | node_modules 24 | build 25 | /package.json 26 | package-lock.json 27 | -------------------------------------------------------------------------------- /.java-version: -------------------------------------------------------------------------------- 1 | 17 2 | -------------------------------------------------------------------------------- /.mvn/jvm.config: -------------------------------------------------------------------------------- 1 | -Xmx1024m -XX:CICompilerCount=1 -XX:TieredStopAtLevel=1 -Djava.security.egd=file:/dev/./urandom -------------------------------------------------------------------------------- /.mvn/maven.config: -------------------------------------------------------------------------------- 1 | -P spring 2 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spring-cloud/spring-cloud-bus/55688a3d381da01d3faff67e1d232f745dacd245/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /.sdkmanrc: -------------------------------------------------------------------------------- 1 | # Enable auto-env through the sdkman_auto_env config 2 | # Add key=value pairs of SDKs to use below 3 | java=17.0.1-tem 4 | -------------------------------------------------------------------------------- /.settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | repo.spring.io 6 | ${env.CI_DEPLOY_USERNAME} 7 | ${env.CI_DEPLOY_PASSWORD} 8 | 9 | 10 | 11 | 12 | 18 | spring 19 | 20 | true 21 | 22 | 23 | 24 | spring-snapshots 25 | Spring Snapshots 26 | https://repo.spring.io/snapshot 27 | 28 | true 29 | 30 | 31 | 32 | spring-milestones 33 | Spring Milestones 34 | https://repo.spring.io/milestone 35 | 36 | false 37 | 38 | 39 | 40 | spring-releases 41 | Spring Releases 42 | https://repo.spring.io/release 43 | 44 | false 45 | 46 | 47 | 48 | 49 | 50 | spring-snapshots 51 | Spring Snapshots 52 | https://repo.spring.io/snapshot 53 | 54 | true 55 | 56 | 57 | 58 | spring-milestones 59 | Spring Milestones 60 | https://repo.spring.io/milestone 61 | 62 | false 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /.springformat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spring-cloud/spring-cloud-bus/55688a3d381da01d3faff67e1d232f745dacd245/.springformat -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | require 'asciidoctor' 2 | require 'erb' 3 | require './src/main/ruby/readme.rb' 4 | 5 | options = {:mkdirs => true, :safe => :unsafe, :attributes => ['linkcss', 'allow-uri-read']} 6 | 7 | guard 'shell' do 8 | watch(/^src\/[A-Z-a-z][^#]*\.adoc$/) {|m| 9 | SpringCloud::Build.render_file('src/main/asciidoc/README.adoc', :to_file => './README.adoc') 10 | Asciidoctor.render_file('src/main/asciidoc/spring-cloud-bus.adoc', options.merge(:to_dir => 'target/generated-docs')) 11 | } 12 | end 13 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | To report security vulnerabilities, please go to https://pivotal.io/security. 6 | -------------------------------------------------------------------------------- /docs/.java-version: -------------------------------------------------------------------------------- 1 | 17 2 | -------------------------------------------------------------------------------- /docs/antora-playbook.yml: -------------------------------------------------------------------------------- 1 | antora: 2 | extensions: 3 | - require: '@springio/antora-extensions' 4 | root_component_name: 'cloud-bus' 5 | site: 6 | title: Spring Cloud Bus 7 | url: https://docs.spring.io/spring-cloud-bus/reference/ 8 | content: 9 | sources: 10 | - url: ./.. 11 | branches: HEAD 12 | start_path: docs 13 | worktrees: true 14 | asciidoc: 15 | attributes: 16 | page-stackoverflow-url: https://stackoverflow.com/tags/spring-cloud 17 | page-pagination: '' 18 | hide-uri-scheme: '@' 19 | tabs-sync-option: '@' 20 | chomp: 'all' 21 | extensions: 22 | - '@asciidoctor/tabs' 23 | - '@springio/asciidoctor-extensions' 24 | sourcemap: true 25 | urls: 26 | latest_version_segment: '' 27 | runtime: 28 | log: 29 | failure_level: warn 30 | format: pretty 31 | ui: 32 | bundle: 33 | url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.15/ui-bundle.zip 34 | -------------------------------------------------------------------------------- /docs/antora.yml: -------------------------------------------------------------------------------- 1 | name: cloud-bus 2 | version: true 3 | title: spring-cloud-bus 4 | nav: 5 | - modules/ROOT/nav.adoc 6 | ext: 7 | collector: 8 | run: 9 | command: ./mvnw --no-transfer-progress -B process-resources -Pdocs -pl docs -Dantora-maven-plugin.phase=none -Dgenerate-docs.phase=none -Dgenerate-readme.phase=none -Dgenerate-cloud-resources.phase=none -Dmaven-dependency-plugin-for-docs.phase=none -Dmaven-dependency-plugin-for-docs-classes.phase=none -DskipTests -DdisableConfigurationProperties 10 | local: true 11 | scan: 12 | dir: ./target/classes/antora-resources/ 13 | -------------------------------------------------------------------------------- /docs/modules/ROOT/nav.adoc: -------------------------------------------------------------------------------- 1 | * xref:index.adoc[] 2 | * xref:quickstart.adoc[] 3 | * xref:spring-cloud-bus.adoc[] 4 | ** xref:spring-cloud-bus/bus-endpoints.adoc[] 5 | ** xref:spring-cloud-bus/addressing.adoc[] 6 | ** xref:spring-cloud-bus/configuration.adoc[] 7 | ** xref:spring-cloud-bus/custom-events.adoc[] 8 | * xref:appendix.adoc[] 9 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/_attributes.adoc: -------------------------------------------------------------------------------- 1 | :doctype: book 2 | :idprefix: 3 | :idseparator: - 4 | :tabsize: 4 5 | :numbered: 6 | :sectanchors: 7 | :sectnums: 8 | :icons: font 9 | :hide-uri-scheme: 10 | :docinfo: shared,private 11 | 12 | :sc-ext: java 13 | :project-full-name: Spring Cloud Bus 14 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/appendix.adoc: -------------------------------------------------------------------------------- 1 | :numbered!: 2 | [appendix] 3 | [[common-application-properties]] 4 | = Common application properties 5 | :page-section-summary-toc: 1 6 | 7 | 8 | Various properties can be specified inside your `application.properties` file, inside your `application.yml` file, or as command line switches. 9 | This appendix provides a list of common Spring Cloud Bus properties and references to the underlying classes that consume them. 10 | 11 | NOTE: Property contributions can come from additional jar files on your classpath, so you should not consider this an exhaustive list. 12 | Also, you can define your own properties. 13 | 14 | include::partial$_configprops.adoc[] -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/configprops.adoc: -------------------------------------------------------------------------------- 1 | [[configuration-properties]] 2 | = Configuration Properties 3 | 4 | Below you can find a list of configuration properties. 5 | 6 | include::partial$_configprops.adoc[] 7 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/index.adoc: -------------------------------------------------------------------------------- 1 | include::intro.adoc[] -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/intro.adoc: -------------------------------------------------------------------------------- 1 | [[spring-cloud-bus-intro]] 2 | = Introduction 3 | 4 | Spring Cloud Bus links the nodes of a distributed system with a lightweight message 5 | broker. This broker can then be used to broadcast state changes (such as configuration 6 | changes) or other management instructions. A key idea is that the bus is like a 7 | distributed actuator for a Spring Boot application that is scaled out. However, it can 8 | also be used as a communication channel between apps. This project provides starters for 9 | either an AMQP broker or Kafka as the transport. 10 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/quickstart.adoc: -------------------------------------------------------------------------------- 1 | [[spring-cloud-gateway-quickstart]] 2 | = Quickstart 3 | 4 | Spring Cloud Bus works by adding Spring Boot autconfiguration if it detects itself on the 5 | classpath. To enable the bus, add `spring-cloud-starter-bus-amqp` or 6 | `spring-cloud-starter-bus-kafka` to your dependency management. Spring Cloud takes care of 7 | the rest. Make sure the broker (RabbitMQ or Kafka) is available and configured. When 8 | running on localhost, you need not do anything. If you run remotely, use Spring Cloud 9 | Connectors or Spring Boot conventions to define the broker credentials, as shown in the 10 | following example for Rabbit: 11 | 12 | .application.yml 13 | ---- 14 | spring: 15 | rabbitmq: 16 | host: mybroker.com 17 | port: 5672 18 | username: user 19 | password: secret 20 | ---- 21 | 22 | The bus currently supports sending messages to all nodes listening or all nodes for a 23 | particular service (as defined by Eureka). The `/bus*` actuator namespace has some HTTP 24 | endpoints. Currently, three are implemented. The first, `/busenv`, sends key/value pairs to 25 | update each node's Spring Environment. The second, `/busrefresh`, reloads each 26 | application's configuration, as though they had all been pinged on their `/refresh` 27 | endpoint. The third `/busshutdown` sends a shutdown event to gracefully shutdown the application instance(s). 28 | 29 | NOTE: The Spring Cloud Bus starters cover Rabbit and Kafka, because those are the two most 30 | common implementations. However, Spring Cloud Stream is quite flexible, and the binder 31 | works with `spring-cloud-bus`. 32 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/spring-cloud-bus.adoc: -------------------------------------------------------------------------------- 1 | [[spring-cloud-bus]] 2 | = Spring Cloud Bus 3 | :page-section-summary-toc: 1 4 | 5 | *{spring-cloud-version}* 6 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/spring-cloud-bus/addressing.adoc: -------------------------------------------------------------------------------- 1 | [[addressing]] 2 | = Addressing Instances 3 | :page-section-summary-toc: 1 4 | 5 | [[addressing-an-instance]] 6 | == Addressing an Instance 7 | 8 | Each instance of the application has a service ID, whose value can be set with 9 | `spring.cloud.bus.id` and whose value is expected to be a colon-separated list of 10 | identifiers, in order from least specific to most specific. The default value is 11 | constructed from the environment as a combination of the `spring.application.name` and 12 | `server.port` (or `spring.application.index`, if set). The default value of the ID is 13 | constructed in the form of `app:index:id`, where: 14 | 15 | * `app` is the `vcap.application.name`, if it exists, or `spring.application.name` 16 | * `index` is the `vcap.application.instance_index`, if it exists, 17 | `spring.application.index`, `local.server.port`, `server.port`, or `0` (in that order). 18 | * `id` is the `vcap.application.instance_id`, if it exists, or a random value. 19 | 20 | The HTTP endpoints accept a "`destination`" path parameter, such as 21 | `/busrefresh/customers:9000`, where `destination` is a service ID. If the ID 22 | is owned by an instance on the bus, it processes the message, and all other instances 23 | ignore it. 24 | 25 | [[addressing-all-instances-of-a-service]] 26 | == Addressing All Instances of a Service 27 | 28 | The "`destination`" parameter is used in a Spring `PathMatcher` (with the path separator 29 | as a colon -- `:`) to determine if an instance processes the message. Using the example 30 | from earlier, `/busenv/customers:**` targets all instances of the 31 | "`customers`" service regardless of the rest of the service ID. 32 | 33 | [[service-id-must-be-unique]] 34 | == Service ID Must Be Unique 35 | 36 | The bus tries twice to eliminate processing an event -- once from the original 37 | `ApplicationEvent` and once from the queue. To do so, it checks the sending service ID 38 | against the current service ID. If multiple instances of a service have the same ID, 39 | events are not processed. When running on a local machine, each service is on a different 40 | port, and that port is part of the ID. Cloud Foundry supplies an index to differentiate. 41 | To ensure that the ID is unique outside Cloud Foundry, set `spring.application.index` to 42 | something unique for each instance of a service. 43 | 44 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/spring-cloud-bus/bus-endpoints.adoc: -------------------------------------------------------------------------------- 1 | [[bus-endpoints]] 2 | = Bus Endpoints 3 | :page-section-summary-toc: 1 4 | 5 | Spring Cloud Bus provides three endpoints, `/actuator/busrefresh`, `/actutator/busshutdown` and `/actuator/busenv` 6 | that correspond to individual actuator endpoints in Spring Cloud Commons, 7 | `/actuator/refresh`, `/actuator/shutdown`, and `/actuator/env` respectively. 8 | 9 | [[bus-refresh-endpoint]] 10 | == Bus Refresh Endpoint 11 | The `/actuator/busrefresh` endpoint clears the `RefreshScope` cache and rebinds 12 | `@ConfigurationProperties`. See the <> documentation for 13 | more information. 14 | 15 | To expose the `/actuator/busrefresh` endpoint, you need to add following configuration to your 16 | application: 17 | 18 | [source,properties] 19 | ---- 20 | management.endpoints.web.exposure.include=busrefresh 21 | ---- 22 | 23 | [[bus-env-endpoint]] 24 | == Bus Env Endpoint 25 | The `/actuator/busenv` endpoint updates each instances environment with the specified 26 | key/value pair across multiple instances. 27 | 28 | To expose the `/actuator/busenv` endpoint, you need to add following configuration to your 29 | application: 30 | 31 | [source,properties] 32 | ---- 33 | management.endpoints.web.exposure.include=busenv 34 | ---- 35 | 36 | The `/actuator/busenv` endpoint accepts `POST` requests with the following shape: 37 | 38 | [source,json] 39 | ---- 40 | { 41 | "name": "key1", 42 | "value": "value1" 43 | } 44 | ---- 45 | 46 | [[bus-shutdown-endpoint]] 47 | == Bus Shutdown Endpoint 48 | The `/actuator/busshutdown` shuts down the application https://docs.spring.io/spring-boot/reference/web/graceful-shutdown.html[gracefully]. 49 | 50 | To expose the `/actuator/busshutdown` endpoint, you need to add following configuration to your 51 | application: 52 | 53 | [source,properties] 54 | ---- 55 | management.endpoints.web.exposure.include=busshutdown 56 | ---- 57 | 58 | You can make a request to the `busshutdown` endpoint by issuing a `POST` request. 59 | 60 | If you would like to target a specific application you can issue a `POST` request to `/busshutdown` and optionally 61 | specify the bus id: 62 | 63 | [source,bash] 64 | ---- 65 | $ curl -X POST http://localhost:8080/actuator/busshutdown 66 | ---- 67 | 68 | You can also target a specific application instance by specifying the bus id: 69 | 70 | [source,bash] 71 | ---- 72 | $ curl -X POST http://localhost:8080/actuator/busshutdown/busid:123 73 | ---- 74 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/spring-cloud-bus/configuration.adoc: -------------------------------------------------------------------------------- 1 | [[configuration]] 2 | = Configuration 3 | :page-section-summary-toc: 1 4 | 5 | [[customizing-the-message-broker]] 6 | == Customizing the Message Broker 7 | 8 | Spring Cloud Bus uses https://cloud.spring.io/spring-cloud-stream[Spring Cloud Stream] to 9 | broadcast the messages. So, to get messages to flow, you need only include the binder 10 | implementation of your choice in the classpath. There are convenient starters for the bus 11 | with AMQP (RabbitMQ) and Kafka (`spring-cloud-starter-bus-[amqp|kafka]`). Generally 12 | speaking, Spring Cloud Stream relies on Spring Boot autoconfiguration conventions for 13 | configuring middleware. For instance, the AMQP broker address can be changed with 14 | `spring.rabbitmq.{asterisk}` configuration properties. Spring Cloud Bus has a handful of 15 | native configuration properties in `spring.cloud.bus.{asterisk}` (for example, 16 | `spring.cloud.bus.destination` is the name of the topic to use as the external 17 | middleware). Normally, the defaults suffice. 18 | 19 | To learn more about how to customize the message broker settings, consult the Spring Cloud 20 | Stream documentation. 21 | 22 | [[tracing-bus-events]] 23 | == Tracing Bus Events 24 | 25 | Bus events (subclasses of `RemoteApplicationEvent`) can be traced by setting 26 | `spring.cloud.bus.trace.enabled=true`. If you do so, the Spring Boot `TraceRepository` 27 | (if it is present) shows each event sent and all the acks from each service instance. The 28 | following example comes from the `/trace` endpoint: 29 | 30 | [source,json] 31 | ---- 32 | { 33 | "timestamp": "2015-11-26T10:24:44.411+0000", 34 | "info": { 35 | "signal": "spring.cloud.bus.ack", 36 | "type": "RefreshRemoteApplicationEvent", 37 | "id": "c4d374b7-58ea-4928-a312-31984def293b", 38 | "origin": "stores:8081", 39 | "destination": "*:**" 40 | } 41 | }, 42 | { 43 | "timestamp": "2015-11-26T10:24:41.864+0000", 44 | "info": { 45 | "signal": "spring.cloud.bus.sent", 46 | "type": "RefreshRemoteApplicationEvent", 47 | "id": "c4d374b7-58ea-4928-a312-31984def293b", 48 | "origin": "customers:9000", 49 | "destination": "*:**" 50 | } 51 | }, 52 | { 53 | "timestamp": "2015-11-26T10:24:41.862+0000", 54 | "info": { 55 | "signal": "spring.cloud.bus.ack", 56 | "type": "RefreshRemoteApplicationEvent", 57 | "id": "c4d374b7-58ea-4928-a312-31984def293b", 58 | "origin": "customers:9000", 59 | "destination": "*:**" 60 | } 61 | } 62 | ---- 63 | 64 | The preceding trace shows that a `RefreshRemoteApplicationEvent` was sent from 65 | `customers:9000`, broadcast to all services, and received (acked) by `customers:9000` and 66 | `stores:8081`. 67 | 68 | To handle the ack signals yourself, you could add an `@EventListener` for the 69 | `AckRemoteApplicationEvent` and `SentApplicationEvent` types to your app (and enable 70 | tracing). Alternatively, you could tap into the `TraceRepository` and mine the data from 71 | there. 72 | 73 | NOTE: Any Bus application can trace acks. However, sometimes, it is 74 | useful to do this in a central service that can do more complex 75 | queries on the data or forward it to a specialized tracing service. 76 | 77 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/spring-cloud-bus/custom-events.adoc: -------------------------------------------------------------------------------- 1 | [[custom-events]] 2 | = Custom Events 3 | :page-section-summary-toc: 1 4 | 5 | [[broadcasting-your-own-events]] 6 | == Broadcasting Your Own Events 7 | 8 | The Bus can carry any event of type `RemoteApplicationEvent`. The default transport is 9 | JSON, and the deserializer needs to know which types are going to be used ahead of time. 10 | To register a new type, you must put it in a subpackage of 11 | `org.springframework.cloud.bus.event`. 12 | 13 | To customise the event name, you can use `@JsonTypeName` on your custom class or rely on 14 | the default strategy, which is to use the simple name of the class. 15 | 16 | NOTE: Both the producer and the consumer need access to the class definition. 17 | 18 | [[registering-events-in-custom-packages]] 19 | === Registering events in custom packages 20 | 21 | If you cannot or do not want to use a subpackage of `org.springframework.cloud.bus.event` 22 | for your custom events, you must specify which packages to scan for events of type 23 | `RemoteApplicationEvent` by using the `@RemoteApplicationEventScan` annotation. Packages 24 | specified with `@RemoteApplicationEventScan` include subpackages. 25 | 26 | For example, consider the following custom event, called `MyEvent`: 27 | 28 | [source,java] 29 | ---- 30 | package com.acme; 31 | 32 | public class MyEvent extends RemoteApplicationEvent { 33 | ... 34 | } 35 | ---- 36 | 37 | You can register that event with the deserializer in the following way: 38 | 39 | [source,java] 40 | ---- 41 | package com.acme; 42 | 43 | @Configuration 44 | @RemoteApplicationEventScan 45 | public class BusConfiguration { 46 | ... 47 | } 48 | ---- 49 | 50 | Without specifying a value, the package of the class where `@RemoteApplicationEventScan` 51 | is used is registered. In this example, `com.acme` is registered by using the package of 52 | `BusConfiguration`. 53 | 54 | You can also explicitly specify the packages to scan by using the `value`, `basePackages` 55 | or `basePackageClasses` properties on `@RemoteApplicationEventScan`, as shown in the 56 | following example: 57 | 58 | [source,java] 59 | ---- 60 | package com.acme; 61 | 62 | @Configuration 63 | //@RemoteApplicationEventScan({"com.acme", "foo.bar"}) 64 | //@RemoteApplicationEventScan(basePackages = {"com.acme", "foo.bar", "fizz.buzz"}) 65 | @RemoteApplicationEventScan(basePackageClasses = BusConfiguration.class) 66 | public class BusConfiguration { 67 | ... 68 | } 69 | ---- 70 | 71 | All of the preceding examples of `@RemoteApplicationEventScan` are equivalent, in that the 72 | `com.acme` package is registered by explicitly specifying the packages on 73 | `@RemoteApplicationEventScan`. 74 | 75 | NOTE: You can specify multiple base packages to scan. 76 | 77 | -------------------------------------------------------------------------------- /docs/modules/ROOT/partials/_configprops.adoc: -------------------------------------------------------------------------------- 1 | |=== 2 | |Name | Default | Description 3 | 4 | |spring.cloud.bus.ack.destination-service | | Service that wants to listen to acks. By default null (meaning all services). 5 | |spring.cloud.bus.ack.enabled | `+++true+++` | Flag to switch off acks (default on). 6 | |spring.cloud.bus.content-type | | The bus mime-type. 7 | |spring.cloud.bus.destination | | Name of Spring Cloud Stream destination for messages. 8 | |spring.cloud.bus.enabled | `+++true+++` | Flag to indicate that the bus is enabled. 9 | |spring.cloud.bus.env.enabled | `+++true+++` | Flag to switch off environment change events (default on). 10 | |spring.cloud.bus.id | `+++application+++` | The identifier for this application instance. 11 | |spring.cloud.bus.refresh.enabled | `+++true+++` | Flag to switch off refresh events (default on). 12 | |spring.cloud.bus.shutdown.enabled | `+++true+++` | Flag to switch off shutdown events (default on). 13 | |spring.cloud.bus.trace.enabled | `+++false+++` | Flag to switch on tracing of acks (default off). 14 | 15 | |=== -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "antora": "3.2.0-alpha.4", 4 | "@antora/atlas-extension": "1.0.0-alpha.2", 5 | "@antora/collector-extension": "1.0.0-alpha.3", 6 | "@asciidoctor/tabs": "1.0.0-beta.6", 7 | "@springio/antora-extensions": "1.11.1", 8 | "@springio/asciidoctor-extensions": "1.0.0-alpha.10" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /docs/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | org.springframework.cloud 7 | spring-cloud-bus-docs 8 | 9 | org.springframework.cloud 10 | spring-cloud-bus-parent 11 | 5.0.0-SNAPSHOT 12 | 13 | jar 14 | Spring Cloud Bus Docs 15 | Spring Cloud Bus Docs 16 | 17 | spring-cloud-bus 18 | ${basedir}/.. 19 | spring.cloud.bus.* 20 | 21 | none 22 | 23 | 24 | src/main/asciidoc 25 | 26 | 27 | 28 | enable-configuration-properties 29 | 30 | 31 | !disableConfigurationProperties 32 | 33 | 34 | 35 | 36 | ${project.groupId} 37 | spring-cloud-starter-bus-amqp 38 | 39 | 40 | ${project.groupId} 41 | spring-cloud-starter-bus-kafka 42 | 43 | 44 | 45 | 46 | docs 47 | 48 | 49 | 50 | src/main/antora/resources/antora-resources 51 | true 52 | 53 | 54 | 55 | 56 | pl.project13.maven 57 | git-commit-id-plugin 58 | 59 | 60 | org.apache.maven.plugins 61 | maven-dependency-plugin 62 | 63 | 64 | org.codehaus.mojo 65 | exec-maven-plugin 66 | 67 | 68 | io.spring.maven.antora 69 | antora-component-version-maven-plugin 70 | 71 | 72 | org.antora 73 | antora-maven-plugin 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-antrun-plugin 78 | 79 | 80 | maven-deploy-plugin 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /docs/src/main/antora/resources/antora-resources/antora.yml: -------------------------------------------------------------------------------- 1 | version: @antora-component.version@ 2 | prerelease: @antora-component.prerelease@ 3 | 4 | asciidoc: 5 | attributes: 6 | attribute-missing: 'warn' 7 | chomp: 'all' 8 | project-root: @maven.multiModuleProjectDirectory@ 9 | github-repo: @docs.main@ 10 | github-raw: https://raw.githubusercontent.com/spring-cloud/@docs.main@/@github-tag@ 11 | github-code: https://github.com/spring-cloud/@docs.main@/tree/@github-tag@ 12 | github-issues: https://github.com/spring-cloud/@docs.main@/issues/ 13 | github-wiki: https://github.com/spring-cloud/@docs.main@/wiki 14 | spring-cloud-version: @project.version@ 15 | github-tag: @github-tag@ 16 | version-type: @version-type@ 17 | docs-url: https://docs.spring.io/@docs.main@/docs/@project.version@ 18 | raw-docs-url: https://raw.githubusercontent.com/spring-cloud/@docs.main@/@github-tag@ 19 | project-version: @project.version@ 20 | project-name: @docs.main@ 21 | -------------------------------------------------------------------------------- /docs/src/main/asciidoc/README.adoc: -------------------------------------------------------------------------------- 1 | [[spring-cloud-bus]] 2 | = Spring Cloud Bus 3 | :page-section-summary-toc: 1 4 | 5 | 6 | [[quick-start]] 7 | == Quick Start 8 | 9 | 10 | [[building]] 11 | == Building 12 | 13 | include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/modules/ROOT/partials/building.adoc[] 14 | 15 | [[contributing]] 16 | == Contributing 17 | 18 | include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/modules/ROOT/partials/contributing.adoc[] 19 | -------------------------------------------------------------------------------- /docs/src/main/asciidoc/sagan-index.adoc: -------------------------------------------------------------------------------- 1 | 2 | Spring Cloud Bus links nodes of a distributed system with a lightweight message broker. This can then be used to broadcast state changes (e.g. configuration changes) or other management instructions. AMQP and Kafka broker implementations are included with the project. Alternatively, any link:https://spring.io/projects/spring-cloud-stream[Spring Cloud Stream] binder found on the classpath will work out of the box as a transport. 3 | 4 | ## Getting Started 5 | As long as Spring Cloud Bus AMQP and RabbitMQ are on the 6 | classpath any Spring Boot application will try to contact a RabbitMQ 7 | server on `localhost:5672` (the default value of 8 | `spring.rabbitmq.addresses`): 9 | 10 | ```java 11 | @Configuration 12 | @EnableAutoConfiguration 13 | @RestController 14 | public class Application { 15 | 16 | @RequestMapping("/") 17 | public String home() { 18 | return "Hello World"; 19 | } 20 | 21 | public static void main(String[] args) { 22 | SpringApplication.run(Application.class, args); 23 | } 24 | 25 | } 26 | ``` 27 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | spring-cloud-bus-parent 8 | 5.0.0-SNAPSHOT 9 | pom 10 | 11 | spring-cloud-bus-parent 12 | Spring Cloud Bus Parent 13 | 14 | 15 | org.springframework.cloud 16 | spring-cloud-build 17 | 5.0.0-SNAPSHOT 18 | 19 | 20 | 21 | 22 | spring-cloud-bus-dependencies 23 | spring-cloud-bus 24 | spring-cloud-bus-tests 25 | spring-cloud-starter-bus-amqp 26 | spring-cloud-starter-bus-kafka 27 | spring-cloud-starter-bus-stream 28 | docs 29 | 30 | 31 | 32 | 5.0.0-SNAPSHOT 33 | 4.3.1-SNAPSHOT 34 | 4.3.1-SNAPSHOT 35 | bus 36 | 37 | 38 | 39 | 40 | 41 | org.codehaus.mojo 42 | flatten-maven-plugin 43 | 44 | 45 | org.apache.maven.plugins 46 | maven-checkstyle-plugin 47 | 48 | 49 | io.spring.javaformat 50 | spring-javaformat-maven-plugin 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.apache.maven.plugins 59 | maven-checkstyle-plugin 60 | 61 | 62 | 63 | 64 | 65 | 66 | spring 67 | 68 | 69 | spring-snapshots 70 | Spring Snapshots 71 | https://repo.spring.io/snapshot 72 | 73 | true 74 | 75 | 76 | false 77 | 78 | 79 | 80 | spring-milestones 81 | Spring Milestones 82 | https://repo.spring.io/milestone 83 | 84 | false 85 | 86 | 87 | 88 | spring-releases 89 | Spring Releases 90 | https://repo.spring.io/release 91 | 92 | false 93 | 94 | 95 | 96 | 97 | 98 | spring-snapshots 99 | Spring Snapshots 100 | https://repo.spring.io/snapshot 101 | 102 | true 103 | 104 | 105 | false 106 | 107 | 108 | 109 | spring-milestones 110 | Spring Milestones 111 | https://repo.spring.io/milestone 112 | 113 | false 114 | 115 | 116 | 117 | spring-releases 118 | Spring Releases 119 | https://repo.spring.io/release 120 | 121 | false 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | org.springframework.cloud 131 | spring-cloud-bus-dependencies 132 | ${project.version} 133 | pom 134 | import 135 | 136 | 137 | org.springframework.cloud 138 | spring-cloud-commons-dependencies 139 | ${spring-cloud-commons.version} 140 | pom 141 | import 142 | 143 | 144 | org.springframework.cloud 145 | spring-cloud-test-support 146 | ${spring-cloud-commons.version} 147 | 148 | 149 | org.springframework.cloud 150 | spring-cloud-stream 151 | ${spring-cloud-stream.version} 152 | 153 | 154 | org.springframework.cloud 155 | spring-cloud-stream-dependencies 156 | ${spring-cloud-stream.version} 157 | pom 158 | import 159 | 160 | 161 | org.springframework.cloud 162 | spring-cloud-function-dependencies 163 | ${spring-cloud-function.version} 164 | pom 165 | import 166 | 167 | 168 | org.springframework.cloud 169 | spring-cloud-function-rsocket 170 | ${spring-cloud-function.version} 171 | 172 | 173 | 174 | 175 | 176 | https://github.com/spring-cloud/spring-cloud-bus 177 | scm:git:git://github.com/spring-cloud/spring-cloud-bus.git 178 | 179 | 180 | scm:git:ssh://git@github.com/spring-cloud/spring-cloud-bus.git 181 | 182 | HEAD 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /spring-cloud-bus-dependencies/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | spring-cloud-dependencies-parent 8 | org.springframework.cloud 9 | 5.0.0-SNAPSHOT 10 | 11 | 12 | spring-cloud-bus-dependencies 13 | 5.0.0-SNAPSHOT 14 | pom 15 | spring-cloud-bus-dependencies 16 | Spring Cloud Bus Dependencies 17 | 18 | 19 | 20 | org.springframework.cloud 21 | spring-cloud-starter-bus-amqp 22 | ${project.version} 23 | 24 | 25 | org.springframework.cloud 26 | spring-cloud-starter-bus-kafka 27 | ${project.version} 28 | 29 | 30 | org.springframework.cloud 31 | spring-cloud-starter-bus-stream 32 | ${project.version} 33 | 34 | 35 | org.springframework.cloud 36 | spring-cloud-bus 37 | ${project.version} 38 | 39 | 40 | 41 | 42 | 43 | spring 44 | 45 | 46 | spring-snapshots 47 | Spring Snapshots 48 | https://repo.spring.io/snapshot 49 | 50 | true 51 | 52 | 53 | false 54 | 55 | 56 | 57 | spring-milestones 58 | Spring Milestones 59 | https://repo.spring.io/milestone 60 | 61 | false 62 | 63 | 64 | 65 | spring-releases 66 | Spring Releases 67 | https://repo.spring.io/release 68 | 69 | false 70 | 71 | 72 | 73 | 74 | 75 | spring-snapshots 76 | Spring Snapshots 77 | https://repo.spring.io/snapshot 78 | 79 | true 80 | 81 | 82 | false 83 | 84 | 85 | 86 | spring-milestones 87 | Spring Milestones 88 | https://repo.spring.io/milestone 89 | 90 | false 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /spring-cloud-bus-tests/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | spring-cloud-bus-tests 8 | jar 9 | 10 | spring-cloud-bus-tests 11 | Spring Cloud Bus Tests 12 | 13 | 14 | org.springframework.cloud 15 | spring-cloud-bus-parent 16 | 5.0.0-SNAPSHOT 17 | .. 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | test 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-webflux 29 | test 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-actuator 34 | test 35 | 36 | 37 | org.springframework.cloud 38 | spring-cloud-starter-bus-amqp 39 | test 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-test 44 | test 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-testcontainers 49 | test 50 | 51 | 52 | org.testcontainers 53 | junit-jupiter 54 | test 55 | 56 | 57 | org.testcontainers 58 | rabbitmq 59 | test 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /spring-cloud-bus-tests/src/test/java/org/springframework/cloud/bus/BusAmqpIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2020 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import java.util.HashMap; 20 | import java.util.concurrent.CountDownLatch; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import org.junit.jupiter.api.AfterAll; 24 | import org.junit.jupiter.api.BeforeAll; 25 | import org.junit.jupiter.api.Test; 26 | import org.testcontainers.containers.RabbitMQContainer; 27 | import org.testcontainers.junit.jupiter.Container; 28 | import org.testcontainers.junit.jupiter.Testcontainers; 29 | 30 | import org.springframework.beans.factory.annotation.Autowired; 31 | import org.springframework.boot.SpringBootConfiguration; 32 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 33 | import org.springframework.boot.builder.SpringApplicationBuilder; 34 | import org.springframework.boot.test.context.SpringBootTest; 35 | import org.springframework.cloud.bus.event.EnvironmentChangeRemoteApplicationEvent; 36 | import org.springframework.cloud.stream.binder.ProducerProperties; 37 | import org.springframework.cloud.stream.config.BindingServiceProperties; 38 | import org.springframework.context.ApplicationListener; 39 | import org.springframework.context.ConfigurableApplicationContext; 40 | import org.springframework.test.context.DynamicPropertyRegistry; 41 | import org.springframework.test.context.DynamicPropertySource; 42 | import org.springframework.test.web.reactive.server.WebTestClient; 43 | 44 | import static org.assertj.core.api.Assertions.assertThat; 45 | import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; 46 | 47 | @SpringBootTest(webEnvironment = RANDOM_PORT, properties = { "management.endpoints.web.exposure.include=*", 48 | "spring.cloud.stream.bindings.springCloudBusOutput.producer.errorChannelEnabled=true", 49 | "logging.level.org.springframework.cloud.bus=TRACE", "spring.cloud.bus.id=app:1", 50 | "spring.autoconfigure.exclude=org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration" }) 51 | @Testcontainers 52 | public class BusAmqpIntegrationTests { 53 | 54 | @Container 55 | private static final RabbitMQContainer rabbitMQContainer = new RabbitMQContainer( 56 | "rabbitmq:3.7.25-management-alpine"); 57 | 58 | private static ConfigurableApplicationContext context; 59 | 60 | @Autowired 61 | private BindingServiceProperties bindingServiceProperties; 62 | 63 | @DynamicPropertySource 64 | static void properties(DynamicPropertyRegistry registry) { 65 | registry.add("spring.rabbitmq.host", rabbitMQContainer::getHost); 66 | registry.add("spring.rabbitmq.port", rabbitMQContainer::getAmqpPort); 67 | } 68 | 69 | @BeforeAll 70 | static void before() { 71 | context = new SpringApplicationBuilder(TestConfig.class).properties("server.port=0", 72 | "spring.rabbitmq.host=" + rabbitMQContainer.getHost(), 73 | "spring.rabbitmq.port=" + rabbitMQContainer.getAmqpPort(), 74 | "management.endpoints.web.exposure.include=*", "spring.cloud.bus.id=app:2", 75 | "spring.autoconfigure.exclude=org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration") 76 | .run(); 77 | } 78 | 79 | @AfterAll 80 | static void after() { 81 | if (context != null) { 82 | context.close(); 83 | } 84 | } 85 | 86 | @Test 87 | void remoteEventsAreSentViaAmqp(@Autowired WebTestClient client, @Autowired TestConfig testConfig) 88 | throws InterruptedException { 89 | assertThat(rabbitMQContainer.isRunning()); 90 | HashMap map = new HashMap<>(); 91 | map.put("name", "foo"); 92 | map.put("value", "bar"); 93 | client.post().uri("/actuator/busenv").bodyValue(map).exchange().expectStatus().is2xxSuccessful(); 94 | TestConfig remoteTestConfig = context.getBean(TestConfig.class); 95 | assertThat(remoteTestConfig.latch.await(5, TimeUnit.SECONDS)).isTrue(); 96 | assertThat(testConfig.latch.await(5, TimeUnit.SECONDS)).isTrue(); 97 | ProducerProperties producerProperties = bindingServiceProperties.getProducerProperties(BusConstants.OUTPUT); 98 | assertThat(producerProperties.isErrorChannelEnabled()).isTrue(); 99 | } 100 | 101 | @SpringBootConfiguration 102 | @EnableAutoConfiguration 103 | static class TestConfig implements ApplicationListener { 104 | 105 | CountDownLatch latch = new CountDownLatch(1); 106 | 107 | @Override 108 | public void onApplicationEvent(EnvironmentChangeRemoteApplicationEvent event) { 109 | latch.countDown(); 110 | } 111 | 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /spring-cloud-bus-tests/src/test/java/org/springframework/cloud/bus/BusJmxEndpointTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.boot.SpringBootConfiguration; 23 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 24 | import org.springframework.boot.test.context.SpringBootTest; 25 | import org.springframework.cloud.bus.endpoint.EnvironmentBusEndpoint; 26 | import org.springframework.cloud.bus.endpoint.RefreshBusEndpoint; 27 | 28 | import static org.assertj.core.api.Assertions.assertThat; 29 | 30 | @SpringBootTest(properties = { "spring.jmx.enabled=true", "endpoints.default.jmx.enabled=true", 31 | "management.endpoints..jmx.exposure.include=busrefresh,busenv", "debug=true" }) 32 | public class BusJmxEndpointTests { 33 | 34 | @Autowired(required = false) 35 | private RefreshBusEndpoint refreshBusEndpoint; 36 | 37 | @Autowired(required = false) 38 | private EnvironmentBusEndpoint environmentBusEndpoint; 39 | 40 | @Test 41 | public void contextLoads() { 42 | assertThat(this.refreshBusEndpoint).isNotNull(); 43 | assertThat(this.environmentBusEndpoint).isNotNull(); 44 | } 45 | 46 | @SpringBootConfiguration 47 | @EnableAutoConfiguration 48 | protected static class TestConfig { 49 | 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /spring-cloud-bus-tests/src/test/java/org/springframework/cloud/bus/ShutdownListenerIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import java.util.concurrent.CountDownLatch; 20 | 21 | import org.junit.jupiter.api.AfterAll; 22 | import org.junit.jupiter.api.BeforeAll; 23 | import org.junit.jupiter.api.Test; 24 | import org.testcontainers.containers.RabbitMQContainer; 25 | import org.testcontainers.junit.jupiter.Container; 26 | import org.testcontainers.junit.jupiter.Testcontainers; 27 | 28 | import org.springframework.beans.factory.annotation.Autowired; 29 | import org.springframework.boot.SpringBootConfiguration; 30 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 31 | import org.springframework.boot.builder.SpringApplicationBuilder; 32 | import org.springframework.boot.test.context.SpringBootTest; 33 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection; 34 | import org.springframework.cloud.bus.event.EnvironmentChangeRemoteApplicationEvent; 35 | import org.springframework.context.ApplicationListener; 36 | import org.springframework.context.ConfigurableApplicationContext; 37 | import org.springframework.test.web.reactive.server.WebTestClient; 38 | 39 | import static org.assertj.core.api.Assertions.assertThat; 40 | import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; 41 | 42 | /** 43 | * @author Ryan Baxter 44 | */ 45 | @SpringBootTest(webEnvironment = RANDOM_PORT, 46 | properties = { "management.endpoints.web.exposure.include=*", 47 | "spring.cloud.stream.bindings.springCloudBusOutput.producer.errorChannelEnabled=true", 48 | "logging.level.org.springframework.cloud.bus=TRACE", "spring.cloud.bus.id=app:1" }) 49 | @Testcontainers 50 | public class ShutdownListenerIntegrationTests { 51 | 52 | private static ConfigurableApplicationContext context; 53 | 54 | @Container 55 | @ServiceConnection 56 | private static final RabbitMQContainer rabbitMQContainer = new RabbitMQContainer("rabbitmq:4.0-management"); 57 | 58 | @BeforeAll 59 | static void before() { 60 | context = new SpringApplicationBuilder(TestConfig.class) 61 | .properties("server.port=0", "spring.rabbitmq.host=" + rabbitMQContainer.getHost(), 62 | "spring.rabbitmq.port=" + rabbitMQContainer.getAmqpPort(), 63 | "management.endpoints.web.exposure.include=*", "spring.cloud.bus.id=app:2", "debug=true") 64 | .run(); 65 | } 66 | 67 | @AfterAll 68 | static void after() { 69 | if (context != null) { 70 | context.close(); 71 | } 72 | } 73 | 74 | @Test 75 | void testShutdown(@Autowired WebTestClient client) { 76 | assertThat(rabbitMQContainer.isRunning()); 77 | client.post().uri("/actuator/busshutdown/app:2").exchange().expectStatus().is2xxSuccessful(); 78 | assertThat(context.isClosed()); 79 | } 80 | 81 | @SpringBootConfiguration 82 | @EnableAutoConfiguration 83 | static class TestConfig implements ApplicationListener { 84 | 85 | CountDownLatch latch = new CountDownLatch(1); 86 | 87 | @Override 88 | public void onApplicationEvent(EnvironmentChangeRemoteApplicationEvent event) { 89 | latch.countDown(); 90 | } 91 | 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /spring-cloud-bus-tests/src/test/java/org/springframework/cloud/bus/jackson/BusJacksonIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.jackson; 18 | 19 | import java.util.Collection; 20 | import java.util.Date; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.Set; 25 | import java.util.concurrent.ConcurrentHashMap; 26 | 27 | import com.fasterxml.jackson.databind.SerializationFeature; 28 | import org.junit.jupiter.api.Test; 29 | 30 | import org.springframework.beans.factory.annotation.Autowired; 31 | import org.springframework.boot.SpringBootConfiguration; 32 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 33 | import org.springframework.boot.test.context.SpringBootTest; 34 | import org.springframework.boot.test.web.client.TestRestTemplate; 35 | import org.springframework.boot.test.web.server.LocalServerPort; 36 | import org.springframework.cloud.bus.ServiceMatcher; 37 | import org.springframework.cloud.bus.event.RemoteApplicationEvent; 38 | import org.springframework.context.ApplicationEventPublisher; 39 | import org.springframework.context.event.EventListener; 40 | import org.springframework.http.HttpStatus; 41 | import org.springframework.http.ResponseEntity; 42 | import org.springframework.test.annotation.DirtiesContext; 43 | import org.springframework.web.bind.annotation.GetMapping; 44 | import org.springframework.web.bind.annotation.PathVariable; 45 | import org.springframework.web.bind.annotation.PutMapping; 46 | import org.springframework.web.bind.annotation.RestController; 47 | 48 | import static org.assertj.core.api.Assertions.assertThat; 49 | import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; 50 | 51 | @SpringBootTest(properties = "spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS:true", 52 | webEnvironment = RANDOM_PORT) 53 | @DirtiesContext 54 | public class BusJacksonIntegrationTests { 55 | 56 | @LocalServerPort 57 | private int port; 58 | 59 | @Autowired 60 | private TestRestTemplate rest; 61 | 62 | @Autowired 63 | private BusJacksonMessageConverter converter; 64 | 65 | @Test 66 | @SuppressWarnings("unchecked") 67 | public void testCustomEventSerializes() { 68 | assertThat(this.converter.isMapperCreated()).isFalse(); 69 | 70 | // set by configuration 71 | assertThat(this.converter.getMapper() 72 | .getSerializationConfig() 73 | .isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)).isTrue(); 74 | 75 | Map map = this.rest.getForObject("http://localhost:" + this.port + "/date", Map.class); 76 | assertThat(map).containsOnlyKeys("date"); 77 | assertThat(map.get("date")).isInstanceOf(Long.class); 78 | 79 | this.rest.put("http://localhost:" + this.port + "/names" + "/foo", null); 80 | this.rest.put("http://localhost:" + this.port + "/names" + "/bar", null); 81 | 82 | ResponseEntity response = this.rest.getForEntity("http://localhost:" + this.port + "/names", List.class); 83 | assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); 84 | assertThat(response.getBody()).contains("foo", "bar"); 85 | } 86 | 87 | public static class NameEvent extends RemoteApplicationEvent { 88 | 89 | private String name; 90 | 91 | protected NameEvent() { 92 | } 93 | 94 | public NameEvent(Object source, String originService, String name) { 95 | super(source, originService); 96 | this.name = name; 97 | } 98 | 99 | public String getName() { 100 | return this.name; 101 | } 102 | 103 | public void setName(String name) { 104 | this.name = name; 105 | } 106 | 107 | } 108 | 109 | @RestController 110 | @EnableAutoConfiguration 111 | @SpringBootConfiguration 112 | @RemoteApplicationEventScan 113 | protected static class Config { 114 | 115 | final private Set names = ConcurrentHashMap.newKeySet(); 116 | 117 | @Autowired 118 | private ServiceMatcher busServiceMatcher; 119 | 120 | @Autowired 121 | private ApplicationEventPublisher publisher; 122 | 123 | @GetMapping("/names") 124 | public Collection names() { 125 | return this.names; 126 | } 127 | 128 | @PutMapping("/names/{name}") 129 | public void sayName(@PathVariable String name) { 130 | this.names.add(name); 131 | this.publisher.publishEvent(new NameEvent(this, this.busServiceMatcher.getBusId(), name)); 132 | } 133 | 134 | @GetMapping("/date") 135 | public Map testTimeJsonSerialization() { 136 | Map map = new HashMap<>(); 137 | map.put("date", new Date()); 138 | return map; 139 | } 140 | 141 | @EventListener 142 | public void handleNameSaid(NameEvent event) { 143 | this.names.add(event.getName()); 144 | } 145 | 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /spring-cloud-bus/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | spring-cloud-bus 8 | jar 9 | 10 | spring-cloud-bus 11 | Spring Cloud Bus 12 | 13 | 14 | org.springframework.cloud 15 | spring-cloud-bus-parent 16 | 5.0.0-SNAPSHOT 17 | .. 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-configuration-processor 24 | true 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-actuator 29 | true 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-web 34 | true 35 | 36 | 37 | org.springframework.cloud 38 | spring-cloud-starter 39 | 40 | 41 | org.springframework.cloud 42 | spring-cloud-stream 43 | true 44 | 45 | 46 | org.springframework.integration 47 | spring-integration-core 48 | 49 | 50 | com.fasterxml.jackson.dataformat 51 | jackson-dataformat-cbor 52 | true 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-autoconfigure-processor 57 | true 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter-test 62 | test 63 | 64 | 65 | org.junit.vintage 66 | junit-vintage-engine 67 | test 68 | 69 | 70 | org.springframework.cloud 71 | spring-cloud-test-support 72 | test 73 | 74 | 75 | org.springframework.cloud 76 | spring-cloud-stream-test-binder 77 | ${spring-cloud-stream.version} 78 | test 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/BusAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import org.springframework.beans.factory.ObjectProvider; 20 | import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; 21 | import org.springframework.boot.actuate.endpoint.annotation.Endpoint; 22 | import org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository; 23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 25 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 26 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 27 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 28 | import org.springframework.cloud.bus.endpoint.EnvironmentBusEndpoint; 29 | import org.springframework.cloud.bus.event.Destination; 30 | import org.springframework.cloud.bus.event.EnvironmentChangeListener; 31 | import org.springframework.cloud.bus.event.PathDestinationFactory; 32 | import org.springframework.cloud.bus.event.TraceListener; 33 | import org.springframework.cloud.context.environment.EnvironmentManager; 34 | import org.springframework.context.ApplicationEventPublisher; 35 | import org.springframework.context.annotation.Bean; 36 | import org.springframework.context.annotation.Configuration; 37 | 38 | import static org.springframework.cloud.bus.BusConstants.BUS_CONSUMER; 39 | 40 | /** 41 | * @author Spencer Gibb 42 | * @author Dave Syer 43 | */ 44 | @Configuration(proxyBeanMethods = false) 45 | @ConditionalOnBusEnabled 46 | @EnableConfigurationProperties(BusProperties.class) 47 | public class BusAutoConfiguration { 48 | 49 | @Bean 50 | @ConditionalOnMissingBean(Destination.Factory.class) 51 | public PathDestinationFactory pathDestinationFactory() { 52 | return new PathDestinationFactory(); 53 | } 54 | 55 | @Bean 56 | @ConditionalOnMissingBean 57 | public RemoteApplicationEventListener busRemoteApplicationEventListener(ServiceMatcher serviceMatcher, 58 | BusBridge busBridge) { 59 | return new RemoteApplicationEventListener(serviceMatcher, busBridge); 60 | } 61 | 62 | @Bean 63 | @ConditionalOnMissingBean(name = BUS_CONSUMER) 64 | public BusConsumer busConsumer(ApplicationEventPublisher applicationEventPublisher, ServiceMatcher serviceMatcher, 65 | ObjectProvider busBridge, BusProperties properties, Destination.Factory destinationFactory) { 66 | return new BusConsumer(applicationEventPublisher, serviceMatcher, busBridge, properties, destinationFactory); 67 | } 68 | 69 | @Configuration(proxyBeanMethods = false) 70 | @ConditionalOnClass({ Endpoint.class }) 71 | @ConditionalOnBean(HttpExchangeRepository.class) 72 | @ConditionalOnProperty(BusProperties.PREFIX + ".trace.enabled") 73 | protected static class BusAckTraceConfiguration { 74 | 75 | @Bean 76 | @ConditionalOnMissingBean 77 | public TraceListener ackTraceListener(HttpExchangeRepository repository) { 78 | return new TraceListener(repository); 79 | } 80 | 81 | } 82 | 83 | @Configuration(proxyBeanMethods = false) 84 | @ConditionalOnClass(EnvironmentManager.class) 85 | @ConditionalOnBean(EnvironmentManager.class) 86 | protected static class BusEnvironmentConfiguration { 87 | 88 | @Bean 89 | @ConditionalOnProperty(value = "spring.cloud.bus.env.enabled", matchIfMissing = true) 90 | public EnvironmentChangeListener environmentChangeListener() { 91 | return new EnvironmentChangeListener(); 92 | } 93 | 94 | @Configuration(proxyBeanMethods = false) 95 | @ConditionalOnClass(Endpoint.class) 96 | protected static class EnvironmentBusEndpointConfiguration { 97 | 98 | @Bean 99 | @ConditionalOnAvailableEndpoint 100 | public EnvironmentBusEndpoint environmentBusEndpoint(ApplicationEventPublisher publisher, BusProperties bus, 101 | Destination.Factory destinationFactory) { 102 | return new EnvironmentBusEndpoint(publisher, bus.getId(), destinationFactory); 103 | } 104 | 105 | } 106 | 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/BusBridge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2020 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import org.springframework.cloud.bus.event.RemoteApplicationEvent; 20 | 21 | public interface BusBridge { 22 | 23 | void send(RemoteApplicationEvent event); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/BusConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | /** 20 | * @author Dave Syer 21 | * 22 | */ 23 | public abstract class BusConstants { 24 | 25 | /** 26 | * Name of the input channel for Spring Cloud Bus. 27 | */ 28 | public static final String INPUT = "springCloudBusInput"; 29 | 30 | /** 31 | * Name of the output channel for Spring Cloud Bus. 32 | */ 33 | public static final String OUTPUT = "springCloudBusOutput"; 34 | 35 | /** 36 | * Name of the binding destination for Spring Cloud Bus. 37 | */ 38 | public static final String DESTINATION = "springCloudBus"; 39 | 40 | /** 41 | * Name of the Spring Cloud Bus function. 42 | */ 43 | public static final String BUS_CONSUMER = "busConsumer"; 44 | 45 | } 46 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/BusConsumer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2020 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import java.util.function.Consumer; 20 | 21 | import org.apache.commons.logging.Log; 22 | import org.apache.commons.logging.LogFactory; 23 | 24 | import org.springframework.beans.factory.ObjectProvider; 25 | import org.springframework.cloud.bus.event.AckRemoteApplicationEvent; 26 | import org.springframework.cloud.bus.event.Destination; 27 | import org.springframework.cloud.bus.event.RemoteApplicationEvent; 28 | import org.springframework.cloud.bus.event.SentApplicationEvent; 29 | import org.springframework.context.ApplicationEventPublisher; 30 | 31 | public class BusConsumer implements Consumer { 32 | 33 | private final Log log = LogFactory.getLog(getClass()); 34 | 35 | private final ApplicationEventPublisher publisher; 36 | 37 | private final ServiceMatcher serviceMatcher; 38 | 39 | private final ObjectProvider busBridge; 40 | 41 | private final BusProperties properties; 42 | 43 | private final Destination.Factory destinationFactory; 44 | 45 | public BusConsumer(ApplicationEventPublisher publisher, ServiceMatcher serviceMatcher, 46 | ObjectProvider busBridge, BusProperties properties, Destination.Factory destinationFactory) { 47 | this.publisher = publisher; 48 | this.serviceMatcher = serviceMatcher; 49 | this.busBridge = busBridge; 50 | this.properties = properties; 51 | this.destinationFactory = destinationFactory; 52 | } 53 | 54 | @Override 55 | public void accept(RemoteApplicationEvent event) { 56 | if (event instanceof AckRemoteApplicationEvent) { 57 | if (this.properties.getTrace().isEnabled() && !this.serviceMatcher.isFromSelf(event) 58 | && this.publisher != null) { 59 | this.publisher.publishEvent(event); 60 | } 61 | // If it's an ACK we are finished processing at this point 62 | return; 63 | } 64 | 65 | if (log.isDebugEnabled()) { 66 | log.debug("Received remote event from bus: " + event); 67 | } 68 | 69 | if (this.serviceMatcher.isForSelf(event) && this.publisher != null) { 70 | if (!this.serviceMatcher.isFromSelf(event)) { 71 | this.publisher.publishEvent(event); 72 | } 73 | if (this.properties.getAck().isEnabled()) { 74 | AckRemoteApplicationEvent ack = new AckRemoteApplicationEvent(this, this.serviceMatcher.getBusId(), 75 | destinationFactory.getDestination(this.properties.getAck().getDestinationService()), 76 | event.getDestinationService(), event.getId(), event.getClass()); 77 | this.busBridge.ifAvailable(bridge -> bridge.send(ack)); 78 | this.publisher.publishEvent(ack); 79 | } 80 | } 81 | if (this.properties.getTrace().isEnabled() && this.publisher != null) { 82 | // We are set to register sent events so publish it for local consumption, 83 | // irrespective of the origin 84 | this.publisher.publishEvent(new SentApplicationEvent(this, event.getOriginService(), 85 | event.getDestinationService(), event.getId(), event.getClass())); 86 | } 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/BusEnvironmentPostProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | import org.springframework.boot.SpringApplication; 23 | import org.springframework.boot.env.EnvironmentPostProcessor; 24 | import org.springframework.cloud.commons.util.IdUtils; 25 | import org.springframework.cloud.function.context.FunctionProperties; 26 | import org.springframework.core.env.ConfigurableEnvironment; 27 | import org.springframework.core.env.MapPropertySource; 28 | import org.springframework.core.env.MutablePropertySources; 29 | import org.springframework.core.env.PropertySource; 30 | import org.springframework.util.StringUtils; 31 | 32 | import static org.springframework.cloud.bus.BusProperties.PREFIX; 33 | 34 | /** 35 | * {@link EnvironmentPostProcessor} that sets the default properties for the Bus. 36 | * 37 | * @author Dave Syer 38 | * @since 1.0.0 39 | */ 40 | public class BusEnvironmentPostProcessor implements EnvironmentPostProcessor { 41 | 42 | static final String DEFAULTS_PROPERTY_SOURCE_NAME = "springCloudBusDefaultProperties"; 43 | 44 | static final String OVERRIDES_PROPERTY_SOURCE_NAME = "springCloudBusOverridesProperties"; 45 | 46 | private static final String FN_DEF_PROP = FunctionProperties.PREFIX + ".definition"; 47 | 48 | @Override 49 | public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { 50 | if (environment.containsProperty(ConditionalOnBusEnabled.SPRING_CLOUD_BUS_ENABLED)) { 51 | if (Boolean.FALSE.toString() 52 | .equalsIgnoreCase(environment.getProperty(ConditionalOnBusEnabled.SPRING_CLOUD_BUS_ENABLED))) { 53 | return; 54 | } 55 | } 56 | Map overrides = new HashMap<>(); 57 | String definition = BusConstants.BUS_CONSUMER; 58 | if (environment.containsProperty(FN_DEF_PROP)) { 59 | String property = environment.getProperty(FN_DEF_PROP); 60 | if (property != null && property.contains(BusConstants.BUS_CONSUMER)) { 61 | // in the case that EnvironmentPostProcessor are run more than once. 62 | return; 63 | } 64 | definition = property + ";" + definition; 65 | } 66 | overrides.put(FN_DEF_PROP, definition); 67 | addOrReplace(environment.getPropertySources(), overrides, OVERRIDES_PROPERTY_SOURCE_NAME, true); 68 | 69 | Map defaults = new HashMap<>(); 70 | defaults.put("spring.cloud.stream.function.bindings." + BusConstants.BUS_CONSUMER + "-in-0", 71 | BusConstants.INPUT); 72 | String destination = environment.getProperty(PREFIX + ".destination", BusConstants.DESTINATION); 73 | defaults.put("spring.cloud.stream.bindings." + BusConstants.INPUT + ".destination", destination); 74 | defaults.put("spring.cloud.stream.bindings." + BusConstants.OUTPUT + ".destination", destination); 75 | if (!environment.containsProperty(PREFIX + ".id")) { 76 | String unresolvedServiceId = IdUtils.getUnresolvedServiceId(); 77 | if (StringUtils.hasText(environment.getProperty("spring.profiles.active"))) { 78 | unresolvedServiceId = IdUtils.getUnresolvedServiceIdWithActiveProfiles(); 79 | } 80 | defaults.put(PREFIX + ".id", unresolvedServiceId); 81 | } 82 | addOrReplace(environment.getPropertySources(), defaults, DEFAULTS_PROPERTY_SOURCE_NAME, false); 83 | } 84 | 85 | public static void addOrReplace(MutablePropertySources propertySources, Map map, 86 | String propertySourceName, boolean first) { 87 | MapPropertySource target = null; 88 | if (propertySources.contains(propertySourceName)) { 89 | PropertySource source = propertySources.get(propertySourceName); 90 | if (source instanceof MapPropertySource) { 91 | target = (MapPropertySource) source; 92 | for (String key : map.keySet()) { 93 | if (!target.containsProperty(key)) { 94 | target.getSource().put(key, map.get(key)); 95 | } 96 | } 97 | } 98 | } 99 | if (target == null) { 100 | target = new MapPropertySource(propertySourceName, map); 101 | } 102 | if (!propertySources.contains(propertySourceName)) { 103 | if (first) { 104 | propertySources.addFirst(target); 105 | } 106 | else { 107 | propertySources.addLast(target); 108 | } 109 | } 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/BusPathMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import java.lang.annotation.Documented; 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Inherited; 22 | import java.lang.annotation.Retention; 23 | import java.lang.annotation.RetentionPolicy; 24 | import java.lang.annotation.Target; 25 | 26 | import org.springframework.beans.factory.annotation.Qualifier; 27 | 28 | /** 29 | * Qualifier annotation for components to do with matching paths in the bus. 30 | * 31 | * @author Dave Syer 32 | * 33 | */ 34 | @Qualifier 35 | @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER }) 36 | @Retention(RetentionPolicy.RUNTIME) 37 | @Inherited 38 | @Documented 39 | public @interface BusPathMatcher { 40 | 41 | } 42 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/BusProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import org.springframework.boot.context.properties.ConfigurationProperties; 20 | import org.springframework.core.style.ToStringCreator; 21 | import org.springframework.util.MimeType; 22 | import org.springframework.util.MimeTypeUtils; 23 | 24 | /** 25 | * @author Dave Syer 26 | * 27 | */ 28 | @ConfigurationProperties(BusProperties.PREFIX) 29 | public class BusProperties { 30 | 31 | /** 32 | * Configuration prefix for spring cloud bus. 33 | */ 34 | public static final String PREFIX = "spring.cloud.bus"; 35 | 36 | /** 37 | * Properties related to acks. 38 | */ 39 | private final Ack ack = new Ack(); 40 | 41 | /** 42 | * Properties related to tracing of acks. 43 | */ 44 | private final Trace trace = new Trace(); 45 | 46 | /** 47 | * Name of Spring Cloud Stream destination for messages. 48 | */ 49 | private String destination = BusConstants.DESTINATION; 50 | 51 | /** 52 | * The identifier for this application instance. 53 | */ 54 | private String id = "application"; 55 | 56 | /** 57 | * The bus mime-type. 58 | */ 59 | private MimeType contentType = MimeTypeUtils.APPLICATION_JSON; 60 | 61 | /** 62 | * Flag to indicate that the bus is enabled. 63 | */ 64 | private boolean enabled = true; 65 | 66 | public Ack getAck() { 67 | return this.ack; 68 | } 69 | 70 | public Trace getTrace() { 71 | return this.trace; 72 | } 73 | 74 | public String getDestination() { 75 | return this.destination; 76 | } 77 | 78 | public void setDestination(String destination) { 79 | this.destination = destination; 80 | } 81 | 82 | public boolean isEnabled() { 83 | return this.enabled; 84 | } 85 | 86 | public void setEnabled(boolean enabled) { 87 | this.enabled = enabled; 88 | } 89 | 90 | public String getId() { 91 | return this.id; 92 | } 93 | 94 | public void setId(String id) { 95 | this.id = id; 96 | } 97 | 98 | public MimeType getContentType() { 99 | return this.contentType; 100 | } 101 | 102 | public void setContentType(MimeType contentType) { 103 | this.contentType = contentType; 104 | } 105 | 106 | @Override 107 | public String toString() { 108 | return new ToStringCreator(this).append("ack", ack) 109 | .append("trace", trace) 110 | .append("destination", destination) 111 | .append("id", id) 112 | .append("contentType", contentType) 113 | .append("enabled", enabled) 114 | .toString(); 115 | 116 | } 117 | 118 | /** 119 | * Spring Cloud Bus properties related to acknowledgments. 120 | */ 121 | public static class Ack { 122 | 123 | /** 124 | * Flag to switch off acks (default on). 125 | */ 126 | private boolean enabled = true; 127 | 128 | /** 129 | * Service that wants to listen to acks. By default null (meaning all services). 130 | */ 131 | private String destinationService; 132 | 133 | public boolean isEnabled() { 134 | return this.enabled; 135 | } 136 | 137 | public void setEnabled(boolean enabled) { 138 | this.enabled = enabled; 139 | } 140 | 141 | public String getDestinationService() { 142 | return this.destinationService; 143 | } 144 | 145 | public void setDestinationService(String destinationService) { 146 | this.destinationService = destinationService; 147 | } 148 | 149 | @Override 150 | public String toString() { 151 | return new ToStringCreator(this).append("enabled", enabled) 152 | .append("destinationService", destinationService) 153 | .toString(); 154 | } 155 | 156 | } 157 | 158 | /** 159 | * Spring Cloud Bus trace properties. 160 | */ 161 | public static class Trace { 162 | 163 | /** 164 | * Flag to switch on tracing of acks (default off). 165 | */ 166 | private boolean enabled = false; 167 | 168 | public boolean isEnabled() { 169 | return this.enabled; 170 | } 171 | 172 | public void setEnabled(boolean enabled) { 173 | this.enabled = enabled; 174 | } 175 | 176 | @Override 177 | public String toString() { 178 | return new ToStringCreator(this).append("enabled", enabled).toString(); 179 | } 180 | 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/BusRefreshAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; 20 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 22 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 24 | import org.springframework.cloud.bus.endpoint.RefreshBusEndpoint; 25 | import org.springframework.cloud.bus.event.Destination; 26 | import org.springframework.cloud.bus.event.RefreshListener; 27 | import org.springframework.cloud.context.refresh.ContextRefresher; 28 | import org.springframework.context.ApplicationEventPublisher; 29 | import org.springframework.context.annotation.Bean; 30 | import org.springframework.context.annotation.Configuration; 31 | 32 | /** 33 | * @author Ryan Baxter 34 | */ 35 | @Configuration(proxyBeanMethods = false) 36 | @ConditionalOnBusEnabled 37 | @AutoConfigureAfter(name = { "org.springframework.cloud.autoconfigure.RefreshAutoConfiguration" }) 38 | public class BusRefreshAutoConfiguration { 39 | 40 | @Bean 41 | @ConditionalOnProperty(value = "spring.cloud.bus.refresh.enabled", matchIfMissing = true) 42 | @ConditionalOnBean(ContextRefresher.class) 43 | public RefreshListener refreshListener(ContextRefresher contextRefresher, ServiceMatcher serviceMatcher) { 44 | return new RefreshListener(contextRefresher, serviceMatcher); 45 | } 46 | 47 | @Configuration(proxyBeanMethods = false) 48 | @ConditionalOnClass(name = { "org.springframework.boot.actuate.endpoint.annotation.Endpoint", 49 | "org.springframework.cloud.context.scope.refresh.RefreshScope" }) 50 | protected static class BusRefreshEndpointConfiguration { 51 | 52 | @Bean 53 | @ConditionalOnAvailableEndpoint 54 | public RefreshBusEndpoint refreshBusEndpoint(ApplicationEventPublisher publisher, BusProperties bus, 55 | Destination.Factory destinationFactory) { 56 | return new RefreshBusEndpoint(publisher, bus.getId(), destinationFactory); 57 | } 58 | 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/BusShutdownAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 20 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 22 | import org.springframework.cloud.bus.endpoint.ShutdownBusEndpoint; 23 | import org.springframework.cloud.bus.event.Destination; 24 | import org.springframework.cloud.bus.event.ShutdownListener; 25 | import org.springframework.context.ApplicationEventPublisher; 26 | import org.springframework.context.annotation.Bean; 27 | import org.springframework.context.annotation.Configuration; 28 | 29 | /** 30 | * @author Ryan Baxter 31 | */ 32 | @Configuration(proxyBeanMethods = false) 33 | @ConditionalOnBusEnabled 34 | public class BusShutdownAutoConfiguration { 35 | 36 | @Bean 37 | @ConditionalOnProperty(value = "spring.cloud.bus.shutdown.enabled", matchIfMissing = true) 38 | @ConditionalOnMissingBean 39 | public ShutdownListener shutdownListener(ServiceMatcher serviceMatcher) { 40 | return new ShutdownListener(serviceMatcher); 41 | } 42 | 43 | @Configuration(proxyBeanMethods = false) 44 | @ConditionalOnClass(name = { "org.springframework.boot.actuate.endpoint.annotation.Endpoint" }) 45 | protected static class BusShutdownEndpointConfiguration { 46 | 47 | @Bean 48 | public ShutdownBusEndpoint shutdownBusEndpoint(ApplicationEventPublisher publisher, BusProperties bus, 49 | Destination.Factory destinationFactory) { 50 | return new ShutdownBusEndpoint(publisher, bus.getId(), destinationFactory); 51 | } 52 | 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/BusStreamAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2020 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 20 | import org.springframework.boot.autoconfigure.AutoConfigureBefore; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 22 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 23 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 24 | import org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration; 25 | import org.springframework.cloud.stream.config.BindingServiceConfiguration; 26 | import org.springframework.cloud.stream.function.StreamBridge; 27 | import org.springframework.context.annotation.Bean; 28 | import org.springframework.context.annotation.Configuration; 29 | 30 | @Configuration(proxyBeanMethods = false) 31 | @ConditionalOnBusEnabled 32 | @ConditionalOnClass({ StreamBridge.class, BindingServiceConfiguration.class }) 33 | @EnableConfigurationProperties(BusProperties.class) 34 | @AutoConfigureBefore({ BindingServiceConfiguration.class, BusAutoConfiguration.class }) 35 | // so stream bindings work properly 36 | @AutoConfigureAfter({ LifecycleMvcEndpointAutoConfiguration.class, PathServiceMatcherAutoConfiguration.class }) 37 | public class BusStreamAutoConfiguration { 38 | 39 | @Bean 40 | @ConditionalOnMissingBean(BusBridge.class) 41 | public StreamBusBridge streamBusBridge(StreamBridge streamBridge, BusProperties properties) { 42 | return new StreamBusBridge(streamBridge, properties); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/ConditionalOnBusEnabled.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 25 | 26 | /** 27 | * @author Spencer Gibb 28 | */ 29 | @ConditionalOnProperty(value = ConditionalOnBusEnabled.SPRING_CLOUD_BUS_ENABLED, matchIfMissing = true) 30 | @Retention(RetentionPolicy.RUNTIME) 31 | @Target({ ElementType.TYPE, ElementType.METHOD }) 32 | public @interface ConditionalOnBusEnabled { 33 | 34 | /** 35 | * Property name to enable / disable Spring Cloud Bus. 36 | */ 37 | String SPRING_CLOUD_BUS_ENABLED = "spring.cloud.bus.enabled"; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/DefaultBusPathMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import java.util.Comparator; 20 | import java.util.Map; 21 | 22 | import org.apache.commons.logging.Log; 23 | import org.apache.commons.logging.LogFactory; 24 | 25 | import org.springframework.util.PathMatcher; 26 | import org.springframework.util.StringUtils; 27 | 28 | import static org.springframework.util.StringUtils.tokenizeToStringArray; 29 | 30 | /** 31 | * {@link BusPathMatcher} that matches application context ids with multiple, 32 | * comma-separated, profiles. Original 33 | * https://gist.github.com/kelapure/61d3f948acf478cc95225ff1d7d239c4 34 | * 35 | * See https://github.com/spring-cloud/spring-cloud-config/issues/678 36 | * 37 | * @author Rohit Kelapure 38 | * @author Spencer Gibb 39 | */ 40 | public class DefaultBusPathMatcher implements PathMatcher { 41 | 42 | private static final Log log = LogFactory.getLog(DefaultBusPathMatcher.class); 43 | 44 | private final PathMatcher delagateMatcher; 45 | 46 | public DefaultBusPathMatcher(PathMatcher delagateMatcher) { 47 | this.delagateMatcher = delagateMatcher; 48 | } 49 | 50 | protected boolean matchMultiProfile(String pattern, String idToMatch) { 51 | 52 | if (log.isDebugEnabled()) { 53 | log.debug("matchMultiProfile : " + pattern + ", " + idToMatch); 54 | } 55 | 56 | // parse the id 57 | String[] tokens = tokenizeToStringArray(idToMatch, ":"); 58 | if (tokens.length <= 1) { 59 | // no parts, default to delegate which already returned false; 60 | return false; 61 | } 62 | String selfProfiles = tokens[1]; 63 | 64 | // short circuit if possible 65 | String[] profiles = tokenizeToStringArray(selfProfiles, ","); 66 | 67 | if (profiles.length == 1) { 68 | // there aren't multiple profiles to check, the delegate match was 69 | // originally false so return what delegate determined 70 | return false; 71 | } 72 | 73 | // gather candidate ids with a single profile rather than a comma separated list 74 | String[] idsWithSingleProfile = new String[profiles.length]; 75 | 76 | for (int i = 0; i < profiles.length; i++) { 77 | // replace comma separated profiles with single profile 78 | String profile = profiles[i]; 79 | String[] newTokens = new String[tokens.length]; 80 | System.arraycopy(tokens, 0, newTokens, 0, tokens.length); 81 | newTokens[1] = profile; 82 | idsWithSingleProfile[i] = StringUtils.arrayToDelimitedString(newTokens, ":"); 83 | } 84 | 85 | for (String id : idsWithSingleProfile) { 86 | if (this.delagateMatcher.match(pattern, id)) { 87 | if (log.isDebugEnabled()) { 88 | log.debug("matched true"); 89 | } 90 | return true; 91 | } 92 | } 93 | 94 | if (log.isDebugEnabled()) { 95 | log.debug("matched false"); 96 | } 97 | return false; 98 | } 99 | 100 | @Override 101 | public boolean isPattern(String path) { 102 | return this.delagateMatcher.isPattern(path); 103 | } 104 | 105 | @Override 106 | public boolean match(String pattern, String path) { 107 | if (log.isDebugEnabled()) { 108 | log.debug("In match: " + pattern + ", " + path); 109 | } 110 | if (!this.delagateMatcher.match(pattern, path)) { 111 | return matchMultiProfile(pattern, path); 112 | } 113 | return true; 114 | } 115 | 116 | @Override 117 | public boolean matchStart(String pattern, String path) { 118 | return this.delagateMatcher.matchStart(pattern, path); 119 | } 120 | 121 | @Override 122 | public String extractPathWithinPattern(String pattern, String path) { 123 | return this.delagateMatcher.extractPathWithinPattern(pattern, path); 124 | } 125 | 126 | @Override 127 | public Map extractUriTemplateVariables(String pattern, String path) { 128 | return this.delagateMatcher.extractUriTemplateVariables(pattern, path); 129 | } 130 | 131 | @Override 132 | public Comparator getPatternComparator(String path) { 133 | return this.delagateMatcher.getPatternComparator(path); 134 | } 135 | 136 | @Override 137 | public String combine(String pattern1, String pattern2) { 138 | return this.delagateMatcher.combine(pattern1, pattern2); 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/PathServiceMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import org.springframework.cloud.bus.event.RemoteApplicationEvent; 20 | import org.springframework.util.PathMatcher; 21 | 22 | /** 23 | * @author Spencer Gibb 24 | */ 25 | public class PathServiceMatcher implements ServiceMatcher { 26 | 27 | private final PathMatcher matcher; 28 | 29 | private final String id; 30 | 31 | private String[] configNames = new String[] {}; 32 | 33 | public PathServiceMatcher(PathMatcher matcher, String id) { 34 | this.matcher = matcher; 35 | this.id = id; 36 | } 37 | 38 | public PathServiceMatcher(PathMatcher matcher, String id, String[] configNames) { 39 | this(matcher, id); 40 | 41 | int colonIndex = id.indexOf(":"); 42 | if (colonIndex >= 0) { 43 | // if the id contains profiles and port, append them to the config names 44 | String profilesAndPort = id.substring(colonIndex); 45 | for (int i = 0; i < configNames.length; i++) { 46 | configNames[i] = configNames[i] + profilesAndPort; 47 | } 48 | } 49 | this.configNames = configNames; 50 | } 51 | 52 | public boolean isFromSelf(RemoteApplicationEvent event) { 53 | String originService = event.getOriginService(); 54 | String serviceId = getBusId(); 55 | return this.matcher.match(originService, serviceId); 56 | } 57 | 58 | public boolean isForSelf(RemoteApplicationEvent event) { 59 | String destinationService = event.getDestinationService(); 60 | if (destinationService == null || destinationService.trim().isEmpty() 61 | || this.matcher.match(destinationService, getBusId())) { 62 | return true; 63 | } 64 | 65 | // Check all potential config names instead of service name 66 | for (String configName : this.configNames) { 67 | if (this.matcher.match(destinationService, configName)) { 68 | return true; 69 | } 70 | } 71 | 72 | return false; 73 | } 74 | 75 | public String getBusId() { 76 | return this.id; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/PathServiceMatcherAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2020 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 20 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 21 | import org.springframework.context.annotation.Bean; 22 | import org.springframework.context.annotation.Configuration; 23 | import org.springframework.core.env.Environment; 24 | import org.springframework.util.AntPathMatcher; 25 | import org.springframework.util.PathMatcher; 26 | 27 | /** 28 | * @author Ryan Baxter 29 | */ 30 | @Configuration(proxyBeanMethods = false) 31 | @ConditionalOnBusEnabled 32 | @EnableConfigurationProperties(BusProperties.class) 33 | public class PathServiceMatcherAutoConfiguration { 34 | 35 | /** 36 | * Name of the Bus path matcher. 37 | */ 38 | public static final String BUS_PATH_MATCHER_NAME = "busPathMatcher"; 39 | 40 | /** 41 | * Name of the Spring Cloud Config property. 42 | */ 43 | public static final String CLOUD_CONFIG_NAME_PROPERTY = "spring.cloud.config.name"; 44 | 45 | @BusPathMatcher 46 | // There is a @Bean of type PathMatcher coming from Spring MVC 47 | @ConditionalOnMissingBean(name = BUS_PATH_MATCHER_NAME) 48 | @Bean(name = BUS_PATH_MATCHER_NAME) 49 | public PathMatcher busPathMatcher() { 50 | return new DefaultBusPathMatcher(new AntPathMatcher(":")); 51 | } 52 | 53 | @Bean 54 | @ConditionalOnMissingBean(ServiceMatcher.class) 55 | public PathServiceMatcher pathServiceMatcher(@BusPathMatcher PathMatcher pathMatcher, BusProperties properties, 56 | Environment environment) { 57 | String[] configNames = environment.getProperty(CLOUD_CONFIG_NAME_PROPERTY, String[].class, new String[] {}); 58 | return new PathServiceMatcher(pathMatcher, properties.getId(), configNames); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/RemoteApplicationEventListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2020 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import org.apache.commons.logging.Log; 20 | import org.apache.commons.logging.LogFactory; 21 | 22 | import org.springframework.cloud.bus.event.AckRemoteApplicationEvent; 23 | import org.springframework.cloud.bus.event.RemoteApplicationEvent; 24 | import org.springframework.context.ApplicationListener; 25 | 26 | public class RemoteApplicationEventListener implements ApplicationListener { 27 | 28 | private final Log log = LogFactory.getLog(getClass()); 29 | 30 | private final ServiceMatcher serviceMatcher; 31 | 32 | private final BusBridge busBridge; 33 | 34 | public RemoteApplicationEventListener(ServiceMatcher serviceMatcher, BusBridge busBridge) { 35 | this.serviceMatcher = serviceMatcher; 36 | this.busBridge = busBridge; 37 | } 38 | 39 | @Override 40 | public void onApplicationEvent(RemoteApplicationEvent event) { 41 | if (this.serviceMatcher.isFromSelf(event) && !(event instanceof AckRemoteApplicationEvent)) { 42 | if (log.isDebugEnabled()) { 43 | log.debug("Sending remote event on bus: " + event); 44 | } 45 | // TODO: configurable mimetype? 46 | this.busBridge.send(event); 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/ServiceMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import org.springframework.cloud.bus.event.RemoteApplicationEvent; 20 | 21 | /** 22 | * @author Spencer Gibb 23 | */ 24 | public interface ServiceMatcher { 25 | 26 | boolean isFromSelf(RemoteApplicationEvent event); 27 | 28 | boolean isForSelf(RemoteApplicationEvent event); 29 | 30 | String getBusId(); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/StreamBusBridge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2020 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import org.springframework.cloud.bus.event.RemoteApplicationEvent; 20 | import org.springframework.cloud.stream.function.StreamBridge; 21 | import org.springframework.messaging.support.MessageBuilder; 22 | 23 | public class StreamBusBridge implements BusBridge { 24 | 25 | private final StreamBridge streamBridge; 26 | 27 | private final BusProperties properties; 28 | 29 | public StreamBusBridge(StreamBridge streamBridge, BusProperties properties) { 30 | this.streamBridge = streamBridge; 31 | this.properties = properties; 32 | } 33 | 34 | public void send(RemoteApplicationEvent event) { 35 | // TODO: configurable mimetype? 36 | this.streamBridge.send(BusConstants.OUTPUT, MessageBuilder.withPayload(event).build()); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/endpoint/AbstractBusEndpoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.endpoint; 18 | 19 | import org.springframework.cloud.bus.event.Destination; 20 | import org.springframework.context.ApplicationEvent; 21 | import org.springframework.context.ApplicationEventPublisher; 22 | 23 | /** 24 | * @author Spencer Gibb 25 | */ 26 | public class AbstractBusEndpoint { 27 | 28 | private ApplicationEventPublisher publisher; 29 | 30 | private String appId; 31 | 32 | private final Destination.Factory destinationFactory; 33 | 34 | public AbstractBusEndpoint(ApplicationEventPublisher publisher, String appId, 35 | Destination.Factory destinationFactory) { 36 | this.publisher = publisher; 37 | this.appId = appId; 38 | this.destinationFactory = destinationFactory; 39 | } 40 | 41 | protected String getInstanceId() { 42 | return this.appId; 43 | } 44 | 45 | protected Destination.Factory getDestinationFactory() { 46 | return this.destinationFactory; 47 | } 48 | 49 | protected Destination getDestination(String original) { 50 | return destinationFactory.getDestination(original); 51 | } 52 | 53 | protected void publish(ApplicationEvent event) { 54 | this.publisher.publishEvent(event); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/endpoint/EnvironmentBusEndpoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.endpoint; 18 | 19 | import java.util.Collections; 20 | import java.util.Map; 21 | 22 | import org.springframework.boot.actuate.endpoint.annotation.Endpoint; 23 | import org.springframework.boot.actuate.endpoint.annotation.Selector; 24 | import org.springframework.boot.actuate.endpoint.annotation.Selector.Match; 25 | import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; 26 | import org.springframework.cloud.bus.event.Destination; 27 | import org.springframework.cloud.bus.event.EnvironmentChangeRemoteApplicationEvent; 28 | import org.springframework.context.ApplicationEventPublisher; 29 | import org.springframework.util.StringUtils; 30 | 31 | /** 32 | * @author Spencer Gibb 33 | */ 34 | @Endpoint(id = "busenv") // TODO: document 35 | public class EnvironmentBusEndpoint extends AbstractBusEndpoint { 36 | 37 | public EnvironmentBusEndpoint(ApplicationEventPublisher publisher, String id, 38 | Destination.Factory destinationFactory) { 39 | super(publisher, id, destinationFactory); 40 | } 41 | 42 | @WriteOperation 43 | // TODO: document params 44 | public void busEnvWithDestination(String name, String value, 45 | @Selector(match = Match.ALL_REMAINING) String[] destinations) { 46 | Map params = Collections.singletonMap(name, value); 47 | String destination = StringUtils.arrayToDelimitedString(destinations, ":"); 48 | publish(new EnvironmentChangeRemoteApplicationEvent(this, getInstanceId(), getDestination(destination), 49 | params)); 50 | } 51 | 52 | @WriteOperation 53 | public void busEnv(String name, String value) { // TODO: document params 54 | Map params = Collections.singletonMap(name, value); 55 | publish(new EnvironmentChangeRemoteApplicationEvent(this, getInstanceId(), getDestination(null), params)); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/endpoint/RefreshBusEndpoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.endpoint; 18 | 19 | import org.springframework.boot.actuate.endpoint.annotation.Endpoint; 20 | import org.springframework.boot.actuate.endpoint.annotation.Selector; 21 | import org.springframework.boot.actuate.endpoint.annotation.Selector.Match; 22 | import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; 23 | import org.springframework.cloud.bus.event.Destination; 24 | import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent; 25 | import org.springframework.context.ApplicationEventPublisher; 26 | import org.springframework.util.StringUtils; 27 | 28 | /** 29 | * @author Spencer Gibb 30 | */ 31 | @Endpoint(id = "busrefresh") // TODO: document new id 32 | public class RefreshBusEndpoint extends AbstractBusEndpoint { 33 | 34 | public RefreshBusEndpoint(ApplicationEventPublisher publisher, String id, Destination.Factory destinationFactory) { 35 | super(publisher, id, destinationFactory); 36 | } 37 | 38 | @WriteOperation 39 | public void busRefreshWithDestination(@Selector(match = Match.ALL_REMAINING) String[] destinations) { 40 | String destination = StringUtils.arrayToDelimitedString(destinations, ":"); 41 | publish(new RefreshRemoteApplicationEvent(this, getInstanceId(), getDestination(destination))); 42 | } 43 | 44 | @WriteOperation 45 | public void busRefresh() { 46 | publish(new RefreshRemoteApplicationEvent(this, getInstanceId(), getDestination(null))); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/endpoint/ShutdownBusEndpoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.endpoint; 18 | 19 | import org.springframework.boot.actuate.endpoint.annotation.Endpoint; 20 | import org.springframework.boot.actuate.endpoint.annotation.Selector; 21 | import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; 22 | import org.springframework.cloud.bus.event.Destination; 23 | import org.springframework.cloud.bus.event.ShutdownRemoteApplicationEvent; 24 | import org.springframework.context.ApplicationEventPublisher; 25 | import org.springframework.util.StringUtils; 26 | 27 | /** 28 | * @author Ryan Baxter 29 | */ 30 | @Endpoint(id = "busshutdown") 31 | public class ShutdownBusEndpoint extends AbstractBusEndpoint { 32 | 33 | public ShutdownBusEndpoint(ApplicationEventPublisher publisher, String id, Destination.Factory destinationFactory) { 34 | super(publisher, id, destinationFactory); 35 | } 36 | 37 | @WriteOperation 38 | public void busShutdownWithDestination(@Selector(match = Selector.Match.ALL_REMAINING) String[] destinations) { 39 | String destination = StringUtils.arrayToDelimitedString(destinations, ":"); 40 | publish(new ShutdownRemoteApplicationEvent(this, getInstanceId(), getDestination(destination))); 41 | } 42 | 43 | @WriteOperation 44 | public void busShutdown() { 45 | publish(new ShutdownRemoteApplicationEvent(this, getInstanceId(), getDestination(null))); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/AckRemoteApplicationEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.event; 18 | 19 | import com.fasterxml.jackson.annotation.JsonProperty; 20 | 21 | /** 22 | * An event that signals an ack of a specific {@link RemoteApplicationEvent}. These events 23 | * can be monitored by any applications that want to audit the responses to bus events. 24 | * They behave like normal remote application events, in the sense that if the destination 25 | * service matches the local service ID the application fires the event in its context. 26 | * 27 | * @author Dave Syer 28 | * 29 | */ 30 | @SuppressWarnings("serial") 31 | public class AckRemoteApplicationEvent extends RemoteApplicationEvent { 32 | 33 | private final String ackId; 34 | 35 | private final String ackDestinationService; 36 | 37 | private Class event; 38 | 39 | @SuppressWarnings("unused") 40 | private AckRemoteApplicationEvent() { 41 | super(); 42 | this.ackDestinationService = null; 43 | this.ackId = null; 44 | this.event = null; 45 | } 46 | 47 | public AckRemoteApplicationEvent(Object source, String originService, Destination destination, 48 | String ackDestinationService, String ackId, Class type) { 49 | super(source, originService, destination); 50 | this.ackDestinationService = ackDestinationService; 51 | this.ackId = ackId; 52 | this.event = type; 53 | } 54 | 55 | public String getAckId() { 56 | return this.ackId; 57 | } 58 | 59 | public String getAckDestinationService() { 60 | return this.ackDestinationService; 61 | } 62 | 63 | public Class getEvent() { 64 | return this.event; 65 | } 66 | 67 | /** 68 | * Used by Jackson to set the remote class name of the event implementation. If the 69 | * implementing class is unknown to this app, set the event to 70 | * {@link UnknownRemoteApplicationEvent}. 71 | * @param eventName the fq class name of the event implementation, not null 72 | */ 73 | @JsonProperty("event") 74 | @SuppressWarnings("unchecked") 75 | public void setEventName(String eventName) { 76 | try { 77 | this.event = (Class) Class.forName(eventName); 78 | } 79 | catch (ClassNotFoundException e) { 80 | this.event = UnknownRemoteApplicationEvent.class; 81 | } 82 | } 83 | 84 | @Override 85 | public int hashCode() { 86 | final int prime = 31; 87 | int result = super.hashCode(); 88 | result = prime * result + ((this.ackDestinationService == null) ? 0 : this.ackDestinationService.hashCode()); 89 | result = prime * result + ((this.ackId == null) ? 0 : this.ackId.hashCode()); 90 | result = prime * result + ((this.event == null) ? 0 : this.event.hashCode()); 91 | return result; 92 | } 93 | 94 | @Override 95 | public boolean equals(Object obj) { 96 | if (this == obj) { 97 | return true; 98 | } 99 | if (!super.equals(obj)) { 100 | return false; 101 | } 102 | if (getClass() != obj.getClass()) { 103 | return false; 104 | } 105 | AckRemoteApplicationEvent other = (AckRemoteApplicationEvent) obj; 106 | if (this.ackDestinationService == null) { 107 | if (other.ackDestinationService != null) { 108 | return false; 109 | } 110 | } 111 | else if (!this.ackDestinationService.equals(other.ackDestinationService)) { 112 | return false; 113 | } 114 | if (this.ackId == null) { 115 | if (other.ackId != null) { 116 | return false; 117 | } 118 | } 119 | else if (!this.ackId.equals(other.ackId)) { 120 | return false; 121 | } 122 | if (this.event == null) { 123 | if (other.event != null) { 124 | return false; 125 | } 126 | } 127 | else if (!this.event.equals(other.event)) { 128 | return false; 129 | } 130 | return true; 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/Destination.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2020 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.event; 18 | 19 | public interface Destination { 20 | 21 | String getDestinationAsString(); 22 | 23 | @FunctionalInterface 24 | interface Factory { 25 | 26 | Destination getDestination(String originalDestination); 27 | 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/EnvironmentChangeListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.event; 18 | 19 | import java.util.Map; 20 | 21 | import org.apache.commons.logging.Log; 22 | import org.apache.commons.logging.LogFactory; 23 | 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.cloud.context.environment.EnvironmentManager; 26 | import org.springframework.context.ApplicationListener; 27 | 28 | /** 29 | * @author Spencer Gibb 30 | */ 31 | public class EnvironmentChangeListener implements ApplicationListener { 32 | 33 | private static Log log = LogFactory.getLog(EnvironmentChangeListener.class); 34 | 35 | @Autowired 36 | private EnvironmentManager env; 37 | 38 | @Override 39 | public void onApplicationEvent(EnvironmentChangeRemoteApplicationEvent event) { 40 | Map values = event.getValues(); 41 | log.info("Received remote environment change request. Keys/values to update " + values); 42 | for (Map.Entry entry : values.entrySet()) { 43 | this.env.setProperty(entry.getKey(), entry.getValue()); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/EnvironmentChangeRemoteApplicationEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.event; 18 | 19 | import java.util.Map; 20 | 21 | import org.springframework.core.style.ToStringCreator; 22 | import org.springframework.util.Assert; 23 | 24 | /** 25 | * @author Spencer Gibb 26 | */ 27 | @SuppressWarnings("serial") 28 | public class EnvironmentChangeRemoteApplicationEvent extends RemoteApplicationEvent { 29 | 30 | private final Map values; 31 | 32 | @SuppressWarnings("unused") 33 | private EnvironmentChangeRemoteApplicationEvent() { 34 | // for serializers 35 | this.values = null; 36 | } 37 | 38 | @Deprecated 39 | public EnvironmentChangeRemoteApplicationEvent(Object source, String originService, String destinationService, 40 | Map values) { 41 | this(source, originService, new PathDestinationFactory().getDestination(destinationService), values); 42 | } 43 | 44 | public EnvironmentChangeRemoteApplicationEvent(Object source, String originService, Destination destination, 45 | Map values) { 46 | super(source, originService, destination); 47 | Assert.notNull(values, "values may not be null"); 48 | this.values = values; 49 | } 50 | 51 | public Map getValues() { 52 | return this.values; 53 | } 54 | 55 | @Override 56 | public int hashCode() { 57 | final int prime = 31; 58 | int result = super.hashCode(); 59 | result = prime * result + ((this.values == null) ? 0 : this.values.hashCode()); 60 | return result; 61 | } 62 | 63 | @Override 64 | public boolean equals(Object obj) { 65 | if (this == obj) { 66 | return true; 67 | } 68 | if (!super.equals(obj)) { 69 | return false; 70 | } 71 | if (getClass() != obj.getClass()) { 72 | return false; 73 | } 74 | EnvironmentChangeRemoteApplicationEvent other = (EnvironmentChangeRemoteApplicationEvent) obj; 75 | if (this.values == null) { 76 | if (other.values != null) { 77 | return false; 78 | } 79 | } 80 | else if (!this.values.equals(other.values)) { 81 | return false; 82 | } 83 | return true; 84 | } 85 | 86 | @Override 87 | public String toString() { 88 | return new ToStringCreator(this).append("id", getId()) 89 | .append("originService", getOriginService()) 90 | .append("destinationService", getDestinationService()) 91 | .append("values", values) 92 | .toString(); 93 | 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/PathDestinationFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2020 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.event; 18 | 19 | import org.springframework.util.StringUtils; 20 | 21 | public class PathDestinationFactory implements Destination.Factory { 22 | 23 | public Destination getDestination(String originalDestination) { 24 | String path = originalDestination; 25 | if (path == null) { 26 | path = "**"; 27 | } 28 | // If the path is not already a wildcard, match everything that 29 | // follows if there at most two path elements, and last element is not a global 30 | // wildcard already 31 | if (!"**".equals(path)) { 32 | if (StringUtils.countOccurrencesOf(path, ":") <= 1 && !StringUtils.endsWithIgnoreCase(path, ":**")) { 33 | // All instances of the destination unless specifically requested 34 | path = path + ":**"; 35 | } 36 | } 37 | 38 | final String finalPath = path; 39 | return () -> finalPath; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/RefreshListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.event; 18 | 19 | import java.util.Set; 20 | 21 | import org.apache.commons.logging.Log; 22 | import org.apache.commons.logging.LogFactory; 23 | 24 | import org.springframework.cloud.bus.ServiceMatcher; 25 | import org.springframework.cloud.context.refresh.ContextRefresher; 26 | import org.springframework.context.ApplicationListener; 27 | 28 | /** 29 | * @author Spencer Gibb 30 | * @author Ryan Baxter 31 | */ 32 | public class RefreshListener implements ApplicationListener { 33 | 34 | private static Log log = LogFactory.getLog(RefreshListener.class); 35 | 36 | private ContextRefresher contextRefresher; 37 | 38 | private ServiceMatcher serviceMatcher; 39 | 40 | public RefreshListener(ContextRefresher contextRefresher, ServiceMatcher serviceMatcher) { 41 | this.contextRefresher = contextRefresher; 42 | this.serviceMatcher = serviceMatcher; 43 | } 44 | 45 | @Override 46 | public void onApplicationEvent(RefreshRemoteApplicationEvent event) { 47 | log.info("Received remote refresh request."); 48 | if (serviceMatcher.isForSelf(event)) { 49 | Set keys = this.contextRefresher.refresh(); 50 | log.info("Keys refreshed " + keys); 51 | } 52 | else { 53 | log.info("Refresh not performed, the event was targeting " + event.getDestinationService()); 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/RefreshRemoteApplicationEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.event; 18 | 19 | /** 20 | * @author Spencer Gibb 21 | */ 22 | @SuppressWarnings("serial") 23 | public class RefreshRemoteApplicationEvent extends RemoteApplicationEvent { 24 | 25 | @SuppressWarnings("unused") 26 | private RefreshRemoteApplicationEvent() { 27 | // for serializers 28 | } 29 | 30 | @Deprecated 31 | public RefreshRemoteApplicationEvent(Object source, String originService, String destination) { 32 | this(source, originService, new PathDestinationFactory().getDestination(destination)); 33 | } 34 | 35 | public RefreshRemoteApplicationEvent(Object source, String originService, Destination destination) { 36 | super(source, originService, destination); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/RemoteApplicationEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.event; 18 | 19 | import java.util.UUID; 20 | 21 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 22 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 23 | 24 | import org.springframework.context.ApplicationEvent; 25 | import org.springframework.core.style.ToStringCreator; 26 | import org.springframework.util.Assert; 27 | 28 | /** 29 | * @author Spencer Gibb 30 | */ 31 | @SuppressWarnings("serial") 32 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 33 | @JsonIgnoreProperties("source") 34 | public abstract class RemoteApplicationEvent extends ApplicationEvent { 35 | 36 | private static final Object TRANSIENT_SOURCE = new Object(); 37 | 38 | private static final String TRANSIENT_ORIGIN = "____transient_origin_service___"; 39 | 40 | private static final String TRANSIENT_DESTINATION = "____transient_destination___"; 41 | 42 | protected static final PathDestinationFactory DEFAULT_DESTINATION_FACTORY = new PathDestinationFactory(); 43 | 44 | private final String originService; 45 | 46 | private final String destinationService; 47 | 48 | private final String id; 49 | 50 | protected RemoteApplicationEvent() { 51 | // for serialization libs like jackson 52 | this(TRANSIENT_SOURCE, TRANSIENT_ORIGIN, DEFAULT_DESTINATION_FACTORY.getDestination(TRANSIENT_DESTINATION)); 53 | } 54 | 55 | @Deprecated 56 | protected RemoteApplicationEvent(Object source, String originService, String destinationService) { 57 | this(source, originService, DEFAULT_DESTINATION_FACTORY.getDestination(destinationService)); 58 | } 59 | 60 | protected RemoteApplicationEvent(Object source, String originService, Destination destination) { 61 | super(source); 62 | if (!originService.equals(TRANSIENT_ORIGIN)) { 63 | Assert.notNull(originService, "originService may not be null"); 64 | this.originService = originService; 65 | } 66 | else { 67 | this.originService = null; 68 | } 69 | Assert.notNull(destination, "destination may not be null"); 70 | this.destinationService = destination.getDestinationAsString(); 71 | Assert.hasText(destinationService, "destinationService may not be empty"); 72 | this.id = UUID.randomUUID().toString(); 73 | } 74 | 75 | @Deprecated 76 | protected RemoteApplicationEvent(Object source, String originService) { 77 | this(source, originService, DEFAULT_DESTINATION_FACTORY.getDestination(null)); 78 | } 79 | 80 | public String getOriginService() { 81 | return this.originService; 82 | } 83 | 84 | public String getDestinationService() { 85 | return this.destinationService; 86 | } 87 | 88 | public String getId() { 89 | return this.id; 90 | } 91 | 92 | @Override 93 | public int hashCode() { 94 | final int prime = 31; 95 | int result = 1; 96 | result = prime * result + ((this.destinationService == null) ? 0 : this.destinationService.hashCode()); 97 | result = prime * result + ((this.id == null) ? 0 : this.id.hashCode()); 98 | result = prime * result + ((this.originService == null) ? 0 : this.originService.hashCode()); 99 | return result; 100 | } 101 | 102 | @Override 103 | public boolean equals(Object obj) { 104 | if (this == obj) { 105 | return true; 106 | } 107 | if (obj == null) { 108 | return false; 109 | } 110 | if (getClass() != obj.getClass()) { 111 | return false; 112 | } 113 | RemoteApplicationEvent other = (RemoteApplicationEvent) obj; 114 | if (this.destinationService == null) { 115 | if (other.destinationService != null) { 116 | return false; 117 | } 118 | } 119 | else if (!this.destinationService.equals(other.destinationService)) { 120 | return false; 121 | } 122 | if (this.id == null) { 123 | if (other.id != null) { 124 | return false; 125 | } 126 | } 127 | else if (!this.id.equals(other.id)) { 128 | return false; 129 | } 130 | if (this.originService == null) { 131 | if (other.originService != null) { 132 | return false; 133 | } 134 | } 135 | else if (!this.originService.equals(other.originService)) { 136 | return false; 137 | } 138 | return true; 139 | } 140 | 141 | @Override 142 | public String toString() { 143 | return new ToStringCreator(this).append("id", id) 144 | .append("originService", originService) 145 | .append("destinationService", destinationService) 146 | .toString(); 147 | 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/SentApplicationEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.event; 18 | 19 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 20 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 21 | 22 | import org.springframework.context.ApplicationEvent; 23 | 24 | /** 25 | * An event signalling that a remote event was sent somewhere in the system. This is not 26 | * itself a {@link RemoteApplicationEvent}, so it isn't sent over the bus, instead it is 27 | * generated locally (possibly in response to a remote event). Applications that want to 28 | * audit remote events can listen for this one and the {@link AckRemoteApplicationEvent} 29 | * from all the consumers (the {@link #getId() id} of this event is the 30 | * {@link AckRemoteApplicationEvent#getAckId() ackId} of the corresponding ACK. 31 | * 32 | * @author Dave Syer 33 | */ 34 | @SuppressWarnings("serial") 35 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 36 | @JsonIgnoreProperties("source") 37 | public class SentApplicationEvent extends ApplicationEvent { 38 | 39 | private static final Object TRANSIENT_SOURCE = new Object(); 40 | 41 | private final String originService; 42 | 43 | private final String destinationService; 44 | 45 | private final String id; 46 | 47 | private Class type; 48 | 49 | protected SentApplicationEvent() { 50 | // for serialization libs like jackson 51 | this(TRANSIENT_SOURCE, null, null, null, RemoteApplicationEvent.class); 52 | } 53 | 54 | public SentApplicationEvent(Object source, String originService, String destinationService, String id, 55 | Class type) { 56 | super(source); 57 | this.originService = originService; 58 | this.type = type; 59 | if (destinationService == null) { 60 | destinationService = "*"; 61 | } 62 | if (!destinationService.contains(":")) { 63 | // All instances of the destination unless specifically requested 64 | destinationService = destinationService + ":**"; 65 | } 66 | this.destinationService = destinationService; 67 | this.id = id; 68 | } 69 | 70 | public Class getType() { 71 | return this.type; 72 | } 73 | 74 | public void setType(Class type) { 75 | this.type = type; 76 | } 77 | 78 | public String getOriginService() { 79 | return this.originService; 80 | } 81 | 82 | public String getDestinationService() { 83 | return this.destinationService; 84 | } 85 | 86 | public String getId() { 87 | return this.id; 88 | } 89 | 90 | @Override 91 | public int hashCode() { 92 | final int prime = 31; 93 | int result = 1; 94 | result = prime * result + ((this.destinationService == null) ? 0 : this.destinationService.hashCode()); 95 | result = prime * result + ((this.id == null) ? 0 : this.id.hashCode()); 96 | result = prime * result + ((this.originService == null) ? 0 : this.originService.hashCode()); 97 | result = prime * result + ((this.type == null) ? 0 : this.type.hashCode()); 98 | return result; 99 | } 100 | 101 | @Override 102 | public boolean equals(Object obj) { 103 | if (this == obj) { 104 | return true; 105 | } 106 | if (obj == null) { 107 | return false; 108 | } 109 | if (getClass() != obj.getClass()) { 110 | return false; 111 | } 112 | SentApplicationEvent other = (SentApplicationEvent) obj; 113 | if (this.destinationService == null) { 114 | if (other.destinationService != null) { 115 | return false; 116 | } 117 | } 118 | else if (!this.destinationService.equals(other.destinationService)) { 119 | return false; 120 | } 121 | if (this.id == null) { 122 | if (other.id != null) { 123 | return false; 124 | } 125 | } 126 | else if (!this.id.equals(other.id)) { 127 | return false; 128 | } 129 | if (this.originService == null) { 130 | if (other.originService != null) { 131 | return false; 132 | } 133 | } 134 | else if (!this.originService.equals(other.originService)) { 135 | return false; 136 | } 137 | if (this.type == null) { 138 | if (other.type != null) { 139 | return false; 140 | } 141 | } 142 | else if (!this.type.equals(other.type)) { 143 | return false; 144 | } 145 | return true; 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/ShutdownListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.event; 18 | 19 | import org.apache.commons.logging.Log; 20 | import org.apache.commons.logging.LogFactory; 21 | 22 | import org.springframework.beans.BeansException; 23 | import org.springframework.boot.SpringApplication; 24 | import org.springframework.cloud.bus.ServiceMatcher; 25 | import org.springframework.context.ApplicationContext; 26 | import org.springframework.context.ApplicationContextAware; 27 | import org.springframework.context.ApplicationListener; 28 | 29 | /** 30 | * @author Ryan Baxter 31 | */ 32 | public class ShutdownListener implements ApplicationListener, ApplicationContextAware { 33 | 34 | private static final Log LOG = LogFactory.getLog(ShutdownListener.class); 35 | 36 | private ApplicationContext context; 37 | 38 | private ServiceMatcher serviceMatcher; 39 | 40 | public ShutdownListener(ServiceMatcher serviceMatcher) { 41 | this.serviceMatcher = serviceMatcher; 42 | } 43 | 44 | @Override 45 | public void onApplicationEvent(ShutdownRemoteApplicationEvent event) { 46 | if (serviceMatcher.isForSelf(event)) { 47 | LOG.warn("Received remote shutdown request from " + event.getOriginService() + ". Shutting down."); 48 | shutdown(); 49 | } 50 | else { 51 | LOG.info("Shutdown not performed, the event was targeting " + event.getDestinationService()); 52 | } 53 | 54 | } 55 | 56 | protected int shutdown() { 57 | return SpringApplication.exit(context, () -> 0); 58 | } 59 | 60 | @Override 61 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 62 | this.context = applicationContext; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/ShutdownRemoteApplicationEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.event; 18 | 19 | /** 20 | * Event which indicates the application should be shutdown. 21 | * 22 | * @author Ryan Baxter 23 | */ 24 | public class ShutdownRemoteApplicationEvent extends RemoteApplicationEvent { 25 | 26 | @SuppressWarnings("unused") 27 | private ShutdownRemoteApplicationEvent() { 28 | // for serializers 29 | } 30 | 31 | public ShutdownRemoteApplicationEvent(Object source, String originService, Destination destination) { 32 | super(source, originService, destination); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/TraceListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.event; 18 | 19 | import java.util.LinkedHashMap; 20 | import java.util.Map; 21 | 22 | import org.apache.commons.logging.Log; 23 | import org.apache.commons.logging.LogFactory; 24 | 25 | import org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository; 26 | import org.springframework.context.event.EventListener; 27 | 28 | /** 29 | * A listener for sends and acks of remote application events. Inserts a record for each 30 | * signal in the {@link HttpExchangeRepository}. 31 | * 32 | * @author Dave Syer 33 | */ 34 | public class TraceListener { 35 | 36 | private static Log log = LogFactory.getLog(TraceListener.class); 37 | 38 | private HttpExchangeRepository repository; 39 | 40 | public TraceListener(HttpExchangeRepository repository) { 41 | this.repository = repository; 42 | } 43 | 44 | @EventListener 45 | public void onAck(AckRemoteApplicationEvent event) { 46 | Map trace = getReceivedTrace(event); 47 | // FIXME boot 2 this.repository.add(trace); 48 | } 49 | 50 | @EventListener 51 | public void onSend(SentApplicationEvent event) { 52 | Map trace = getSentTrace(event); 53 | // FIXME boot 2 this.repository.add(trace); 54 | } 55 | 56 | protected Map getSentTrace(SentApplicationEvent event) { 57 | Map map = new LinkedHashMap(); 58 | map.put("signal", "spring.cloud.bus.sent"); 59 | map.put("type", event.getType().getSimpleName()); 60 | map.put("id", event.getId()); 61 | map.put("origin", event.getOriginService()); 62 | map.put("destination", event.getDestinationService()); 63 | if (log.isDebugEnabled()) { 64 | log.debug(map); 65 | } 66 | return map; 67 | } 68 | 69 | protected Map getReceivedTrace(AckRemoteApplicationEvent event) { 70 | Map map = new LinkedHashMap(); 71 | map.put("signal", "spring.cloud.bus.ack"); 72 | map.put("event", event.getEvent().getSimpleName()); 73 | map.put("id", event.getAckId()); 74 | map.put("origin", event.getOriginService()); 75 | map.put("destination", event.getAckDestinationService()); 76 | if (log.isDebugEnabled()) { 77 | log.debug(map); 78 | } 79 | return map; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/event/UnknownRemoteApplicationEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.event; 18 | 19 | import org.springframework.core.style.ToStringCreator; 20 | 21 | /** 22 | * @author Stefan Pfeiffer 23 | */ 24 | public class UnknownRemoteApplicationEvent extends RemoteApplicationEvent { 25 | 26 | protected String typeInfo; 27 | 28 | protected byte[] payload; 29 | 30 | @SuppressWarnings("unused") 31 | private UnknownRemoteApplicationEvent() { 32 | super(); 33 | this.typeInfo = null; 34 | this.payload = null; 35 | } 36 | 37 | public UnknownRemoteApplicationEvent(Object source, String typeInfo, byte[] payload) { 38 | // Initialize originService with an empty String, to avoid NullPointer in 39 | // AntPathMatcher. 40 | super(source, "", () -> "unknown"); 41 | this.typeInfo = typeInfo; 42 | this.payload = payload; 43 | } 44 | 45 | public String getTypeInfo() { 46 | return this.typeInfo; 47 | } 48 | 49 | public byte[] getPayload() { 50 | return this.payload; 51 | } 52 | 53 | public String getPayloadAsString() { 54 | if (this.payload != null) { 55 | return new String(this.payload); 56 | } 57 | return null; 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return new ToStringCreator(this).append("id", getId()) 63 | .append("originService", getOriginService()) 64 | .append("destinationService", getDestinationService()) 65 | .append("typeInfo", typeInfo) 66 | .append("payload", getPayloadAsString()) 67 | .toString(); 68 | 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/jackson/RemoteApplicationEventRegistrar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.jackson; 18 | 19 | import java.util.Arrays; 20 | import java.util.HashSet; 21 | import java.util.Map; 22 | import java.util.Set; 23 | 24 | import org.springframework.beans.factory.config.BeanDefinitionHolder; 25 | import org.springframework.beans.factory.support.AbstractBeanDefinition; 26 | import org.springframework.beans.factory.support.BeanDefinitionBuilder; 27 | import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; 28 | import org.springframework.beans.factory.support.BeanDefinitionRegistry; 29 | import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; 30 | import org.springframework.core.type.AnnotationMetadata; 31 | import org.springframework.util.ClassUtils; 32 | import org.springframework.util.StringUtils; 33 | 34 | /** 35 | * @author Donovan Muller 36 | */ 37 | public class RemoteApplicationEventRegistrar implements ImportBeanDefinitionRegistrar { 38 | 39 | private static final String PACKAGES_TO_SCAN = "packagesToScan"; 40 | 41 | private static final String BUS_JSON_CONVERTER = "busJsonConverter"; 42 | 43 | // patterned after Spring Integration IntegrationComponentScanRegistrar 44 | 45 | @Override 46 | public void registerBeanDefinitions(final AnnotationMetadata importingClassMetadata, 47 | final BeanDefinitionRegistry registry) { 48 | 49 | Map componentScan = importingClassMetadata 50 | .getAnnotationAttributes(RemoteApplicationEventScan.class.getName(), false); 51 | 52 | Set basePackages = new HashSet<>(); 53 | for (String pkg : (String[]) componentScan.get("value")) { 54 | if (StringUtils.hasText(pkg)) { 55 | basePackages.add(pkg); 56 | } 57 | } 58 | for (String pkg : (String[]) componentScan.get("basePackages")) { 59 | if (StringUtils.hasText(pkg)) { 60 | basePackages.add(pkg); 61 | } 62 | } 63 | for (Class clazz : (Class[]) componentScan.get("basePackageClasses")) { 64 | basePackages.add(ClassUtils.getPackageName(clazz)); 65 | } 66 | 67 | if (basePackages.isEmpty()) { 68 | basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName())); 69 | } 70 | 71 | if (!registry.containsBeanDefinition(BUS_JSON_CONVERTER)) { 72 | BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder 73 | .genericBeanDefinition(BusJacksonMessageConverter.class); 74 | beanDefinitionBuilder.addPropertyValue(PACKAGES_TO_SCAN, 75 | basePackages.toArray(new String[basePackages.size()])); 76 | AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition(); 77 | 78 | BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, BUS_JSON_CONVERTER); 79 | BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); 80 | } 81 | else { 82 | basePackages.addAll(getEarlierPackagesToScan(registry)); 83 | registry.getBeanDefinition(BUS_JSON_CONVERTER) 84 | .getPropertyValues() 85 | .addPropertyValue(PACKAGES_TO_SCAN, basePackages.toArray(new String[basePackages.size()])); 86 | } 87 | } 88 | 89 | private Set getEarlierPackagesToScan(final BeanDefinitionRegistry registry) { 90 | if (registry.containsBeanDefinition(BUS_JSON_CONVERTER) 91 | && registry.getBeanDefinition(BUS_JSON_CONVERTER).getPropertyValues().get(PACKAGES_TO_SCAN) != null) { 92 | String[] earlierValues = (String[]) registry.getBeanDefinition(BUS_JSON_CONVERTER) 93 | .getPropertyValues() 94 | .get(PACKAGES_TO_SCAN); 95 | return new HashSet<>(Arrays.asList(earlierValues)); 96 | } 97 | 98 | return new HashSet<>(); 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/jackson/RemoteApplicationEventScan.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.jackson; 18 | 19 | import java.lang.annotation.Documented; 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Retention; 22 | import java.lang.annotation.RetentionPolicy; 23 | import java.lang.annotation.Target; 24 | 25 | import org.springframework.context.annotation.Import; 26 | 27 | /** 28 | * @author Donovan Muller 29 | */ 30 | @Retention(RetentionPolicy.RUNTIME) 31 | @Target(ElementType.TYPE) 32 | @Documented 33 | @Import(RemoteApplicationEventRegistrar.class) 34 | public @interface RemoteApplicationEventScan { 35 | 36 | String[] value() default {}; 37 | 38 | String[] basePackages() default {}; 39 | 40 | Class[] basePackageClasses() default {}; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/java/org/springframework/cloud/bus/jackson/SubtypeModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.jackson; 18 | 19 | import com.fasterxml.jackson.databind.module.SimpleModule; 20 | 21 | /** 22 | * @author Spencer Gibb 23 | */ 24 | @SuppressWarnings("serial") 25 | public class SubtypeModule extends SimpleModule { 26 | 27 | private Class[] subtypes; 28 | 29 | public SubtypeModule(Class... subtypes) { 30 | this.subtypes = subtypes; 31 | } 32 | 33 | @Override 34 | public void setupModule(SetupContext context) { 35 | context.registerSubtypes(this.subtypes); 36 | super.setupModule(context); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/resources/META-INF/additional-spring-configuration-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": [ 3 | { 4 | "name": "spring.cloud.bus.env.enabled", 5 | "type": "java.lang.Boolean", 6 | "description": "Flag to switch off environment change events (default on).", 7 | "defaultValue": true 8 | }, 9 | { 10 | "name": "spring.cloud.bus.refresh.enabled", 11 | "type": "java.lang.Boolean", 12 | "description": "Flag to switch off refresh events (default on).", 13 | "defaultValue": true 14 | }, 15 | { 16 | "name": "spring.cloud.bus.shutdown.enabled", 17 | "type": "java.lang.Boolean", 18 | "description": "Flag to switch off shutdown events (default on).", 19 | "defaultValue": true 20 | }, 21 | { 22 | "name": "spring.cloud.bus.trace.enabled", 23 | "type": "java.lang.Boolean", 24 | "description": "Flag to switch on tracing of acks (default off).", 25 | "defaultValue": false 26 | } 27 | ] 28 | } 29 | 30 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | # Environment Post Processor 2 | org.springframework.boot.env.EnvironmentPostProcessor=\ 3 | org.springframework.cloud.bus.BusEnvironmentPostProcessor 4 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | org.springframework.cloud.bus.PathServiceMatcherAutoConfiguration 2 | org.springframework.cloud.bus.BusAutoConfiguration 3 | org.springframework.cloud.bus.BusRefreshAutoConfiguration 4 | org.springframework.cloud.bus.BusShutdownAutoConfiguration 5 | org.springframework.cloud.bus.BusStreamAutoConfiguration 6 | org.springframework.cloud.bus.jackson.BusJacksonAutoConfiguration 7 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/test/java/org/springframework/cloud/bus/BusAutoConfigurationClassPathTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import org.junit.Test; 20 | 21 | import org.springframework.boot.autoconfigure.AutoConfigurations; 22 | import org.springframework.boot.test.context.FilteredClassLoader; 23 | import org.springframework.boot.test.context.runner.ApplicationContextRunner; 24 | import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; 25 | import org.springframework.cloud.bus.endpoint.RefreshBusEndpoint; 26 | import org.springframework.cloud.bus.event.RefreshListener; 27 | 28 | import static org.assertj.core.api.Assertions.assertThat; 29 | 30 | public class BusAutoConfigurationClassPathTests { 31 | 32 | @Test 33 | public void refreshListenerCreatedWithoutActuator() { 34 | new ApplicationContextRunner().withClassLoader(new FilteredClassLoader("org.springframework.boot.actuate")) 35 | .withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class, 36 | PathServiceMatcherAutoConfiguration.class, BusRefreshAutoConfiguration.class)) 37 | .run(context -> assertThat(context).hasSingleBean(RefreshListener.class) 38 | .doesNotHaveBean(RefreshBusEndpoint.class)); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/test/java/org/springframework/cloud/bus/BusEnvironmentPostProcessorTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import org.springframework.boot.SpringApplication; 22 | import org.springframework.cloud.function.context.FunctionProperties; 23 | import org.springframework.mock.env.MockEnvironment; 24 | 25 | import static org.assertj.core.api.Assertions.assertThat; 26 | import static org.mockito.Mockito.mock; 27 | import static org.springframework.cloud.bus.BusConstants.BUS_CONSUMER; 28 | import static org.springframework.cloud.bus.BusConstants.DESTINATION; 29 | import static org.springframework.cloud.bus.BusConstants.INPUT; 30 | import static org.springframework.cloud.bus.BusConstants.OUTPUT; 31 | import static org.springframework.cloud.bus.BusEnvironmentPostProcessor.DEFAULTS_PROPERTY_SOURCE_NAME; 32 | import static org.springframework.cloud.bus.BusEnvironmentPostProcessor.OVERRIDES_PROPERTY_SOURCE_NAME; 33 | 34 | public class BusEnvironmentPostProcessorTests { 35 | 36 | @Test 37 | void testDefaults() { 38 | MockEnvironment env = new MockEnvironment().withProperty("cachedrandom.application.value", "123"); 39 | new BusEnvironmentPostProcessor().postProcessEnvironment(env, mock(SpringApplication.class)); 40 | assertThat(env.getProperty(FunctionProperties.PREFIX + ".definition")).isEqualTo(BUS_CONSUMER); 41 | assertThat(env.getProperty("spring.cloud.stream.function.bindings." + BUS_CONSUMER + "-in-0")).isEqualTo(INPUT); 42 | assertThat(env.getProperty("spring.cloud.stream.bindings." + INPUT + ".destination")).isEqualTo(DESTINATION); 43 | assertThat(env.getProperty("spring.cloud.stream.bindings." + OUTPUT + ".destination")).isEqualTo(DESTINATION); 44 | assertThat(env.getProperty(BusProperties.PREFIX + ".id")).isEqualTo("application:8080:123"); 45 | assertThat(env.getPropertySources().contains(OVERRIDES_PROPERTY_SOURCE_NAME)); 46 | assertThat(env.getPropertySources().contains(DEFAULTS_PROPERTY_SOURCE_NAME)); 47 | } 48 | 49 | @Test 50 | void testWithActiveProfile() { 51 | MockEnvironment env = new MockEnvironment().withProperty("cachedrandom.application.value", "123") 52 | .withProperty("spring.profiles.active", "dev"); 53 | new BusEnvironmentPostProcessor().postProcessEnvironment(env, mock(SpringApplication.class)); 54 | assertThat(env.getProperty(FunctionProperties.PREFIX + ".definition")).isEqualTo(BUS_CONSUMER); 55 | assertThat(env.getProperty("spring.cloud.stream.function.bindings." + BUS_CONSUMER + "-in-0")).isEqualTo(INPUT); 56 | assertThat(env.getProperty("spring.cloud.stream.bindings." + INPUT + ".destination")).isEqualTo(DESTINATION); 57 | assertThat(env.getProperty("spring.cloud.stream.bindings." + OUTPUT + ".destination")).isEqualTo(DESTINATION); 58 | assertThat(env.getProperty(BusProperties.PREFIX + ".id")).isEqualTo("application:dev:8080:123"); 59 | assertThat(env.getPropertySources().contains(OVERRIDES_PROPERTY_SOURCE_NAME)); 60 | assertThat(env.getPropertySources().contains(DEFAULTS_PROPERTY_SOURCE_NAME)); 61 | } 62 | 63 | @Test 64 | void testOverrides() { 65 | String fnDefKey = FunctionProperties.PREFIX + ".definition"; 66 | String idKey = BusProperties.PREFIX + ".id"; 67 | MockEnvironment env = new MockEnvironment().withProperty("cachedrandom.application.value", "123") 68 | .withProperty(BusProperties.PREFIX + ".destination", "mydestination") 69 | .withProperty(idKey, "app:1") 70 | .withProperty(fnDefKey, "uppercase"); 71 | new BusEnvironmentPostProcessor().postProcessEnvironment(env, mock(SpringApplication.class)); 72 | assertThat(env.getProperty(fnDefKey)).isEqualTo("uppercase;" + BUS_CONSUMER); 73 | assertThat(env.getProperty("spring.cloud.stream.function.bindings." + BUS_CONSUMER + "-in-0")).isEqualTo(INPUT); 74 | assertThat(env.getProperty("spring.cloud.stream.bindings." + INPUT + ".destination")) 75 | .isEqualTo("mydestination"); 76 | assertThat(env.getProperty("spring.cloud.stream.bindings." + OUTPUT + ".destination")) 77 | .isEqualTo("mydestination"); 78 | assertThat(env.getProperty(idKey)).isEqualTo("app:1"); 79 | assertThat(env.getPropertySources().contains(OVERRIDES_PROPERTY_SOURCE_NAME)); 80 | assertThat(env.getPropertySources().contains(DEFAULTS_PROPERTY_SOURCE_NAME)); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/test/java/org/springframework/cloud/bus/ConditionalOnBusEnabledTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import org.junit.After; 20 | import org.junit.Rule; 21 | import org.junit.Test; 22 | import org.junit.rules.ExpectedException; 23 | 24 | import org.springframework.boot.test.util.TestPropertyValues; 25 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 26 | import org.springframework.context.annotation.Bean; 27 | import org.springframework.context.annotation.Configuration; 28 | 29 | import static org.assertj.core.api.Assertions.assertThat; 30 | 31 | /** 32 | * @author Spencer Gibb 33 | */ 34 | public class ConditionalOnBusEnabledTests { 35 | 36 | /** 37 | * {@link ExpectedException} rule for verifying thrown exceptions. 38 | */ 39 | @Rule 40 | public ExpectedException thrown = ExpectedException.none(); 41 | 42 | private AnnotationConfigApplicationContext context; 43 | 44 | @After 45 | public void tearDown() { 46 | if (this.context != null) { 47 | this.context.close(); 48 | } 49 | } 50 | 51 | @Test 52 | public void busEnabledTrue() { 53 | load(MyBusEnabledConfig.class, ConditionalOnBusEnabled.SPRING_CLOUD_BUS_ENABLED + ":true"); 54 | assertThat(this.context.containsBean("foo")).as("missing bean from @ConditionalOnBusEnabled config").isTrue(); 55 | } 56 | 57 | @Test 58 | public void busEnabledMissing() { 59 | load(MyBusEnabledConfig.class); 60 | assertThat(this.context.containsBean("foo")).as("missing bean from @ConditionalOnBusEnabled config").isTrue(); 61 | } 62 | 63 | @Test 64 | public void busDisabled() { 65 | load(MyBusEnabledConfig.class, ConditionalOnBusEnabled.SPRING_CLOUD_BUS_ENABLED + ":false"); 66 | assertThat(this.context.containsBean("foo")).as("bean exists from disabled @ConditionalOnBusEnabled config") 67 | .isFalse(); 68 | } 69 | 70 | private void load(Class config, String... environment) { 71 | this.context = new AnnotationConfigApplicationContext(); 72 | TestPropertyValues.of(environment).applyTo(this.context); 73 | this.context.register(config); 74 | this.context.refresh(); 75 | } 76 | 77 | @Configuration(proxyBeanMethods = false) 78 | @ConditionalOnBusEnabled 79 | protected static class MyBusEnabledConfig { 80 | 81 | @Bean 82 | public String foo() { 83 | return "foo"; 84 | } 85 | 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/test/java/org/springframework/cloud/bus/PathServiceMatcherTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import java.util.Collections; 20 | import java.util.Map; 21 | 22 | import org.junit.Before; 23 | import org.junit.Test; 24 | 25 | import org.springframework.cloud.bus.event.EnvironmentChangeRemoteApplicationEvent; 26 | import org.springframework.util.AntPathMatcher; 27 | 28 | import static org.assertj.core.api.Assertions.assertThat; 29 | 30 | /** 31 | * @author Dave Syer 32 | * 33 | */ 34 | public class PathServiceMatcherTests { 35 | 36 | private static final Map EMPTY_MAP = Collections.emptyMap(); 37 | 38 | private ServiceMatcher matcher; 39 | 40 | @Before 41 | public void init() { 42 | initMatcher("one:two:8888"); 43 | } 44 | 45 | private void initMatcher(String id) { 46 | BusProperties properties = new BusProperties(); 47 | properties.setId(id); 48 | DefaultBusPathMatcher pathMatcher = new DefaultBusPathMatcher(new AntPathMatcher(":")); 49 | this.matcher = new PathServiceMatcher(pathMatcher, properties.getId()); 50 | } 51 | 52 | @Test 53 | public void fromSelf() { 54 | assertThat(this.matcher 55 | .isFromSelf(new EnvironmentChangeRemoteApplicationEvent(this, "one:two:8888", "foo:bar:spam", EMPTY_MAP))) 56 | .isTrue(); 57 | } 58 | 59 | @Test 60 | public void forSelf() { 61 | assertThat(this.matcher 62 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "one:two:8888", EMPTY_MAP))) 63 | .isTrue(); 64 | } 65 | 66 | @Test 67 | public void forSelfWithWildcard() { 68 | assertThat(this.matcher 69 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "one:two:*", EMPTY_MAP))) 70 | .isTrue(); 71 | } 72 | 73 | @Test 74 | public void forSelfWithGlobalWildcard() { 75 | assertThat(this.matcher 76 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "**", EMPTY_MAP))).isTrue(); 77 | } 78 | 79 | @Test 80 | public void forSelfWithWildcardName() { 81 | assertThat(this.matcher 82 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "o*", EMPTY_MAP))).isTrue(); 83 | } 84 | 85 | @Test 86 | public void forSelfWithWildcardNameAndProfile() { 87 | assertThat(this.matcher 88 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "o*:t*", EMPTY_MAP))).isTrue(); 89 | } 90 | 91 | @Test 92 | public void forSelfWithWildcardString() { 93 | assertThat(this.matcher 94 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "o*", EMPTY_MAP))).isTrue(); 95 | } 96 | 97 | @Test 98 | public void notForSelfWithWildCardNameAndMismatchingProfile() { 99 | assertThat(this.matcher 100 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "o*:f*", EMPTY_MAP))) 101 | .isFalse(); 102 | } 103 | 104 | @Test 105 | public void forSelfWithDoubleWildcard() { 106 | assertThat(this.matcher 107 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "one:**", EMPTY_MAP))) 108 | .isTrue(); 109 | } 110 | 111 | @Test 112 | public void forSelfWithNoWildcard() { 113 | assertThat(this.matcher 114 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "one", EMPTY_MAP))).isTrue(); 115 | } 116 | 117 | @Test 118 | public void forSelfWithProfileNoWildcard() { 119 | assertThat(this.matcher 120 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "one:two", EMPTY_MAP))) 121 | .isTrue(); 122 | } 123 | 124 | @Test 125 | public void notForSelf() { 126 | assertThat(this.matcher 127 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "one:two:9999", EMPTY_MAP))) 128 | .isFalse(); 129 | } 130 | 131 | @Test 132 | public void notFromSelf() { 133 | assertThat(this.matcher 134 | .isFromSelf(new EnvironmentChangeRemoteApplicationEvent(this, "one:two:9999", "foo:bar:spam", EMPTY_MAP))) 135 | .isFalse(); 136 | } 137 | 138 | /** 139 | * see gh-678. 140 | */ 141 | @Test 142 | public void forSelfWithMultipleProfiles() { 143 | initMatcher("customerportal:dev,cloud:80"); 144 | assertThat(this.matcher.isForSelf( 145 | new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "customerportal:cloud:*", EMPTY_MAP))) 146 | .isTrue(); 147 | } 148 | 149 | /** 150 | * see gh-678. 151 | */ 152 | @Test 153 | public void notForSelfWithMultipleProfiles() { 154 | initMatcher("customerportal:dev,cloud:80"); 155 | assertThat(this.matcher 156 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "bar:cloud:*", EMPTY_MAP))) 157 | .isFalse(); 158 | } 159 | 160 | /** 161 | * see gh-678. 162 | */ 163 | @Test 164 | public void notForSelfWithMultipleProfilesDifferentPort() { 165 | initMatcher("customerportal:dev,cloud:80"); 166 | assertThat(this.matcher.isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", 167 | "customerportal:cloud:8008", EMPTY_MAP))) 168 | .isFalse(); 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/test/java/org/springframework/cloud/bus/PathServiceMatcherWithConfigNamesTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import java.util.Collections; 20 | import java.util.Map; 21 | 22 | import org.junit.Before; 23 | import org.junit.Test; 24 | 25 | import org.springframework.cloud.bus.event.EnvironmentChangeRemoteApplicationEvent; 26 | import org.springframework.util.AntPathMatcher; 27 | 28 | import static org.assertj.core.api.Assertions.assertThat; 29 | 30 | /** 31 | * @author Stefan Pfeiffer 32 | * 33 | */ 34 | public class PathServiceMatcherWithConfigNamesTests { 35 | 36 | private static final Map EMPTY_MAP = Collections.emptyMap(); 37 | 38 | private ServiceMatcher matcher; 39 | 40 | @Before 41 | public void init() { 42 | initMatcher("otherid:two:8888", new String[] { "one", "three" }); 43 | } 44 | 45 | private void initMatcher(String id, String[] configNames) { 46 | BusProperties properties = new BusProperties(); 47 | properties.setId(id); 48 | DefaultBusPathMatcher pathMatcher = new DefaultBusPathMatcher(new AntPathMatcher(":")); 49 | this.matcher = new PathServiceMatcher(pathMatcher, properties.getId(), configNames); 50 | } 51 | 52 | @Test 53 | public void forSelfWithWildcard() { 54 | assertThat(this.matcher 55 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "one:two:*", EMPTY_MAP))) 56 | .isTrue(); 57 | } 58 | 59 | @Test 60 | public void forSelfWithWildcardAndOtherConfigName() { 61 | assertThat(this.matcher 62 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "three:two:*", EMPTY_MAP))) 63 | .isTrue(); 64 | } 65 | 66 | @Test 67 | public void forSelfWithGlobalWildcard() { 68 | assertThat(this.matcher 69 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "**", EMPTY_MAP))).isTrue(); 70 | } 71 | 72 | @Test 73 | public void forSelfWithWildcardName() { 74 | assertThat(this.matcher 75 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "o*", EMPTY_MAP))).isTrue(); 76 | } 77 | 78 | @Test 79 | public void forSelfWithWildcardNameAndProfile() { 80 | assertThat(this.matcher 81 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "o*:t*", EMPTY_MAP))).isTrue(); 82 | } 83 | 84 | @Test 85 | public void forSelfWithWildcardString() { 86 | assertThat(this.matcher 87 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "o*", EMPTY_MAP))).isTrue(); 88 | } 89 | 90 | @Test 91 | public void notForSelfWithWildCardNameAndMismatchingProfile() { 92 | assertThat(this.matcher 93 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "o*:f*", EMPTY_MAP))) 94 | .isFalse(); 95 | } 96 | 97 | @Test 98 | public void forSelfWithDoubleWildcard() { 99 | assertThat(this.matcher 100 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "one:**", EMPTY_MAP))) 101 | .isTrue(); 102 | } 103 | 104 | @Test 105 | public void forSelfWithNoWildcard() { 106 | assertThat(this.matcher 107 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "one", EMPTY_MAP))).isTrue(); 108 | } 109 | 110 | @Test 111 | public void forSelfWithProfileNoWildcard() { 112 | assertThat(this.matcher 113 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "one:two", EMPTY_MAP))) 114 | .isTrue(); 115 | } 116 | 117 | @Test 118 | public void notForSelf() { 119 | assertThat(this.matcher 120 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "one:two:9999", EMPTY_MAP))) 121 | .isFalse(); 122 | } 123 | 124 | @Test 125 | public void forSelfWithMultipleProfiles() { 126 | initMatcher("customerportal:dev,cloud:80", new String[] { "one", "three" }); 127 | assertThat(this.matcher 128 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "one:cloud:*", EMPTY_MAP))) 129 | .isTrue(); 130 | } 131 | 132 | @Test 133 | public void notForSelfWithMultipleProfiles() { 134 | initMatcher("customerportal:dev,cloud:80", new String[] { "one", "three" }); 135 | assertThat(this.matcher 136 | .isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", "bar:cloud:*", EMPTY_MAP))) 137 | .isFalse(); 138 | } 139 | 140 | @Test 141 | public void notForSelfWithMultipleProfilesDifferentPort() { 142 | initMatcher("customerportal:dev,cloud:80", new String[] { "one", "three" }); 143 | assertThat(this.matcher.isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, "foo:bar:spam", 144 | "customerportal:cloud:8008", EMPTY_MAP))) 145 | .isFalse(); 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/test/java/org/springframework/cloud/bus/RefreshListenerIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus; 18 | 19 | import java.util.HashMap; 20 | 21 | import org.junit.Test; 22 | import org.junit.runner.RunWith; 23 | 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.boot.autoconfigure.SpringBootApplication; 26 | import org.springframework.boot.test.context.SpringBootTest; 27 | import org.springframework.boot.test.mock.mockito.MockBean; 28 | import org.springframework.boot.test.web.client.TestRestTemplate; 29 | import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration; 30 | import org.springframework.context.annotation.Import; 31 | import org.springframework.http.HttpStatus; 32 | import org.springframework.test.context.junit4.SpringRunner; 33 | 34 | import static org.assertj.core.api.Assertions.assertThat; 35 | import static org.mockito.ArgumentMatchers.any; 36 | import static org.mockito.Mockito.times; 37 | import static org.mockito.Mockito.verify; 38 | 39 | /** 40 | * @author Ryan Baxter 41 | */ 42 | @RunWith(SpringRunner.class) 43 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, 44 | classes = RefreshListenerIntegrationTests.MyApp.class, 45 | properties = { "management.endpoints.web.exposure.include=*", "spring.application.name=foobar" }) 46 | public class RefreshListenerIntegrationTests { 47 | 48 | @Autowired 49 | private TestRestTemplate rest; 50 | 51 | @MockBean 52 | private BusBridge busBridge; 53 | 54 | @Test 55 | public void testEndpoint() { 56 | System.out.println(rest.getForObject("/actuator", String.class)); 57 | assertThat(rest.postForEntity("/actuator/busrefresh/demoapp", new HashMap<>(), String.class).getStatusCode()) 58 | .isEqualTo(HttpStatus.NO_CONTENT); 59 | assertThat(rest.postForEntity("/actuator/busrefresh/foobar", new HashMap<>(), String.class).getStatusCode()) 60 | .isEqualTo(HttpStatus.NO_CONTENT); 61 | verify(busBridge, times(2)).send(any()); 62 | } 63 | 64 | @SpringBootApplication 65 | @Import(TestChannelBinderConfiguration.class) 66 | static class MyApp { 67 | 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/test/java/org/springframework/cloud/bus/endpoint/RefreshBusEndpointTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.endpoint; 18 | 19 | import org.junit.Test; 20 | 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | 23 | /** 24 | * @author Dave Syer 25 | */ 26 | public class RefreshBusEndpointTests { 27 | 28 | @Test 29 | public void instanceId() throws Exception { 30 | RefreshBusEndpoint endpoint = new RefreshBusEndpoint(null, "foo", 31 | originalDestination -> () -> originalDestination); 32 | assertThat(endpoint.getInstanceId()).isEqualTo("foo"); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/test/java/org/springframework/cloud/bus/event/test/TestRemoteApplicationEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.event.test; 18 | 19 | import org.springframework.cloud.bus.event.RemoteApplicationEvent; 20 | 21 | @SuppressWarnings("serial") 22 | public class TestRemoteApplicationEvent extends RemoteApplicationEvent { 23 | 24 | @SuppressWarnings("unused") 25 | private TestRemoteApplicationEvent() { 26 | } 27 | 28 | protected TestRemoteApplicationEvent(Object source, String originService, String destinationService) { 29 | super(source, originService, destinationService); 30 | } 31 | 32 | protected TestRemoteApplicationEvent(Object source, String originService) { 33 | super(source, originService); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/test/java/org/springframework/cloud/bus/event/test/TypedRemoteApplicationEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.event.test; 18 | 19 | import com.fasterxml.jackson.annotation.JsonTypeName; 20 | 21 | import org.springframework.cloud.bus.event.RemoteApplicationEvent; 22 | 23 | @SuppressWarnings("serial") 24 | @JsonTypeName("typed") 25 | public class TypedRemoteApplicationEvent extends RemoteApplicationEvent { 26 | 27 | @SuppressWarnings("unused") 28 | private TypedRemoteApplicationEvent() { 29 | } 30 | 31 | protected TypedRemoteApplicationEvent(Object source, String originService, String destinationService) { 32 | super(source, originService, destinationService); 33 | } 34 | 35 | protected TypedRemoteApplicationEvent(Object source, String originService) { 36 | super(source, originService); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/test/java/org/springframework/cloud/bus/jackson/RemoteApplicationEventScanTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.jackson; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Arrays; 21 | import java.util.LinkedHashSet; 22 | import java.util.List; 23 | 24 | import com.fasterxml.jackson.databind.ObjectMapper; 25 | import com.fasterxml.jackson.databind.jsontype.NamedType; 26 | import org.junit.Test; 27 | import test.foo.bar.FooBarTestRemoteApplicationEvent; 28 | 29 | import org.springframework.boot.Banner; 30 | import org.springframework.boot.WebApplicationType; 31 | import org.springframework.boot.builder.SpringApplicationBuilder; 32 | import org.springframework.cloud.bus.event.AckRemoteApplicationEvent; 33 | import org.springframework.cloud.bus.event.EnvironmentChangeRemoteApplicationEvent; 34 | import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent; 35 | import org.springframework.cloud.bus.event.ShutdownRemoteApplicationEvent; 36 | import org.springframework.cloud.bus.event.UnknownRemoteApplicationEvent; 37 | import org.springframework.cloud.bus.event.test.TestRemoteApplicationEvent; 38 | import org.springframework.cloud.bus.event.test.TypedRemoteApplicationEvent; 39 | import org.springframework.cloud.bus.jackson.SubtypeModuleTests.AnotherRemoteApplicationEvent; 40 | import org.springframework.cloud.bus.jackson.SubtypeModuleTests.MyRemoteApplicationEvent; 41 | import org.springframework.context.ConfigurableApplicationContext; 42 | import org.springframework.context.annotation.Configuration; 43 | import org.springframework.context.annotation.Import; 44 | import org.springframework.test.util.ReflectionTestUtils; 45 | 46 | import static org.assertj.core.api.Assertions.assertThat; 47 | 48 | public class RemoteApplicationEventScanTests { 49 | 50 | private BusJacksonMessageConverter converter; 51 | 52 | @Test 53 | public void importingClassMetadataPackageRegistered() { 54 | this.converter = createTestContext(DefaultConfig.class).getBean(BusJacksonMessageConverter.class); 55 | 56 | assertConverterBeanAfterPropertiesSet( 57 | new String[] { "org.springframework.cloud.bus.jackson", "org.springframework.cloud.bus.event" }, 58 | AnotherRemoteApplicationEvent.class, MyRemoteApplicationEvent.class, TestRemoteApplicationEvent.class, 59 | TypedRemoteApplicationEvent.class); 60 | } 61 | 62 | @Test 63 | public void annotationValuePackagesRegistered() { 64 | this.converter = createTestContext(ValueConfig.class).getBean(BusJacksonMessageConverter.class); 65 | 66 | assertConverterBeanAfterPropertiesSet( 67 | new String[] { "test.foo.bar", "com.acme", "org.springframework.cloud.bus.event" }, 68 | FooBarTestRemoteApplicationEvent.class, TestRemoteApplicationEvent.class, 69 | TypedRemoteApplicationEvent.class); 70 | } 71 | 72 | @Test 73 | public void annotationValueBasePackagesRegistered() { 74 | this.converter = createTestContext(BasePackagesConfig.class).getBean(BusJacksonMessageConverter.class); 75 | 76 | assertConverterBeanAfterPropertiesSet( 77 | new String[] { "test.foo.bar", "fizz.buzz", "com.acme", "org.springframework.cloud.bus.event" }, 78 | FooBarTestRemoteApplicationEvent.class, TestRemoteApplicationEvent.class, 79 | TypedRemoteApplicationEvent.class); 80 | } 81 | 82 | @Test 83 | public void annotationBasePackagesRegistered() { 84 | this.converter = createTestContext(BasePackageClassesConfig.class).getBean(BusJacksonMessageConverter.class); 85 | 86 | assertConverterBeanAfterPropertiesSet( 87 | new String[] { "org.springframework.cloud.bus.event.test", "org.springframework.cloud.bus.event" }, 88 | TestRemoteApplicationEvent.class, TypedRemoteApplicationEvent.class); 89 | } 90 | 91 | private ConfigurableApplicationContext createTestContext(Class configuration) { 92 | return new SpringApplicationBuilder(configuration).web(WebApplicationType.NONE) 93 | .bannerMode(Banner.Mode.OFF) 94 | .run(); 95 | } 96 | 97 | private void assertConverterBeanAfterPropertiesSet(final String[] expectedPackageToScan, 98 | final Class... expectedRegisterdClasses) { 99 | final ObjectMapper mapper = (ObjectMapper) ReflectionTestUtils.getField(this.converter, "mapper"); 100 | 101 | @SuppressWarnings("unchecked") 102 | final LinkedHashSet registeredSubtypes = (LinkedHashSet) ReflectionTestUtils 103 | .getField(mapper.getSubtypeResolver(), "_registeredSubtypes"); 104 | 105 | final List> expectedRegisterdClassesAsList = new ArrayList<>(Arrays.asList(expectedRegisterdClasses)); 106 | addStandardSpringCloudEventBusEvents(expectedRegisterdClassesAsList); 107 | 108 | assertThat(expectedRegisterdClassesAsList.size() == registeredSubtypes.size()) 109 | .as("Wrong RemoteApplicationEvent classes are registerd in object mapper") 110 | .isTrue(); 111 | 112 | for (final NamedType namedType : registeredSubtypes) { 113 | assertThat(expectedRegisterdClassesAsList.contains(namedType.getType())).isTrue(); 114 | } 115 | 116 | assertThat(Arrays.asList((String[]) ReflectionTestUtils.getField(this.converter, "packagesToScan"))) 117 | .as("RemoteApplicationEvent packages not registered") 118 | .contains(expectedPackageToScan); 119 | 120 | } 121 | 122 | private void addStandardSpringCloudEventBusEvents(final List> expectedRegisterdClassesAsList) { 123 | expectedRegisterdClassesAsList.add(AckRemoteApplicationEvent.class); 124 | expectedRegisterdClassesAsList.add(EnvironmentChangeRemoteApplicationEvent.class); 125 | expectedRegisterdClassesAsList.add(RefreshRemoteApplicationEvent.class); 126 | expectedRegisterdClassesAsList.add(UnknownRemoteApplicationEvent.class); 127 | expectedRegisterdClassesAsList.add(ShutdownRemoteApplicationEvent.class); 128 | } 129 | 130 | @Configuration(proxyBeanMethods = false) 131 | @RemoteApplicationEventScan 132 | static class DefaultConfig { 133 | 134 | } 135 | 136 | @Configuration(proxyBeanMethods = false) 137 | @RemoteApplicationEventScan({ "com.acme", "test.foo.bar" }) 138 | static class ValueConfig { 139 | 140 | } 141 | 142 | @Configuration(proxyBeanMethods = false) 143 | @Import(ExtraBasePackagesConfig.class) 144 | @RemoteApplicationEventScan(basePackages = { "com.acme", "test.foo.bar" }) 145 | static class BasePackagesConfig { 146 | 147 | } 148 | 149 | @Configuration(proxyBeanMethods = false) 150 | @RemoteApplicationEventScan(basePackages = { "fizz.buzz" }) 151 | static class ExtraBasePackagesConfig { 152 | 153 | } 154 | 155 | @Configuration(proxyBeanMethods = false) 156 | @RemoteApplicationEventScan(basePackageClasses = TestRemoteApplicationEvent.class) 157 | static class BasePackageClassesConfig { 158 | 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/test/java/org/springframework/cloud/bus/jackson/SerializationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.cloud.bus.jackson; 18 | 19 | import java.util.Collections; 20 | 21 | import com.fasterxml.jackson.databind.ObjectMapper; 22 | import org.junit.Test; 23 | 24 | import org.springframework.cloud.bus.event.EnvironmentChangeRemoteApplicationEvent; 25 | import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent; 26 | import org.springframework.cloud.bus.event.RemoteApplicationEvent; 27 | 28 | import static org.assertj.core.api.Assertions.assertThat; 29 | 30 | /** 31 | * @author Dave Syer 32 | * 33 | */ 34 | public class SerializationTests { 35 | 36 | ObjectMapper mapper = new ObjectMapper(); 37 | 38 | @Test 39 | public void vanillaDeserialize() throws Exception { 40 | this.mapper.registerModule( 41 | new SubtypeModule(RefreshRemoteApplicationEvent.class, EnvironmentChangeRemoteApplicationEvent.class)); 42 | EnvironmentChangeRemoteApplicationEvent source = new EnvironmentChangeRemoteApplicationEvent(this, "foo", "bar", 43 | Collections.emptyMap()); 44 | String value = this.mapper.writeValueAsString(source); 45 | RemoteApplicationEvent event = this.mapper.readValue(value, RemoteApplicationEvent.class); 46 | assertThat(event instanceof EnvironmentChangeRemoteApplicationEvent).isTrue(); 47 | assertThat(event.getId()).isNotNull(); 48 | assertThat(event.getId().equals(source.getId())).isTrue(); 49 | } 50 | 51 | @Test 52 | public void deserializeOldValueWithNoId() throws Exception { 53 | this.mapper.registerModule( 54 | new SubtypeModule(RefreshRemoteApplicationEvent.class, EnvironmentChangeRemoteApplicationEvent.class)); 55 | EnvironmentChangeRemoteApplicationEvent source = new EnvironmentChangeRemoteApplicationEvent(this, "foo", "bar", 56 | Collections.emptyMap()); 57 | String value = this.mapper.writeValueAsString(source); 58 | value = value.replaceAll(",\"id\":\"[a-f0-9-]*\"", ""); 59 | RemoteApplicationEvent event = this.mapper.readValue(value, RemoteApplicationEvent.class); 60 | assertThat(event instanceof EnvironmentChangeRemoteApplicationEvent).isTrue(); 61 | assertThat(event.getId()).isNotNull(); 62 | assertThat(event.getId().equals(source.getId())).isFalse(); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/test/java/test/foo/bar/FooBarTestRemoteApplicationEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package test.foo.bar; 18 | 19 | import org.springframework.cloud.bus.event.RemoteApplicationEvent; 20 | 21 | @SuppressWarnings("serial") 22 | public class FooBarTestRemoteApplicationEvent extends RemoteApplicationEvent { 23 | 24 | @SuppressWarnings("unused") 25 | private FooBarTestRemoteApplicationEvent() { 26 | } 27 | 28 | protected FooBarTestRemoteApplicationEvent(final Object source, final String originService, 29 | final String destinationService) { 30 | super(source, originService, destinationService); 31 | } 32 | 33 | protected FooBarTestRemoteApplicationEvent(final Object source, final String originService) { 34 | super(source, originService); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /spring-cloud-bus/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.main.web-environment:false 2 | -------------------------------------------------------------------------------- /spring-cloud-starter-bus-amqp/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.cloud 8 | spring-cloud-bus-parent 9 | 5.0.0-SNAPSHOT 10 | .. 11 | 12 | spring-cloud-starter-bus-amqp 13 | spring-cloud-starter-bus-amqp 14 | Spring Cloud Starter 15 | https://projects.spring.io/spring-cloud 16 | 17 | Pivotal Software, Inc. 18 | https://www.spring.io 19 | 20 | 21 | ${basedir}/../.. 22 | 23 | 24 | 25 | org.springframework.cloud 26 | spring-cloud-starter-stream-rabbit 27 | 28 | 29 | org.springframework.cloud 30 | spring-cloud-bus 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /spring-cloud-starter-bus-kafka/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.cloud 8 | spring-cloud-bus-parent 9 | 5.0.0-SNAPSHOT 10 | .. 11 | 12 | spring-cloud-starter-bus-kafka 13 | spring-cloud-starter-bus-kafka 14 | Spring Cloud Starter 15 | https://projects.spring.io/spring-cloud 16 | 17 | Pivotal Software, Inc. 18 | https://www.spring.io 19 | 20 | 21 | ${basedir}/../.. 22 | 23 | 24 | 25 | org.springframework.cloud 26 | spring-cloud-starter-stream-kafka 27 | 28 | 29 | org.springframework.cloud 30 | spring-cloud-bus 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /spring-cloud-starter-bus-stream/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.cloud 8 | spring-cloud-bus-parent 9 | 5.0.0-SNAPSHOT 10 | .. 11 | 12 | spring-cloud-starter-bus-stream 13 | spring-cloud-starter-bus-stream 14 | Spring Cloud Starter 15 | https://projects.spring.io/spring-cloud 16 | 17 | Pivotal Software, Inc. 18 | https://www.spring.io 19 | 20 | 21 | ${basedir}/../.. 22 | 23 | 24 | 25 | org.springframework.cloud 26 | spring-cloud-stream 27 | 28 | 29 | org.springframework.cloud 30 | spring-cloud-bus 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/checkstyle/checkstyle-suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | --------------------------------------------------------------------------------